pam_systemd.c revision b80120c4cba7d134b5437a58437a23fdf7ab2084
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen This file is part of systemd.
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen Copyright 2010 Lennart Poettering
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen systemd is free software; you can redistribute it and/or modify it
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen under the terms of the GNU Lesser General Public License as published by
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen the Free Software Foundation; either version 2.1 of the License, or
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen (at your option) any later version.
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen systemd is distributed in the hope that it will be useful, but
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen WITHOUT ANY WARRANTY; without even the implied warranty of
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen Lesser General Public License for more details.
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen You should have received a copy of the GNU Lesser General Public License
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen along with systemd; If not, see <http://www.gnu.org/licenses/>.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering for (i = 0; i < (unsigned) argc; i++) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering } else if (startswith(argv[i], "type=")) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering } else if (startswith(argv[i], "debug=")) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering pam_syslog(handle, LOG_WARNING, "Failed to parse debug= argument, ignoring.");
21d73c87b09ec2b8642424bc714ce9af3da4fc40Lennart Poettering pam_syslog(handle, LOG_WARNING, "Unknown parameter '%s', ignoring", argv[i]);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering pam_syslog(handle, LOG_ERR, "Failed to get user name.");
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering pam_syslog(handle, LOG_ERR, "User name not valid.");
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering pw = pam_modutil_getpwnam(handle, username);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering pam_syslog(handle, LOG_ERR, "Failed to get user data.");
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringstatic int get_seat_from_display(const char *display, const char **seat, uint32_t *vtnr) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering _cleanup_free_ char *p = NULL, *tty = NULL;
1c4baffc1895809bae9ac36b670af90a4cb9cd7dTom Gundersen /* We deduce the X11 socket from the display name, then use
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering * SO_PEERCRED to determine the X11 server process, ask for
1c4baffc1895809bae9ac36b670af90a4cb9cd7dTom Gundersen * the controlling tty of that and if it's a VC then we know
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering * the seat and the virtual terminal. Sounds ugly, is only
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering * semi-ugly. */
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering strncpy(sa.un.sun_path, p, sizeof(sa.un.sun_path)-1);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering else if (v == 0)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering /* skip export if kdbus is not active */
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (asprintf(&s, KERNEL_USER_BUS_ADDRESS_FMT ";" UNIX_USER_BUS_ADDRESS_FMT, uid, runtime) < 0) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering pam_syslog(handle, LOG_ERR, "Failed to set bus variable.");
1c4baffc1895809bae9ac36b670af90a4cb9cd7dTom Gundersen r = pam_misc_setenv(handle, "DBUS_SESSION_BUS_ADDRESS", s, 0);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering pam_syslog(handle, LOG_ERR, "Failed to set bus variable.");
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering_public_ PAM_EXTERN int pam_sm_open_session(
1c4baffc1895809bae9ac36b670af90a4cb9cd7dTom Gundersen _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering *username, *id, *object_path, *runtime_path,
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering *remote_user = NULL, *remote_host = NULL,
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering *class_pam = NULL, *type_pam = NULL, *cvtnr = NULL, *desktop = NULL;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering /* Make this a NOP on non-logind systems */
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering pam_syslog(handle, LOG_DEBUG, "pam-systemd initializing");
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering r = get_user_data(handle, &username, &pw);
1c4baffc1895809bae9ac36b670af90a4cb9cd7dTom Gundersen pam_syslog(handle, LOG_ERR, "Failed to get user data.");
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering /* Make sure we don't enter a loop by talking to
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering * systemd-logind when it is actually waiting for the
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering * background to finish start-up. If the service is
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering * "systemd-user" we simply set XDG_RUNTIME_DIR and
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering pam_get_item(handle, PAM_SERVICE, (const void**) &service);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (streq_ptr(service, "systemd-user")) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (asprintf(&p, "/run/systemd/users/"UID_FMT, pw->pw_uid) < 0)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (r < 0 && r != -ENOENT)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering r = export_legacy_dbus_address(handle, pw->pw_uid, rt);
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidt /* Otherwise, we ask logind to create a session for us */
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering pam_get_item(handle, PAM_XDISPLAY, (const void**) &display);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering pam_get_item(handle, PAM_TTY, (const void**) &tty);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering pam_get_item(handle, PAM_RUSER, (const void**) &remote_user);
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidt pam_get_item(handle, PAM_RHOST, (const void**) &remote_host);
0014a4ad505d119c7ac4346d9d774c3f17f663a5Lennart Poettering type = pam_getenv(handle, "XDG_SESSION_TYPE");
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering class = pam_getenv(handle, "XDG_SESSION_CLASS");
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering desktop = pam_getenv(handle, "XDG_SESSION_DESKTOP");
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering /* A tty with a colon is usually an X11 display,
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering * placed there to show up in utmp. We rearrange
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering * things and don't pretend that an X display was a
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering /* cron has been setting PAM_TTY to "cron" for a very
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering * long time and it probably shouldn't stop doing that
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering * for compatibility reasons. */
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering /* ssh has been setting PAM_TTY to "ssh" for a very
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering * long time and probably shouldn't stop doing that
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering * for compatibility reasons. */
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering /* If this fails vtnr will be 0, that's intended */
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering get_seat_from_display(display, &seat, &vtnr);
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering get_seat_from_display(display, NULL, &vtnr);
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering if (seat && !streq(seat, "seat0") && vtnr != 0) {
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering pam_syslog(handle, LOG_DEBUG, "Ignoring vtnr %"PRIu32" for %s which is not seat0", vtnr, seat);
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering class = streq(type, "unspecified") ? "background" : "user";
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering remote = !isempty(remote_host) && !is_localhost(remote_host);
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering /* Talk to logind over the message bus */
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror(-r));
56f64d95763a799ba4475daf44d8e9f72a1bd474Michal Schmidt pam_syslog(handle, LOG_DEBUG, "Asking logind to create session: "
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering "uid="UID_FMT" pid="PID_FMT" service=%s type=%s class=%s desktop=%s seat=%s vtnr=%"PRIu32" tty=%s display=%s remote=%s remote_user=%s remote_host=%s",
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering strempty(seat), vtnr, strempty(tty), strempty(display),
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering yes_no(remote), strempty(remote_user), strempty(remote_host));
8d3d7072e609ef0e0fb37e1d19a29307d58146c3Michal Schmidt "org.freedesktop.login1",
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering "org.freedesktop.login1.Manager",
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering "CreateSession",
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering "uusssssussbssa(sv)",
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen if (sd_bus_error_has_name(&error, BUS_ERROR_SESSION_BUSY)) {
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen pam_syslog(handle, LOG_DEBUG, "Cannot create session: %s", bus_error_message(&error, r));
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering pam_syslog(handle, LOG_ERR, "Failed to create session: %s", bus_error_message(&error, r));
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen pam_syslog(handle, LOG_ERR, "Failed to parse message: %s", strerror(-r));
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering pam_syslog(handle, LOG_DEBUG, "Reply from logind: "
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering "id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u original_uid=%u",
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering id, object_path, runtime_path, session_fd, seat, vtnr, original_uid);
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering r = pam_misc_setenv(handle, "XDG_SESSION_ID", id, 0);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering pam_syslog(handle, LOG_ERR, "Failed to set session id.");
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering /* Don't set $XDG_RUNTIME_DIR if the user we now
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering * authenticated for does not match the original user
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering * of the session. We do this in order not to result
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering * in privileged apps clobbering the runtime directory
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering * unnecessarily. */
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", runtime_path, 0);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen r = export_legacy_dbus_address(handle, pw->pw_uid, runtime_path);
edc501d4674dadc304d45a7e1c5b69e207eb8cd4Lennart Poettering r = pam_misc_setenv(handle, "XDG_SEAT", seat, 0);
edc501d4674dadc304d45a7e1c5b69e207eb8cd4Lennart Poettering pam_syslog(handle, LOG_ERR, "Failed to set seat.");
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen r = pam_misc_setenv(handle, "XDG_VTNR", buf, 0);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering pam_syslog(handle, LOG_ERR, "Failed to set virtual terminal number.");
f0e1546763304aedc90e91d70dab9eeb7c966cf8Lennart Poettering r = pam_set_data(handle, "systemd.existing", INT_TO_PTR(!!existing), NULL);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering pam_syslog(handle, LOG_ERR, "Failed to install existing flag.");
cab5b05903096e1c9cf5575ccc73f89d15c8db69Lennart Poettering session_fd = fcntl(session_fd, F_DUPFD_CLOEXEC, 3);
f0e1546763304aedc90e91d70dab9eeb7c966cf8Lennart Poettering pam_syslog(handle, LOG_ERR, "Failed to dup session fd: %m");
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen r = pam_set_data(handle, "systemd.session-fd", INT_TO_PTR(session_fd+1), NULL);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering pam_syslog(handle, LOG_ERR, "Failed to install session fd.");
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
eb60f9cd4e93ff5016dc1b5486fd1b7e1565fd92Lennart Poettering _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen /* Only release session if it wasn't pre-existing when we
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen * tried to create it */
5cb36f41f01cf4b1f4395abfffd1b33116591e58Lennart Poettering pam_get_data(handle, "systemd.existing", &existing);
5cb36f41f01cf4b1f4395abfffd1b33116591e58Lennart Poettering id = pam_getenv(handle, "XDG_SESSION_ID");
5cb36f41f01cf4b1f4395abfffd1b33116591e58Lennart Poettering /* Before we go and close the FIFO we need to tell
5cb36f41f01cf4b1f4395abfffd1b33116591e58Lennart Poettering * logind that this is a clean session shutdown, so
5cb36f41f01cf4b1f4395abfffd1b33116591e58Lennart Poettering * that it doesn't just go and slaughter us
5cb36f41f01cf4b1f4395abfffd1b33116591e58Lennart Poettering * immediately after closing the fd */
5cb36f41f01cf4b1f4395abfffd1b33116591e58Lennart Poettering pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror(-r));
5cb36f41f01cf4b1f4395abfffd1b33116591e58Lennart Poettering "org.freedesktop.login1",
56f64d95763a799ba4475daf44d8e9f72a1bd474Michal Schmidt "org.freedesktop.login1.Manager",
5cb36f41f01cf4b1f4395abfffd1b33116591e58Lennart Poettering "ReleaseSession",
5cb36f41f01cf4b1f4395abfffd1b33116591e58Lennart Poettering pam_syslog(handle, LOG_ERR, "Failed to release session: %s", bus_error_message(&error, r));
5cb36f41f01cf4b1f4395abfffd1b33116591e58Lennart Poettering /* Note that we are knowingly leaking the FIFO fd here. This
5cb36f41f01cf4b1f4395abfffd1b33116591e58Lennart Poettering * way, logind can watch us die. If we closed it here it would
5cb36f41f01cf4b1f4395abfffd1b33116591e58Lennart Poettering * not have any clue when that is completed. Given that one
5cb36f41f01cf4b1f4395abfffd1b33116591e58Lennart Poettering * cannot really have multiple PAM sessions open from the same
5cb36f41f01cf4b1f4395abfffd1b33116591e58Lennart Poettering * process this means we will leak one FD at max. */