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