]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/pam_systemd.c
util-lib: split string parsing related calls from util.[ch] into parse-util.[ch]
[thirdparty/systemd.git] / src / login / pam_systemd.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
00229fe4 22#include <endian.h>
8c6db833
LP
23#include <errno.h>
24#include <fcntl.h>
8c6db833 25#include <pwd.h>
8c6db833 26#include <security/_pam_macros.h>
8c6db833
LP
27#include <security/pam_ext.h>
28#include <security/pam_misc.h>
00229fe4
LP
29#include <security/pam_modules.h>
30#include <security/pam_modutil.h>
31#include <sys/file.h>
8c6db833 32
d7832d2c 33#include "audit.h"
00229fe4
LP
34#include "bus-common-errors.h"
35#include "bus-error.h"
ffcfcb6b 36#include "bus-util.h"
98a28fef 37#include "def.h"
3ffd4af2 38#include "fd-util.h"
a5c32cff 39#include "fileio.h"
6482f626 40#include "formats-util.h"
958b66ea 41#include "hostname-util.h"
00229fe4
LP
42#include "login-util.h"
43#include "macro.h"
6bedfcbb 44#include "parse-util.h"
00229fe4
LP
45#include "socket-util.h"
46#include "strv.h"
47#include "terminal-util.h"
48#include "util.h"
8c6db833 49
baae0358
LP
50static int parse_argv(
51 pam_handle_t *handle,
52 int argc, const char **argv,
53 const char **class,
49ebd11f 54 const char **type,
baae0358 55 bool *debug) {
8c6db833
LP
56
57 unsigned i;
58
59 assert(argc >= 0);
60 assert(argc == 0 || argv);
61
49ebd11f 62 for (i = 0; i < (unsigned) argc; i++) {
fb6becb4 63 if (startswith(argv[i], "class=")) {
485507b8
MM
64 if (class)
65 *class = argv[i] + 6;
66
49ebd11f
LP
67 } else if (startswith(argv[i], "type=")) {
68 if (type)
69 *type = argv[i] + 5;
70
05a049cc
ZJS
71 } else if (streq(argv[i], "debug")) {
72 if (debug)
73 *debug = true;
fb6becb4 74
05a049cc
ZJS
75 } else if (startswith(argv[i], "debug=")) {
76 int k;
0e318cad 77
05a049cc
ZJS
78 k = parse_boolean(argv[i] + 6);
79 if (k < 0)
80 pam_syslog(handle, LOG_WARNING, "Failed to parse debug= argument, ignoring.");
81 else if (debug)
0e318cad
MS
82 *debug = k;
83
05a049cc 84 } else
fb6becb4 85 pam_syslog(handle, LOG_WARNING, "Unknown parameter '%s', ignoring", argv[i]);
49ebd11f 86 }
8c6db833 87
8c6db833
LP
88 return 0;
89}
90
8c6db833
LP
91static int get_user_data(
92 pam_handle_t *handle,
93 const char **ret_username,
94 struct passwd **ret_pw) {
95
d90b9d27
LP
96 const char *username = NULL;
97 struct passwd *pw = NULL;
8c6db833
LP
98 int r;
99
100 assert(handle);
101 assert(ret_username);
102 assert(ret_pw);
103
baae0358
LP
104 r = pam_get_user(handle, &username, NULL);
105 if (r != PAM_SUCCESS) {
106 pam_syslog(handle, LOG_ERR, "Failed to get user name.");
107 return r;
108 }
d90b9d27 109
baae0358
LP
110 if (isempty(username)) {
111 pam_syslog(handle, LOG_ERR, "User name not valid.");
112 return PAM_AUTH_ERR;
8c6db833
LP
113 }
114
baae0358 115 pw = pam_modutil_getpwnam(handle, username);
d90b9d27 116 if (!pw) {
8c6db833
LP
117 pam_syslog(handle, LOG_ERR, "Failed to get user data.");
118 return PAM_USER_UNKNOWN;
119 }
120
121 *ret_pw = pw;
8e24a4f8 122 *ret_username = username;
8c6db833
LP
123
124 return PAM_SUCCESS;
125}
126
4d6d6518 127static int get_seat_from_display(const char *display, const char **seat, uint32_t *vtnr) {
b92bea5d
ZJS
128 union sockaddr_union sa = {
129 .un.sun_family = AF_UNIX,
130 };
baae0358
LP
131 _cleanup_free_ char *p = NULL, *tty = NULL;
132 _cleanup_close_ int fd = -1;
4d6d6518 133 struct ucred ucred;
baae0358 134 int v, r;
4d6d6518
LP
135
136 assert(display);
4d6d6518
LP
137 assert(vtnr);
138
139 /* We deduce the X11 socket from the display name, then use
140 * SO_PEERCRED to determine the X11 server process, ask for
141 * the controlling tty of that and if it's a VC then we know
142 * the seat and the virtual terminal. Sounds ugly, is only
143 * semi-ugly. */
144
145 r = socket_from_display(display, &p);
146 if (r < 0)
147 return r;
b92bea5d 148 strncpy(sa.un.sun_path, p, sizeof(sa.un.sun_path)-1);
4d6d6518
LP
149
150 fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
b92bea5d 151 if (fd < 0)
4d6d6518 152 return -errno;
4d6d6518 153
b92bea5d 154 if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0)
4d6d6518 155 return -errno;
4d6d6518 156
eff05270 157 r = getpeercred(fd, &ucred);
4d6d6518 158 if (r < 0)
eff05270 159 return r;
4d6d6518
LP
160
161 r = get_ctty(ucred.pid, NULL, &tty);
162 if (r < 0)
163 return r;
164
165 v = vtnr_from_tty(tty);
4d6d6518
LP
166 if (v < 0)
167 return v;
168 else if (v == 0)
169 return -ENOENT;
170
fc7985ed
LP
171 if (seat)
172 *seat = "seat0";
4d6d6518
LP
173 *vtnr = (uint32_t) v;
174
175 return 0;
176}
177
8b255ecd
KS
178static int export_legacy_dbus_address(
179 pam_handle_t *handle,
180 uid_t uid,
181 const char *runtime) {
182
8b255ecd 183 _cleanup_free_ char *s = NULL;
3df49c28
DH
184 int r = PAM_BUF_ERR;
185
186 if (is_kdbus_available()) {
187 if (asprintf(&s, KERNEL_USER_BUS_ADDRESS_FMT ";" UNIX_USER_BUS_ADDRESS_FMT, uid, runtime) < 0)
188 goto error;
189 } else {
dc61b7e4 190 /* FIXME: We *really* should move the access() check into the
3df49c28
DH
191 * daemons that spawn dbus-daemon, instead of forcing
192 * DBUS_SESSION_BUS_ADDRESS= here. */
193
194 s = strjoin(runtime, "/bus", NULL);
195 if (!s)
196 goto error;
197
198 if (access(s, F_OK) < 0)
199 return PAM_SUCCESS;
200
201 s = mfree(s);
202 if (asprintf(&s, UNIX_USER_BUS_ADDRESS_FMT, runtime) < 0)
203 goto error;
8b255ecd
KS
204 }
205
206 r = pam_misc_setenv(handle, "DBUS_SESSION_BUS_ADDRESS", s, 0);
3df49c28
DH
207 if (r != PAM_SUCCESS)
208 goto error;
1b09f548 209
8b255ecd 210 return PAM_SUCCESS;
3df49c28
DH
211
212error:
213 pam_syslog(handle, LOG_ERR, "Failed to set bus variable.");
214 return r;
8b255ecd
KS
215}
216
98a28fef 217_public_ PAM_EXTERN int pam_sm_open_session(
8c6db833
LP
218 pam_handle_t *handle,
219 int flags,
220 int argc, const char **argv) {
221
ffcfcb6b
ZJS
222 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
223 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
d1529c9e
LP
224 const char
225 *username, *id, *object_path, *runtime_path,
226 *service = NULL,
227 *tty = NULL, *display = NULL,
228 *remote_user = NULL, *remote_host = NULL,
229 *seat = NULL,
230 *type = NULL, *class = NULL,
a4cd87e9 231 *class_pam = NULL, *type_pam = NULL, *cvtnr = NULL, *desktop = NULL;
03976f7b 232 _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
d1529c9e 233 int session_fd = -1, existing, r;
d1529c9e
LP
234 bool debug = false, remote;
235 struct passwd *pw;
baae0358
LP
236 uint32_t vtnr = 0;
237 uid_t original_uid;
8c6db833 238
ffcfcb6b 239 assert(handle);
98a28fef 240
79d860fe
MP
241 /* Make this a NOP on non-logind systems */
242 if (!logind_running())
8c6db833
LP
243 return PAM_SUCCESS;
244
e9fbc77c
LP
245 if (parse_argv(handle,
246 argc, argv,
fb6becb4 247 &class_pam,
49ebd11f 248 &type_pam,
5a330cda
ZJS
249 &debug) < 0)
250 return PAM_SESSION_ERR;
74fe1fe3 251
baae0358 252 if (debug)
3831838a 253 pam_syslog(handle, LOG_DEBUG, "pam-systemd initializing");
baae0358 254
98a28fef 255 r = get_user_data(handle, &username, &pw);
5a330cda
ZJS
256 if (r != PAM_SUCCESS) {
257 pam_syslog(handle, LOG_ERR, "Failed to get user data.");
258 return r;
259 }
8c6db833 260
30b2c336
LP
261 /* Make sure we don't enter a loop by talking to
262 * systemd-logind when it is actually waiting for the
263 * background to finish start-up. If the service is
5c390a4a 264 * "systemd-user" we simply set XDG_RUNTIME_DIR and
30b2c336
LP
265 * leave. */
266
267 pam_get_item(handle, PAM_SERVICE, (const void**) &service);
5c390a4a 268 if (streq_ptr(service, "systemd-user")) {
05a049cc 269 _cleanup_free_ char *p = NULL, *rt = NULL;
30b2c336 270
de0671ee 271 if (asprintf(&p, "/run/systemd/users/"UID_FMT, pw->pw_uid) < 0)
ffcfcb6b 272 return PAM_BUF_ERR;
30b2c336
LP
273
274 r = parse_env_file(p, NEWLINE,
275 "RUNTIME", &rt,
276 NULL);
ffcfcb6b
ZJS
277 if (r < 0 && r != -ENOENT)
278 return PAM_SESSION_ERR;
30b2c336
LP
279
280 if (rt) {
281 r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0);
30b2c336
LP
282 if (r != PAM_SUCCESS) {
283 pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
ffcfcb6b 284 return r;
30b2c336 285 }
8b255ecd
KS
286
287 r = export_legacy_dbus_address(handle, pw->pw_uid, rt);
288 if (r != PAM_SUCCESS)
289 return r;
30b2c336
LP
290 }
291
ffcfcb6b 292 return PAM_SUCCESS;
8c6db833
LP
293 }
294
ffcfcb6b 295 /* Otherwise, we ask logind to create a session for us */
8c6db833 296
98a28fef
LP
297 pam_get_item(handle, PAM_XDISPLAY, (const void**) &display);
298 pam_get_item(handle, PAM_TTY, (const void**) &tty);
299 pam_get_item(handle, PAM_RUSER, (const void**) &remote_user);
300 pam_get_item(handle, PAM_RHOST, (const void**) &remote_host);
a8573ccc 301
bbc73283 302 seat = pam_getenv(handle, "XDG_SEAT");
a8573ccc
LP
303 if (isempty(seat))
304 seat = getenv("XDG_SEAT");
305
bbc73283 306 cvtnr = pam_getenv(handle, "XDG_VTNR");
a8573ccc
LP
307 if (isempty(cvtnr))
308 cvtnr = getenv("XDG_VTNR");
98a28fef 309
49ebd11f
LP
310 type = pam_getenv(handle, "XDG_SESSION_TYPE");
311 if (isempty(type))
312 type = getenv("XDG_SESSION_TYPE");
313 if (isempty(type))
314 type = type_pam;
315
316 class = pam_getenv(handle, "XDG_SESSION_CLASS");
317 if (isempty(class))
318 class = getenv("XDG_SESSION_CLASS");
319 if (isempty(class))
320 class = class_pam;
321
a4cd87e9
LP
322 desktop = pam_getenv(handle, "XDG_SESSION_DESKTOP");
323 if (isempty(desktop))
324 desktop = getenv("XDG_SESSION_DESKTOP");
325
ed18b08b 326 tty = strempty(tty);
98a28fef 327
ee8545b0 328 if (strchr(tty, ':')) {
e2acb67b
LP
329 /* A tty with a colon is usually an X11 display,
330 * placed there to show up in utmp. We rearrange
331 * things and don't pretend that an X display was a
332 * tty. */
ee8545b0
LP
333
334 if (isempty(display))
335 display = tty;
a4cd87e9 336 tty = NULL;
1a4459d6 337 } else if (streq(tty, "cron")) {
0ad1271f
LP
338 /* cron has been setting PAM_TTY to "cron" for a very
339 * long time and it probably shouldn't stop doing that
340 * for compatibility reasons. */
0ad1271f 341 type = "unspecified";
49ebd11f 342 class = "background";
a4cd87e9 343 tty = NULL;
0ad1271f
LP
344 } else if (streq(tty, "ssh")) {
345 /* ssh has been setting PAM_TTY to "ssh" for a very
346 * long time and probably shouldn't stop doing that
347 * for compatibility reasons. */
0ad1271f 348 type ="tty";
49ebd11f 349 class = "user";
a4cd87e9 350 tty = NULL;
ee8545b0
LP
351 }
352
8e7705e5 353 /* If this fails vtnr will be 0, that's intended */
4d6d6518 354 if (!isempty(cvtnr))
2ae4842b 355 (void) safe_atou32(cvtnr, &vtnr);
4d6d6518 356
92bd5ff3 357 if (!isempty(display) && !vtnr) {
fc7985ed 358 if (isempty(seat))
6ef25fb6 359 get_seat_from_display(display, &seat, &vtnr);
fc7985ed 360 else if (streq(seat, "seat0"))
6ef25fb6 361 get_seat_from_display(display, NULL, &vtnr);
fc7985ed 362 }
4d6d6518 363
49ebd11f 364 if (seat && !streq(seat, "seat0") && vtnr != 0) {
1fa2f38f 365 pam_syslog(handle, LOG_DEBUG, "Ignoring vtnr %"PRIu32" for %s which is not seat0", vtnr, seat);
d7353ef6
MM
366 vtnr = 0;
367 }
368
49ebd11f 369 if (isempty(type))
0ad1271f 370 type = !isempty(display) ? "x11" :
49ebd11f 371 !isempty(tty) ? "tty" : "unspecified";
98a28fef 372
55efac6c 373 if (isempty(class))
e2acb67b 374 class = streq(type, "unspecified") ? "background" : "user";
55efac6c 375
fecc80c1 376 remote = !isempty(remote_host) && !is_localhost(remote_host);
98a28fef 377
4d49b48c 378 /* Talk to logind over the message bus */
5a330cda 379
ffcfcb6b
ZJS
380 r = sd_bus_open_system(&bus);
381 if (r < 0) {
382 pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror(-r));
383 return PAM_SESSION_ERR;
cc377381
LP
384 }
385
ce959314
MS
386 if (debug)
387 pam_syslog(handle, LOG_DEBUG, "Asking logind to create session: "
1fa2f38f 388 "uid="UID_FMT" pid="PID_FMT" service=%s type=%s class=%s desktop=%s seat=%s vtnr=%"PRIu32" tty=%s display=%s remote=%s remote_user=%s remote_host=%s",
baae0358
LP
389 pw->pw_uid, getpid(),
390 strempty(service),
cda7ecb0 391 type, class, strempty(desktop),
a4cd87e9 392 strempty(seat), vtnr, strempty(tty), strempty(display),
baae0358 393 yes_no(remote), strempty(remote_user), strempty(remote_host));
ce959314 394
ffcfcb6b
ZJS
395 r = sd_bus_call_method(bus,
396 "org.freedesktop.login1",
397 "/org/freedesktop/login1",
398 "org.freedesktop.login1.Manager",
399 "CreateSession",
400 &error,
401 &reply,
a4cd87e9 402 "uusssssussbssa(sv)",
baae0358
LP
403 (uint32_t) pw->pw_uid,
404 (uint32_t) getpid(),
a4cd87e9 405 service,
ffcfcb6b
ZJS
406 type,
407 class,
a4cd87e9
LP
408 desktop,
409 seat,
ffcfcb6b
ZJS
410 vtnr,
411 tty,
a4cd87e9 412 display,
ffcfcb6b 413 remote,
a4cd87e9
LP
414 remote_user,
415 remote_host,
ffcfcb6b
ZJS
416 0);
417 if (r < 0) {
b80120c4
DH
418 if (sd_bus_error_has_name(&error, BUS_ERROR_SESSION_BUSY)) {
419 pam_syslog(handle, LOG_DEBUG, "Cannot create session: %s", bus_error_message(&error, r));
420 return PAM_SUCCESS;
421 } else {
422 pam_syslog(handle, LOG_ERR, "Failed to create session: %s", bus_error_message(&error, r));
423 return PAM_SYSTEM_ERR;
424 }
98a28fef 425 }
74fe1fe3 426
ffcfcb6b 427 r = sd_bus_message_read(reply,
baae0358 428 "soshusub",
ffcfcb6b
ZJS
429 &id,
430 &object_path,
431 &runtime_path,
432 &session_fd,
baae0358 433 &original_uid,
ffcfcb6b
ZJS
434 &seat,
435 &vtnr,
436 &existing);
437 if (r < 0) {
438 pam_syslog(handle, LOG_ERR, "Failed to parse message: %s", strerror(-r));
5a330cda 439 return PAM_SESSION_ERR;
98a28fef 440 }
74fe1fe3 441
ce959314
MS
442 if (debug)
443 pam_syslog(handle, LOG_DEBUG, "Reply from logind: "
baae0358
LP
444 "id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u original_uid=%u",
445 id, object_path, runtime_path, session_fd, seat, vtnr, original_uid);
ce959314 446
98a28fef
LP
447 r = pam_misc_setenv(handle, "XDG_SESSION_ID", id, 0);
448 if (r != PAM_SUCCESS) {
449 pam_syslog(handle, LOG_ERR, "Failed to set session id.");
5a330cda 450 return r;
8c6db833
LP
451 }
452
baae0358
LP
453 if (original_uid == pw->pw_uid) {
454 /* Don't set $XDG_RUNTIME_DIR if the user we now
455 * authenticated for does not match the original user
456 * of the session. We do this in order not to result
457 * in privileged apps clobbering the runtime directory
458 * unnecessarily. */
459
460 r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", runtime_path, 0);
461 if (r != PAM_SUCCESS) {
462 pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
463 return r;
464 }
8b255ecd
KS
465
466 r = export_legacy_dbus_address(handle, pw->pw_uid, runtime_path);
467 if (r != PAM_SUCCESS)
468 return r;
98a28fef 469 }
8c6db833 470
bbc73283
LP
471 if (!isempty(seat)) {
472 r = pam_misc_setenv(handle, "XDG_SEAT", seat, 0);
473 if (r != PAM_SUCCESS) {
474 pam_syslog(handle, LOG_ERR, "Failed to set seat.");
5a330cda 475 return r;
bbc73283
LP
476 }
477 }
478
479 if (vtnr > 0) {
29d230f6 480 char buf[DECIMAL_STR_MAX(vtnr)];
baae0358 481 sprintf(buf, "%u", vtnr);
bbc73283
LP
482
483 r = pam_misc_setenv(handle, "XDG_VTNR", buf, 0);
484 if (r != PAM_SUCCESS) {
485 pam_syslog(handle, LOG_ERR, "Failed to set virtual terminal number.");
5a330cda 486 return r;
bbc73283
LP
487 }
488 }
489
77085881
LP
490 r = pam_set_data(handle, "systemd.existing", INT_TO_PTR(!!existing), NULL);
491 if (r != PAM_SUCCESS) {
492 pam_syslog(handle, LOG_ERR, "Failed to install existing flag.");
5a330cda 493 return r;
77085881
LP
494 }
495
21c390cc 496 if (session_fd >= 0) {
85c08dc0 497 session_fd = fcntl(session_fd, F_DUPFD_CLOEXEC, 3);
5a330cda
ZJS
498 if (session_fd < 0) {
499 pam_syslog(handle, LOG_ERR, "Failed to dup session fd: %m");
500 return PAM_SESSION_ERR;
501 }
502
21c390cc
LP
503 r = pam_set_data(handle, "systemd.session-fd", INT_TO_PTR(session_fd+1), NULL);
504 if (r != PAM_SUCCESS) {
505 pam_syslog(handle, LOG_ERR, "Failed to install session fd.");
03e334a1 506 safe_close(session_fd);
5a330cda 507 return r;
21c390cc 508 }
98a28fef 509 }
8c6db833 510
ffcfcb6b 511 return PAM_SUCCESS;
98a28fef 512}
8c6db833 513
98a28fef
LP
514_public_ PAM_EXTERN int pam_sm_close_session(
515 pam_handle_t *handle,
516 int flags,
517 int argc, const char **argv) {
8c6db833 518
29d230f6 519 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
03976f7b 520 _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
5f41d1f1 521 const void *existing = NULL;
75c8e3cf 522 const char *id;
75c8e3cf 523 int r;
8c6db833 524
ffcfcb6b 525 assert(handle);
75c8e3cf 526
77085881
LP
527 /* Only release session if it wasn't pre-existing when we
528 * tried to create it */
529 pam_get_data(handle, "systemd.existing", &existing);
530
75c8e3cf 531 id = pam_getenv(handle, "XDG_SESSION_ID");
77085881 532 if (id && !existing) {
75c8e3cf
LP
533
534 /* Before we go and close the FIFO we need to tell
535 * logind that this is a clean session shutdown, so
536 * that it doesn't just go and slaughter us
537 * immediately after closing the fd */
538
ffcfcb6b
ZJS
539 r = sd_bus_open_system(&bus);
540 if (r < 0) {
5f41d1f1
LP
541 pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror(-r));
542 return PAM_SESSION_ERR;
75c8e3cf
LP
543 }
544
ffcfcb6b
ZJS
545 r = sd_bus_call_method(bus,
546 "org.freedesktop.login1",
547 "/org/freedesktop/login1",
548 "org.freedesktop.login1.Manager",
549 "ReleaseSession",
550 &error,
551 NULL,
552 "s",
553 id);
554 if (r < 0) {
5f41d1f1
LP
555 pam_syslog(handle, LOG_ERR, "Failed to release session: %s", bus_error_message(&error, r));
556 return PAM_SESSION_ERR;
75c8e3cf
LP
557 }
558 }
559
5f41d1f1
LP
560 /* Note that we are knowingly leaking the FIFO fd here. This
561 * way, logind can watch us die. If we closed it here it would
562 * not have any clue when that is completed. Given that one
563 * cannot really have multiple PAM sessions open from the same
564 * process this means we will leak one FD at max. */
75c8e3cf 565
5f41d1f1 566 return PAM_SUCCESS;
8c6db833 567}