]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/pam_systemd.c
sd-bus: rename default bus address constants, they aren't "paths" but "addresses"
[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
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
8c6db833 35#include "util.h"
d7832d2c 36#include "audit.h"
8c6db833 37#include "macro.h"
74fe1fe3 38#include "strv.h"
ffcfcb6b 39#include "bus-util.h"
98a28fef 40#include "def.h"
4d6d6518 41#include "socket-util.h"
a5c32cff 42#include "fileio.h"
8159d91a 43#include "bus-error.h"
8c6db833 44
baae0358
LP
45static int parse_argv(
46 pam_handle_t *handle,
47 int argc, const char **argv,
48 const char **class,
49ebd11f 49 const char **type,
baae0358 50 bool *debug) {
8c6db833
LP
51
52 unsigned i;
53
54 assert(argc >= 0);
55 assert(argc == 0 || argv);
56
49ebd11f 57 for (i = 0; i < (unsigned) argc; i++) {
fb6becb4 58 if (startswith(argv[i], "class=")) {
485507b8
MM
59 if (class)
60 *class = argv[i] + 6;
61
49ebd11f
LP
62 } else if (startswith(argv[i], "type=")) {
63 if (type)
64 *type = argv[i] + 5;
65
05a049cc
ZJS
66 } else if (streq(argv[i], "debug")) {
67 if (debug)
68 *debug = true;
fb6becb4 69
05a049cc
ZJS
70 } else if (startswith(argv[i], "debug=")) {
71 int k;
0e318cad 72
05a049cc
ZJS
73 k = parse_boolean(argv[i] + 6);
74 if (k < 0)
75 pam_syslog(handle, LOG_WARNING, "Failed to parse debug= argument, ignoring.");
76 else if (debug)
0e318cad
MS
77 *debug = k;
78
05a049cc 79 } else
fb6becb4 80 pam_syslog(handle, LOG_WARNING, "Unknown parameter '%s', ignoring", argv[i]);
49ebd11f 81 }
8c6db833 82
8c6db833
LP
83 return 0;
84}
85
8c6db833
LP
86static int get_user_data(
87 pam_handle_t *handle,
88 const char **ret_username,
89 struct passwd **ret_pw) {
90
d90b9d27
LP
91 const char *username = NULL;
92 struct passwd *pw = NULL;
8c6db833
LP
93 int r;
94
95 assert(handle);
96 assert(ret_username);
97 assert(ret_pw);
98
baae0358
LP
99 r = pam_get_user(handle, &username, NULL);
100 if (r != PAM_SUCCESS) {
101 pam_syslog(handle, LOG_ERR, "Failed to get user name.");
102 return r;
103 }
d90b9d27 104
baae0358
LP
105 if (isempty(username)) {
106 pam_syslog(handle, LOG_ERR, "User name not valid.");
107 return PAM_AUTH_ERR;
8c6db833
LP
108 }
109
baae0358 110 pw = pam_modutil_getpwnam(handle, username);
d90b9d27 111 if (!pw) {
8c6db833
LP
112 pam_syslog(handle, LOG_ERR, "Failed to get user data.");
113 return PAM_USER_UNKNOWN;
114 }
115
116 *ret_pw = pw;
8e24a4f8 117 *ret_username = username;
8c6db833
LP
118
119 return PAM_SUCCESS;
120}
121
4d6d6518 122static int get_seat_from_display(const char *display, const char **seat, uint32_t *vtnr) {
b92bea5d
ZJS
123 union sockaddr_union sa = {
124 .un.sun_family = AF_UNIX,
125 };
baae0358
LP
126 _cleanup_free_ char *p = NULL, *tty = NULL;
127 _cleanup_close_ int fd = -1;
4d6d6518 128 struct ucred ucred;
baae0358 129 int v, r;
4d6d6518
LP
130
131 assert(display);
4d6d6518
LP
132 assert(vtnr);
133
134 /* We deduce the X11 socket from the display name, then use
135 * SO_PEERCRED to determine the X11 server process, ask for
136 * the controlling tty of that and if it's a VC then we know
137 * the seat and the virtual terminal. Sounds ugly, is only
138 * semi-ugly. */
139
140 r = socket_from_display(display, &p);
141 if (r < 0)
142 return r;
b92bea5d 143 strncpy(sa.un.sun_path, p, sizeof(sa.un.sun_path)-1);
4d6d6518
LP
144
145 fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
b92bea5d 146 if (fd < 0)
4d6d6518 147 return -errno;
4d6d6518 148
b92bea5d 149 if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0)
4d6d6518 150 return -errno;
4d6d6518 151
eff05270 152 r = getpeercred(fd, &ucred);
4d6d6518 153 if (r < 0)
eff05270 154 return r;
4d6d6518
LP
155
156 r = get_ctty(ucred.pid, NULL, &tty);
157 if (r < 0)
158 return r;
159
160 v = vtnr_from_tty(tty);
4d6d6518
LP
161 if (v < 0)
162 return v;
163 else if (v == 0)
164 return -ENOENT;
165
fc7985ed
LP
166 if (seat)
167 *seat = "seat0";
4d6d6518
LP
168 *vtnr = (uint32_t) v;
169
170 return 0;
171}
172
8b255ecd
KS
173static int export_legacy_dbus_address(
174 pam_handle_t *handle,
175 uid_t uid,
176 const char *runtime) {
177
5eecd85f 178#ifdef ENABLE_KDBUS
8b255ecd
KS
179 _cleanup_free_ char *s = NULL;
180 int r;
181
8042e377 182 /* skip export if kdbus is not active */
63cc4c31 183 if (access("/sys/fs/kdbus", F_OK) < 0)
8042e377
KS
184 return PAM_SUCCESS;
185
e3afaf6b 186 if (asprintf(&s, KERNEL_USER_BUS_ADDRESS_FMT ";" UNIX_USER_BUS_ADDRESS_FMT, uid, runtime) < 0) {
8b255ecd
KS
187 pam_syslog(handle, LOG_ERR, "Failed to set bus variable.");
188 return PAM_BUF_ERR;
189 }
190
191 r = pam_misc_setenv(handle, "DBUS_SESSION_BUS_ADDRESS", s, 0);
192 if (r != PAM_SUCCESS) {
193 pam_syslog(handle, LOG_ERR, "Failed to set bus variable.");
194 return r;
195 }
5eecd85f 196#endif
8b255ecd
KS
197 return PAM_SUCCESS;
198}
199
98a28fef 200_public_ PAM_EXTERN int pam_sm_open_session(
8c6db833
LP
201 pam_handle_t *handle,
202 int flags,
203 int argc, const char **argv) {
204
ffcfcb6b
ZJS
205 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
206 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
d1529c9e
LP
207 const char
208 *username, *id, *object_path, *runtime_path,
209 *service = NULL,
210 *tty = NULL, *display = NULL,
211 *remote_user = NULL, *remote_host = NULL,
212 *seat = NULL,
213 *type = NULL, *class = NULL,
a4cd87e9 214 *class_pam = NULL, *type_pam = NULL, *cvtnr = NULL, *desktop = NULL;
24996861 215 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
d1529c9e 216 int session_fd = -1, existing, r;
d1529c9e
LP
217 bool debug = false, remote;
218 struct passwd *pw;
baae0358
LP
219 uint32_t vtnr = 0;
220 uid_t original_uid;
8c6db833 221
ffcfcb6b 222 assert(handle);
98a28fef 223
79d860fe
MP
224 /* Make this a NOP on non-logind systems */
225 if (!logind_running())
8c6db833
LP
226 return PAM_SUCCESS;
227
e9fbc77c
LP
228 if (parse_argv(handle,
229 argc, argv,
fb6becb4 230 &class_pam,
49ebd11f 231 &type_pam,
5a330cda
ZJS
232 &debug) < 0)
233 return PAM_SESSION_ERR;
74fe1fe3 234
baae0358 235 if (debug)
3831838a 236 pam_syslog(handle, LOG_DEBUG, "pam-systemd initializing");
baae0358 237
98a28fef 238 r = get_user_data(handle, &username, &pw);
5a330cda
ZJS
239 if (r != PAM_SUCCESS) {
240 pam_syslog(handle, LOG_ERR, "Failed to get user data.");
241 return r;
242 }
8c6db833 243
30b2c336
LP
244 /* Make sure we don't enter a loop by talking to
245 * systemd-logind when it is actually waiting for the
246 * background to finish start-up. If the service is
5c390a4a 247 * "systemd-user" we simply set XDG_RUNTIME_DIR and
30b2c336
LP
248 * leave. */
249
250 pam_get_item(handle, PAM_SERVICE, (const void**) &service);
5c390a4a 251 if (streq_ptr(service, "systemd-user")) {
05a049cc 252 _cleanup_free_ char *p = NULL, *rt = NULL;
30b2c336 253
de0671ee 254 if (asprintf(&p, "/run/systemd/users/"UID_FMT, pw->pw_uid) < 0)
ffcfcb6b 255 return PAM_BUF_ERR;
30b2c336
LP
256
257 r = parse_env_file(p, NEWLINE,
258 "RUNTIME", &rt,
259 NULL);
ffcfcb6b
ZJS
260 if (r < 0 && r != -ENOENT)
261 return PAM_SESSION_ERR;
30b2c336
LP
262
263 if (rt) {
264 r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0);
30b2c336
LP
265 if (r != PAM_SUCCESS) {
266 pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
ffcfcb6b 267 return r;
30b2c336 268 }
8b255ecd
KS
269
270 r = export_legacy_dbus_address(handle, pw->pw_uid, rt);
271 if (r != PAM_SUCCESS)
272 return r;
30b2c336
LP
273 }
274
ffcfcb6b 275 return PAM_SUCCESS;
8c6db833
LP
276 }
277
ffcfcb6b 278 /* Otherwise, we ask logind to create a session for us */
8c6db833 279
98a28fef
LP
280 pam_get_item(handle, PAM_XDISPLAY, (const void**) &display);
281 pam_get_item(handle, PAM_TTY, (const void**) &tty);
282 pam_get_item(handle, PAM_RUSER, (const void**) &remote_user);
283 pam_get_item(handle, PAM_RHOST, (const void**) &remote_host);
a8573ccc 284
bbc73283 285 seat = pam_getenv(handle, "XDG_SEAT");
a8573ccc
LP
286 if (isempty(seat))
287 seat = getenv("XDG_SEAT");
288
bbc73283 289 cvtnr = pam_getenv(handle, "XDG_VTNR");
a8573ccc
LP
290 if (isempty(cvtnr))
291 cvtnr = getenv("XDG_VTNR");
98a28fef 292
49ebd11f
LP
293 type = pam_getenv(handle, "XDG_SESSION_TYPE");
294 if (isempty(type))
295 type = getenv("XDG_SESSION_TYPE");
296 if (isempty(type))
297 type = type_pam;
298
299 class = pam_getenv(handle, "XDG_SESSION_CLASS");
300 if (isempty(class))
301 class = getenv("XDG_SESSION_CLASS");
302 if (isempty(class))
303 class = class_pam;
304
a4cd87e9
LP
305 desktop = pam_getenv(handle, "XDG_SESSION_DESKTOP");
306 if (isempty(desktop))
307 desktop = getenv("XDG_SESSION_DESKTOP");
308
ed18b08b 309 tty = strempty(tty);
98a28fef 310
ee8545b0 311 if (strchr(tty, ':')) {
e2acb67b
LP
312 /* A tty with a colon is usually an X11 display,
313 * placed there to show up in utmp. We rearrange
314 * things and don't pretend that an X display was a
315 * tty. */
ee8545b0
LP
316
317 if (isempty(display))
318 display = tty;
a4cd87e9 319 tty = NULL;
1a4459d6 320 } else if (streq(tty, "cron")) {
0ad1271f
LP
321 /* cron has been setting PAM_TTY to "cron" for a very
322 * long time and it probably shouldn't stop doing that
323 * for compatibility reasons. */
0ad1271f 324 type = "unspecified";
49ebd11f 325 class = "background";
a4cd87e9 326 tty = NULL;
0ad1271f
LP
327 } else if (streq(tty, "ssh")) {
328 /* ssh has been setting PAM_TTY to "ssh" for a very
329 * long time and probably shouldn't stop doing that
330 * for compatibility reasons. */
0ad1271f 331 type ="tty";
49ebd11f 332 class = "user";
a4cd87e9 333 tty = NULL;
ee8545b0
LP
334 }
335
8e7705e5 336 /* If this fails vtnr will be 0, that's intended */
4d6d6518
LP
337 if (!isempty(cvtnr))
338 safe_atou32(cvtnr, &vtnr);
339
92bd5ff3 340 if (!isempty(display) && !vtnr) {
fc7985ed 341 if (isempty(seat))
6ef25fb6 342 get_seat_from_display(display, &seat, &vtnr);
fc7985ed 343 else if (streq(seat, "seat0"))
6ef25fb6 344 get_seat_from_display(display, NULL, &vtnr);
fc7985ed 345 }
4d6d6518 346
49ebd11f
LP
347 if (seat && !streq(seat, "seat0") && vtnr != 0) {
348 pam_syslog(handle, LOG_DEBUG, "Ignoring vtnr %d for %s which is not seat0", vtnr, seat);
d7353ef6
MM
349 vtnr = 0;
350 }
351
49ebd11f 352 if (isempty(type))
0ad1271f 353 type = !isempty(display) ? "x11" :
49ebd11f 354 !isempty(tty) ? "tty" : "unspecified";
98a28fef 355
55efac6c 356 if (isempty(class))
e2acb67b 357 class = streq(type, "unspecified") ? "background" : "user";
55efac6c 358
fecc80c1 359 remote = !isempty(remote_host) && !is_localhost(remote_host);
98a28fef 360
4d49b48c 361 /* Talk to logind over the message bus */
5a330cda 362
ffcfcb6b
ZJS
363 r = sd_bus_open_system(&bus);
364 if (r < 0) {
365 pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror(-r));
366 return PAM_SESSION_ERR;
cc377381
LP
367 }
368
ce959314
MS
369 if (debug)
370 pam_syslog(handle, LOG_DEBUG, "Asking logind to create session: "
a4cd87e9 371 "uid=%u pid=%u service=%s type=%s class=%s desktop=%s seat=%s vtnr=%u tty=%s display=%s remote=%s remote_user=%s remote_host=%s",
baae0358
LP
372 pw->pw_uid, getpid(),
373 strempty(service),
cda7ecb0 374 type, class, strempty(desktop),
a4cd87e9 375 strempty(seat), vtnr, strempty(tty), strempty(display),
baae0358 376 yes_no(remote), strempty(remote_user), strempty(remote_host));
ce959314 377
ffcfcb6b
ZJS
378 r = sd_bus_call_method(bus,
379 "org.freedesktop.login1",
380 "/org/freedesktop/login1",
381 "org.freedesktop.login1.Manager",
382 "CreateSession",
383 &error,
384 &reply,
a4cd87e9 385 "uusssssussbssa(sv)",
baae0358
LP
386 (uint32_t) pw->pw_uid,
387 (uint32_t) getpid(),
a4cd87e9 388 service,
ffcfcb6b
ZJS
389 type,
390 class,
a4cd87e9
LP
391 desktop,
392 seat,
ffcfcb6b
ZJS
393 vtnr,
394 tty,
a4cd87e9 395 display,
ffcfcb6b 396 remote,
a4cd87e9
LP
397 remote_user,
398 remote_host,
ffcfcb6b
ZJS
399 0);
400 if (r < 0) {
8159d91a 401 pam_syslog(handle, LOG_ERR, "Failed to create session: %s", bus_error_message(&error, r));
ffcfcb6b 402 return PAM_SYSTEM_ERR;
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
21c390cc
LP
481 r = pam_set_data(handle, "systemd.session-fd", INT_TO_PTR(session_fd+1), NULL);
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
29d230f6 497 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
24996861 498 _cleanup_bus_close_unref_ 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}