1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
27 #include <sys/capability.h>
29 #include <security/pam_modules.h>
30 #include <security/_pam_macros.h>
31 #include <security/pam_modutil.h>
32 #include <security/pam_ext.h>
33 #include <security/pam_misc.h>
35 #include <systemd/sd-daemon.h>
41 #include "dbus-common.h"
43 #include "socket-util.h"
45 static int parse_argv(pam_handle_t *handle,
46 int argc, const char **argv,
48 char ***reset_controllers,
50 char ***kill_only_users,
51 char ***kill_exclude_users,
57 assert(argc == 0 || argv);
59 for (i = 0; i < (unsigned) argc; i++) {
62 if (startswith(argv[i], "kill-session-processes=")) {
63 if ((k = parse_boolean(argv[i] + 23)) < 0) {
64 pam_syslog(handle, LOG_ERR, "Failed to parse kill-session-processes= argument.");
71 } else if (startswith(argv[i], "kill-session=")) {
72 /* As compatibility for old versions */
74 if ((k = parse_boolean(argv[i] + 13)) < 0) {
75 pam_syslog(handle, LOG_ERR, "Failed to parse kill-session= argument.");
82 } else if (startswith(argv[i], "controllers=")) {
87 if (!(l = strv_split(argv[i] + 12, ","))) {
88 pam_syslog(handle, LOG_ERR, "Out of memory.");
92 strv_free(*controllers);
96 } else if (startswith(argv[i], "reset-controllers=")) {
98 if (reset_controllers) {
101 if (!(l = strv_split(argv[i] + 18, ","))) {
102 pam_syslog(handle, LOG_ERR, "Out of memory.");
106 strv_free(*reset_controllers);
107 *reset_controllers = l;
110 } else if (startswith(argv[i], "kill-only-users=")) {
112 if (kill_only_users) {
115 if (!(l = strv_split(argv[i] + 16, ","))) {
116 pam_syslog(handle, LOG_ERR, "Out of memory.");
120 strv_free(*kill_only_users);
121 *kill_only_users = l;
124 } else if (startswith(argv[i], "kill-exclude-users=")) {
126 if (kill_exclude_users) {
129 if (!(l = strv_split(argv[i] + 19, ","))) {
130 pam_syslog(handle, LOG_ERR, "Out of memory.");
134 strv_free(*kill_exclude_users);
135 *kill_exclude_users = l;
138 } else if (startswith(argv[i], "debug=")) {
139 if ((k = parse_boolean(argv[i] + 6)) < 0) {
140 pam_syslog(handle, LOG_ERR, "Failed to parse debug= argument.");
147 } else if (startswith(argv[i], "create-session=") ||
148 startswith(argv[i], "kill-user=")) {
150 pam_syslog(handle, LOG_WARNING, "Option %s not supported anymore, ignoring.", argv[i]);
153 pam_syslog(handle, LOG_ERR, "Unknown parameter '%s'.", argv[i]);
161 static int get_user_data(
162 pam_handle_t *handle,
163 const char **ret_username,
164 struct passwd **ret_pw) {
166 const char *username = NULL;
167 struct passwd *pw = NULL;
172 assert(ret_username);
175 r = audit_loginuid_from_pid(0, &uid);
177 pw = pam_modutil_getpwuid(handle, uid);
179 r = pam_get_user(handle, &username, NULL);
180 if (r != PAM_SUCCESS) {
181 pam_syslog(handle, LOG_ERR, "Failed to get user name.");
185 if (isempty(username)) {
186 pam_syslog(handle, LOG_ERR, "User name not valid.");
190 pw = pam_modutil_getpwnam(handle, username);
194 pam_syslog(handle, LOG_ERR, "Failed to get user data.");
195 return PAM_USER_UNKNOWN;
199 *ret_username = username ? username : pw->pw_name;
204 static bool check_user_lists(
205 pam_handle_t *handle,
207 char **kill_only_users,
208 char **kill_exclude_users) {
210 const char *name = NULL;
216 name = "root"; /* Avoid obvious NSS requests, to suppress network traffic */
220 pw = pam_modutil_getpwuid(handle, uid);
225 STRV_FOREACH(l, kill_exclude_users) {
228 if (parse_uid(*l, &u) >= 0)
232 if (name && streq(name, *l))
236 if (strv_isempty(kill_only_users))
239 STRV_FOREACH(l, kill_only_users) {
242 if (parse_uid(*l, &u) >= 0)
246 if (name && streq(name, *l))
253 static int get_seat_from_display(const char *display, const char **seat, uint32_t *vtnr) {
257 union sockaddr_union sa;
266 /* We deduce the X11 socket from the display name, then use
267 * SO_PEERCRED to determine the X11 server process, ask for
268 * the controlling tty of that and if it's a VC then we know
269 * the seat and the virtual terminal. Sounds ugly, is only
272 r = socket_from_display(display, &p);
276 fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
283 sa.un.sun_family = AF_UNIX;
284 strncpy(sa.un.sun_path, p, sizeof(sa.un.sun_path)-1);
287 if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
288 close_nointr_nofail(fd);
293 r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l);
294 close_nointr_nofail(fd);
299 r = get_ctty(ucred.pid, NULL, &tty);
303 v = vtnr_from_tty(tty);
313 *vtnr = (uint32_t) v;
318 _public_ PAM_EXTERN int pam_sm_open_session(
319 pam_handle_t *handle,
321 int argc, const char **argv) {
324 bool kill_processes = false, debug = false;
325 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;
326 char **controllers = NULL, **reset_controllers = NULL, **kill_only_users = NULL, **kill_exclude_users = NULL;
329 DBusMessageIter iter;
332 DBusConnection *bus = NULL;
333 DBusMessage *m = NULL, *reply = NULL;
340 dbus_error_init(&error);
342 /* pam_syslog(handle, LOG_INFO, "pam-systemd initializing"); */
344 /* Make this a NOP on non-systemd systems */
345 if (sd_booted() <= 0)
348 if (parse_argv(handle,
350 &controllers, &reset_controllers,
351 &kill_processes, &kill_only_users, &kill_exclude_users,
357 r = get_user_data(handle, &username, &pw);
358 if (r != PAM_SUCCESS)
361 /* Make sure we don't enter a loop by talking to
362 * systemd-logind when it is actually waiting for the
363 * background to finish start-up. If the service is
364 * "systemd-shared" we simply set XDG_RUNTIME_DIR and
367 pam_get_item(handle, PAM_SERVICE, (const void**) &service);
368 if (streq_ptr(service, "systemd-shared")) {
371 if (asprintf(&p, "/run/systemd/users/%lu", (unsigned long) pw->pw_uid) < 0) {
376 r = parse_env_file(p, NEWLINE,
381 if (r < 0 && r != -ENOENT) {
388 r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0);
391 if (r != PAM_SUCCESS) {
392 pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
402 kill_processes = check_user_lists(handle, pw->pw_uid, kill_only_users, kill_exclude_users);
404 dbus_connection_set_change_sigpipe(FALSE);
406 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
408 pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", bus_error_message(&error));
413 m = dbus_message_new_method_call(
414 "org.freedesktop.login1",
415 "/org/freedesktop/login1",
416 "org.freedesktop.login1.Manager",
419 pam_syslog(handle, LOG_ERR, "Could not allocate create session message.");
427 pam_get_item(handle, PAM_XDISPLAY, (const void**) &display);
428 pam_get_item(handle, PAM_TTY, (const void**) &tty);
429 pam_get_item(handle, PAM_RUSER, (const void**) &remote_user);
430 pam_get_item(handle, PAM_RHOST, (const void**) &remote_host);
431 seat = pam_getenv(handle, "XDG_SEAT");
432 cvtnr = pam_getenv(handle, "XDG_VTNR");
434 service = strempty(service);
436 display = strempty(display);
437 remote_user = strempty(remote_user);
438 remote_host = strempty(remote_host);
439 seat = strempty(seat);
441 if (strchr(tty, ':')) {
442 /* A tty with a colon is usually an X11 display, place
443 * there to show up in utmp. We rearrange things and
444 * don't pretend that an X display was a tty */
446 if (isempty(display))
449 } else if (streq(tty, "cron")) {
450 /* cron has been setting PAM_TTY to "cron" for a very long time
451 * and it cannot stop doing that for compatibility reasons. */
456 safe_atou32(cvtnr, &vtnr);
458 if (!isempty(display) && vtnr <= 0) {
460 get_seat_from_display(display, &seat, &vtnr);
461 else if (streq(seat, "seat0"))
462 get_seat_from_display(display, NULL, &vtnr);
465 type = !isempty(display) ? "x11" :
466 !isempty(tty) ? "tty" : "unspecified";
468 class = pam_getenv(handle, "XDG_SESSION_CLASS");
472 remote = !isempty(remote_host) &&
473 !streq(remote_host, "localhost") &&
474 !streq(remote_host, "localhost.localdomain");
476 if (!dbus_message_append_args(m,
477 DBUS_TYPE_UINT32, &uid,
478 DBUS_TYPE_UINT32, &pid,
479 DBUS_TYPE_STRING, &service,
480 DBUS_TYPE_STRING, &type,
481 DBUS_TYPE_STRING, &class,
482 DBUS_TYPE_STRING, &seat,
483 DBUS_TYPE_UINT32, &vtnr,
484 DBUS_TYPE_STRING, &tty,
485 DBUS_TYPE_STRING, &display,
486 DBUS_TYPE_BOOLEAN, &remote,
487 DBUS_TYPE_STRING, &remote_user,
488 DBUS_TYPE_STRING, &remote_host,
489 DBUS_TYPE_INVALID)) {
490 pam_syslog(handle, LOG_ERR, "Could not attach parameters to message.");
495 dbus_message_iter_init_append(m, &iter);
497 r = bus_append_strv_iter(&iter, controllers);
499 pam_syslog(handle, LOG_ERR, "Could not attach parameter to message.");
504 r = bus_append_strv_iter(&iter, reset_controllers);
506 pam_syslog(handle, LOG_ERR, "Could not attach parameter to message.");
512 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &kp)) {
513 pam_syslog(handle, LOG_ERR, "Could not attach parameter to message.");
519 pam_syslog(handle, LOG_DEBUG, "Asking logind to create session: "
520 "uid=%u pid=%u service=%s type=%s seat=%s vtnr=%u tty=%s display=%s remote=%s remote_user=%s remote_host=%s",
521 uid, pid, service, type, seat, vtnr, tty, display, yes_no(remote), remote_user, remote_host);
523 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
525 pam_syslog(handle, LOG_ERR, "Failed to create session: %s", bus_error_message(&error));
530 if (!dbus_message_get_args(reply, &error,
531 DBUS_TYPE_STRING, &id,
532 DBUS_TYPE_OBJECT_PATH, &object_path,
533 DBUS_TYPE_STRING, &runtime_path,
534 DBUS_TYPE_UNIX_FD, &session_fd,
535 DBUS_TYPE_STRING, &seat,
536 DBUS_TYPE_UINT32, &vtnr,
537 DBUS_TYPE_INVALID)) {
538 pam_syslog(handle, LOG_ERR, "Failed to parse message: %s", bus_error_message(&error));
544 pam_syslog(handle, LOG_DEBUG, "Reply from logind: "
545 "id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u",
546 id, object_path, runtime_path, session_fd, seat, vtnr);
548 r = pam_misc_setenv(handle, "XDG_SESSION_ID", id, 0);
549 if (r != PAM_SUCCESS) {
550 pam_syslog(handle, LOG_ERR, "Failed to set session id.");
554 r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", runtime_path, 0);
555 if (r != PAM_SUCCESS) {
556 pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
560 if (!isempty(seat)) {
561 r = pam_misc_setenv(handle, "XDG_SEAT", seat, 0);
562 if (r != PAM_SUCCESS) {
563 pam_syslog(handle, LOG_ERR, "Failed to set seat.");
570 snprintf(buf, sizeof(buf), "%u", vtnr);
573 r = pam_misc_setenv(handle, "XDG_VTNR", buf, 0);
574 if (r != PAM_SUCCESS) {
575 pam_syslog(handle, LOG_ERR, "Failed to set virtual terminal number.");
580 if (session_fd >= 0) {
581 r = pam_set_data(handle, "systemd.session-fd", INT_TO_PTR(session_fd+1), NULL);
582 if (r != PAM_SUCCESS) {
583 pam_syslog(handle, LOG_ERR, "Failed to install session fd.");
593 strv_free(controllers);
594 strv_free(reset_controllers);
595 strv_free(kill_only_users);
596 strv_free(kill_exclude_users);
598 dbus_error_free(&error);
601 dbus_connection_close(bus);
602 dbus_connection_unref(bus);
606 dbus_message_unref(m);
609 dbus_message_unref(reply);
612 close_nointr_nofail(session_fd);
617 _public_ PAM_EXTERN int pam_sm_close_session(
618 pam_handle_t *handle,
620 int argc, const char **argv) {
622 const void *p = NULL;
624 DBusConnection *bus = NULL;
625 DBusMessage *m = NULL, *reply = NULL;
631 dbus_error_init(&error);
633 id = pam_getenv(handle, "XDG_SESSION_ID");
636 /* Before we go and close the FIFO we need to tell
637 * logind that this is a clean session shutdown, so
638 * that it doesn't just go and slaughter us
639 * immediately after closing the fd */
641 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
643 pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", bus_error_message(&error));
648 m = dbus_message_new_method_call(
649 "org.freedesktop.login1",
650 "/org/freedesktop/login1",
651 "org.freedesktop.login1.Manager",
654 pam_syslog(handle, LOG_ERR, "Could not allocate release session message.");
659 if (!dbus_message_append_args(m,
660 DBUS_TYPE_STRING, &id,
661 DBUS_TYPE_INVALID)) {
662 pam_syslog(handle, LOG_ERR, "Could not attach parameters to message.");
667 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
669 pam_syslog(handle, LOG_ERR, "Failed to release session: %s", bus_error_message(&error));
678 pam_get_data(handle, "systemd.session-fd", &p);
680 close_nointr(PTR_TO_INT(p) - 1);
682 dbus_error_free(&error);
685 dbus_connection_close(bus);
686 dbus_connection_unref(bus);
690 dbus_message_unref(m);
693 dbus_message_unref(reply);