X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Flogin%2Fpam-module.c;h=1259457efc5f43351a6cf2f826e8a228cdeb91cf;hp=58f974241b8db12e5e02be33548ace1e75e4a08a;hb=85c08dc013f9f99b58bc9b79284af0b35304237b;hpb=5a330cda0ce47bd333a4cf2c559031788a26e62a diff --git a/src/login/pam-module.c b/src/login/pam-module.c index 58f974241..1259457ef 100644 --- a/src/login/pam-module.c +++ b/src/login/pam-module.c @@ -40,22 +40,29 @@ #include "def.h" #include "socket-util.h" #include "fileio.h" +#include "bus-error.h" -static int parse_argv(pam_handle_t *handle, - int argc, const char **argv, - const char **class, - bool *debug) { +static int parse_argv( + pam_handle_t *handle, + int argc, const char **argv, + const char **class, + const char **type, + bool *debug) { unsigned i; assert(argc >= 0); assert(argc == 0 || argv); - for (i = 0; i < (unsigned) argc; i++) + for (i = 0; i < (unsigned) argc; i++) { if (startswith(argv[i], "class=")) { if (class) *class = argv[i] + 6; + } else if (startswith(argv[i], "type=")) { + if (type) + *type = argv[i] + 5; + } else if (streq(argv[i], "debug")) { if (debug) *debug = true; @@ -71,6 +78,7 @@ static int parse_argv(pam_handle_t *handle, } else pam_syslog(handle, LOG_WARNING, "Unknown parameter '%s', ignoring", argv[i]); + } return 0; } @@ -82,31 +90,24 @@ static int get_user_data( const char *username = NULL; struct passwd *pw = NULL; - uid_t uid; int r; assert(handle); assert(ret_username); assert(ret_pw); - r = audit_loginuid_from_pid(0, &uid); - if (r >= 0) - pw = pam_modutil_getpwuid(handle, uid); - else { - r = pam_get_user(handle, &username, NULL); - if (r != PAM_SUCCESS) { - pam_syslog(handle, LOG_ERR, "Failed to get user name."); - return r; - } - - if (isempty(username)) { - pam_syslog(handle, LOG_ERR, "User name not valid."); - return PAM_AUTH_ERR; - } + r = pam_get_user(handle, &username, NULL); + if (r != PAM_SUCCESS) { + pam_syslog(handle, LOG_ERR, "Failed to get user name."); + return r; + } - pw = pam_modutil_getpwnam(handle, username); + if (isempty(username)) { + pam_syslog(handle, LOG_ERR, "User name not valid."); + return PAM_AUTH_ERR; } + pw = pam_modutil_getpwnam(handle, username); if (!pw) { pam_syslog(handle, LOG_ERR, "Failed to get user data."); return PAM_USER_UNKNOWN; @@ -119,16 +120,13 @@ static int get_user_data( } static int get_seat_from_display(const char *display, const char **seat, uint32_t *vtnr) { - _cleanup_free_ char *p = NULL; - int r; - _cleanup_close_ int fd = -1; union sockaddr_union sa = { .un.sun_family = AF_UNIX, }; + _cleanup_free_ char *p = NULL, *tty = NULL; + _cleanup_close_ int fd = -1; struct ucred ucred; - socklen_t l; - _cleanup_free_ char *tty = NULL; - int v; + int v, r; assert(display); assert(vtnr); @@ -151,10 +149,9 @@ static int get_seat_from_display(const char *display, const char **seat, uint32_ if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) return -errno; - l = sizeof(ucred); - r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l); + r = getpeercred(fd, &ucred); if (r < 0) - return -errno; + return r; r = get_ctty(ucred.pid, NULL, &tty); if (r < 0) @@ -173,6 +170,34 @@ 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) { + +#ifdef ENABLE_KDBUS + _cleanup_free_ char *s = NULL; + int r; + + /* skip export if kdbus is not active */ + if (access("/dev/kdbus", F_OK) < 0) + return PAM_SUCCESS; + + if (asprintf(&s, KERNEL_USER_BUS_FMT ";" UNIX_USER_BUS_FMT, + (unsigned long) uid, runtime) < 0) { + pam_syslog(handle, LOG_ERR, "Failed to set bus variable."); + return PAM_BUF_ERR; + } + + r = pam_misc_setenv(handle, "DBUS_SESSION_BUS_ADDRESS", s, 0); + if (r != PAM_SUCCESS) { + pam_syslog(handle, LOG_ERR, "Failed to set bus variable."); + return r; + } +#endif + return PAM_SUCCESS; +} + _public_ PAM_EXTERN int pam_sm_open_session( pam_handle_t *handle, int flags, @@ -187,18 +212,16 @@ _public_ PAM_EXTERN int pam_sm_open_session( *remote_user = NULL, *remote_host = NULL, *seat = NULL, *type = NULL, *class = NULL, - *class_pam = NULL, *cvtnr = NULL; + *class_pam = NULL, *type_pam = NULL, *cvtnr = NULL, *desktop = NULL; _cleanup_bus_unref_ sd_bus *bus = NULL; int session_fd = -1, existing, r; - uint32_t uid, pid, vtnr = 0; bool debug = false, remote; struct passwd *pw; + uint32_t vtnr = 0; + uid_t original_uid; assert(handle); - if (debug) - pam_syslog(handle, LOG_INFO, "pam-systemd initializing"); - /* Make this a NOP on non-logind systems */ if (!logind_running()) return PAM_SUCCESS; @@ -206,9 +229,13 @@ _public_ PAM_EXTERN int pam_sm_open_session( if (parse_argv(handle, argc, argv, &class_pam, + &type_pam, &debug) < 0) return PAM_SESSION_ERR; + if (debug) + pam_syslog(handle, LOG_DEBUG, "pam-systemd initializing"); + r = get_user_data(handle, &username, &pw); if (r != PAM_SUCCESS) { pam_syslog(handle, LOG_ERR, "Failed to get user data."); @@ -240,6 +267,10 @@ _public_ PAM_EXTERN int pam_sm_open_session( pam_syslog(handle, LOG_ERR, "Failed to set runtime dir."); return r; } + + r = export_legacy_dbus_address(handle, pw->pw_uid, rt); + if (r != PAM_SUCCESS) + return r; } return PAM_SUCCESS; @@ -247,9 +278,6 @@ _public_ PAM_EXTERN int pam_sm_open_session( /* Otherwise, we ask logind to create a session for us */ - uid = pw->pw_uid; - pid = getpid(); - pam_get_item(handle, PAM_XDISPLAY, (const void**) &display); pam_get_item(handle, PAM_TTY, (const void**) &tty); pam_get_item(handle, PAM_RUSER, (const void**) &remote_user); @@ -263,12 +291,23 @@ _public_ PAM_EXTERN int pam_sm_open_session( if (isempty(cvtnr)) cvtnr = getenv("XDG_VTNR"); - service = strempty(service); + 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"); + tty = strempty(tty); - display = strempty(display); - remote_user = strempty(remote_user); - remote_host = strempty(remote_host); - seat = strempty(seat); if (strchr(tty, ':')) { /* A tty with a colon is usually an X11 display, @@ -278,47 +317,49 @@ _public_ PAM_EXTERN int pam_sm_open_session( if (isempty(display)) display = tty; - tty = ""; + tty = NULL; } else if (streq(tty, "cron")) { /* cron has been setting PAM_TTY to "cron" for a very * long time and it probably shouldn't stop doing that * for compatibility reasons. */ - tty = ""; type = "unspecified"; + class = "background"; + tty = NULL; } else if (streq(tty, "ssh")) { /* ssh has been setting PAM_TTY to "ssh" for a very * long time and probably shouldn't stop doing that * for compatibility reasons. */ - tty = ""; type ="tty"; + class = "user"; + tty = NULL; } /* If this fails vtnr will be 0, that's intended */ if (!isempty(cvtnr)) safe_atou32(cvtnr, &vtnr); - if (!isempty(display) && vtnr <= 0) { + if (!isempty(display) && !vtnr) { if (isempty(seat)) get_seat_from_display(display, &seat, &vtnr); else if (streq(seat, "seat0")) get_seat_from_display(display, NULL, &vtnr); } - if (!type) + if (seat && !streq(seat, "seat0") && vtnr != 0) { + pam_syslog(handle, LOG_DEBUG, "Ignoring vtnr %d for %s which is not seat0", vtnr, seat); + vtnr = 0; + } + + if (isempty(type)) type = !isempty(display) ? "x11" : - !isempty(tty) ? "tty" : "unspecified"; + !isempty(tty) ? "tty" : "unspecified"; - class = pam_getenv(handle, "XDG_SESSION_CLASS"); - if (isempty(class)) - class = getenv("XDG_SESSION_CLASS"); - if (isempty(class)) - class = class_pam; if (isempty(class)) class = streq(type, "unspecified") ? "background" : "user"; remote = !isempty(remote_host) && - !streq(remote_host, "localhost") && - !streq(remote_host, "localhost.localdomain"); + !streq_ptr(remote_host, "localhost") && + !streq_ptr(remote_host, "localhost.localdomain"); /* Talk to logind over the message bus */ @@ -330,8 +371,12 @@ _public_ PAM_EXTERN int pam_sm_open_session( if (debug) pam_syslog(handle, LOG_DEBUG, "Asking logind to create session: " - "uid=%u pid=%u service=%s type=%s class=%s seat=%s vtnr=%u tty=%s display=%s remote=%s remote_user=%s remote_host=%s", - uid, pid, service, type, class, seat, vtnr, tty, display, yes_no(remote), remote_user, remote_host); + "uid=%u pid=%u service=%s type=%s class=%s desktop=%s seat=%s vtnr=%u tty=%s display=%s remote=%s remote_user=%s remote_host=%s", + pw->pw_uid, getpid(), + strempty(service), + type, class, strempty(desktop), + strempty(seat), vtnr, strempty(tty), strempty(display), + yes_no(remote), strempty(remote_user), strempty(remote_host)); r = sd_bus_call_method(bus, "org.freedesktop.login1", @@ -340,12 +385,13 @@ _public_ PAM_EXTERN int pam_sm_open_session( "CreateSession", &error, &reply, - "uussssussbssa(sv)", - uid, - pid, + "uusssssussbssa(sv)", + (uint32_t) pw->pw_uid, + (uint32_t) getpid(), service, type, class, + desktop, seat, vtnr, tty, @@ -355,20 +401,17 @@ _public_ PAM_EXTERN int pam_sm_open_session( remote_host, 0); if (r < 0) { - pam_syslog(handle, LOG_ERR, "Failed to communicate with systemd-logind: %s", strerror(-r)); - if (error.name || error.message) - pam_syslog(handle, LOG_ERR, "systemd-logind returned %s: %s", - error.name ?: "unknown error", - error.message ?: "no message"); + pam_syslog(handle, LOG_ERR, "Failed to create session: %s", bus_error_message(&error, r)); return PAM_SYSTEM_ERR; } r = sd_bus_message_read(reply, - "soshsub", + "soshusub", &id, &object_path, &runtime_path, &session_fd, + &original_uid, &seat, &vtnr, &existing); @@ -379,8 +422,8 @@ _public_ PAM_EXTERN int pam_sm_open_session( if (debug) pam_syslog(handle, LOG_DEBUG, "Reply from logind: " - "id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u", - id, object_path, runtime_path, session_fd, seat, vtnr); + "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) { @@ -388,10 +431,22 @@ _public_ PAM_EXTERN int pam_sm_open_session( return r; } - 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; + if (original_uid == pw->pw_uid) { + /* Don't set $XDG_RUNTIME_DIR if the user we now + * authenticated for does not match the original user + * of the session. We do this in order not to result + * 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); + if (r != PAM_SUCCESS) + return r; } if (!isempty(seat)) { @@ -403,9 +458,8 @@ _public_ PAM_EXTERN int pam_sm_open_session( } if (vtnr > 0) { - char buf[11]; - snprintf(buf, sizeof(buf), "%u", vtnr); - char_array_0(buf); + char buf[DECIMAL_STR_MAX(vtnr)]; + sprintf(buf, "%u", vtnr); r = pam_misc_setenv(handle, "XDG_VTNR", buf, 0); if (r != PAM_SUCCESS) { @@ -421,7 +475,7 @@ _public_ PAM_EXTERN int pam_sm_open_session( } if (session_fd >= 0) { - session_fd = dup(session_fd); + session_fd = fcntl(session_fd, F_DUPFD_CLOEXEC, 3); if (session_fd < 0) { pam_syslog(handle, LOG_ERR, "Failed to dup session fd: %m"); return PAM_SESSION_ERR; @@ -430,7 +484,7 @@ _public_ PAM_EXTERN int pam_sm_open_session( r = pam_set_data(handle, "systemd.session-fd", INT_TO_PTR(session_fd+1), NULL); if (r != PAM_SUCCESS) { pam_syslog(handle, LOG_ERR, "Failed to install session fd."); - close_nointr_nofail(session_fd); + safe_close(session_fd); return r; } } @@ -443,14 +497,12 @@ _public_ PAM_EXTERN int pam_sm_close_session( int flags, int argc, const char **argv) { - const void *p = NULL, *existing = NULL; + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_bus_unref_ sd_bus *bus = NULL; + const void *existing = NULL; const char *id; int r; - _cleanup_bus_unref_ sd_bus *bus = NULL; - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - assert(handle); /* Only release session if it wasn't pre-existing when we @@ -467,10 +519,8 @@ _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)); - r = PAM_SESSION_ERR; - goto finish; + pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror(-r)); + return PAM_SESSION_ERR; } r = sd_bus_call_method(bus, @@ -483,24 +533,16 @@ _public_ PAM_EXTERN int pam_sm_close_session( "s", id); if (r < 0) { - pam_syslog(handle, LOG_ERR, - "Failed to release session: %s", strerror(-r)); - if (error.name || error.message) - pam_syslog(handle, LOG_ERR, "systemd-logind returned %s: %s", - error.name ?: "unknown error", - error.message ?: "no message"); - - r = PAM_SESSION_ERR; - goto finish; + pam_syslog(handle, LOG_ERR, "Failed to release session: %s", bus_error_message(&error, r)); + return PAM_SESSION_ERR; } } - r = PAM_SUCCESS; + /* Note that we are knowingly leaking the FIFO fd here. This + * way, logind can watch us die. If we closed it here it would + * not have any clue when that is completed. Given that one + * cannot really have multiple PAM sessions open from the same + * process this means we will leak one FD at max. */ -finish: - pam_get_data(handle, "systemd.session-fd", &p); - if (p) - close_nointr(PTR_TO_INT(p) - 1); - - return r; + return PAM_SUCCESS; }