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