]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/login/pam_systemd.c
pam_systemd: do no allocate the path of fixed length
[thirdparty/systemd.git] / src / login / pam_systemd.c
index 8c5afeb1f7398cc9470a21c63dab7c8e8cca78c1..a33d8f05c50fee73960af5dbed0680a15daa97f7 100644 (file)
@@ -28,6 +28,7 @@
 #include "path-util.h"
 #include "process-util.h"
 #include "socket-util.h"
+#include "stdio-util.h"
 #include "strv.h"
 #include "terminal-util.h"
 #include "util.h"
@@ -114,14 +115,37 @@ static int get_user_data(
         return PAM_SUCCESS;
 }
 
+static int socket_from_display(const char *display, char **path) {
+        size_t k;
+        char *f, *c;
+
+        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);
+        if (!f)
+                return -ENOMEM;
+
+        c = stpcpy(f, "/tmp/.X11-unix/X");
+        memcpy(c, display+1, k);
+        c[k] = 0;
+
+        *path = f;
+
+        return 0;
+}
+
 static int get_seat_from_display(const char *display, const char **seat, uint32_t *vtnr) {
-        union sockaddr_union sa = {
-                .un.sun_family = AF_UNIX,
-        };
+        union sockaddr_union sa = {};
         _cleanup_free_ char *p = NULL, *tty = NULL;
         _cleanup_close_ int fd = -1;
         struct ucred ucred;
-        int v, r;
+        int v, r, salen;
 
         assert(display);
         assert(vtnr);
@@ -135,13 +159,15 @@ static int get_seat_from_display(const char *display, const char **seat, uint32_
         r = socket_from_display(display, &p);
         if (r < 0)
                 return r;
-        strncpy(sa.un.sun_path, p, sizeof(sa.un.sun_path)-1);
+        salen = sockaddr_un_set_path(&sa.un, p);
+        if (salen < 0)
+                return salen;
 
         fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
         if (fd < 0)
                 return -errno;
 
-        if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0)
+        if (connect(fd, &sa.sa, salen) < 0)
                 return -errno;
 
         r = getpeercred(fd, &ucred);
@@ -165,6 +191,28 @@ static int get_seat_from_display(const char *display, const char **seat, uint32_
         return 0;
 }
 
+static int export_legacy_dbus_address(
+                pam_handle_t *handle,
+                uid_t uid,
+                const char *runtime) {
+
+        _cleanup_free_ char *s = NULL;
+        int r = PAM_BUF_ERR;
+
+        if (asprintf(&s, DEFAULT_USER_BUS_ADDRESS_FMT, runtime) < 0)
+                goto error;
+
+        r = pam_misc_setenv(handle, "DBUS_SESSION_BUS_ADDRESS", s, 0);
+        if (r != PAM_SUCCESS)
+                goto error;
+
+        return PAM_SUCCESS;
+
+error:
+        pam_syslog(handle, LOG_ERR, "Failed to set bus variable.");
+        return r;
+}
+
 static int append_session_memory_max(pam_handle_t *handle, sd_bus_message *m, const char *limit) {
         uint64_t val;
         int r;
@@ -179,9 +227,9 @@ static int append_session_memory_max(pam_handle_t *handle, sd_bus_message *m, co
                         return r;
                 }
         } else {
-                r = parse_percent(limit);
+                r = parse_permille(limit);
                 if (r >= 0) {
-                        r = sd_bus_message_append(m, "(sv)", "MemoryMaxScale", "u", (uint32_t) (((uint64_t) UINT32_MAX * r) / 100U));
+                        r = sd_bus_message_append(m, "(sv)", "MemoryMaxScale", "u", (uint32_t) (((uint64_t) r * UINT32_MAX) / 1000U));
                         if (r < 0) {
                                 pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror(-r));
                                 return r;
@@ -227,19 +275,20 @@ static int append_session_cg_weight(pam_handle_t *handle, sd_bus_message *m, con
         uint64_t val;
         int r;
 
-        if (!isempty(limit)) {
-                r = cg_weight_parse(limit, &val);
-                if (r >= 0) {
-                        r = sd_bus_message_append(m, "(sv)", field, "t", val);
-                        if (r < 0) {
-                                pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror(-r));
-                                return r;
-                        }
-                } else if (streq(field, "CPUWeight"))
-                        pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.cpu_weight: %s, ignoring.", limit);
-                else
-                        pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.io_weight: %s, ignoring.", limit);
-        }
+        if (isempty(limit))
+                return 0;
+
+        r = cg_weight_parse(limit, &val);
+        if (r >= 0) {
+                r = sd_bus_message_append(m, "(sv)", field, "t", val);
+                if (r < 0) {
+                        pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror(-r));
+                        return r;
+                }
+        } else if (streq(field, "CPUWeight"))
+                pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.cpu_weight: %s, ignoring.", limit);
+        else
+                pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.io_weight: %s, ignoring.", limit);
 
         return 0;
 }
@@ -282,6 +331,36 @@ static int update_environment(pam_handle_t *handle, const char *key, const char
         return r;
 }
 
+static bool validate_runtime_directory(pam_handle_t *handle, const char *path, uid_t uid) {
+        struct stat st;
+
+        assert(path);
+
+        /* Just some extra paranoia: let's not set $XDG_RUNTIME_DIR if the directory we'd set it to isn't actually set
+         * up properly for us. */
+
+        if (lstat(path, &st) < 0) {
+                pam_syslog(handle, LOG_ERR, "Failed to stat() runtime directory '%s': %s", path, strerror(errno));
+                goto fail;
+        }
+
+        if (!S_ISDIR(st.st_mode)) {
+                pam_syslog(handle, LOG_ERR, "Runtime directory '%s' is not actually a directory.", path);
+                goto fail;
+        }
+
+        if (st.st_uid != uid) {
+                pam_syslog(handle, LOG_ERR, "Runtime directory '%s' is not owned by UID " UID_FMT ", as it should.", path, uid);
+                goto fail;
+        }
+
+        return true;
+
+fail:
+        pam_syslog(handle, LOG_WARNING, "Not setting $XDG_RUNTIME_DIR, as the directory is not in order.");
+        return false;
+}
+
 _public_ PAM_EXTERN int pam_sm_open_session(
                 pam_handle_t *handle,
                 int flags,
@@ -336,16 +415,20 @@ _public_ PAM_EXTERN int pam_sm_open_session(
 
         pam_get_item(handle, PAM_SERVICE, (const void**) &service);
         if (streq_ptr(service, "systemd-user")) {
-                _cleanup_free_ char *rt = NULL;
+                char rt[STRLEN("/run/user/") + DECIMAL_STR_MAX(uid_t)];
 
-                if (asprintf(&rt, "/run/user/"UID_FMT, pw->pw_uid) < 0)
-                        return PAM_BUF_ERR;
+                xsprintf(rt, "/run/user/"UID_FMT, pw->pw_uid);
+                if (validate_runtime_directory(handle, rt, pw->pw_uid)) {
+                        r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0);
+                        if (r != PAM_SUCCESS) {
+                                pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
+                                return r;
+                        }
+                }
 
-                r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0);
-                if (r != PAM_SUCCESS) {
-                        pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
+                r = export_legacy_dbus_address(handle, pw->pw_uid, rt);
+                if (r != PAM_SUCCESS)
                         return r;
-                }
 
                 return PAM_SUCCESS;
         }
@@ -402,13 +485,14 @@ _public_ PAM_EXTERN int pam_sm_open_session(
 
         if (!isempty(display) && !vtnr) {
                 if (isempty(seat))
-                        get_seat_from_display(display, &seat, &vtnr);
+                        (void) get_seat_from_display(display, &seat, &vtnr);
                 else if (streq(seat, "seat0"))
-                        get_seat_from_display(display, NULL, &vtnr);
+                        (void) get_seat_from_display(display, NULL, &vtnr);
         }
 
         if (seat && !streq(seat, "seat0") && vtnr != 0) {
-                pam_syslog(handle, LOG_DEBUG, "Ignoring vtnr %"PRIu32" for %s which is not seat0", vtnr, seat);
+                if (debug)
+                        pam_syslog(handle, LOG_DEBUG, "Ignoring vtnr %"PRIu32" for %s which is not seat0", vtnr, seat);
                 vtnr = 0;
         }
 
@@ -461,7 +545,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
 
         r = sd_bus_message_append(m, "uusssssussbss",
                         (uint32_t) pw->pw_uid,
-                        (uint32_t) getpid_cached(),
+                        0,
                         service,
                         type,
                         class,
@@ -509,7 +593,8 @@ _public_ PAM_EXTERN int pam_sm_open_session(
         r = sd_bus_call(bus, m, 0, &error, &reply);
         if (r < 0) {
                 if (sd_bus_error_has_name(&error, BUS_ERROR_SESSION_BUSY)) {
-                        pam_syslog(handle, LOG_DEBUG, "Cannot create session: %s", bus_error_message(&error, r));
+                        if (debug)
+                                pam_syslog(handle, LOG_DEBUG, "Cannot create session: %s", bus_error_message(&error, r));
                         return PAM_SUCCESS;
                 } else {
                         pam_syslog(handle, LOG_ERR, "Failed to create session: %s", bus_error_message(&error, r));
@@ -548,7 +633,13 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                  * in privileged apps clobbering the runtime directory
                  * unnecessarily. */
 
-                r = update_environment(handle, "XDG_RUNTIME_DIR", runtime_path);
+                if (validate_runtime_directory(handle, runtime_path, pw->pw_uid)) {
+                        r = update_environment(handle, "XDG_RUNTIME_DIR", runtime_path);
+                        if (r != PAM_SUCCESS)
+                                return r;
+                }
+
+                r = export_legacy_dbus_address(handle, pw->pw_uid, runtime_path);
                 if (r != PAM_SUCCESS)
                         return r;
         }
@@ -621,7 +712,7 @@ _public_ PAM_EXTERN int pam_sm_close_session(
 
         /* Only release session if it wasn't pre-existing when we
          * tried to create it */
-        pam_get_data(handle, "systemd.existing", &existing);
+        (void) pam_get_data(handle, "systemd.existing", &existing);
 
         id = pam_getenv(handle, "XDG_SESSION_ID");
         if (id && !existing) {