]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/login/pam_systemd.c
pam-systemd: include PAM error code in all our log messages where that makes sense
[thirdparty/systemd.git] / src / login / pam_systemd.c
index e2b2281c6b4018ae930e220f37e91d0e77da9895..ef8a599122e74213ae15b53bd5159a6c7e06ea37 100644 (file)
 #include <security/pam_modules.h>
 #include <security/pam_modutil.h>
 #include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 #include "alloc-util.h"
 #include "audit-util.h"
 #include "bus-common-errors.h"
 #include "bus-error.h"
+#include "bus-internal.h"
 #include "bus-util.h"
-#include "cgroup-util.h"
-#include "def.h"
+#include "cgroup-setup.h"
+#include "errno-util.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "format-util.h"
@@ -28,9 +32,9 @@
 #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"
 
 static int parse_argv(
                 pam_handle_t *handle,
@@ -46,28 +50,30 @@ static int parse_argv(
         assert(argc == 0 || argv);
 
         for (i = 0; i < (unsigned) argc; i++) {
-                if (startswith(argv[i], "class=")) {
+                const char *p;
+
+                if ((p = startswith(argv[i], "class="))) {
                         if (class)
-                                *class = argv[i] + 6;
+                                *class = p;
 
-                } else if (startswith(argv[i], "type=")) {
+                } else if ((p = startswith(argv[i], "type="))) {
                         if (type)
-                                *type = argv[i] + 5;
+                                *type = p;
 
-                } else if (startswith(argv[i], "desktop=")) {
+                } else if ((p = startswith(argv[i], "desktop="))) {
                         if (desktop)
-                                *desktop = argv[i] + 8;
+                                *desktop = p;
 
                 } else if (streq(argv[i], "debug")) {
                         if (debug)
                                 *debug = true;
 
-                } else if (startswith(argv[i], "debug=")) {
+                } else if ((p = startswith(argv[i], "debug="))) {
                         int k;
 
-                        k = parse_boolean(argv[i] + 6);
+                        k = parse_boolean(p);
                         if (k < 0)
-                                pam_syslog(handle, LOG_WARNING, "Failed to parse debug= argument, ignoring.");
+                                pam_syslog(handle, LOG_WARNING, "Failed to parse debug= argument, ignoring: %s", p);
                         else if (debug)
                                 *debug = k;
 
@@ -93,7 +99,7 @@ static int get_user_data(
 
         r = pam_get_user(handle, &username, NULL);
         if (r != PAM_SUCCESS) {
-                pam_syslog(handle, LOG_ERR, "Failed to get user name.");
+                pam_syslog(handle, LOG_ERR, "Failed to get user name: %s", pam_strerror(handle, r));
                 return r;
         }
 
@@ -114,6 +120,15 @@ static int get_user_data(
         return PAM_SUCCESS;
 }
 
+static bool display_is_local(const char *display) {
+        assert(display);
+
+        return
+                display[0] == ':' &&
+                display[1] >= '0' &&
+                display[1] <= '9';
+}
+
 static int socket_from_display(const char *display, char **path) {
         size_t k;
         char *f, *c;
@@ -190,6 +205,45 @@ 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) {
+
+        const char *s;
+        _cleanup_free_ char *t = NULL;
+        int r = PAM_BUF_ERR;
+
+        /* We need to export $DBUS_SESSION_BUS_ADDRESS because various applications will not connect
+         * correctly to the bus without it. This setting matches what dbus.socket does for the user
+         * session using 'systemctl --user set-environment'. We want to have the same configuration
+         * in processes started from the PAM session.
+         *
+         * The setting of the address is guarded by the access() check because it is also possible to compile
+         * dbus without --enable-user-session, in which case this socket is not used, and
+         * $DBUS_SESSION_BUS_ADDRESS should not be set. An alternative approach would to not do the access()
+         * check here, and let applications try on their own, by using "unix:path=%s/bus;autolaunch:". But we
+         * expect the socket to be present by the time we do this check, so we can just as well check once
+         * here. */
+
+        s = strjoina(runtime, "/bus");
+        if (access(s, F_OK) < 0)
+                return PAM_SUCCESS;
+
+        if (asprintf(&t, DEFAULT_USER_BUS_ADDRESS_FMT, runtime) < 0)
+                goto error;
+
+        r = pam_misc_setenv(handle, "DBUS_SESSION_BUS_ADDRESS", t, 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;
@@ -200,7 +254,7 @@ static int append_session_memory_max(pam_handle_t *handle, sd_bus_message *m, co
         if (streq(limit, "infinity")) {
                 r = sd_bus_message_append(m, "(sv)", "MemoryMax", "t", (uint64_t)-1);
                 if (r < 0) {
-                        pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror(-r));
+                        pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
                         return r;
                 }
         } else {
@@ -208,7 +262,7 @@ static int append_session_memory_max(pam_handle_t *handle, sd_bus_message *m, co
                 if (r >= 0) {
                         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));
+                                pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
                                 return r;
                         }
                 } else {
@@ -216,17 +270,38 @@ static int append_session_memory_max(pam_handle_t *handle, sd_bus_message *m, co
                         if (r >= 0) {
                                 r = sd_bus_message_append(m, "(sv)", "MemoryMax", "t", val);
                                 if (r < 0) {
-                                        pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror(-r));
+                                        pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
                                         return r;
                                 }
                         } else
-                                pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.limit: %s, ignoring.", limit);
+                                pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.memory_max: %s, ignoring.", limit);
                 }
         }
 
         return 0;
 }
 
+static int append_session_runtime_max_sec(pam_handle_t *handle, sd_bus_message *m, const char *limit) {
+        usec_t val;
+        int r;
+
+        /* No need to parse "infinity" here, it will be set by default later in scope_init() */
+        if (isempty(limit) || streq(limit, "infinity"))
+                return 0;
+
+        r = parse_sec(limit, &val);
+        if (r >= 0) {
+                r = sd_bus_message_append(m, "(sv)", "RuntimeMaxUSec", "t", (uint64_t) val);
+                if (r < 0) {
+                        pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
+                        return r;
+                }
+        } else
+                pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.runtime_max_sec: %s, ignoring.", limit);
+
+        return 0;
+}
+
 static int append_session_tasks_max(pam_handle_t *handle, sd_bus_message *m, const char *limit) {
         uint64_t val;
         int r;
@@ -239,11 +314,11 @@ static int append_session_tasks_max(pam_handle_t *handle, sd_bus_message *m, con
         if (r >= 0) {
                 r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", val);
                 if (r < 0) {
-                        pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror(-r));
+                        pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
                         return r;
                 }
         } else
-                pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.limit: %s, ignoring.", limit);
+                pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.tasks_max: %s, ignoring.", limit);
 
         return 0;
 }
@@ -259,7 +334,7 @@ static int append_session_cg_weight(pam_handle_t *handle, sd_bus_message *m, con
         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));
+                        pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
                         return r;
                 }
         } else if (streq(field, "CPUWeight"))
@@ -276,14 +351,21 @@ static const char* getenv_harder(pam_handle_t *handle, const char *key, const ch
         assert(handle);
         assert(key);
 
-        /* Looks for an environment variable, preferrably in the environment block associated with the specified PAM
-         * handle, falling back to the process' block instead. */
+        /* Looks for an environment variable, preferably in the environment block associated with the
+         * specified PAM handle, falling back to the process' block instead. Why check both? Because we want
+         * to permit configuration of session properties from unit files that invoke PAM services, so that
+         * PAM services don't have to be reworked to set systemd-specific properties, but these properties
+         * can still be set from the unit file Environment= block. */
 
         v = pam_getenv(handle, key);
         if (!isempty(v))
                 return v;
 
-        v = getenv(key);
+        /* We use secure_getenv() here, since we might get loaded into su/sudo, which are SUID. Ideally
+         * they'd clean up the environment before invoking foreign code (such as PAM modules), but alas they
+         * currently don't (to be precise, they clean up the environment they pass to their children, but
+         * not their own environ[]). */
+        v = secure_getenv(key);
         if (!isempty(v))
                 return v;
 
@@ -303,7 +385,7 @@ static int update_environment(pam_handle_t *handle, const char *key, const char
 
         r = pam_misc_setenv(handle, key, value, 0);
         if (r != PAM_SUCCESS)
-                pam_syslog(handle, LOG_ERR, "Failed to set environment variable %s.", key);
+                pam_syslog(handle, LOG_ERR, "Failed to set environment variable %s: %s", key, pam_strerror(handle, r));
 
         return r;
 }
@@ -317,7 +399,7 @@ static bool validate_runtime_directory(pam_handle_t *handle, const char *path, u
          * up properly for us. */
 
         if (lstat(path, &st) < 0) {
-                pam_syslog(handle, LOG_ERR, "Failed to stat() runtime directory '%s': %s", path, strerror(errno));
+                pam_syslog(handle, LOG_ERR, "Failed to stat() runtime directory '%s': %s", path, strerror_safe(errno));
                 goto fail;
         }
 
@@ -353,7 +435,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                 *seat = NULL,
                 *type = NULL, *class = NULL,
                 *class_pam = NULL, *type_pam = NULL, *cvtnr = NULL, *desktop = NULL, *desktop_pam = NULL,
-                *memory_max = NULL, *tasks_max = NULL, *cpu_weight = NULL, *io_weight = NULL;
+                *memory_max = NULL, *tasks_max = NULL, *cpu_weight = NULL, *io_weight = NULL, *runtime_max_sec = NULL;
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         int session_fd = -1, existing, r;
         bool debug = false, remote;
@@ -392,19 +474,21 @@ _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;
-
-                if (asprintf(&rt, "/run/user/"UID_FMT, pw->pw_uid) < 0)
-                        return PAM_BUF_ERR;
+                char rt[STRLEN("/run/user/") + DECIMAL_STR_MAX(uid_t)];
 
+                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.");
+                                pam_syslog(handle, LOG_ERR, "Failed to set runtime dir: %s", pam_strerror(handle, r));
                                 return r;
                         }
                 }
 
+                r = export_legacy_dbus_address(handle, pw->pw_uid, rt);
+                if (r != PAM_SUCCESS)
+                        return r;
+
                 return PAM_SUCCESS;
         }
 
@@ -466,7 +550,8 @@ _public_ PAM_EXTERN int pam_sm_open_session(
         }
 
         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;
         }
 
@@ -483,12 +568,13 @@ _public_ PAM_EXTERN int pam_sm_open_session(
         (void) pam_get_data(handle, "systemd.tasks_max",  (const void **)&tasks_max);
         (void) pam_get_data(handle, "systemd.cpu_weight", (const void **)&cpu_weight);
         (void) pam_get_data(handle, "systemd.io_weight",  (const void **)&io_weight);
+        (void) pam_get_data(handle, "systemd.runtime_max_sec", (const void **)&runtime_max_sec);
 
         /* Talk to logind over the message bus */
 
         r = sd_bus_open_system(&bus);
         if (r < 0) {
-                pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror(-r));
+                pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror_safe(r));
                 return PAM_SESSION_ERR;
         }
 
@@ -501,8 +587,8 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                            strempty(seat), vtnr, strempty(tty), strempty(display),
                            yes_no(remote), strempty(remote_user), strempty(remote_host));
                 pam_syslog(handle, LOG_DEBUG, "Session limits: "
-                           "memory_max=%s tasks_max=%s cpu_weight=%s io_weight=%s",
-                           strna(memory_max), strna(tasks_max), strna(cpu_weight), strna(io_weight));
+                           "memory_max=%s tasks_max=%s cpu_weight=%s io_weight=%s runtime_max_sec=%s",
+                           strna(memory_max), strna(tasks_max), strna(cpu_weight), strna(io_weight), strna(runtime_max_sec));
         }
 
         r = sd_bus_message_new_method_call(
@@ -513,7 +599,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                         "org.freedesktop.login1.Manager",
                         "CreateSession");
         if (r < 0) {
-                pam_syslog(handle, LOG_ERR, "Failed to create CreateSession method call: %s", strerror(-r));
+                pam_syslog(handle, LOG_ERR, "Failed to create CreateSession method call: %s", strerror_safe(r));
                 return PAM_SESSION_ERR;
         }
 
@@ -532,13 +618,13 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                         remote_user,
                         remote_host);
         if (r < 0) {
-                pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror(-r));
+                pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
                 return PAM_SESSION_ERR;
         }
 
         r = sd_bus_message_open_container(m, 'a', "(sv)");
         if (r < 0) {
-                pam_syslog(handle, LOG_ERR, "Failed to open message container: %s", strerror(-r));
+                pam_syslog(handle, LOG_ERR, "Failed to open message container: %s", strerror_safe(r));
                 return PAM_SYSTEM_ERR;
         }
 
@@ -546,6 +632,10 @@ _public_ PAM_EXTERN int pam_sm_open_session(
         if (r < 0)
                 return PAM_SESSION_ERR;
 
+        r = append_session_runtime_max_sec(handle, m, runtime_max_sec);
+        if (r < 0)
+                return PAM_SESSION_ERR;
+
         r = append_session_tasks_max(handle, m, tasks_max);
         if (r < 0)
                 return PAM_SESSION_ERR;
@@ -560,14 +650,15 @@ _public_ PAM_EXTERN int pam_sm_open_session(
 
         r = sd_bus_message_close_container(m);
         if (r < 0) {
-                pam_syslog(handle, LOG_ERR, "Failed to close message container: %s", strerror(-r));
+                pam_syslog(handle, LOG_ERR, "Failed to close message container: %s", strerror_safe(r));
                 return PAM_SYSTEM_ERR;
         }
 
         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, "Not creating 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));
@@ -586,7 +677,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                                 &vtnr,
                                 &existing);
         if (r < 0) {
-                pam_syslog(handle, LOG_ERR, "Failed to parse message: %s", strerror(-r));
+                pam_syslog(handle, LOG_ERR, "Failed to parse message: %s", strerror_safe(r));
                 return PAM_SESSION_ERR;
         }
 
@@ -611,6 +702,10 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                         if (r != PAM_SUCCESS)
                                 return r;
                 }
+
+                r = export_legacy_dbus_address(handle, pw->pw_uid, runtime_path);
+                if (r != PAM_SUCCESS)
+                        return r;
         }
 
         /* Most likely we got the session/type/class from environment variables, but might have gotten the data
@@ -644,7 +739,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
 
         r = pam_set_data(handle, "systemd.existing", INT_TO_PTR(!!existing), NULL);
         if (r != PAM_SUCCESS) {
-                pam_syslog(handle, LOG_ERR, "Failed to install existing flag.");
+                pam_syslog(handle, LOG_ERR, "Failed to install existing flag: %s", pam_strerror(handle, r));
                 return r;
         }
 
@@ -657,7 +752,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
 
                 r = pam_set_data(handle, "systemd.session-fd", FD_TO_PTR(session_fd), NULL);
                 if (r != PAM_SUCCESS) {
-                        pam_syslog(handle, LOG_ERR, "Failed to install session fd.");
+                        pam_syslog(handle, LOG_ERR, "Failed to install session fd: %s", pam_strerror(handle, r));
                         safe_close(session_fd);
                         return r;
                 }
@@ -693,7 +788,7 @@ _public_ PAM_EXTERN int pam_sm_close_session(
 
                 r = sd_bus_open_system(&bus);
                 if (r < 0) {
-                        pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror(-r));
+                        pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror_safe(r));
                         return PAM_SESSION_ERR;
                 }