]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/pam_systemd.c
systemd-nspawn: decrease non-fatal mount errors to debug level (#4569)
[thirdparty/systemd.git] / src / login / pam_systemd.c
CommitLineData
8c6db833
LP
1/***
2 This file is part of systemd.
3
4 Copyright 2010 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
8c6db833
LP
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 14 Lesser General Public License for more details.
8c6db833 15
5430f7f2 16 You should have received a copy of the GNU Lesser General Public License
8c6db833
LP
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
00229fe4 20#include <endian.h>
8c6db833
LP
21#include <errno.h>
22#include <fcntl.h>
8c6db833 23#include <pwd.h>
8c6db833 24#include <security/_pam_macros.h>
8c6db833
LP
25#include <security/pam_ext.h>
26#include <security/pam_misc.h>
00229fe4
LP
27#include <security/pam_modules.h>
28#include <security/pam_modutil.h>
29#include <sys/file.h>
8c6db833 30
b5efdb8a 31#include "alloc-util.h"
430f0182 32#include "audit-util.h"
00229fe4
LP
33#include "bus-common-errors.h"
34#include "bus-error.h"
ffcfcb6b 35#include "bus-util.h"
98a28fef 36#include "def.h"
3ffd4af2 37#include "fd-util.h"
a5c32cff 38#include "fileio.h"
6482f626 39#include "formats-util.h"
958b66ea 40#include "hostname-util.h"
00229fe4
LP
41#include "login-util.h"
42#include "macro.h"
6bedfcbb 43#include "parse-util.h"
00229fe4
LP
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
fc2fffe7 153 if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 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
222953e8
DR
185 /* FIXME: We *really* should move the access() check into the
186 * daemons that spawn dbus-daemon, instead of forcing
187 * DBUS_SESSION_BUS_ADDRESS= here. */
188
605405c6 189 s = strjoin(runtime, "/bus");
222953e8
DR
190 if (!s)
191 goto error;
192
193 if (access(s, F_OK) < 0)
194 return PAM_SUCCESS;
195
196 s = mfree(s);
197 if (asprintf(&s, UNIX_USER_BUS_ADDRESS_FMT, runtime) < 0)
198 goto error;
8b255ecd
KS
199
200 r = pam_misc_setenv(handle, "DBUS_SESSION_BUS_ADDRESS", s, 0);
3df49c28
DH
201 if (r != PAM_SUCCESS)
202 goto error;
1b09f548 203
8b255ecd 204 return PAM_SUCCESS;
3df49c28
DH
205
206error:
207 pam_syslog(handle, LOG_ERR, "Failed to set bus variable.");
208 return r;
8b255ecd
KS
209}
210
98a28fef 211_public_ PAM_EXTERN int pam_sm_open_session(
8c6db833
LP
212 pam_handle_t *handle,
213 int flags,
214 int argc, const char **argv) {
215
4afd3348
LP
216 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
217 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
d1529c9e
LP
218 const char
219 *username, *id, *object_path, *runtime_path,
220 *service = NULL,
221 *tty = NULL, *display = NULL,
222 *remote_user = NULL, *remote_host = NULL,
223 *seat = NULL,
224 *type = NULL, *class = NULL,
a4cd87e9 225 *class_pam = NULL, *type_pam = NULL, *cvtnr = NULL, *desktop = NULL;
4afd3348 226 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
d1529c9e 227 int session_fd = -1, existing, r;
d1529c9e
LP
228 bool debug = false, remote;
229 struct passwd *pw;
baae0358
LP
230 uint32_t vtnr = 0;
231 uid_t original_uid;
8c6db833 232
ffcfcb6b 233 assert(handle);
98a28fef 234
79d860fe
MP
235 /* Make this a NOP on non-logind systems */
236 if (!logind_running())
8c6db833
LP
237 return PAM_SUCCESS;
238
e9fbc77c
LP
239 if (parse_argv(handle,
240 argc, argv,
fb6becb4 241 &class_pam,
49ebd11f 242 &type_pam,
5a330cda
ZJS
243 &debug) < 0)
244 return PAM_SESSION_ERR;
74fe1fe3 245
baae0358 246 if (debug)
3831838a 247 pam_syslog(handle, LOG_DEBUG, "pam-systemd initializing");
baae0358 248
98a28fef 249 r = get_user_data(handle, &username, &pw);
5a330cda
ZJS
250 if (r != PAM_SUCCESS) {
251 pam_syslog(handle, LOG_ERR, "Failed to get user data.");
252 return r;
253 }
8c6db833 254
30b2c336
LP
255 /* Make sure we don't enter a loop by talking to
256 * systemd-logind when it is actually waiting for the
257 * background to finish start-up. If the service is
5c390a4a 258 * "systemd-user" we simply set XDG_RUNTIME_DIR and
30b2c336
LP
259 * leave. */
260
261 pam_get_item(handle, PAM_SERVICE, (const void**) &service);
5c390a4a 262 if (streq_ptr(service, "systemd-user")) {
f9e4283d 263 _cleanup_free_ char *rt = NULL;
30b2c336 264
f9e4283d 265 if (asprintf(&rt, "/run/user/"UID_FMT, pw->pw_uid) < 0)
ffcfcb6b 266 return PAM_BUF_ERR;
30b2c336 267
f9e4283d
DH
268 r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0);
269 if (r != PAM_SUCCESS) {
270 pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
271 return r;
30b2c336
LP
272 }
273
f9e4283d
DH
274 r = export_legacy_dbus_address(handle, pw->pw_uid, rt);
275 if (r != PAM_SUCCESS)
276 return r;
277
ffcfcb6b 278 return PAM_SUCCESS;
8c6db833
LP
279 }
280
ffcfcb6b 281 /* Otherwise, we ask logind to create a session for us */
8c6db833 282
98a28fef
LP
283 pam_get_item(handle, PAM_XDISPLAY, (const void**) &display);
284 pam_get_item(handle, PAM_TTY, (const void**) &tty);
285 pam_get_item(handle, PAM_RUSER, (const void**) &remote_user);
286 pam_get_item(handle, PAM_RHOST, (const void**) &remote_host);
a8573ccc 287
bbc73283 288 seat = pam_getenv(handle, "XDG_SEAT");
a8573ccc
LP
289 if (isempty(seat))
290 seat = getenv("XDG_SEAT");
291
bbc73283 292 cvtnr = pam_getenv(handle, "XDG_VTNR");
a8573ccc
LP
293 if (isempty(cvtnr))
294 cvtnr = getenv("XDG_VTNR");
98a28fef 295
49ebd11f
LP
296 type = pam_getenv(handle, "XDG_SESSION_TYPE");
297 if (isempty(type))
298 type = getenv("XDG_SESSION_TYPE");
299 if (isempty(type))
300 type = type_pam;
301
302 class = pam_getenv(handle, "XDG_SESSION_CLASS");
303 if (isempty(class))
304 class = getenv("XDG_SESSION_CLASS");
305 if (isempty(class))
306 class = class_pam;
307
a4cd87e9
LP
308 desktop = pam_getenv(handle, "XDG_SESSION_DESKTOP");
309 if (isempty(desktop))
310 desktop = getenv("XDG_SESSION_DESKTOP");
311
ed18b08b 312 tty = strempty(tty);
98a28fef 313
ee8545b0 314 if (strchr(tty, ':')) {
e2acb67b
LP
315 /* A tty with a colon is usually an X11 display,
316 * placed there to show up in utmp. We rearrange
317 * things and don't pretend that an X display was a
318 * tty. */
ee8545b0
LP
319
320 if (isempty(display))
321 display = tty;
a4cd87e9 322 tty = NULL;
1a4459d6 323 } else if (streq(tty, "cron")) {
0ad1271f
LP
324 /* cron has been setting PAM_TTY to "cron" for a very
325 * long time and it probably shouldn't stop doing that
326 * for compatibility reasons. */
0ad1271f 327 type = "unspecified";
49ebd11f 328 class = "background";
a4cd87e9 329 tty = NULL;
0ad1271f
LP
330 } else if (streq(tty, "ssh")) {
331 /* ssh has been setting PAM_TTY to "ssh" for a very
332 * long time and probably shouldn't stop doing that
333 * for compatibility reasons. */
0ad1271f 334 type ="tty";
49ebd11f 335 class = "user";
a4cd87e9 336 tty = NULL;
ee8545b0
LP
337 }
338
8e7705e5 339 /* If this fails vtnr will be 0, that's intended */
4d6d6518 340 if (!isempty(cvtnr))
2ae4842b 341 (void) safe_atou32(cvtnr, &vtnr);
4d6d6518 342
92bd5ff3 343 if (!isempty(display) && !vtnr) {
fc7985ed 344 if (isempty(seat))
6ef25fb6 345 get_seat_from_display(display, &seat, &vtnr);
fc7985ed 346 else if (streq(seat, "seat0"))
6ef25fb6 347 get_seat_from_display(display, NULL, &vtnr);
fc7985ed 348 }
4d6d6518 349
49ebd11f 350 if (seat && !streq(seat, "seat0") && vtnr != 0) {
1fa2f38f 351 pam_syslog(handle, LOG_DEBUG, "Ignoring vtnr %"PRIu32" for %s which is not seat0", vtnr, seat);
d7353ef6
MM
352 vtnr = 0;
353 }
354
49ebd11f 355 if (isempty(type))
0ad1271f 356 type = !isempty(display) ? "x11" :
49ebd11f 357 !isempty(tty) ? "tty" : "unspecified";
98a28fef 358
55efac6c 359 if (isempty(class))
e2acb67b 360 class = streq(type, "unspecified") ? "background" : "user";
55efac6c 361
fecc80c1 362 remote = !isempty(remote_host) && !is_localhost(remote_host);
98a28fef 363
4d49b48c 364 /* Talk to logind over the message bus */
5a330cda 365
ffcfcb6b
ZJS
366 r = sd_bus_open_system(&bus);
367 if (r < 0) {
368 pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror(-r));
369 return PAM_SESSION_ERR;
cc377381
LP
370 }
371
ce959314
MS
372 if (debug)
373 pam_syslog(handle, LOG_DEBUG, "Asking logind to create session: "
1fa2f38f 374 "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
375 pw->pw_uid, getpid(),
376 strempty(service),
cda7ecb0 377 type, class, strempty(desktop),
a4cd87e9 378 strempty(seat), vtnr, strempty(tty), strempty(display),
baae0358 379 yes_no(remote), strempty(remote_user), strempty(remote_host));
ce959314 380
ffcfcb6b
ZJS
381 r = sd_bus_call_method(bus,
382 "org.freedesktop.login1",
383 "/org/freedesktop/login1",
384 "org.freedesktop.login1.Manager",
385 "CreateSession",
386 &error,
387 &reply,
a4cd87e9 388 "uusssssussbssa(sv)",
baae0358
LP
389 (uint32_t) pw->pw_uid,
390 (uint32_t) getpid(),
a4cd87e9 391 service,
ffcfcb6b
ZJS
392 type,
393 class,
a4cd87e9
LP
394 desktop,
395 seat,
ffcfcb6b
ZJS
396 vtnr,
397 tty,
a4cd87e9 398 display,
ffcfcb6b 399 remote,
a4cd87e9
LP
400 remote_user,
401 remote_host,
ffcfcb6b
ZJS
402 0);
403 if (r < 0) {
b80120c4
DH
404 if (sd_bus_error_has_name(&error, BUS_ERROR_SESSION_BUSY)) {
405 pam_syslog(handle, LOG_DEBUG, "Cannot create session: %s", bus_error_message(&error, r));
406 return PAM_SUCCESS;
407 } else {
408 pam_syslog(handle, LOG_ERR, "Failed to create session: %s", bus_error_message(&error, r));
409 return PAM_SYSTEM_ERR;
410 }
98a28fef 411 }
74fe1fe3 412
ffcfcb6b 413 r = sd_bus_message_read(reply,
baae0358 414 "soshusub",
ffcfcb6b
ZJS
415 &id,
416 &object_path,
417 &runtime_path,
418 &session_fd,
baae0358 419 &original_uid,
ffcfcb6b
ZJS
420 &seat,
421 &vtnr,
422 &existing);
423 if (r < 0) {
424 pam_syslog(handle, LOG_ERR, "Failed to parse message: %s", strerror(-r));
5a330cda 425 return PAM_SESSION_ERR;
98a28fef 426 }
74fe1fe3 427
ce959314
MS
428 if (debug)
429 pam_syslog(handle, LOG_DEBUG, "Reply from logind: "
baae0358
LP
430 "id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u original_uid=%u",
431 id, object_path, runtime_path, session_fd, seat, vtnr, original_uid);
ce959314 432
98a28fef
LP
433 r = pam_misc_setenv(handle, "XDG_SESSION_ID", id, 0);
434 if (r != PAM_SUCCESS) {
435 pam_syslog(handle, LOG_ERR, "Failed to set session id.");
5a330cda 436 return r;
8c6db833
LP
437 }
438
baae0358
LP
439 if (original_uid == pw->pw_uid) {
440 /* Don't set $XDG_RUNTIME_DIR if the user we now
441 * authenticated for does not match the original user
442 * of the session. We do this in order not to result
443 * in privileged apps clobbering the runtime directory
444 * unnecessarily. */
445
446 r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", runtime_path, 0);
447 if (r != PAM_SUCCESS) {
448 pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
449 return r;
450 }
8b255ecd
KS
451
452 r = export_legacy_dbus_address(handle, pw->pw_uid, runtime_path);
453 if (r != PAM_SUCCESS)
454 return r;
98a28fef 455 }
8c6db833 456
bbc73283
LP
457 if (!isempty(seat)) {
458 r = pam_misc_setenv(handle, "XDG_SEAT", seat, 0);
459 if (r != PAM_SUCCESS) {
460 pam_syslog(handle, LOG_ERR, "Failed to set seat.");
5a330cda 461 return r;
bbc73283
LP
462 }
463 }
464
465 if (vtnr > 0) {
29d230f6 466 char buf[DECIMAL_STR_MAX(vtnr)];
baae0358 467 sprintf(buf, "%u", vtnr);
bbc73283
LP
468
469 r = pam_misc_setenv(handle, "XDG_VTNR", buf, 0);
470 if (r != PAM_SUCCESS) {
471 pam_syslog(handle, LOG_ERR, "Failed to set virtual terminal number.");
5a330cda 472 return r;
bbc73283
LP
473 }
474 }
475
77085881
LP
476 r = pam_set_data(handle, "systemd.existing", INT_TO_PTR(!!existing), NULL);
477 if (r != PAM_SUCCESS) {
478 pam_syslog(handle, LOG_ERR, "Failed to install existing flag.");
5a330cda 479 return r;
77085881
LP
480 }
481
21c390cc 482 if (session_fd >= 0) {
85c08dc0 483 session_fd = fcntl(session_fd, F_DUPFD_CLOEXEC, 3);
5a330cda
ZJS
484 if (session_fd < 0) {
485 pam_syslog(handle, LOG_ERR, "Failed to dup session fd: %m");
486 return PAM_SESSION_ERR;
487 }
488
23e096cc 489 r = pam_set_data(handle, "systemd.session-fd", FD_TO_PTR(session_fd), NULL);
21c390cc
LP
490 if (r != PAM_SUCCESS) {
491 pam_syslog(handle, LOG_ERR, "Failed to install session fd.");
03e334a1 492 safe_close(session_fd);
5a330cda 493 return r;
21c390cc 494 }
98a28fef 495 }
8c6db833 496
ffcfcb6b 497 return PAM_SUCCESS;
98a28fef 498}
8c6db833 499
98a28fef
LP
500_public_ PAM_EXTERN int pam_sm_close_session(
501 pam_handle_t *handle,
502 int flags,
503 int argc, const char **argv) {
8c6db833 504
4afd3348
LP
505 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
506 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
5f41d1f1 507 const void *existing = NULL;
75c8e3cf 508 const char *id;
75c8e3cf 509 int r;
8c6db833 510
ffcfcb6b 511 assert(handle);
75c8e3cf 512
77085881
LP
513 /* Only release session if it wasn't pre-existing when we
514 * tried to create it */
515 pam_get_data(handle, "systemd.existing", &existing);
516
75c8e3cf 517 id = pam_getenv(handle, "XDG_SESSION_ID");
77085881 518 if (id && !existing) {
75c8e3cf
LP
519
520 /* Before we go and close the FIFO we need to tell
521 * logind that this is a clean session shutdown, so
522 * that it doesn't just go and slaughter us
523 * immediately after closing the fd */
524
ffcfcb6b
ZJS
525 r = sd_bus_open_system(&bus);
526 if (r < 0) {
5f41d1f1
LP
527 pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror(-r));
528 return PAM_SESSION_ERR;
75c8e3cf
LP
529 }
530
ffcfcb6b
ZJS
531 r = sd_bus_call_method(bus,
532 "org.freedesktop.login1",
533 "/org/freedesktop/login1",
534 "org.freedesktop.login1.Manager",
535 "ReleaseSession",
536 &error,
537 NULL,
538 "s",
539 id);
540 if (r < 0) {
5f41d1f1
LP
541 pam_syslog(handle, LOG_ERR, "Failed to release session: %s", bus_error_message(&error, r));
542 return PAM_SESSION_ERR;
75c8e3cf
LP
543 }
544 }
545
5f41d1f1
LP
546 /* Note that we are knowingly leaking the FIFO fd here. This
547 * way, logind can watch us die. If we closed it here it would
548 * not have any clue when that is completed. Given that one
549 * cannot really have multiple PAM sessions open from the same
550 * process this means we will leak one FD at max. */
75c8e3cf 551
5f41d1f1 552 return PAM_SUCCESS;
8c6db833 553}