]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/pam-module.c
logind: make idle hint logic work
[thirdparty/systemd.git] / src / pam-module.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
8c6db833
LP
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
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 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <errno.h>
23#include <fcntl.h>
24#include <sys/file.h>
25#include <pwd.h>
a838e6a1 26#include <endian.h>
ac123445 27#include <sys/capability.h>
8c6db833
LP
28
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>
34
8c6db833
LP
35#include "util.h"
36#include "cgroup-util.h"
37#include "macro.h"
38#include "sd-daemon.h"
74fe1fe3 39#include "strv.h"
8c6db833
LP
40
41static int parse_argv(pam_handle_t *handle,
42 int argc, const char **argv,
43 bool *create_session,
44 bool *kill_session,
74fe1fe3 45 bool *kill_user,
b20c6be6 46 char ***controllers,
e9fbc77c
LP
47 char ***reset_controllers,
48 char ***kill_only_users,
0e318cad
MS
49 char ***kill_exclude_users,
50 bool *debug) {
8c6db833
LP
51
52 unsigned i;
b20c6be6 53 bool reset_controller_set = false;
e9fbc77c 54 bool kill_exclude_users_set = false;
8c6db833
LP
55
56 assert(argc >= 0);
57 assert(argc == 0 || argv);
58
59 for (i = 0; i < (unsigned) argc; i++) {
60 int k;
61
62 if (startswith(argv[i], "create-session=")) {
63 if ((k = parse_boolean(argv[i] + 15)) < 0) {
64 pam_syslog(handle, LOG_ERR, "Failed to parse create-session= argument.");
65 return k;
66 }
67
68 if (create_session)
69 *create_session = k;
74fe1fe3 70
8c6db833
LP
71 } else if (startswith(argv[i], "kill-session=")) {
72 if ((k = parse_boolean(argv[i] + 13)) < 0) {
73 pam_syslog(handle, LOG_ERR, "Failed to parse kill-session= argument.");
74 return k;
75 }
76
77 if (kill_session)
78 *kill_session = k;
79
80 } else if (startswith(argv[i], "kill-user=")) {
81 if ((k = parse_boolean(argv[i] + 10)) < 0) {
82 pam_syslog(handle, LOG_ERR, "Failed to parse kill-user= argument.");
83 return k;
84 }
85
86 if (kill_user)
87 *kill_user = k;
74fe1fe3
LP
88
89 } else if (startswith(argv[i], "controllers=")) {
90
91 if (controllers) {
92 char **l;
93
94 if (!(l = strv_split(argv[i] + 12, ","))) {
95 pam_syslog(handle, LOG_ERR, "Out of memory.");
96 return -ENOMEM;
97 }
98
99 strv_free(*controllers);
100 *controllers = l;
101 }
102
b20c6be6
LP
103 } else if (startswith(argv[i], "reset-controllers=")) {
104
105 if (reset_controllers) {
106 char **l;
107
108 if (!(l = strv_split(argv[i] + 18, ","))) {
109 pam_syslog(handle, LOG_ERR, "Out of memory.");
110 return -ENOMEM;
111 }
112
113 strv_free(*reset_controllers);
114 *reset_controllers = l;
115 }
116
117 reset_controller_set = true;
74fe1fe3 118
e9fbc77c
LP
119 } else if (startswith(argv[i], "kill-only-users=")) {
120
121 if (kill_only_users) {
122 char **l;
123
124 if (!(l = strv_split(argv[i] + 16, ","))) {
125 pam_syslog(handle, LOG_ERR, "Out of memory.");
126 return -ENOMEM;
127 }
128
129 strv_free(*kill_only_users);
130 *kill_only_users = l;
131 }
132
133 } else if (startswith(argv[i], "kill-exclude-users=")) {
134
135 if (kill_exclude_users) {
136 char **l;
137
138 if (!(l = strv_split(argv[i] + 19, ","))) {
139 pam_syslog(handle, LOG_ERR, "Out of memory.");
140 return -ENOMEM;
141 }
142
143 strv_free(*kill_exclude_users);
144 *kill_exclude_users = l;
145 }
146
147 kill_exclude_users_set = true;
148
0e318cad
MS
149 } else if (startswith(argv[i], "debug=")) {
150 if ((k = parse_boolean(argv[i] + 6)) < 0) {
151 pam_syslog(handle, LOG_ERR, "Failed to parse debug= argument.");
152 return k;
153 }
154
155 if (debug)
156 *debug = k;
157
8c6db833
LP
158 } else {
159 pam_syslog(handle, LOG_ERR, "Unknown parameter '%s'.", argv[i]);
160 return -EINVAL;
161 }
162 }
163
b20c6be6 164 if (!reset_controller_set && reset_controllers) {
74fe1fe3
LP
165 char **l;
166
167 if (!(l = strv_new("cpu", NULL))) {
168 pam_syslog(handle, LOG_ERR, "Out of memory");
169 return -ENOMEM;
170 }
171
b20c6be6 172 *reset_controllers = l;
74fe1fe3
LP
173 }
174
175 if (controllers)
176 strv_remove(*controllers, "name=systemd");
177
b20c6be6
LP
178 if (reset_controllers)
179 strv_remove(*reset_controllers, "name=systemd");
180
8c6db833
LP
181 if (kill_session && *kill_session && kill_user)
182 *kill_user = true;
183
e9fbc77c
LP
184 if (!kill_exclude_users_set && kill_exclude_users) {
185 char **l;
186
187 if (!(l = strv_new("root", NULL))) {
188 pam_syslog(handle, LOG_ERR, "Out of memory");
189 return -ENOMEM;
190 }
191
192 *kill_exclude_users = l;
193 }
194
8c6db833
LP
195 return 0;
196}
197
198static int open_file_and_lock(const char *fn) {
199 int fd;
200
201 assert(fn);
202
203 if ((fd = open(fn, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_CREAT, 0600)) < 0)
204 return -errno;
205
2e225d65
LP
206 /* The BSD socket semantics are a lot nicer than those of
207 * POSIX locks. Which is why we use flock() here. BSD locking
208 * does not work across NFS which however is not needed here
209 * as the filesystems in question should be local, and only
210 * locally accessible, and most likely even tmpfs. */
211
90102b22 212 if (flock(fd, LOCK_EX) < 0) {
90102b22 213 close_nointr_nofail(fd);
1f73f0f1 214 return -errno;
90102b22 215 }
8c6db833
LP
216
217 return fd;
218}
219
2e225d65
LP
220enum {
221 SESSION_ID_AUDIT = 'a',
222 SESSION_ID_COUNTER = 'c',
223 SESSION_ID_RANDOM = 'r'
224};
225
226static uint64_t get_session_id(int *mode) {
a838e6a1
LP
227 char *s;
228 int fd;
8c6db833 229
2e225d65
LP
230 assert(mode);
231
a838e6a1
LP
232 /* First attempt: let's use the session ID of the audit
233 * system, if it is available. */
81481c99
LP
234 if (have_effective_cap(CAP_AUDIT_CONTROL) > 0)
235 if (read_one_line_file("/proc/self/sessionid", &s) >= 0) {
236 uint32_t u;
237 int r;
8c6db833 238
81481c99
LP
239 r = safe_atou32(s, &u);
240 free(s);
8c6db833 241
81481c99
LP
242 if (r >= 0 && u != (uint32_t) -1 && u > 0) {
243 *mode = SESSION_ID_AUDIT;
244 return (uint64_t) u;
245 }
2e225d65 246 }
8c6db833 247
a838e6a1
LP
248 /* Second attempt, use our own counter. */
249 if ((fd = open_file_and_lock(RUNTIME_DIR "/user/.pam-systemd-session")) >= 0) {
250 uint64_t counter;
251 ssize_t r;
8c6db833 252
a838e6a1 253 /* We do a bit of endianess swapping here, just to be
bb29785e
LP
254 * sure. /run should be machine specific anyway, and
255 * even mounted from tmpfs, so this byteswapping
256 * should really not be necessary. But then again, you
257 * never know, so let's avoid any risk. */
8c6db833 258
a838e6a1
LP
259 if (loop_read(fd, &counter, sizeof(counter), false) != sizeof(counter))
260 counter = 1;
261 else
262 counter = le64toh(counter) + 1;
8c6db833 263
a838e6a1
LP
264 if (lseek(fd, 0, SEEK_SET) == 0) {
265 uint64_t swapped = htole64(counter);
266
267 r = loop_write(fd, &swapped, sizeof(swapped), false);
268
269 if (r != sizeof(swapped))
270 r = -EIO;
271 } else
272 r = -errno;
8c6db833 273
a838e6a1
LP
274 close_nointr_nofail(fd);
275
2e225d65
LP
276 if (r >= 0) {
277 *mode = SESSION_ID_COUNTER;
a838e6a1 278 return counter;
2e225d65 279 }
a838e6a1
LP
280 }
281
2e225d65
LP
282 *mode = SESSION_ID_RANDOM;
283
a838e6a1
LP
284 /* Last attempt, pick a random value */
285 return (uint64_t) random_ull();
286}
1f73f0f1 287
8c6db833
LP
288static int get_user_data(
289 pam_handle_t *handle,
290 const char **ret_username,
291 struct passwd **ret_pw) {
292
d90b9d27
LP
293 const char *username = NULL;
294 struct passwd *pw = NULL;
8c6db833 295 int r;
d90b9d27
LP
296 bool have_loginuid = false;
297 char *s;
8c6db833
LP
298
299 assert(handle);
300 assert(ret_username);
301 assert(ret_pw);
302
81481c99 303 if (have_effective_cap(CAP_AUDIT_CONTROL) > 0) {
ac123445
LP
304 /* Only use audit login uid if we are executed with
305 * sufficient capabilities so that pam_loginuid could
306 * do its job. If we are lacking the CAP_AUDIT_CONTROL
307 * capabality we most likely are being run in a
308 * container and /proc/self/loginuid is useless since
309 * it probably contains a uid of the host system. */
d90b9d27 310
ac123445
LP
311 if (read_one_line_file("/proc/self/loginuid", &s) >= 0) {
312 uint32_t u;
d90b9d27 313
ac123445
LP
314 r = safe_atou32(s, &u);
315 free(s);
316
317 if (r >= 0 && u != (uint32_t) -1 && u > 0) {
318 have_loginuid = true;
319 pw = pam_modutil_getpwuid(handle, u);
320 }
d90b9d27 321 }
8c6db833
LP
322 }
323
d90b9d27
LP
324 if (!have_loginuid) {
325 if ((r = pam_get_user(handle, &username, NULL)) != PAM_SUCCESS) {
326 pam_syslog(handle, LOG_ERR, "Failed to get user name.");
327 return r;
328 }
329
330 if (!username || !*username) {
331 pam_syslog(handle, LOG_ERR, "User name not valid.");
332 return PAM_AUTH_ERR;
333 }
334
335 pw = pam_modutil_getpwnam(handle, username);
8c6db833
LP
336 }
337
d90b9d27 338 if (!pw) {
8c6db833
LP
339 pam_syslog(handle, LOG_ERR, "Failed to get user data.");
340 return PAM_USER_UNKNOWN;
341 }
342
343 *ret_pw = pw;
d90b9d27 344 *ret_username = username ? username : pw->pw_name;
8c6db833
LP
345
346 return PAM_SUCCESS;
347}
348
74fe1fe3
LP
349static int create_user_group(
350 pam_handle_t *handle,
351 const char *controller,
352 const char *group,
353 struct passwd *pw,
354 bool attach,
355 bool remember) {
356
8c6db833
LP
357 int r;
358
359 assert(handle);
360 assert(group);
361
362 if (attach)
74fe1fe3 363 r = cg_create_and_attach(controller, group, 0);
8c6db833 364 else
74fe1fe3 365 r = cg_create(controller, group);
8c6db833
LP
366
367 if (r < 0) {
368 pam_syslog(handle, LOG_ERR, "Failed to create cgroup: %s", strerror(-r));
369 return PAM_SESSION_ERR;
370 }
371
672c48cc
LP
372 if (r > 0 && remember) {
373 /* Remember that it was us who created this group, and
374 * that hence we need to remove it too. This is a
375 * protection against removing the cgroup when run
376 * recursively. */
377 if ((r = pam_set_data(handle, "systemd.created", INT_TO_PTR(1), NULL)) != PAM_SUCCESS) {
378 pam_syslog(handle, LOG_ERR, "Failed to install created variable.");
379 return r;
380 }
381 }
382
74fe1fe3
LP
383 if ((r = cg_set_task_access(controller, group, 0644, pw->pw_uid, pw->pw_gid)) < 0 ||
384 (r = cg_set_group_access(controller, group, 0755, pw->pw_uid, pw->pw_gid)) < 0) {
8c6db833
LP
385 pam_syslog(handle, LOG_ERR, "Failed to change access modes: %s", strerror(-r));
386 return PAM_SESSION_ERR;
387 }
388
389 return PAM_SUCCESS;
390}
391
b20c6be6
LP
392static int reset_group(
393 pam_handle_t *handle,
394 const char *controller) {
395
396 int r;
397
398 assert(handle);
399
400 if ((r = cg_attach(controller, "/", 0)) < 0) {
401 pam_syslog(handle, LOG_ERR, "Failed to reset cgroup for controller %s: %s", controller, strerror(-r));
402 return PAM_SESSION_ERR;
403 }
404
405 return PAM_SUCCESS;
406}
407
8c6db833
LP
408_public_ PAM_EXTERN int pam_sm_open_session(
409 pam_handle_t *handle,
410 int flags,
411 int argc, const char **argv) {
412
413 const char *username = NULL;
414 struct passwd *pw;
415 int r;
416 char *buf = NULL;
417 int lock_fd = -1;
418 bool create_session = true;
0e318cad 419 bool debug = false;
b20c6be6 420 char **controllers = NULL, **reset_controllers = NULL, **c;
1f73f0f1 421 char *cgroup_user_tree = NULL;
8c6db833
LP
422
423 assert(handle);
424
672c48cc 425 /* pam_syslog(handle, LOG_DEBUG, "pam-systemd initializing"); */
8c6db833 426
8c6db833
LP
427 /* Make this a NOP on non-systemd systems */
428 if (sd_booted() <= 0)
429 return PAM_SUCCESS;
430
e9fbc77c
LP
431 if (parse_argv(handle,
432 argc, argv,
433 &create_session, NULL, NULL,
434 &controllers, &reset_controllers,
0e318cad 435 NULL, NULL, &debug) < 0)
74fe1fe3
LP
436 return PAM_SESSION_ERR;
437
8c6db833
LP
438 if ((r = get_user_data(handle, &username, &pw)) != PAM_SUCCESS)
439 goto finish;
440
1f73f0f1
LP
441 if ((r = cg_get_user_path(&cgroup_user_tree)) < 0) {
442 pam_syslog(handle, LOG_ERR, "Failed to determine user cgroup tree: %s", strerror(-r));
443 r = PAM_SYSTEM_ERR;
444 goto finish;
445 }
446
8c6db833
LP
447 if (safe_mkdir(RUNTIME_DIR "/user", 0755, 0, 0) < 0) {
448 pam_syslog(handle, LOG_ERR, "Failed to create runtime directory: %m");
449 r = PAM_SYSTEM_ERR;
450 goto finish;
451 }
452
453 if ((lock_fd = open_file_and_lock(RUNTIME_DIR "/user/.pam-systemd-lock")) < 0) {
454 pam_syslog(handle, LOG_ERR, "Failed to lock runtime directory: %m");
455 r = PAM_SYSTEM_ERR;
456 goto finish;
457 }
458
bb29785e 459 /* Create /run/user/$USER */
8c6db833
LP
460 free(buf);
461 if (asprintf(&buf, RUNTIME_DIR "/user/%s", username) < 0) {
462 r = PAM_BUF_ERR;
463 goto finish;
464 }
465
466 if (safe_mkdir(buf, 0700, pw->pw_uid, pw->pw_gid) < 0) {
467 pam_syslog(handle, LOG_WARNING, "Failed to create runtime directory: %m");
468 r = PAM_SYSTEM_ERR;
469 goto finish;
470 } else if ((r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", buf, 0)) != PAM_SUCCESS) {
471 pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
472 goto finish;
473 }
474
475 free(buf);
476 buf = NULL;
477
478 if (create_session) {
a838e6a1 479 const char *id;
8c6db833
LP
480
481 /* Reuse or create XDG session ID */
a838e6a1 482 if (!(id = pam_getenv(handle, "XDG_SESSION_ID"))) {
2e225d65 483 int mode;
a838e6a1 484
2e225d65 485 if (asprintf(&buf, "%llux", (unsigned long long) get_session_id(&mode)) < 0) {
8c6db833
LP
486 r = PAM_BUF_ERR;
487 goto finish;
488 }
489
2e225d65
LP
490 /* To avoid id clashes we add the session id
491 * source to our session ids. Note that the
492 * session id source might change during
493 * runtime, because a filesystem became
494 * writable or the system reconfigured. */
495 buf[strlen(buf)-1] =
496 mode != SESSION_ID_AUDIT ? (char) mode : 0;
497
a838e6a1
LP
498 if ((r = pam_misc_setenv(handle, "XDG_SESSION_ID", buf, 0)) != PAM_SUCCESS) {
499 pam_syslog(handle, LOG_ERR, "Failed to set session id.");
8c6db833
LP
500 goto finish;
501 }
502
a838e6a1
LP
503 if (!(id = pam_getenv(handle, "XDG_SESSION_ID"))) {
504 pam_syslog(handle, LOG_ERR, "Failed to get session id.");
8c6db833
LP
505 r = PAM_SESSION_ERR;
506 goto finish;
507 }
508 }
509
1f73f0f1 510 r = asprintf(&buf, "%s/%s/%s", cgroup_user_tree, username, id);
8c6db833 511 } else
1f73f0f1 512 r = asprintf(&buf, "%s/%s/master", cgroup_user_tree, username);
8c6db833
LP
513
514 if (r < 0) {
515 r = PAM_BUF_ERR;
516 goto finish;
517 }
518
0e318cad
MS
519 if (debug)
520 pam_syslog(handle, LOG_DEBUG, "Moving new user session for %s into control group %s.", username, buf);
672c48cc 521
74fe1fe3 522 if ((r = create_user_group(handle, SYSTEMD_CGROUP_CONTROLLER, buf, pw, true, true)) != PAM_SUCCESS)
8c6db833
LP
523 goto finish;
524
74fe1fe3
LP
525 /* The additional controllers don't really matter, so we
526 * ignore the return value */
527 STRV_FOREACH(c, controllers)
528 create_user_group(handle, *c, buf, pw, true, false);
529
b20c6be6
LP
530 STRV_FOREACH(c, reset_controllers)
531 reset_group(handle, *c);
532
8c6db833
LP
533 r = PAM_SUCCESS;
534
535finish:
536 free(buf);
537
538 if (lock_fd >= 0)
539 close_nointr_nofail(lock_fd);
540
74fe1fe3 541 strv_free(controllers);
b20c6be6 542 strv_free(reset_controllers);
74fe1fe3 543
1f73f0f1
LP
544 free(cgroup_user_tree);
545
8c6db833
LP
546 return r;
547}
548
549static int session_remains(pam_handle_t *handle, const char *user_path) {
35d2e7ec 550 int r;
8c6db833 551 bool remains = false;
35d2e7ec
LP
552 DIR *d;
553 char *subgroup;
8c6db833 554
35d2e7ec
LP
555 if ((r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, user_path, &d)) < 0)
556 return r;
8c6db833 557
35d2e7ec 558 while ((r = cg_read_subgroup(d, &subgroup)) > 0) {
8c6db833 559
96a8cbfa 560 remains = !streq(subgroup, "master");
35d2e7ec 561 free(subgroup);
8c6db833 562
35d2e7ec
LP
563 if (remains)
564 break;
8c6db833
LP
565 }
566
35d2e7ec 567 closedir(d);
8c6db833 568
35d2e7ec
LP
569 if (r < 0)
570 return r;
8c6db833 571
35d2e7ec 572 return !!remains;
8c6db833
LP
573}
574
e9fbc77c
LP
575static bool check_user_lists(
576 pam_handle_t *handle,
577 uid_t uid,
578 char **kill_only_users,
579 char **kill_exclude_users) {
580
581 const char *name = NULL;
582 char **l;
583
584 assert(handle);
585
586 if (uid == 0)
587 name = "root"; /* Avoid obvious NSS requests, to suppress network traffic */
588 else {
589 struct passwd *pw;
590
591 if ((pw = pam_modutil_getpwuid(handle, uid)))
592 name = pw->pw_name;
593 }
594
595 STRV_FOREACH(l, kill_exclude_users) {
596 uint32_t id;
597
598 if (safe_atou32(*l, &id) >= 0)
599 if ((uid_t) id == uid)
600 return false;
601
602 if (name && streq(name, *l))
603 return false;
604 }
605
606 if (strv_isempty(kill_only_users))
607 return true;
608
609 STRV_FOREACH(l, kill_only_users) {
610 uint32_t id;
611
612 if (safe_atou32(*l, &id) >= 0)
613 if ((uid_t) id == uid)
614 return true;
615
616 if (name && streq(name, *l))
617 return true;
618 }
619
620 return false;
621}
622
8c6db833
LP
623_public_ PAM_EXTERN int pam_sm_close_session(
624 pam_handle_t *handle,
625 int flags,
626 int argc, const char **argv) {
627
628 const char *username = NULL;
629 bool kill_session = false;
630 bool kill_user = false;
0e318cad 631 bool debug = false;
8c6db833
LP
632 int lock_fd = -1, r;
633 char *session_path = NULL, *nosession_path = NULL, *user_path = NULL;
a838e6a1 634 const char *id;
8c6db833 635 struct passwd *pw;
672c48cc 636 const void *created = NULL;
e9fbc77c 637 char **controllers = NULL, **c, **kill_only_users = NULL, **kill_exclude_users = NULL;
1f73f0f1 638 char *cgroup_user_tree = NULL;
8c6db833
LP
639
640 assert(handle);
641
8c6db833
LP
642 /* Make this a NOP on non-systemd systems */
643 if (sd_booted() <= 0)
644 return PAM_SUCCESS;
645
e9fbc77c
LP
646 if (parse_argv(handle,
647 argc, argv,
648 NULL, &kill_session, &kill_user,
649 &controllers, NULL,
0e318cad 650 &kill_only_users, &kill_exclude_users, &debug) < 0)
74fe1fe3
LP
651 return PAM_SESSION_ERR;
652
8c6db833
LP
653 if ((r = get_user_data(handle, &username, &pw)) != PAM_SUCCESS)
654 goto finish;
655
1f73f0f1
LP
656 if ((r = cg_get_user_path(&cgroup_user_tree)) < 0) {
657 pam_syslog(handle, LOG_ERR, "Failed to determine user cgroup tree: %s", strerror(-r));
658 r = PAM_SYSTEM_ERR;
659 goto finish;
660 }
661
8c6db833
LP
662 if ((lock_fd = open_file_and_lock(RUNTIME_DIR "/user/.pam-systemd-lock")) < 0) {
663 pam_syslog(handle, LOG_ERR, "Failed to lock runtime directory: %m");
664 r = PAM_SYSTEM_ERR;
665 goto finish;
666 }
667
824a1d59 668 /* We are probably still in some session/user dir. Move ourselves out of the way as first step */
1f73f0f1 669 if ((r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, cgroup_user_tree, 0)) < 0)
35d2e7ec
LP
670 pam_syslog(handle, LOG_ERR, "Failed to move us away: %s", strerror(-r));
671
74fe1fe3 672 STRV_FOREACH(c, controllers)
1f73f0f1 673 if ((r = cg_attach(*c, cgroup_user_tree, 0)) < 0)
74fe1fe3
LP
674 pam_syslog(handle, LOG_ERR, "Failed to move us away in %s hierarchy: %s", *c, strerror(-r));
675
1f73f0f1 676 if (asprintf(&user_path, "%s/%s", cgroup_user_tree, username) < 0) {
8c6db833
LP
677 r = PAM_BUF_ERR;
678 goto finish;
679 }
680
672c48cc
LP
681 pam_get_data(handle, "systemd.created", &created);
682
683 if ((id = pam_getenv(handle, "XDG_SESSION_ID")) && created) {
8c6db833 684
1f73f0f1
LP
685 if (asprintf(&session_path, "%s/%s/%s", cgroup_user_tree, username, id) < 0 ||
686 asprintf(&nosession_path, "%s/%s/master", cgroup_user_tree, username) < 0) {
8c6db833
LP
687 r = PAM_BUF_ERR;
688 goto finish;
689 }
690
e9fbc77c 691 if (kill_session && check_user_lists(handle, pw->pw_uid, kill_only_users, kill_exclude_users)) {
0e318cad
MS
692 if (debug)
693 pam_syslog(handle, LOG_DEBUG, "Killing remaining processes of user session %s of %s.", id, username);
672c48cc 694
35d2e7ec
LP
695 /* Kill processes in session cgroup, and delete it */
696 if ((r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, session_path, true)) < 0)
8c6db833 697 pam_syslog(handle, LOG_ERR, "Failed to kill session cgroup: %s", strerror(-r));
35d2e7ec 698 } else {
0e318cad
MS
699 if (debug)
700 pam_syslog(handle, LOG_DEBUG, "Moving remaining processes of user session %s of %s into control group %s.", id, username, nosession_path);
672c48cc 701
824a1d59
LP
702 /* Migrate processes from session to user
703 * cgroup. First, try to create the user group
704 * in case it doesn't exist yet. Also, delete
705 * the session group. */
74fe1fe3 706 create_user_group(handle, SYSTEMD_CGROUP_CONTROLLER, nosession_path, pw, false, false);
8c6db833 707
35d2e7ec 708 if ((r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, session_path, nosession_path, false, true)) < 0)
8c6db833
LP
709 pam_syslog(handle, LOG_ERR, "Failed to migrate session cgroup: %s", strerror(-r));
710 }
74fe1fe3
LP
711
712 STRV_FOREACH(c, controllers) {
713 create_user_group(handle, *c, nosession_path, pw, false, false);
714
715 if ((r = cg_migrate_recursive(*c, session_path, nosession_path, false, true)) < 0)
716 pam_syslog(handle, LOG_ERR, "Failed to migrate session cgroup in hierarchy %s: %s", *c, strerror(-r));
717 }
8c6db833
LP
718 }
719
720 /* GC user tree */
c6c18be3 721 cg_trim(SYSTEMD_CGROUP_CONTROLLER, user_path, false);
8c6db833
LP
722
723 if ((r = session_remains(handle, user_path)) < 0)
724 pam_syslog(handle, LOG_ERR, "Failed to determine whether a session remains: %s", strerror(-r));
725
726 /* Kill user processes not attached to any session */
e9fbc77c 727 if (kill_user && r == 0 && check_user_lists(handle, pw->pw_uid, kill_only_users, kill_exclude_users)) {
8c6db833 728
824a1d59 729 /* Kill user cgroup */
35d2e7ec 730 if ((r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, user_path, true)) < 0)
8c6db833
LP
731 pam_syslog(handle, LOG_ERR, "Failed to kill user cgroup: %s", strerror(-r));
732 } else {
733
c6c18be3 734 if ((r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, user_path, true)) < 0)
8c6db833
LP
735 pam_syslog(handle, LOG_ERR, "Failed to check user cgroup: %s", strerror(-r));
736
35d2e7ec
LP
737 /* Remove user cgroup */
738 if (r > 0) {
739 if ((r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, user_path)) < 0)
740 pam_syslog(handle, LOG_ERR, "Failed to delete user cgroup: %s", strerror(-r));
741
742 /* If we managed to find somebody, don't cleanup the cgroup. */
743 } else if (r == 0)
8c6db833
LP
744 r = -EBUSY;
745 }
746
74fe1fe3
LP
747 STRV_FOREACH(c, controllers)
748 cg_trim(*c, user_path, true);
749
8c6db833
LP
750 if (r >= 0) {
751 const char *runtime_dir;
752
8c6db833
LP
753 if ((runtime_dir = pam_getenv(handle, "XDG_RUNTIME_DIR")))
754 if ((r = rm_rf(runtime_dir, false, true)) < 0)
755 pam_syslog(handle, LOG_ERR, "Failed to remove runtime directory: %s", strerror(-r));
756 }
757
672c48cc
LP
758 /* pam_syslog(handle, LOG_DEBUG, "pam-systemd done"); */
759
8c6db833
LP
760 r = PAM_SUCCESS;
761
762finish:
763 if (lock_fd >= 0)
764 close_nointr_nofail(lock_fd);
765
766 free(session_path);
767 free(nosession_path);
768 free(user_path);
769
74fe1fe3 770 strv_free(controllers);
e9fbc77c
LP
771 strv_free(kill_exclude_users);
772 strv_free(kill_only_users);
74fe1fe3 773
1f73f0f1
LP
774 free(cgroup_user_tree);
775
8c6db833
LP
776 return r;
777}