]> 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;
55efac6c 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;
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
MS
455 } else if (streq(tty, "cron")) {
456 /* cron has been setting PAM_TTY to "cron" for a very long time
457 * and it cannot stop doing that for compatibility reasons. */
458 tty = "";
ee8545b0
LP
459 }
460
8e7705e5 461 /* If this fails vtnr will be 0, that's intended */
4d6d6518
LP
462 if (!isempty(cvtnr))
463 safe_atou32(cvtnr, &vtnr);
464
fc7985ed
LP
465 if (!isempty(display) && vtnr <= 0) {
466 if (isempty(seat))
6ef25fb6 467 get_seat_from_display(display, &seat, &vtnr);
fc7985ed 468 else if (streq(seat, "seat0"))
6ef25fb6 469 get_seat_from_display(display, NULL, &vtnr);
fc7985ed 470 }
4d6d6518 471
98a28fef 472 type = !isempty(display) ? "x11" :
1dc99537 473 !isempty(tty) ? "tty" : "unspecified";
98a28fef 474
55efac6c 475 class = pam_getenv(handle, "XDG_SESSION_CLASS");
a8573ccc
LP
476 if (isempty(class))
477 class = getenv("XDG_SESSION_CLASS");
55efac6c
LP
478 if (isempty(class))
479 class = "user";
480
481 remote = !isempty(remote_host) &&
482 !streq(remote_host, "localhost") &&
483 !streq(remote_host, "localhost.localdomain");
98a28fef
LP
484
485 if (!dbus_message_append_args(m,
486 DBUS_TYPE_UINT32, &uid,
487 DBUS_TYPE_UINT32, &pid,
488 DBUS_TYPE_STRING, &service,
489 DBUS_TYPE_STRING, &type,
55efac6c 490 DBUS_TYPE_STRING, &class,
98a28fef 491 DBUS_TYPE_STRING, &seat,
4d6d6518 492 DBUS_TYPE_UINT32, &vtnr,
98a28fef
LP
493 DBUS_TYPE_STRING, &tty,
494 DBUS_TYPE_STRING, &display,
495 DBUS_TYPE_BOOLEAN, &remote,
496 DBUS_TYPE_STRING, &remote_user,
497 DBUS_TYPE_STRING, &remote_host,
498 DBUS_TYPE_INVALID)) {
499 pam_syslog(handle, LOG_ERR, "Could not attach parameters to message.");
500 r = PAM_BUF_ERR;
501 goto finish;
502 }
8c6db833 503
98a28fef 504 dbus_message_iter_init_append(m, &iter);
8c6db833 505
98a28fef
LP
506 r = bus_append_strv_iter(&iter, controllers);
507 if (r < 0) {
508 pam_syslog(handle, LOG_ERR, "Could not attach parameter to message.");
509 r = PAM_BUF_ERR;
510 goto finish;
511 }
672c48cc 512
98a28fef
LP
513 r = bus_append_strv_iter(&iter, reset_controllers);
514 if (r < 0) {
515 pam_syslog(handle, LOG_ERR, "Could not attach parameter to message.");
516 r = PAM_BUF_ERR;
517 goto finish;
518 }
672c48cc 519
98a28fef
LP
520 kp = kill_processes;
521 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &kp)) {
522 pam_syslog(handle, LOG_ERR, "Could not attach parameter to message.");
523 r = PAM_BUF_ERR;
524 goto finish;
525 }
8c6db833 526
ce959314
MS
527 if (debug)
528 pam_syslog(handle, LOG_DEBUG, "Asking logind to create session: "
f904bdf2
CG
529 "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",
530 uid, pid, service, type, class, seat, vtnr, tty, display, yes_no(remote), remote_user, remote_host);
ce959314 531
98a28fef
LP
532 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
533 if (!reply) {
534 pam_syslog(handle, LOG_ERR, "Failed to create session: %s", bus_error_message(&error));
535 r = PAM_SESSION_ERR;
536 goto finish;
537 }
74fe1fe3 538
98a28fef
LP
539 if (!dbus_message_get_args(reply, &error,
540 DBUS_TYPE_STRING, &id,
541 DBUS_TYPE_OBJECT_PATH, &object_path,
542 DBUS_TYPE_STRING, &runtime_path,
543 DBUS_TYPE_UNIX_FD, &session_fd,
bbc73283
LP
544 DBUS_TYPE_STRING, &seat,
545 DBUS_TYPE_UINT32, &vtnr,
77085881 546 DBUS_TYPE_BOOLEAN, &existing,
98a28fef
LP
547 DBUS_TYPE_INVALID)) {
548 pam_syslog(handle, LOG_ERR, "Failed to parse message: %s", bus_error_message(&error));
549 r = PAM_SESSION_ERR;
550 goto finish;
551 }
74fe1fe3 552
ce959314
MS
553 if (debug)
554 pam_syslog(handle, LOG_DEBUG, "Reply from logind: "
555 "id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u",
556 id, object_path, runtime_path, session_fd, seat, vtnr);
557
98a28fef
LP
558 r = pam_misc_setenv(handle, "XDG_SESSION_ID", id, 0);
559 if (r != PAM_SUCCESS) {
560 pam_syslog(handle, LOG_ERR, "Failed to set session id.");
561 goto finish;
8c6db833
LP
562 }
563
98a28fef
LP
564 r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", runtime_path, 0);
565 if (r != PAM_SUCCESS) {
566 pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
567 goto finish;
568 }
8c6db833 569
bbc73283
LP
570 if (!isempty(seat)) {
571 r = pam_misc_setenv(handle, "XDG_SEAT", seat, 0);
572 if (r != PAM_SUCCESS) {
573 pam_syslog(handle, LOG_ERR, "Failed to set seat.");
574 goto finish;
575 }
576 }
577
578 if (vtnr > 0) {
579 char buf[11];
580 snprintf(buf, sizeof(buf), "%u", vtnr);
581 char_array_0(buf);
582
583 r = pam_misc_setenv(handle, "XDG_VTNR", buf, 0);
584 if (r != PAM_SUCCESS) {
585 pam_syslog(handle, LOG_ERR, "Failed to set virtual terminal number.");
586 goto finish;
587 }
588 }
589
77085881
LP
590 r = pam_set_data(handle, "systemd.existing", INT_TO_PTR(!!existing), NULL);
591 if (r != PAM_SUCCESS) {
592 pam_syslog(handle, LOG_ERR, "Failed to install existing flag.");
593 return r;
594 }
595
21c390cc
LP
596 if (session_fd >= 0) {
597 r = pam_set_data(handle, "systemd.session-fd", INT_TO_PTR(session_fd+1), NULL);
598 if (r != PAM_SUCCESS) {
599 pam_syslog(handle, LOG_ERR, "Failed to install session fd.");
600 return r;
601 }
98a28fef 602 }
8c6db833 603
98a28fef 604 session_fd = -1;
8c6db833 605
98a28fef 606 r = PAM_SUCCESS;
8c6db833 607
98a28fef
LP
608finish:
609 strv_free(controllers);
610 strv_free(reset_controllers);
611 strv_free(kill_only_users);
612 strv_free(kill_exclude_users);
8c6db833 613
98a28fef 614 dbus_error_free(&error);
35d2e7ec 615
98a28fef
LP
616 if (bus) {
617 dbus_connection_close(bus);
618 dbus_connection_unref(bus);
8c6db833
LP
619 }
620
98a28fef
LP
621 if (m)
622 dbus_message_unref(m);
8c6db833 623
ed18b08b
LP
624 if (reply)
625 dbus_message_unref(reply);
626
98a28fef
LP
627 if (session_fd >= 0)
628 close_nointr_nofail(session_fd);
672c48cc 629
98a28fef
LP
630 return r;
631}
8c6db833 632
98a28fef
LP
633_public_ PAM_EXTERN int pam_sm_close_session(
634 pam_handle_t *handle,
635 int flags,
636 int argc, const char **argv) {
8c6db833 637
77085881 638 const void *p = NULL, *existing = NULL;
75c8e3cf
LP
639 const char *id;
640 DBusConnection *bus = NULL;
641 DBusMessage *m = NULL, *reply = NULL;
642 DBusError error;
643 int r;
8c6db833 644
75c8e3cf
LP
645 assert(handle);
646
647 dbus_error_init(&error);
648
77085881
LP
649 /* Only release session if it wasn't pre-existing when we
650 * tried to create it */
651 pam_get_data(handle, "systemd.existing", &existing);
652
75c8e3cf 653 id = pam_getenv(handle, "XDG_SESSION_ID");
77085881 654 if (id && !existing) {
75c8e3cf
LP
655
656 /* Before we go and close the FIFO we need to tell
657 * logind that this is a clean session shutdown, so
658 * that it doesn't just go and slaughter us
659 * immediately after closing the fd */
660
661 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
662 if (!bus) {
663 pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", bus_error_message(&error));
664 r = PAM_SESSION_ERR;
665 goto finish;
666 }
667
668 m = dbus_message_new_method_call(
669 "org.freedesktop.login1",
670 "/org/freedesktop/login1",
671 "org.freedesktop.login1.Manager",
672 "ReleaseSession");
673 if (!m) {
674 pam_syslog(handle, LOG_ERR, "Could not allocate release session message.");
675 r = PAM_BUF_ERR;
676 goto finish;
677 }
678
679 if (!dbus_message_append_args(m,
680 DBUS_TYPE_STRING, &id,
681 DBUS_TYPE_INVALID)) {
682 pam_syslog(handle, LOG_ERR, "Could not attach parameters to message.");
683 r = PAM_BUF_ERR;
684 goto finish;
685 }
74fe1fe3 686
75c8e3cf
LP
687 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
688 if (!reply) {
689 pam_syslog(handle, LOG_ERR, "Failed to release session: %s", bus_error_message(&error));
690 r = PAM_SESSION_ERR;
691 goto finish;
692 }
693 }
694
695 r = PAM_SUCCESS;
696
697finish:
698 pam_get_data(handle, "systemd.session-fd", &p);
98a28fef
LP
699 if (p)
700 close_nointr(PTR_TO_INT(p) - 1);
1f73f0f1 701
75c8e3cf
LP
702 dbus_error_free(&error);
703
704 if (bus) {
705 dbus_connection_close(bus);
706 dbus_connection_unref(bus);
707 }
708
709 if (m)
710 dbus_message_unref(m);
711
712 if (reply)
713 dbus_message_unref(reply);
714
715 return r;
8c6db833 716}