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>
39
#include "dbus-common.h"
41
#include "socket-util.h"
44
static int parse_argv(pam_handle_t *handle,
45
int argc, const char **argv,
47
char ***reset_controllers,
49
char ***kill_only_users,
50
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], "class=")) {
141
*class = argv[i] + 6;
143
} else if (startswith(argv[i], "debug=")) {
144
if ((k = parse_boolean(argv[i] + 6)) < 0) {
145
pam_syslog(handle, LOG_ERR, "Failed to parse debug= argument.");
152
} else if (startswith(argv[i], "create-session=") ||
153
startswith(argv[i], "kill-user=")) {
155
pam_syslog(handle, LOG_WARNING, "Option %s not supported anymore, ignoring.", argv[i]);
158
pam_syslog(handle, LOG_ERR, "Unknown parameter '%s'.", argv[i]);
166
static int get_user_data(
167
pam_handle_t *handle,
168
const char **ret_username,
169
struct passwd **ret_pw) {
171
const char *username = NULL;
172
struct passwd *pw = NULL;
177
assert(ret_username);
180
r = audit_loginuid_from_pid(0, &uid);
182
pw = pam_modutil_getpwuid(handle, uid);
184
r = pam_get_user(handle, &username, NULL);
185
if (r != PAM_SUCCESS) {
186
pam_syslog(handle, LOG_ERR, "Failed to get user name.");
190
if (isempty(username)) {
191
pam_syslog(handle, LOG_ERR, "User name not valid.");
195
pw = pam_modutil_getpwnam(handle, username);
199
pam_syslog(handle, LOG_ERR, "Failed to get user data.");
200
return PAM_USER_UNKNOWN;
204
*ret_username = username ? username : pw->pw_name;
209
static bool check_user_lists(
210
pam_handle_t *handle,
212
char **kill_only_users,
213
char **kill_exclude_users) {
215
const char *name = NULL;
221
name = "root"; /* Avoid obvious NSS requests, to suppress network traffic */
225
pw = pam_modutil_getpwuid(handle, uid);
230
STRV_FOREACH(l, kill_exclude_users) {
233
if (parse_uid(*l, &u) >= 0)
237
if (name && streq(name, *l))
241
if (strv_isempty(kill_only_users))
244
STRV_FOREACH(l, kill_only_users) {
247
if (parse_uid(*l, &u) >= 0)
251
if (name && streq(name, *l))
258
static int get_seat_from_display(const char *display, const char **seat, uint32_t *vtnr) {
259
_cleanup_free_ char *p = NULL;
261
_cleanup_close_ int fd = -1;
262
union sockaddr_union sa = {
263
.un.sun_family = AF_UNIX,
267
_cleanup_free_ char *tty = NULL;
273
/* We deduce the X11 socket from the display name, then use
274
* SO_PEERCRED to determine the X11 server process, ask for
275
* the controlling tty of that and if it's a VC then we know
276
* the seat and the virtual terminal. Sounds ugly, is only
279
r = socket_from_display(display, &p);
282
strncpy(sa.un.sun_path, p, sizeof(sa.un.sun_path)-1);
284
fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
288
if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0)
292
r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l);
296
r = get_ctty(ucred.pid, NULL, &tty);
300
v = vtnr_from_tty(tty);
308
*vtnr = (uint32_t) v;
313
_public_ PAM_EXTERN int pam_sm_open_session(
314
pam_handle_t *handle,
316
int argc, const char **argv) {
319
bool kill_processes = false, debug = false;
320
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;
321
char **controllers = NULL, **reset_controllers = NULL, **kill_only_users = NULL, **kill_exclude_users = NULL;
324
DBusMessageIter iter;
327
DBusConnection *bus = NULL;
328
DBusMessage *m = NULL, *reply = NULL;
329
dbus_bool_t remote, existing;
335
dbus_error_init(&error);
337
/* pam_syslog(handle, LOG_INFO, "pam-systemd initializing"); */
339
/* Make this a NOP on non-logind systems */
340
if (!logind_running())
343
if (parse_argv(handle,
345
&controllers, &reset_controllers,
346
&kill_processes, &kill_only_users, &kill_exclude_users,
347
&class_pam, &debug) < 0) {
352
r = get_user_data(handle, &username, &pw);
353
if (r != PAM_SUCCESS)
356
/* Make sure we don't enter a loop by talking to
357
* systemd-logind when it is actually waiting for the
358
* background to finish start-up. If the service is
359
* "systemd-shared" we simply set XDG_RUNTIME_DIR and
362
pam_get_item(handle, PAM_SERVICE, (const void**) &service);
363
if (streq_ptr(service, "systemd-shared")) {
366
if (asprintf(&p, "/run/systemd/users/%lu", (unsigned long) pw->pw_uid) < 0) {
371
r = parse_env_file(p, NEWLINE,
376
if (r < 0 && r != -ENOENT) {
383
r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0);
386
if (r != PAM_SUCCESS) {
387
pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
397
kill_processes = check_user_lists(handle, pw->pw_uid, kill_only_users, kill_exclude_users);
399
dbus_connection_set_change_sigpipe(FALSE);
401
bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
403
pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", bus_error_message(&error));
408
m = dbus_message_new_method_call(
409
"org.freedesktop.login1",
410
"/org/freedesktop/login1",
411
"org.freedesktop.login1.Manager",
414
pam_syslog(handle, LOG_ERR, "Could not allocate create session message.");
422
pam_get_item(handle, PAM_XDISPLAY, (const void**) &display);
423
pam_get_item(handle, PAM_TTY, (const void**) &tty);
424
pam_get_item(handle, PAM_RUSER, (const void**) &remote_user);
425
pam_get_item(handle, PAM_RHOST, (const void**) &remote_host);
427
seat = pam_getenv(handle, "XDG_SEAT");
429
seat = getenv("XDG_SEAT");
431
cvtnr = pam_getenv(handle, "XDG_VTNR");
433
cvtnr = getenv("XDG_VTNR");
435
service = strempty(service);
437
display = strempty(display);
438
remote_user = strempty(remote_user);
439
remote_host = strempty(remote_host);
440
seat = strempty(seat);
442
if (strchr(tty, ':')) {
443
/* A tty with a colon is usually an X11 display,
444
* placed there to show up in utmp. We rearrange
445
* things and don't pretend that an X display was a
448
if (isempty(display))
451
} else if (streq(tty, "cron")) {
452
/* cron has been setting PAM_TTY to "cron" for a very
453
* long time and it probably shouldn't stop doing that
454
* for compatibility reasons. */
456
type = "unspecified";
457
} else if (streq(tty, "ssh")) {
458
/* ssh has been setting PAM_TTY to "ssh" for a very
459
* long time and probably shouldn't stop doing that
460
* for compatibility reasons. */
465
/* If this fails vtnr will be 0, that's intended */
467
safe_atou32(cvtnr, &vtnr);
469
if (!isempty(display) && vtnr <= 0) {
471
get_seat_from_display(display, &seat, &vtnr);
472
else if (streq(seat, "seat0"))
473
get_seat_from_display(display, NULL, &vtnr);
477
type = !isempty(display) ? "x11" :
478
!isempty(tty) ? "tty" : "unspecified";
480
class = pam_getenv(handle, "XDG_SESSION_CLASS");
482
class = getenv("XDG_SESSION_CLASS");
486
class = streq(type, "unspecified") ? "background" : "user";
488
remote = !isempty(remote_host) &&
489
!streq(remote_host, "localhost") &&
490
!streq(remote_host, "localhost.localdomain");
492
if (!dbus_message_append_args(m,
493
DBUS_TYPE_UINT32, &uid,
494
DBUS_TYPE_UINT32, &pid,
495
DBUS_TYPE_STRING, &service,
496
DBUS_TYPE_STRING, &type,
497
DBUS_TYPE_STRING, &class,
498
DBUS_TYPE_STRING, &seat,
499
DBUS_TYPE_UINT32, &vtnr,
500
DBUS_TYPE_STRING, &tty,
501
DBUS_TYPE_STRING, &display,
502
DBUS_TYPE_BOOLEAN, &remote,
503
DBUS_TYPE_STRING, &remote_user,
504
DBUS_TYPE_STRING, &remote_host,
505
DBUS_TYPE_INVALID)) {
506
pam_syslog(handle, LOG_ERR, "Could not attach parameters to message.");
511
dbus_message_iter_init_append(m, &iter);
513
r = bus_append_strv_iter(&iter, controllers);
515
pam_syslog(handle, LOG_ERR, "Could not attach parameter to message.");
520
r = bus_append_strv_iter(&iter, reset_controllers);
522
pam_syslog(handle, LOG_ERR, "Could not attach parameter to message.");
528
if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &kp)) {
529
pam_syslog(handle, LOG_ERR, "Could not attach parameter to message.");
535
pam_syslog(handle, LOG_DEBUG, "Asking logind to create session: "
536
"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",
537
uid, pid, service, type, class, seat, vtnr, tty, display, yes_no(remote), remote_user, remote_host);
539
reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
541
pam_syslog(handle, LOG_ERR, "Failed to create session: %s", bus_error_message(&error));
546
if (!dbus_message_get_args(reply, &error,
547
DBUS_TYPE_STRING, &id,
548
DBUS_TYPE_OBJECT_PATH, &object_path,
549
DBUS_TYPE_STRING, &runtime_path,
550
DBUS_TYPE_UNIX_FD, &session_fd,
551
DBUS_TYPE_STRING, &seat,
552
DBUS_TYPE_UINT32, &vtnr,
553
DBUS_TYPE_BOOLEAN, &existing,
554
DBUS_TYPE_INVALID)) {
555
pam_syslog(handle, LOG_ERR, "Failed to parse message: %s", bus_error_message(&error));
561
pam_syslog(handle, LOG_DEBUG, "Reply from logind: "
562
"id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u",
563
id, object_path, runtime_path, session_fd, seat, vtnr);
565
r = pam_misc_setenv(handle, "XDG_SESSION_ID", id, 0);
566
if (r != PAM_SUCCESS) {
567
pam_syslog(handle, LOG_ERR, "Failed to set session id.");
571
r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", runtime_path, 0);
572
if (r != PAM_SUCCESS) {
573
pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
577
if (!isempty(seat)) {
578
r = pam_misc_setenv(handle, "XDG_SEAT", seat, 0);
579
if (r != PAM_SUCCESS) {
580
pam_syslog(handle, LOG_ERR, "Failed to set seat.");
587
snprintf(buf, sizeof(buf), "%u", vtnr);
590
r = pam_misc_setenv(handle, "XDG_VTNR", buf, 0);
591
if (r != PAM_SUCCESS) {
592
pam_syslog(handle, LOG_ERR, "Failed to set virtual terminal number.");
597
r = pam_set_data(handle, "systemd.existing", INT_TO_PTR(!!existing), NULL);
598
if (r != PAM_SUCCESS) {
599
pam_syslog(handle, LOG_ERR, "Failed to install existing flag.");
603
if (session_fd >= 0) {
604
r = pam_set_data(handle, "systemd.session-fd", INT_TO_PTR(session_fd+1), NULL);
605
if (r != PAM_SUCCESS) {
606
pam_syslog(handle, LOG_ERR, "Failed to install session fd.");
616
strv_free(controllers);
617
strv_free(reset_controllers);
618
strv_free(kill_only_users);
619
strv_free(kill_exclude_users);
621
dbus_error_free(&error);
624
dbus_connection_close(bus);
625
dbus_connection_unref(bus);
629
dbus_message_unref(m);
632
dbus_message_unref(reply);
635
close_nointr_nofail(session_fd);
640
_public_ PAM_EXTERN int pam_sm_close_session(
641
pam_handle_t *handle,
643
int argc, const char **argv) {
645
const void *p = NULL, *existing = NULL;
647
DBusConnection *bus = NULL;
648
DBusMessage *m = NULL, *reply = NULL;
654
dbus_error_init(&error);
656
/* Only release session if it wasn't pre-existing when we
657
* tried to create it */
658
pam_get_data(handle, "systemd.existing", &existing);
660
id = pam_getenv(handle, "XDG_SESSION_ID");
661
if (id && !existing) {
663
/* Before we go and close the FIFO we need to tell
664
* logind that this is a clean session shutdown, so
665
* that it doesn't just go and slaughter us
666
* immediately after closing the fd */
668
bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
670
pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", bus_error_message(&error));
675
m = dbus_message_new_method_call(
676
"org.freedesktop.login1",
677
"/org/freedesktop/login1",
678
"org.freedesktop.login1.Manager",
681
pam_syslog(handle, LOG_ERR, "Could not allocate release session message.");
686
if (!dbus_message_append_args(m,
687
DBUS_TYPE_STRING, &id,
688
DBUS_TYPE_INVALID)) {
689
pam_syslog(handle, LOG_ERR, "Could not attach parameters to message.");
694
reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
696
pam_syslog(handle, LOG_ERR, "Failed to release session: %s", bus_error_message(&error));
705
pam_get_data(handle, "systemd.session-fd", &p);
707
close_nointr(PTR_TO_INT(p) - 1);
709
dbus_error_free(&error);
712
dbus_connection_close(bus);
713
dbus_connection_unref(bus);
717
dbus_message_unref(m);
720
dbus_message_unref(reply);