diff --git a/README b/README index 6801d0dc2d..c0b264abf4 100644 --- a/README +++ b/README @@ -169,7 +169,7 @@ REQUIREMENTS: dependencies: util-linux >= v2.27.1 required - dbus >= 1.4.0 (strictly speaking optional, but recommended) + dbus >= 1.9.14 (strictly speaking optional, but recommended) NOTE: If using dbus < 1.9.18, you should override the default policy directory (--with-dbuspolicydir=/etc/dbus-1/system.d). dracut (optional) diff --git a/man/pam_systemd.xml b/man/pam_systemd.xml index 5eab995a52..3ce3b282bd 100644 --- a/man/pam_systemd.xml +++ b/man/pam_systemd.xml @@ -84,40 +84,43 @@ - Takes a string argument which sets the session - class. The XDG_SESSION_CLASS environmental variable takes - precedence. One of - user, - greeter, - lock-screen or - background. See - sd_session_get_class3 - for details about the session class. + Takes a string argument which sets the session class. The XDG_SESSION_CLASS + environment variable (see below) takes precedence. One of user, greeter, + lock-screen or background. See + sd_session_get_class3 for + details about the session class. - Takes a string argument which sets the session - type. The XDG_SESSION_TYPE environmental variable takes - precedence. One of - unspecified, - tty, - x11, - wayland or - mir. See - sd_session_get_type3 - for details about the session type. + Takes a string argument which sets the session type. The XDG_SESSION_TYPE + environment variable (see below) takes precedence. One of unspecified, + tty, x11, wayland or mir. See + sd_session_get_type3 for + details about the session type. + + + + + + Takes a single, short identifier string for the desktop environment. The + XDG_SESSION_DESKTOP environment variable (see below) takes precedence. This may be used to + indicate the session desktop used, where this applies and if this information is available. For example: + GNOME, or KDE. It is recommended to use the same identifiers and + capitalization as for $XDG_CURRENT_DESKTOP, as defined by the Desktop Entry + Specification. (However, note that the option only takes a single item, and not a colon-separated list + like $XDG_CURRENT_DESKTOP.) See + sd_session_get_desktop3 for + further details. - Takes an optional - boolean argument. If yes or without - the argument, the module will log - debugging information as it - operates. + Takes an optional boolean argument. If yes or without the argument, the module will log + debugging information as it operates. @@ -131,20 +134,20 @@ Environment - The following environment variables are set for the - processes of the user's session: + The following environment variables are initialized by the module and available to the processes of the + user's session: $XDG_SESSION_ID - A session identifier, suitable to be used in - filenames. The string itself should be considered opaque, - although often it is just the audit session ID as reported by - /proc/self/sessionid. Each ID will be - assigned only once during machine uptime. It may hence be used - to uniquely label files or other resources of this - session. + A short session identifier, suitable to be used in filenames. The string itself should be + considered opaque, although often it is just the audit session ID as reported by + /proc/self/sessionid. Each ID will be assigned only once during machine uptime. It may + hence be used to uniquely label files or other resources of this session. Combine this ID with the boot + identifier, as returned by + sd_id128_get_boot3, for a + globally unique identifier for the current session. @@ -174,45 +177,31 @@ - The following environment variables are read by the module - and may be used by the PAM service to pass metadata to the - module: + The following environment variables are read by the module and may be used by the PAM service to pass + metadata to the module. If these variables are not set when the PAM module is invoked but can be determined + otherwise they are set by the module, so that these variables are initialized for the session and applications if + known at all. $XDG_SESSION_TYPE - The session type. This may be used instead of - on the module parameter line, and is - usually preferred. + The session type. This may be used instead of on the module parameter + line, and is usually preferred. $XDG_SESSION_CLASS - The session class. This may be used instead of - on the module parameter line, and is - usually preferred. + The session class. This may be used instead of on the module parameter + line, and is usually preferred. $XDG_SESSION_DESKTOP - A single, short identifier string for the - desktop environment. This may be used to indicate the session - desktop used, where this applies and if this information is - available. For example: GNOME, or - KDE. It is recommended to use the same - identifiers and capitalization as for - $XDG_CURRENT_DESKTOP, as defined by the - Desktop - Entry Specification. (However, note that - $XDG_SESSION_DESKTOP only takes a single - item, and not a colon-separated list like - $XDG_CURRENT_DESKTOP.) See - sd_session_get_desktop3 - for more details. + The desktop identifier. This may be used instead of on the module + parameter line, and is usually preferred. @@ -231,9 +220,9 @@ - If not set, pam_systemd will determine the - values for $XDG_SEAT and $XDG_VTNR - based on the $DISPLAY variable. + If not set, pam_systemd will initialize + $XDG_SEAT and $XDG_VTNR + based on the $DISPLAY variable (if the latter is set). diff --git a/src/basic/util.c b/src/basic/util.c index 8f2d6061da..2206c1b4ad 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -77,31 +77,6 @@ bool display_is_local(const char *display) { display[1] <= '9'; } -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; -} - bool kexec_loaded(void) { _cleanup_free_ char *s = NULL; diff --git a/src/basic/util.h b/src/basic/util.h index 9699d228f9..42c262f598 100644 --- a/src/basic/util.h +++ b/src/basic/util.h @@ -50,7 +50,6 @@ static inline const char* enable_disable(bool b) { bool plymouth_running(void); bool display_is_local(const char *display) _pure_; -int socket_from_display(const char *display, char **path); #define NULSTR_FOREACH(i, l) \ for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1) diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c index 7037c13cd8..4d0e7fd5e6 100644 --- a/src/login/pam_systemd.c +++ b/src/login/pam_systemd.c @@ -16,6 +16,7 @@ #include "bus-common-errors.h" #include "bus-error.h" #include "bus-util.h" +#include "cgroup-util.h" #include "def.h" #include "fd-util.h" #include "fileio.h" @@ -24,19 +25,19 @@ #include "login-util.h" #include "macro.h" #include "parse-util.h" +#include "path-util.h" #include "process-util.h" #include "socket-util.h" #include "strv.h" #include "terminal-util.h" #include "util.h" -#include "path-util.h" -#include "cgroup-util.h" static int parse_argv( pam_handle_t *handle, int argc, const char **argv, const char **class, const char **type, + const char **desktop, bool *debug) { unsigned i; @@ -53,6 +54,10 @@ static int parse_argv( if (type) *type = argv[i] + 5; + } else if (startswith(argv[i], "desktop=")) { + if (desktop) + *desktop = argv[i] + 8; + } else if (streq(argv[i], "debug")) { if (debug) *debug = true; @@ -109,6 +114,31 @@ 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, @@ -160,40 +190,6 @@ 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; - - /* FIXME: We *really* should move the access() check into the - * daemons that spawn dbus-daemon, instead of forcing - * DBUS_SESSION_BUS_ADDRESS= here. */ - - s = strjoin(runtime, "/bus"); - if (!s) - goto error; - - if (access(s, F_OK) < 0) - return PAM_SUCCESS; - - s = mfree(s); - 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; @@ -231,8 +227,7 @@ static int append_session_memory_max(pam_handle_t *handle, sd_bus_message *m, co return 0; } -static int append_session_tasks_max(pam_handle_t *handle, sd_bus_message *m, const char *limit) -{ +static int append_session_tasks_max(pam_handle_t *handle, sd_bus_message *m, const char *limit) { uint64_t val; int r; @@ -257,23 +252,62 @@ 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; } +static const char* getenv_harder(pam_handle_t *handle, const char *key, const char *fallback) { + const char *v; + + 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. */ + + v = pam_getenv(handle, key); + if (!isempty(v)) + return v; + + v = getenv(key); + if (!isempty(v)) + return v; + + return fallback; +} + +static int update_environment(pam_handle_t *handle, const char *key, const char *value) { + int r; + + assert(handle); + assert(key); + + /* Updates the environment, but only if there's actually a value set. Also, log about errors */ + + if (isempty(value)) + return PAM_SUCCESS; + + r = pam_misc_setenv(handle, key, value, 0); + if (r != PAM_SUCCESS) + pam_syslog(handle, LOG_ERR, "Failed to set environment variable %s.", key); + + return r; +} + _public_ PAM_EXTERN int pam_sm_open_session( pam_handle_t *handle, int flags, @@ -288,7 +322,7 @@ _public_ PAM_EXTERN int pam_sm_open_session( *remote_user = NULL, *remote_host = NULL, *seat = NULL, *type = NULL, *class = NULL, - *class_pam = NULL, *type_pam = NULL, *cvtnr = NULL, *desktop = 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; _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; int session_fd = -1, existing, r; @@ -307,6 +341,7 @@ _public_ PAM_EXTERN int pam_sm_open_session( argc, argv, &class_pam, &type_pam, + &desktop_pam, &debug) < 0) return PAM_SESSION_ERR; @@ -338,10 +373,6 @@ _public_ PAM_EXTERN int pam_sm_open_session( return r; } - r = export_legacy_dbus_address(handle, pw->pw_uid, rt); - if (r != PAM_SUCCESS) - return r; - return PAM_SUCCESS; } @@ -352,29 +383,11 @@ _public_ PAM_EXTERN int pam_sm_open_session( pam_get_item(handle, PAM_RUSER, (const void**) &remote_user); pam_get_item(handle, PAM_RHOST, (const void**) &remote_host); - seat = pam_getenv(handle, "XDG_SEAT"); - if (isempty(seat)) - seat = getenv("XDG_SEAT"); - - cvtnr = pam_getenv(handle, "XDG_VTNR"); - if (isempty(cvtnr)) - cvtnr = getenv("XDG_VTNR"); - - type = pam_getenv(handle, "XDG_SESSION_TYPE"); - if (isempty(type)) - type = getenv("XDG_SESSION_TYPE"); - if (isempty(type)) - type = type_pam; - - class = pam_getenv(handle, "XDG_SESSION_CLASS"); - if (isempty(class)) - class = getenv("XDG_SESSION_CLASS"); - if (isempty(class)) - class = class_pam; - - desktop = pam_getenv(handle, "XDG_SESSION_DESKTOP"); - if (isempty(desktop)) - desktop = getenv("XDG_SESSION_DESKTOP"); + seat = getenv_harder(handle, "XDG_SEAT", NULL); + cvtnr = getenv_harder(handle, "XDG_VTNR", NULL); + type = getenv_harder(handle, "XDG_SESSION_TYPE", type_pam); + class = getenv_harder(handle, "XDG_SESSION_CLASS", class_pam); + desktop = getenv_harder(handle, "XDG_SESSION_DESKTOP", desktop_pam); tty = strempty(tty); @@ -415,9 +428,9 @@ _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) { @@ -550,11 +563,9 @@ _public_ PAM_EXTERN int pam_sm_open_session( "id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u original_uid=%u", id, object_path, runtime_path, session_fd, seat, vtnr, original_uid); - r = pam_misc_setenv(handle, "XDG_SESSION_ID", id, 0); - if (r != PAM_SUCCESS) { - pam_syslog(handle, LOG_ERR, "Failed to set session id."); + r = update_environment(handle, "XDG_SESSION_ID", id); + if (r != PAM_SUCCESS) return r; - } if (original_uid == pw->pw_uid) { /* Don't set $XDG_RUNTIME_DIR if the user we now @@ -563,34 +574,38 @@ _public_ PAM_EXTERN int pam_sm_open_session( * in privileged apps clobbering the runtime directory * unnecessarily. */ - r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", runtime_path, 0); - if (r != PAM_SUCCESS) { - pam_syslog(handle, LOG_ERR, "Failed to set runtime dir."); - return r; - } - - r = export_legacy_dbus_address(handle, pw->pw_uid, runtime_path); + r = update_environment(handle, "XDG_RUNTIME_DIR", runtime_path); if (r != PAM_SUCCESS) return r; } - if (!isempty(seat)) { - r = pam_misc_setenv(handle, "XDG_SEAT", seat, 0); - if (r != PAM_SUCCESS) { - pam_syslog(handle, LOG_ERR, "Failed to set seat."); - return r; - } - } + /* Most likely we got the session/type/class from environment variables, but might have gotten the data + * somewhere else (for example PAM module parameters). Let's now update the environment variables, so that this + * data is inherited into the session processes, and programs can rely on them to be initialized. */ + + r = update_environment(handle, "XDG_SESSION_TYPE", type); + if (r != PAM_SUCCESS) + return r; + + r = update_environment(handle, "XDG_SESSION_CLASS", class); + if (r != PAM_SUCCESS) + return r; + + r = update_environment(handle, "XDG_SESSION_DESKTOP", desktop); + if (r != PAM_SUCCESS) + return r; + + r = update_environment(handle, "XDG_SEAT", seat); + if (r != PAM_SUCCESS) + return r; if (vtnr > 0) { char buf[DECIMAL_STR_MAX(vtnr)]; sprintf(buf, "%u", vtnr); - r = pam_misc_setenv(handle, "XDG_VTNR", buf, 0); - if (r != PAM_SUCCESS) { - pam_syslog(handle, LOG_ERR, "Failed to set virtual terminal number."); + r = update_environment(handle, "XDG_VTNR", buf); + if (r != PAM_SUCCESS) return r; - } } r = pam_set_data(handle, "systemd.existing", INT_TO_PTR(!!existing), NULL); @@ -632,7 +647,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) {