]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
pam_systemd: Check also abstract socket for X11
authorTopi Miettinen <toiwoton@gmail.com>
Fri, 26 Nov 2021 14:34:48 +0000 (16:34 +0200)
committerLuca Boccassi <luca.boccassi@gmail.com>
Sun, 28 Nov 2021 18:24:39 +0000 (18:24 +0000)
It seems that `pam_systemd` was the only thing left that wanted to use sockets
in file system path `/tmp/.X11-unix/X*`. X11 apps actually prefer using the
abstract socket version.

This allows running Xserver with `-nolisten tcp -nolisten unix`, which makes
the server only listen to an abstract socket.

Also in my setup, Xserver is running as a separate system service instead of
starting from display manager service, and now `PrivateTmp=yes` can be used for
both. The file system of the display manager service is inherited by user apps
and now their `/tmp` will be separate from `/tmp` of PID1 namespace as well as
`/tmp` of Xserver.

src/login/pam_systemd.c

index 1b643d52ca95a8e95f52818ff6cf2252e7ff3db8..5bd7efc3e87f5796d6da0ac740afa4da4d0fc886 100644 (file)
@@ -199,35 +199,60 @@ static bool display_is_local(const char *display) {
                 display[1] <= '9';
 }
 
-static int socket_from_display(const char *display, char **path) {
+static int socket_from_display(const char *display) {
+        _cleanup_free_ char *f = NULL;
         size_t k;
-        char *f, *c;
+        char *c;
+        union sockaddr_union sa;
+        socklen_t sa_len;
+        _cleanup_close_ int fd = -1;
+        int r;
 
         assert(display);
-        assert(path);
 
         if (!display_is_local(display))
                 return -EINVAL;
 
         k = strspn(display+1, "0123456789");
 
-        f = new(char, STRLEN("/tmp/.X11-unix/X") + k + 1);
+        /* Try abstract socket first. */
+        f = new(char, STRLEN("@/tmp/.X11-unix/X") + k + 1);
         if (!f)
                 return -ENOMEM;
 
-        c = stpcpy(f, "/tmp/.X11-unix/X");
+        c = stpcpy(f, "@/tmp/.X11-unix/X");
         memcpy(c, display+1, k);
         c[k] = 0;
 
-        *path = f;
+        r = sockaddr_un_set_path(&sa.un, f);
+        if (r < 0)
+                return r;
+        sa_len = r;
+
+        fd = RET_NERRNO(socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0));
+        if (fd < 0)
+                return fd;
 
-        return 0;
+        r = RET_NERRNO(connect(fd, &sa.sa, sa_len));
+        if (r >= 0)
+                return TAKE_FD(fd);
+        if (r != -ECONNREFUSED)
+                return r;
+
+        /* Try also non-abstract socket. */
+        r = sockaddr_un_set_path(&sa.un, f + 1);
+        if (r < 0)
+                return r;
+        sa_len = r;
+
+        r = RET_NERRNO(connect(fd, &sa.sa, sa_len));
+        if (r >= 0)
+                return TAKE_FD(fd);
+        return r;
 }
 
 static int get_seat_from_display(const char *display, const char **seat, uint32_t *vtnr) {
-        union sockaddr_union sa;
-        socklen_t sa_len;
-        _cleanup_free_ char *p = NULL, *sys_path = NULL, *tty = NULL;
+        _cleanup_free_ char *sys_path = NULL, *tty = NULL;
         _cleanup_close_ int fd = -1;
         struct ucred ucred;
         int v, r;
@@ -242,20 +267,9 @@ static int get_seat_from_display(const char *display, const char **seat, uint32_
          * the seat and the virtual terminal. Sounds ugly, is only
          * semi-ugly. */
 
-        r = socket_from_display(display, &p);
-        if (r < 0)
-                return r;
-        r = sockaddr_un_set_path(&sa.un, p);
-        if (r < 0)
-                return r;
-        sa_len = r;
-
-        fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
+        fd = socket_from_display(display);
         if (fd < 0)
-                return -errno;
-
-        if (connect(fd, &sa.sa, sa_len) < 0)
-                return -errno;
+                return fd;
 
         r = getpeercred(fd, &ucred);
         if (r < 0)