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