]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/pam-module.c
update TODO
[thirdparty/systemd.git] / src / login / 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
5430f7f2
LP
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
8c6db833
LP
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
5430f7f2 16 Lesser General Public License for more details.
8c6db833 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
8c6db833
LP
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
81527be1
LP
35#include <systemd/sd-daemon.h>
36
8c6db833 37#include "util.h"
d7832d2c 38#include "audit.h"
8c6db833 39#include "macro.h"
74fe1fe3 40#include "strv.h"
98a28fef
LP
41#include "dbus-common.h"
42#include "def.h"
4d6d6518 43#include "socket-util.h"
8c6db833
LP
44
45static int parse_argv(pam_handle_t *handle,
46 int argc, const char **argv,
b20c6be6 47 char ***controllers,
e9fbc77c 48 char ***reset_controllers,
98a28fef 49 bool *kill_processes,
e9fbc77c 50 char ***kill_only_users,
0e318cad
MS
51 char ***kill_exclude_users,
52 bool *debug) {
8c6db833
LP
53
54 unsigned i;
55
56 assert(argc >= 0);
57 assert(argc == 0 || argv);
58
59 for (i = 0; i < (unsigned) argc; i++) {
60 int k;
61
c36eecdf
LP
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.");
8c6db833
LP
65 return k;
66 }
67
98a28fef
LP
68 if (kill_processes)
69 *kill_processes = k;
74fe1fe3 70
8c6db833 71 } else if (startswith(argv[i], "kill-session=")) {
98a28fef
LP
72 /* As compatibility for old versions */
73
8c6db833
LP
74 if ((k = parse_boolean(argv[i] + 13)) < 0) {
75 pam_syslog(handle, LOG_ERR, "Failed to parse kill-session= argument.");
76 return k;
77 }
78
98a28fef
LP
79 if (kill_processes)
80 *kill_processes = k;
74fe1fe3
LP
81
82 } else if (startswith(argv[i], "controllers=")) {
83
84 if (controllers) {
85 char **l;
86
87 if (!(l = strv_split(argv[i] + 12, ","))) {
88 pam_syslog(handle, LOG_ERR, "Out of memory.");
89 return -ENOMEM;
90 }
91
92 strv_free(*controllers);
93 *controllers = l;
94 }
95
b20c6be6
LP
96 } else if (startswith(argv[i], "reset-controllers=")) {
97
98 if (reset_controllers) {
99 char **l;
100
101 if (!(l = strv_split(argv[i] + 18, ","))) {
102 pam_syslog(handle, LOG_ERR, "Out of memory.");
103 return -ENOMEM;
104 }
105
106 strv_free(*reset_controllers);
107 *reset_controllers = l;
108 }
109
e9fbc77c
LP
110 } else if (startswith(argv[i], "kill-only-users=")) {
111
112 if (kill_only_users) {
113 char **l;
114
115 if (!(l = strv_split(argv[i] + 16, ","))) {
116 pam_syslog(handle, LOG_ERR, "Out of memory.");
117 return -ENOMEM;
118 }
119
120 strv_free(*kill_only_users);
121 *kill_only_users = l;
122 }
123
124 } else if (startswith(argv[i], "kill-exclude-users=")) {
125
126 if (kill_exclude_users) {
127 char **l;
128
129 if (!(l = strv_split(argv[i] + 19, ","))) {
130 pam_syslog(handle, LOG_ERR, "Out of memory.");
131 return -ENOMEM;
132 }
133
134 strv_free(*kill_exclude_users);
135 *kill_exclude_users = l;
136 }
137
0e318cad
MS
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.");
141 return k;
142 }
143
144 if (debug)
145 *debug = k;
146
98a28fef
LP
147 } else if (startswith(argv[i], "create-session=") ||
148 startswith(argv[i], "kill-user=")) {
149
150 pam_syslog(handle, LOG_WARNING, "Option %s not supported anymore, ignoring.", argv[i]);
151
8c6db833
LP
152 } else {
153 pam_syslog(handle, LOG_ERR, "Unknown parameter '%s'.", argv[i]);
154 return -EINVAL;
155 }
156 }
157
8c6db833
LP
158 return 0;
159}
160
8c6db833
LP
161static int get_user_data(
162 pam_handle_t *handle,
163 const char **ret_username,
164 struct passwd **ret_pw) {
165
d90b9d27
LP
166 const char *username = NULL;
167 struct passwd *pw = NULL;
87d2c1ff 168 uid_t uid;
8c6db833
LP
169 int r;
170
171 assert(handle);
172 assert(ret_username);
173 assert(ret_pw);
174
87d2c1ff
LP
175 r = audit_loginuid_from_pid(0, &uid);
176 if (r >= 0)
177 pw = pam_modutil_getpwuid(handle, uid);
178 else {
179 r = pam_get_user(handle, &username, NULL);
180 if (r != PAM_SUCCESS) {
d90b9d27
LP
181 pam_syslog(handle, LOG_ERR, "Failed to get user name.");
182 return r;
183 }
184
87d2c1ff 185 if (isempty(username)) {
d90b9d27
LP
186 pam_syslog(handle, LOG_ERR, "User name not valid.");
187 return PAM_AUTH_ERR;
188 }
189
190 pw = pam_modutil_getpwnam(handle, username);
8c6db833
LP
191 }
192
d90b9d27 193 if (!pw) {
8c6db833
LP
194 pam_syslog(handle, LOG_ERR, "Failed to get user data.");
195 return PAM_USER_UNKNOWN;
196 }
197
198 *ret_pw = pw;
d90b9d27 199 *ret_username = username ? username : pw->pw_name;
8c6db833
LP
200
201 return PAM_SUCCESS;
202}
203
e9fbc77c
LP
204static bool check_user_lists(
205 pam_handle_t *handle,
206 uid_t uid,
207 char **kill_only_users,
208 char **kill_exclude_users) {
209
210 const char *name = NULL;
211 char **l;
212
213 assert(handle);
214
215 if (uid == 0)
216 name = "root"; /* Avoid obvious NSS requests, to suppress network traffic */
217 else {
218 struct passwd *pw;
219
98a28fef
LP
220 pw = pam_modutil_getpwuid(handle, uid);
221 if (pw)
e9fbc77c
LP
222 name = pw->pw_name;
223 }
224
225 STRV_FOREACH(l, kill_exclude_users) {
ddd88763 226 uid_t u;
e9fbc77c 227
ddd88763
LP
228 if (parse_uid(*l, &u) >= 0)
229 if (u == uid)
e9fbc77c
LP
230 return false;
231
232 if (name && streq(name, *l))
233 return false;
234 }
235
236 if (strv_isempty(kill_only_users))
237 return true;
238
239 STRV_FOREACH(l, kill_only_users) {
ddd88763 240 uid_t u;
e9fbc77c 241
ddd88763
LP
242 if (parse_uid(*l, &u) >= 0)
243 if (u == uid)
e9fbc77c
LP
244 return true;
245
246 if (name && streq(name, *l))
247 return true;
248 }
249
250 return false;
251}
252
4d6d6518
LP
253static int get_seat_from_display(const char *display, const char **seat, uint32_t *vtnr) {
254 char *p = NULL;
255 int r;
256 int fd;
257 union sockaddr_union sa;
258 struct ucred ucred;
259 socklen_t l;
260 char *tty;
261 int v;
262
263 assert(display);
4d6d6518
LP
264 assert(vtnr);
265
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
270 * semi-ugly. */
271
272 r = socket_from_display(display, &p);
273 if (r < 0)
274 return r;
275
276 fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
277 if (fd < 0) {
278 free(p);
279 return -errno;
280 }
281
282 zero(sa);
283 sa.un.sun_family = AF_UNIX;
284 strncpy(sa.un.sun_path, p, sizeof(sa.un.sun_path)-1);
285 free(p);
286
287 if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
288 close_nointr_nofail(fd);
289 return -errno;
290 }
291
292 l = sizeof(ucred);
293 r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l);
294 close_nointr_nofail(fd);
295
296 if (r < 0)
297 return -errno;
298
299 r = get_ctty(ucred.pid, NULL, &tty);
300 if (r < 0)
301 return r;
302
303 v = vtnr_from_tty(tty);
304 free(tty);
305
306 if (v < 0)
307 return v;
308 else if (v == 0)
309 return -ENOENT;
310
fc7985ed
LP
311 if (seat)
312 *seat = "seat0";
4d6d6518
LP
313 *vtnr = (uint32_t) v;
314
315 return 0;
316}
317
98a28fef 318_public_ PAM_EXTERN int pam_sm_open_session(
8c6db833
LP
319 pam_handle_t *handle,
320 int flags,
321 int argc, const char **argv) {
322
8c6db833 323 struct passwd *pw;
98a28fef 324 bool kill_processes = false, debug = false;
0ad1271f 325 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, *cvtnr = NULL;
98a28fef 326 char **controllers = NULL, **reset_controllers = NULL, **kill_only_users = NULL, **kill_exclude_users = NULL;
98a28fef
LP
327 DBusError error;
328 uint32_t uid, pid;
329 DBusMessageIter iter;
330 dbus_bool_t kp;
98a28fef
LP
331 int session_fd = -1;
332 DBusConnection *bus = NULL;
333 DBusMessage *m = NULL, *reply = NULL;
77085881 334 dbus_bool_t remote, existing;
ed18b08b 335 int r;
4d6d6518 336 uint32_t vtnr = 0;
8c6db833
LP
337
338 assert(handle);
339
98a28fef
LP
340 dbus_error_init(&error);
341
ed18b08b 342 /* pam_syslog(handle, LOG_INFO, "pam-systemd initializing"); */
98a28fef 343
8c6db833
LP
344 /* Make this a NOP on non-systemd systems */
345 if (sd_booted() <= 0)
346 return PAM_SUCCESS;
347
e9fbc77c
LP
348 if (parse_argv(handle,
349 argc, argv,
98a28fef
LP
350 &controllers, &reset_controllers,
351 &kill_processes, &kill_only_users, &kill_exclude_users,
ed18b08b
LP
352 &debug) < 0) {
353 r = PAM_SESSION_ERR;
354 goto finish;
355 }
74fe1fe3 356
98a28fef
LP
357 r = get_user_data(handle, &username, &pw);
358 if (r != PAM_SUCCESS)
8c6db833
LP
359 goto finish;
360
30b2c336
LP
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
365 * leave. */
366
367 pam_get_item(handle, PAM_SERVICE, (const void**) &service);
368 if (streq_ptr(service, "systemd-shared")) {
369 char *p, *rt = NULL;
370
371 if (asprintf(&p, "/run/systemd/users/%lu", (unsigned long) pw->pw_uid) < 0) {
372 r = PAM_BUF_ERR;
373 goto finish;
374 }
375
376 r = parse_env_file(p, NEWLINE,
377 "RUNTIME", &rt,
378 NULL);
379 free(p);
380
381 if (r < 0 && r != -ENOENT) {
382 r = PAM_SESSION_ERR;
383 free(rt);
384 goto finish;
385 }
386
387 if (rt) {
388 r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0);
389 free(rt);
390
391 if (r != PAM_SUCCESS) {
392 pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
393 goto finish;
394 }
395 }
396
397 r = PAM_SUCCESS;
398 goto finish;
399 }
400
98a28fef
LP
401 if (kill_processes)
402 kill_processes = check_user_lists(handle, pw->pw_uid, kill_only_users, kill_exclude_users);
1f73f0f1 403
ed18b08b
LP
404 dbus_connection_set_change_sigpipe(FALSE);
405
98a28fef
LP
406 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
407 if (!bus) {
408 pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", bus_error_message(&error));
409 r = PAM_SESSION_ERR;
8c6db833
LP
410 goto finish;
411 }
412
98a28fef
LP
413 m = dbus_message_new_method_call(
414 "org.freedesktop.login1",
415 "/org/freedesktop/login1",
416 "org.freedesktop.login1.Manager",
417 "CreateSession");
98a28fef
LP
418 if (!m) {
419 pam_syslog(handle, LOG_ERR, "Could not allocate create session message.");
8c6db833
LP
420 r = PAM_BUF_ERR;
421 goto finish;
422 }
423
98a28fef
LP
424 uid = pw->pw_uid;
425 pid = getpid();
426
98a28fef
LP
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);
a8573ccc 431
bbc73283 432 seat = pam_getenv(handle, "XDG_SEAT");
a8573ccc
LP
433 if (isempty(seat))
434 seat = getenv("XDG_SEAT");
435
bbc73283 436 cvtnr = pam_getenv(handle, "XDG_VTNR");
a8573ccc
LP
437 if (isempty(cvtnr))
438 cvtnr = getenv("XDG_VTNR");
98a28fef 439
ed18b08b
LP
440 service = strempty(service);
441 tty = strempty(tty);
442 display = strempty(display);
443 remote_user = strempty(remote_user);
444 remote_host = strempty(remote_host);
445 seat = strempty(seat);
98a28fef 446
ee8545b0
LP
447 if (strchr(tty, ':')) {
448 /* A tty with a colon is usually an X11 display, place
449 * there to show up in utmp. We rearrange things and
450 * don't pretend that an X display was a tty */
451
452 if (isempty(display))
453 display = tty;
cd58752a 454 tty = "";
1a4459d6 455 } else if (streq(tty, "cron")) {
0ad1271f
LP
456 /* cron has been setting PAM_TTY to "cron" for a very
457 * long time and it probably shouldn't stop doing that
458 * for compatibility reasons. */
1a4459d6 459 tty = "";
0ad1271f
LP
460 type = "unspecified";
461 } else if (streq(tty, "ssh")) {
462 /* ssh has been setting PAM_TTY to "ssh" for a very
463 * long time and probably shouldn't stop doing that
464 * for compatibility reasons. */
465 tty = "";
466 type ="tty";
ee8545b0
LP
467 }
468
8e7705e5 469 /* If this fails vtnr will be 0, that's intended */
4d6d6518
LP
470 if (!isempty(cvtnr))
471 safe_atou32(cvtnr, &vtnr);
472
fc7985ed
LP
473 if (!isempty(display) && vtnr <= 0) {
474 if (isempty(seat))
6ef25fb6 475 get_seat_from_display(display, &seat, &vtnr);
fc7985ed 476 else if (streq(seat, "seat0"))
6ef25fb6 477 get_seat_from_display(display, NULL, &vtnr);
fc7985ed 478 }
4d6d6518 479
0ad1271f
LP
480 if (!type)
481 type = !isempty(display) ? "x11" :
482 !isempty(tty) ? "tty" : "unspecified";
98a28fef 483
55efac6c 484 class = pam_getenv(handle, "XDG_SESSION_CLASS");
a8573ccc
LP
485 if (isempty(class))
486 class = getenv("XDG_SESSION_CLASS");
55efac6c
LP
487 if (isempty(class))
488 class = "user";
489
490 remote = !isempty(remote_host) &&
491 !streq(remote_host, "localhost") &&
492 !streq(remote_host, "localhost.localdomain");
98a28fef
LP
493
494 if (!dbus_message_append_args(m,
495 DBUS_TYPE_UINT32, &uid,
496 DBUS_TYPE_UINT32, &pid,
497 DBUS_TYPE_STRING, &service,
498 DBUS_TYPE_STRING, &type,
55efac6c 499 DBUS_TYPE_STRING, &class,
98a28fef 500 DBUS_TYPE_STRING, &seat,
4d6d6518 501 DBUS_TYPE_UINT32, &vtnr,
98a28fef
LP
502 DBUS_TYPE_STRING, &tty,
503 DBUS_TYPE_STRING, &display,
504 DBUS_TYPE_BOOLEAN, &remote,
505 DBUS_TYPE_STRING, &remote_user,
506 DBUS_TYPE_STRING, &remote_host,
507 DBUS_TYPE_INVALID)) {
508 pam_syslog(handle, LOG_ERR, "Could not attach parameters to message.");
509 r = PAM_BUF_ERR;
510 goto finish;
511 }
8c6db833 512
98a28fef 513 dbus_message_iter_init_append(m, &iter);
8c6db833 514
98a28fef
LP
515 r = bus_append_strv_iter(&iter, controllers);
516 if (r < 0) {
517 pam_syslog(handle, LOG_ERR, "Could not attach parameter to message.");
518 r = PAM_BUF_ERR;
519 goto finish;
520 }
672c48cc 521
98a28fef
LP
522 r = bus_append_strv_iter(&iter, reset_controllers);
523 if (r < 0) {
524 pam_syslog(handle, LOG_ERR, "Could not attach parameter to message.");
525 r = PAM_BUF_ERR;
526 goto finish;
527 }
672c48cc 528
98a28fef
LP
529 kp = kill_processes;
530 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &kp)) {
531 pam_syslog(handle, LOG_ERR, "Could not attach parameter to message.");
532 r = PAM_BUF_ERR;
533 goto finish;
534 }
8c6db833 535
ce959314
MS
536 if (debug)
537 pam_syslog(handle, LOG_DEBUG, "Asking logind to create session: "
f904bdf2
CG
538 "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",
539 uid, pid, service, type, class, seat, vtnr, tty, display, yes_no(remote), remote_user, remote_host);
ce959314 540
98a28fef
LP
541 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
542 if (!reply) {
543 pam_syslog(handle, LOG_ERR, "Failed to create session: %s", bus_error_message(&error));
544 r = PAM_SESSION_ERR;
545 goto finish;
546 }
74fe1fe3 547
98a28fef
LP
548 if (!dbus_message_get_args(reply, &error,
549 DBUS_TYPE_STRING, &id,
550 DBUS_TYPE_OBJECT_PATH, &object_path,
551 DBUS_TYPE_STRING, &runtime_path,
552 DBUS_TYPE_UNIX_FD, &session_fd,
bbc73283
LP
553 DBUS_TYPE_STRING, &seat,
554 DBUS_TYPE_UINT32, &vtnr,
77085881 555 DBUS_TYPE_BOOLEAN, &existing,
98a28fef
LP
556 DBUS_TYPE_INVALID)) {
557 pam_syslog(handle, LOG_ERR, "Failed to parse message: %s", bus_error_message(&error));
558 r = PAM_SESSION_ERR;
559 goto finish;
560 }
74fe1fe3 561
ce959314
MS
562 if (debug)
563 pam_syslog(handle, LOG_DEBUG, "Reply from logind: "
564 "id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u",
565 id, object_path, runtime_path, session_fd, seat, vtnr);
566
98a28fef
LP
567 r = pam_misc_setenv(handle, "XDG_SESSION_ID", id, 0);
568 if (r != PAM_SUCCESS) {
569 pam_syslog(handle, LOG_ERR, "Failed to set session id.");
570 goto finish;
8c6db833
LP
571 }
572
98a28fef
LP
573 r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", runtime_path, 0);
574 if (r != PAM_SUCCESS) {
575 pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
576 goto finish;
577 }
8c6db833 578
bbc73283
LP
579 if (!isempty(seat)) {
580 r = pam_misc_setenv(handle, "XDG_SEAT", seat, 0);
581 if (r != PAM_SUCCESS) {
582 pam_syslog(handle, LOG_ERR, "Failed to set seat.");
583 goto finish;
584 }
585 }
586
587 if (vtnr > 0) {
588 char buf[11];
589 snprintf(buf, sizeof(buf), "%u", vtnr);
590 char_array_0(buf);
591
592 r = pam_misc_setenv(handle, "XDG_VTNR", buf, 0);
593 if (r != PAM_SUCCESS) {
594 pam_syslog(handle, LOG_ERR, "Failed to set virtual terminal number.");
595 goto finish;
596 }
597 }
598
77085881
LP
599 r = pam_set_data(handle, "systemd.existing", INT_TO_PTR(!!existing), NULL);
600 if (r != PAM_SUCCESS) {
601 pam_syslog(handle, LOG_ERR, "Failed to install existing flag.");
602 return r;
603 }
604
21c390cc
LP
605 if (session_fd >= 0) {
606 r = pam_set_data(handle, "systemd.session-fd", INT_TO_PTR(session_fd+1), NULL);
607 if (r != PAM_SUCCESS) {
608 pam_syslog(handle, LOG_ERR, "Failed to install session fd.");
609 return r;
610 }
98a28fef 611 }
8c6db833 612
98a28fef 613 session_fd = -1;
8c6db833 614
98a28fef 615 r = PAM_SUCCESS;
8c6db833 616
98a28fef
LP
617finish:
618 strv_free(controllers);
619 strv_free(reset_controllers);
620 strv_free(kill_only_users);
621 strv_free(kill_exclude_users);
8c6db833 622
98a28fef 623 dbus_error_free(&error);
35d2e7ec 624
98a28fef
LP
625 if (bus) {
626 dbus_connection_close(bus);
627 dbus_connection_unref(bus);
8c6db833
LP
628 }
629
98a28fef
LP
630 if (m)
631 dbus_message_unref(m);
8c6db833 632
ed18b08b
LP
633 if (reply)
634 dbus_message_unref(reply);
635
98a28fef
LP
636 if (session_fd >= 0)
637 close_nointr_nofail(session_fd);
672c48cc 638
98a28fef
LP
639 return r;
640}
8c6db833 641
98a28fef
LP
642_public_ PAM_EXTERN int pam_sm_close_session(
643 pam_handle_t *handle,
644 int flags,
645 int argc, const char **argv) {
8c6db833 646
77085881 647 const void *p = NULL, *existing = NULL;
75c8e3cf
LP
648 const char *id;
649 DBusConnection *bus = NULL;
650 DBusMessage *m = NULL, *reply = NULL;
651 DBusError error;
652 int r;
8c6db833 653
75c8e3cf
LP
654 assert(handle);
655
656 dbus_error_init(&error);
657
77085881
LP
658 /* Only release session if it wasn't pre-existing when we
659 * tried to create it */
660 pam_get_data(handle, "systemd.existing", &existing);
661
75c8e3cf 662 id = pam_getenv(handle, "XDG_SESSION_ID");
77085881 663 if (id && !existing) {
75c8e3cf
LP
664
665 /* Before we go and close the FIFO we need to tell
666 * logind that this is a clean session shutdown, so
667 * that it doesn't just go and slaughter us
668 * immediately after closing the fd */
669
670 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
671 if (!bus) {
672 pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", bus_error_message(&error));
673 r = PAM_SESSION_ERR;
674 goto finish;
675 }
676
677 m = dbus_message_new_method_call(
678 "org.freedesktop.login1",
679 "/org/freedesktop/login1",
680 "org.freedesktop.login1.Manager",
681 "ReleaseSession");
682 if (!m) {
683 pam_syslog(handle, LOG_ERR, "Could not allocate release session message.");
684 r = PAM_BUF_ERR;
685 goto finish;
686 }
687
688 if (!dbus_message_append_args(m,
689 DBUS_TYPE_STRING, &id,
690 DBUS_TYPE_INVALID)) {
691 pam_syslog(handle, LOG_ERR, "Could not attach parameters to message.");
692 r = PAM_BUF_ERR;
693 goto finish;
694 }
74fe1fe3 695
75c8e3cf
LP
696 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
697 if (!reply) {
698 pam_syslog(handle, LOG_ERR, "Failed to release session: %s", bus_error_message(&error));
699 r = PAM_SESSION_ERR;
700 goto finish;
701 }
702 }
703
704 r = PAM_SUCCESS;
705
706finish:
707 pam_get_data(handle, "systemd.session-fd", &p);
98a28fef
LP
708 if (p)
709 close_nointr(PTR_TO_INT(p) - 1);
1f73f0f1 710
75c8e3cf
LP
711 dbus_error_free(&error);
712
713 if (bus) {
714 dbus_connection_close(bus);
715 dbus_connection_unref(bus);
716 }
717
718 if (m)
719 dbus_message_unref(m);
720
721 if (reply)
722 dbus_message_unref(reply);
723
724 return r;
8c6db833 725}