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=61df3e9d890a64353b4858159cae764d3bd3e522;hp=8544413a0834ff15dec3e9108a602e6a98439bc2;hb=4d49b48c60a4ac66fa764fd7a6ae6d0bd0bd9c37;hpb=55efac6cbcea0d8edda9c6820620ceb390009e7a diff --git a/src/login/pam-module.c b/src/login/pam-module.c index 8544413a0..61df3e9d8 100644 --- a/src/login/pam-module.c +++ b/src/login/pam-module.c @@ -6,16 +6,16 @@ Copyright 2010 Lennart Poettering systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. systemd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. + Lesser General Public License for more details. - You should have received a copy of the GNU General Public License + You should have received a copy of the GNU Lesser General Public License along with systemd; If not, see . ***/ @@ -32,22 +32,18 @@ #include #include -#include - #include "util.h" +#include "audit.h" #include "macro.h" #include "strv.h" -#include "dbus-common.h" +#include "bus-util.h" #include "def.h" #include "socket-util.h" +#include "fileio.h" static int parse_argv(pam_handle_t *handle, int argc, const char **argv, - char ***controllers, - char ***reset_controllers, - bool *kill_processes, - char ***kill_only_users, - char ***kill_exclude_users, + const char **class, bool *debug) { unsigned i; @@ -55,104 +51,26 @@ static int parse_argv(pam_handle_t *handle, assert(argc >= 0); assert(argc == 0 || argv); - for (i = 0; i < (unsigned) argc; i++) { - int k; - - if (startswith(argv[i], "kill-session-processes=")) { - if ((k = parse_boolean(argv[i] + 23)) < 0) { - pam_syslog(handle, LOG_ERR, "Failed to parse kill-session-processes= argument."); - return k; - } - - if (kill_processes) - *kill_processes = k; - - } else if (startswith(argv[i], "kill-session=")) { - /* As compatibility for old versions */ - - if ((k = parse_boolean(argv[i] + 13)) < 0) { - pam_syslog(handle, LOG_ERR, "Failed to parse kill-session= argument."); - return k; - } - - if (kill_processes) - *kill_processes = k; - - } else if (startswith(argv[i], "controllers=")) { - - if (controllers) { - char **l; - - if (!(l = strv_split(argv[i] + 12, ","))) { - pam_syslog(handle, LOG_ERR, "Out of memory."); - return -ENOMEM; - } - - strv_free(*controllers); - *controllers = l; - } - - } else if (startswith(argv[i], "reset-controllers=")) { - - if (reset_controllers) { - char **l; - - if (!(l = strv_split(argv[i] + 18, ","))) { - pam_syslog(handle, LOG_ERR, "Out of memory."); - return -ENOMEM; - } - - strv_free(*reset_controllers); - *reset_controllers = l; - } - - } else if (startswith(argv[i], "kill-only-users=")) { + for (i = 0; i < (unsigned) argc; i++) + if (startswith(argv[i], "class=")) { + if (class) + *class = argv[i] + 6; - if (kill_only_users) { - char **l; - - if (!(l = strv_split(argv[i] + 16, ","))) { - pam_syslog(handle, LOG_ERR, "Out of memory."); - return -ENOMEM; - } - - strv_free(*kill_only_users); - *kill_only_users = l; - } - - } else if (startswith(argv[i], "kill-exclude-users=")) { - - if (kill_exclude_users) { - char **l; - - if (!(l = strv_split(argv[i] + 19, ","))) { - pam_syslog(handle, LOG_ERR, "Out of memory."); - return -ENOMEM; - } - - strv_free(*kill_exclude_users); - *kill_exclude_users = l; - } + } else if (streq(argv[i], "debug")) { + if (debug) + *debug = true; } else if (startswith(argv[i], "debug=")) { - if ((k = parse_boolean(argv[i] + 6)) < 0) { - pam_syslog(handle, LOG_ERR, "Failed to parse debug= argument."); - return k; - } + int k; - if (debug) + k = parse_boolean(argv[i] + 6); + if (k < 0) + pam_syslog(handle, LOG_WARNING, "Failed to parse debug= argument, ignoring."); + else if (debug) *debug = k; - } else if (startswith(argv[i], "create-session=") || - startswith(argv[i], "kill-user=")) { - - pam_syslog(handle, LOG_WARNING, "Option %s not supported anymore, ignoring.", argv[i]); - - } else { - pam_syslog(handle, LOG_ERR, "Unknown parameter '%s'.", argv[i]); - return -EINVAL; - } - } + } else + pam_syslog(handle, LOG_WARNING, "Unknown parameter '%s', ignoring", argv[i]); return 0; } @@ -200,63 +118,16 @@ static int get_user_data( return PAM_SUCCESS; } -static bool check_user_lists( - pam_handle_t *handle, - uid_t uid, - char **kill_only_users, - char **kill_exclude_users) { - - const char *name = NULL; - char **l; - - assert(handle); - - if (uid == 0) - name = "root"; /* Avoid obvious NSS requests, to suppress network traffic */ - else { - struct passwd *pw; - - pw = pam_modutil_getpwuid(handle, uid); - if (pw) - name = pw->pw_name; - } - - STRV_FOREACH(l, kill_exclude_users) { - uid_t u; - - if (parse_uid(*l, &u) >= 0) - if (u == uid) - return false; - - if (name && streq(name, *l)) - return false; - } - - if (strv_isempty(kill_only_users)) - return true; - - STRV_FOREACH(l, kill_only_users) { - uid_t u; - - if (parse_uid(*l, &u) >= 0) - if (u == uid) - return true; - - if (name && streq(name, *l)) - return true; - } - - return false; -} - static int get_seat_from_display(const char *display, const char **seat, uint32_t *vtnr) { - char *p = NULL; + _cleanup_free_ char *p = NULL; int r; - int fd; - union sockaddr_union sa; + _cleanup_close_ int fd = -1; + union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + }; struct ucred ucred; socklen_t l; - char *tty; + _cleanup_free_ char *tty = NULL; int v; assert(display); @@ -271,27 +142,17 @@ 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); fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); - if (fd < 0) { - free(p); + if (fd < 0) return -errno; - } - zero(sa); - sa.un.sun_family = AF_UNIX; - strncpy(sa.un.sun_path, p, sizeof(sa.un.sun_path)-1); - free(p); - - if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) { - close_nointr_nofail(fd); + 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); - close_nointr_nofail(fd); - if (r < 0) return -errno; @@ -300,8 +161,6 @@ static int get_seat_from_display(const char *display, const char **seat, uint32_ return r; v = vtnr_from_tty(tty); - free(tty); - if (v < 0) return v; else if (v == 0) @@ -320,34 +179,31 @@ _public_ PAM_EXTERN int pam_sm_open_session( int argc, const char **argv) { struct passwd *pw; - bool kill_processes = false, debug = false; - const char *username, *id, *object_path, *runtime_path, *service = NULL, *tty = NULL, *display = NULL, *remote_user = NULL, *remote_host = NULL, *seat = NULL, *type, *class, *cvtnr = NULL; - char **controllers = NULL, **reset_controllers = NULL, **kill_only_users = NULL, **kill_exclude_users = NULL; - DBusError error; + bool debug = false; + const char *username, *id, *object_path, *runtime_path, *service = NULL, *tty = NULL, *display = NULL, *remote_user = NULL, *remote_host = NULL, *seat = NULL, *type = NULL, *class = NULL, *class_pam = NULL, *cvtnr = NULL; uint32_t uid, pid; - DBusMessageIter iter; - dbus_bool_t kp; int session_fd = -1; - DBusConnection *bus = NULL; - DBusMessage *m = NULL, *reply = NULL; - dbus_bool_t remote; - int r; + bool remote; + unsigned existing; uint32_t vtnr = 0; + int r; - assert(handle); + _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; - dbus_error_init(&error); + assert(handle); - /* pam_syslog(handle, LOG_INFO, "pam-systemd initializing"); */ + if (debug) + pam_syslog(handle, LOG_INFO, "pam-systemd initializing"); - /* Make this a NOP on non-systemd systems */ - if (sd_booted() <= 0) + /* Make this a NOP on non-logind systems */ + if (!logind_running()) return PAM_SUCCESS; if (parse_argv(handle, argc, argv, - &controllers, &reset_controllers, - &kill_processes, &kill_only_users, &kill_exclude_users, + &class_pam, &debug) < 0) { r = PAM_SESSION_ERR; goto finish; @@ -360,66 +216,34 @@ _public_ PAM_EXTERN int pam_sm_open_session( /* Make sure we don't enter a loop by talking to * systemd-logind when it is actually waiting for the * background to finish start-up. If the service is - * "systemd-shared" we simply set XDG_RUNTIME_DIR and + * "systemd-user" we simply set XDG_RUNTIME_DIR and * leave. */ pam_get_item(handle, PAM_SERVICE, (const void**) &service); - if (streq_ptr(service, "systemd-shared")) { - char *p, *rt = NULL; + if (streq_ptr(service, "systemd-user")) { + _cleanup_free_ char *p = NULL, *rt = NULL; - if (asprintf(&p, "/run/systemd/users/%lu", (unsigned long) pw->pw_uid) < 0) { - r = PAM_BUF_ERR; - goto finish; - } + if (asprintf(&p, "/run/systemd/users/%lu", (unsigned long) pw->pw_uid) < 0) + return PAM_BUF_ERR; r = parse_env_file(p, NEWLINE, "RUNTIME", &rt, NULL); - free(p); - - if (r < 0 && r != -ENOENT) { - r = PAM_SESSION_ERR; - free(rt); - goto finish; - } + if (r < 0 && r != -ENOENT) + return PAM_SESSION_ERR; if (rt) { r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0); - free(rt); - if (r != PAM_SUCCESS) { pam_syslog(handle, LOG_ERR, "Failed to set runtime dir."); - goto finish; + return r; } } - r = PAM_SUCCESS; - goto finish; - } - - if (kill_processes) - kill_processes = check_user_lists(handle, pw->pw_uid, kill_only_users, kill_exclude_users); - - dbus_connection_set_change_sigpipe(FALSE); - - bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error); - if (!bus) { - pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", bus_error_message(&error)); - r = PAM_SESSION_ERR; - goto finish; + return PAM_SUCCESS; } - m = dbus_message_new_method_call( - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "CreateSession"); - - if (!m) { - pam_syslog(handle, LOG_ERR, "Could not allocate create session message."); - r = PAM_BUF_ERR; - goto finish; - } + /* Otherwise, we ask logind to create a session for us */ uid = pw->pw_uid; pid = getpid(); @@ -428,8 +252,14 @@ _public_ PAM_EXTERN int pam_sm_open_session( pam_get_item(handle, PAM_TTY, (const void**) &tty); 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"); service = strempty(service); tty = strempty(tty); @@ -439,19 +269,29 @@ _public_ PAM_EXTERN int pam_sm_open_session( seat = strempty(seat); if (strchr(tty, ':')) { - /* A tty with a colon is usually an X11 display, place - * there to show up in utmp. We rearrange things and - * don't pretend that an X display was a tty */ + /* A tty with a colon is usually an X11 display, + * placed there to show up in utmp. We rearrange + * things and don't pretend that an X display was a + * tty. */ if (isempty(display)) display = tty; tty = ""; } else if (streq(tty, "cron")) { - /* cron has been setting PAM_TTY to "cron" for a very long time - * and it cannot stop doing that for compatibility reasons. */ + /* 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"; + } 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"; } + /* If this fails vtnr will be 0, that's intended */ if (!isempty(cvtnr)) safe_atou32(cvtnr, &vtnr); @@ -462,80 +302,75 @@ _public_ PAM_EXTERN int pam_sm_open_session( get_seat_from_display(display, NULL, &vtnr); } - type = !isempty(display) ? "x11" : - !isempty(tty) ? "tty" : "unspecified"; + if (!type) + type = !isempty(display) ? "x11" : + !isempty(tty) ? "tty" : "unspecified"; class = pam_getenv(handle, "XDG_SESSION_CLASS"); if (isempty(class)) - class = "user"; + 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"); - if (!dbus_message_append_args(m, - DBUS_TYPE_UINT32, &uid, - DBUS_TYPE_UINT32, &pid, - DBUS_TYPE_STRING, &service, - DBUS_TYPE_STRING, &type, - DBUS_TYPE_STRING, &class, - DBUS_TYPE_STRING, &seat, - DBUS_TYPE_UINT32, &vtnr, - DBUS_TYPE_STRING, &tty, - DBUS_TYPE_STRING, &display, - DBUS_TYPE_BOOLEAN, &remote, - DBUS_TYPE_STRING, &remote_user, - DBUS_TYPE_STRING, &remote_host, - DBUS_TYPE_INVALID)) { - pam_syslog(handle, LOG_ERR, "Could not attach parameters to message."); - r = PAM_BUF_ERR; - goto finish; - } - - dbus_message_iter_init_append(m, &iter); - - r = bus_append_strv_iter(&iter, controllers); + /* Talk to logind over the message bus */ + r = sd_bus_open_system(&bus); if (r < 0) { - pam_syslog(handle, LOG_ERR, "Could not attach parameter to message."); - r = PAM_BUF_ERR; - goto finish; - } - - r = bus_append_strv_iter(&iter, reset_controllers); - if (r < 0) { - pam_syslog(handle, LOG_ERR, "Could not attach parameter to message."); - r = PAM_BUF_ERR; - goto finish; - } - - kp = kill_processes; - if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &kp)) { - pam_syslog(handle, LOG_ERR, "Could not attach parameter to message."); - r = PAM_BUF_ERR; - goto finish; + pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror(-r)); + return PAM_SESSION_ERR; } if (debug) pam_syslog(handle, LOG_DEBUG, "Asking logind to create session: " - "uid=%u pid=%u service=%s type=%s seat=%s vtnr=%u tty=%s display=%s remote=%s remote_user=%s remote_host=%s", - uid, pid, service, type, seat, vtnr, tty, display, yes_no(remote), remote_user, remote_host); - - reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); - if (!reply) { - pam_syslog(handle, LOG_ERR, "Failed to create session: %s", bus_error_message(&error)); - r = PAM_SESSION_ERR; - goto finish; + "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); + + r = sd_bus_call_method(bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "CreateSession", + &error, + &reply, + "uussssussbssa(sv)", + uid, + pid, + service, + type, + class, + seat, + vtnr, + tty, + display, + remote, + remote_user, + 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"); + return PAM_SYSTEM_ERR; } - if (!dbus_message_get_args(reply, &error, - DBUS_TYPE_STRING, &id, - DBUS_TYPE_OBJECT_PATH, &object_path, - DBUS_TYPE_STRING, &runtime_path, - DBUS_TYPE_UNIX_FD, &session_fd, - DBUS_TYPE_STRING, &seat, - DBUS_TYPE_UINT32, &vtnr, - DBUS_TYPE_INVALID)) { - pam_syslog(handle, LOG_ERR, "Failed to parse message: %s", bus_error_message(&error)); + r = sd_bus_message_read(reply, + "soshsub", + &id, + &object_path, + &runtime_path, + &session_fd, + &seat, + &vtnr, + &existing); + if (r < 0) { + pam_syslog(handle, LOG_ERR, "Failed to parse message: %s", strerror(-r)); r = PAM_SESSION_ERR; goto finish; } @@ -577,37 +412,23 @@ _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."); + goto finish; + } + if (session_fd >= 0) { 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."); - return r; + goto finish; } } - session_fd = -1; - - r = PAM_SUCCESS; + return PAM_SUCCESS; finish: - strv_free(controllers); - strv_free(reset_controllers); - strv_free(kill_only_users); - strv_free(kill_exclude_users); - - dbus_error_free(&error); - - if (bus) { - dbus_connection_close(bus); - dbus_connection_unref(bus); - } - - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - if (session_fd >= 0) close_nointr_nofail(session_fd); @@ -619,12 +440,64 @@ _public_ PAM_EXTERN int pam_sm_close_session( int flags, int argc, const char **argv) { - const void *p = NULL; + const void *p = NULL, *existing = NULL; + const char *id; + int r; - pam_get_data(handle, "systemd.session-fd", &p); + _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 + * tried to create it */ + pam_get_data(handle, "systemd.existing", &existing); + id = pam_getenv(handle, "XDG_SESSION_ID"); + if (id && !existing) { + + /* Before we go and close the FIFO we need to tell + * logind that this is a clean session shutdown, so + * that it doesn't just go and slaughter us + * immediately after closing the fd */ + + 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; + } + + r = sd_bus_call_method(bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "ReleaseSession", + &error, + NULL, + "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; + } + } + + r = PAM_SUCCESS; + +finish: + pam_get_data(handle, "systemd.session-fd", &p); if (p) close_nointr(PTR_TO_INT(p) - 1); - return PAM_SUCCESS; + return r; }