X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=src%2Flogin%2Fpam_systemd.c;h=cd070329f4c409a6ac6fec2353005b2b379f068c;hb=4b381a9ef65d68dc79760b093436a9c81f43fa5d;hp=8464df122c403f4373bce752ba652372f80380f5;hpb=903511951892008b991991962fef2e8ecc65bc6c;p=thirdparty%2Fsystemd.git diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c index 8464df122c4..cd070329f4c 100644 --- a/src/login/pam_systemd.c +++ b/src/login/pam_systemd.c @@ -10,14 +10,17 @@ #include #include #include +#include +#include +#include #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 "fd-util.h" #include "fileio.h" #include "format-util.h" @@ -28,9 +31,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, @@ -114,6 +117,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; @@ -140,13 +152,11 @@ static int socket_from_display(const char *display, char **path) { } 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); @@ -160,13 +170,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); @@ -190,6 +202,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; @@ -220,7 +271,7 @@ static int append_session_memory_max(pam_handle_t *handle, sd_bus_message *m, co 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); } } @@ -243,7 +294,7 @@ static int append_session_tasks_max(pam_handle_t *handle, sd_bus_message *m, con 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; } @@ -276,14 +327,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; @@ -308,6 +366,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, @@ -362,16 +450,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; } @@ -434,7 +526,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; } @@ -535,7 +628,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, "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)); @@ -574,7 +668,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; }