]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/pam-module.c
nspawn: improve error message when we cannot resolve the root directory argument
[thirdparty/systemd.git] / src / login / pam-module.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
LP
44
45static int parse_argv(pam_handle_t *handle,
46 int argc, const char **argv,
485507b8 47 const char **class,
0e318cad 48 bool *debug) {
8c6db833
LP
49
50 unsigned i;
51
52 assert(argc >= 0);
53 assert(argc == 0 || argv);
54
05a049cc 55 for (i = 0; i < (unsigned) argc; i++)
fb6becb4 56 if (startswith(argv[i], "class=")) {
485507b8
MM
57 if (class)
58 *class = argv[i] + 6;
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]);
8c6db833 75
8c6db833
LP
76 return 0;
77}
78
8c6db833
LP
79static int get_user_data(
80 pam_handle_t *handle,
81 const char **ret_username,
82 struct passwd **ret_pw) {
83
d90b9d27
LP
84 const char *username = NULL;
85 struct passwd *pw = NULL;
87d2c1ff 86 uid_t uid;
8c6db833
LP
87 int r;
88
89 assert(handle);
90 assert(ret_username);
91 assert(ret_pw);
92
87d2c1ff
LP
93 r = audit_loginuid_from_pid(0, &uid);
94 if (r >= 0)
95 pw = pam_modutil_getpwuid(handle, uid);
96 else {
97 r = pam_get_user(handle, &username, NULL);
98 if (r != PAM_SUCCESS) {
d90b9d27
LP
99 pam_syslog(handle, LOG_ERR, "Failed to get user name.");
100 return r;
101 }
102
87d2c1ff 103 if (isempty(username)) {
d90b9d27
LP
104 pam_syslog(handle, LOG_ERR, "User name not valid.");
105 return PAM_AUTH_ERR;
106 }
107
108 pw = pam_modutil_getpwnam(handle, username);
8c6db833
LP
109 }
110
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;
d90b9d27 117 *ret_username = username ? username : pw->pw_name;
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) {
7fd1b19b 123 _cleanup_free_ char *p = NULL;
4d6d6518 124 int r;
7fd1b19b 125 _cleanup_close_ int fd = -1;
b92bea5d
ZJS
126 union sockaddr_union sa = {
127 .un.sun_family = AF_UNIX,
128 };
4d6d6518
LP
129 struct ucred ucred;
130 socklen_t l;
7fd1b19b 131 _cleanup_free_ char *tty = NULL;
4d6d6518
LP
132 int v;
133
134 assert(display);
4d6d6518
LP
135 assert(vtnr);
136
137 /* We deduce the X11 socket from the display name, then use
138 * SO_PEERCRED to determine the X11 server process, ask for
139 * the controlling tty of that and if it's a VC then we know
140 * the seat and the virtual terminal. Sounds ugly, is only
141 * semi-ugly. */
142
143 r = socket_from_display(display, &p);
144 if (r < 0)
145 return r;
b92bea5d 146 strncpy(sa.un.sun_path, p, sizeof(sa.un.sun_path)-1);
4d6d6518
LP
147
148 fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
b92bea5d 149 if (fd < 0)
4d6d6518 150 return -errno;
4d6d6518 151
b92bea5d 152 if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0)
4d6d6518 153 return -errno;
4d6d6518
LP
154
155 l = sizeof(ucred);
156 r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l);
4d6d6518
LP
157 if (r < 0)
158 return -errno;
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
98a28fef 177_public_ PAM_EXTERN int pam_sm_open_session(
8c6db833
LP
178 pam_handle_t *handle,
179 int flags,
180 int argc, const char **argv) {
181
ffcfcb6b
ZJS
182 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
183 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
d1529c9e
LP
184 const char
185 *username, *id, *object_path, *runtime_path,
186 *service = NULL,
187 *tty = NULL, *display = NULL,
188 *remote_user = NULL, *remote_host = NULL,
189 *seat = NULL,
190 *type = NULL, *class = NULL,
191 *class_pam = NULL, *cvtnr = NULL;
192 _cleanup_bus_unref_ sd_bus *bus = NULL;
193 int session_fd = -1, existing, r;
194 uint32_t uid, pid, vtnr = 0;
195 bool debug = false, remote;
196 struct passwd *pw;
8c6db833 197
ffcfcb6b 198 assert(handle);
98a28fef 199
970edce6
ZJS
200 if (debug)
201 pam_syslog(handle, LOG_INFO, "pam-systemd initializing");
98a28fef 202
79d860fe
MP
203 /* Make this a NOP on non-logind systems */
204 if (!logind_running())
8c6db833
LP
205 return PAM_SUCCESS;
206
e9fbc77c
LP
207 if (parse_argv(handle,
208 argc, argv,
fb6becb4 209 &class_pam,
5a330cda
ZJS
210 &debug) < 0)
211 return PAM_SESSION_ERR;
74fe1fe3 212
98a28fef 213 r = get_user_data(handle, &username, &pw);
5a330cda
ZJS
214 if (r != PAM_SUCCESS) {
215 pam_syslog(handle, LOG_ERR, "Failed to get user data.");
216 return r;
217 }
8c6db833 218
30b2c336
LP
219 /* Make sure we don't enter a loop by talking to
220 * systemd-logind when it is actually waiting for the
221 * background to finish start-up. If the service is
5c390a4a 222 * "systemd-user" we simply set XDG_RUNTIME_DIR and
30b2c336
LP
223 * leave. */
224
225 pam_get_item(handle, PAM_SERVICE, (const void**) &service);
5c390a4a 226 if (streq_ptr(service, "systemd-user")) {
05a049cc 227 _cleanup_free_ char *p = NULL, *rt = NULL;
30b2c336 228
ffcfcb6b
ZJS
229 if (asprintf(&p, "/run/systemd/users/%lu", (unsigned long) pw->pw_uid) < 0)
230 return PAM_BUF_ERR;
30b2c336
LP
231
232 r = parse_env_file(p, NEWLINE,
233 "RUNTIME", &rt,
234 NULL);
ffcfcb6b
ZJS
235 if (r < 0 && r != -ENOENT)
236 return PAM_SESSION_ERR;
30b2c336
LP
237
238 if (rt) {
239 r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0);
30b2c336
LP
240 if (r != PAM_SUCCESS) {
241 pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
ffcfcb6b 242 return r;
30b2c336
LP
243 }
244 }
245
ffcfcb6b 246 return PAM_SUCCESS;
8c6db833
LP
247 }
248
ffcfcb6b 249 /* Otherwise, we ask logind to create a session for us */
8c6db833 250
98a28fef
LP
251 uid = pw->pw_uid;
252 pid = getpid();
253
98a28fef
LP
254 pam_get_item(handle, PAM_XDISPLAY, (const void**) &display);
255 pam_get_item(handle, PAM_TTY, (const void**) &tty);
256 pam_get_item(handle, PAM_RUSER, (const void**) &remote_user);
257 pam_get_item(handle, PAM_RHOST, (const void**) &remote_host);
a8573ccc 258
bbc73283 259 seat = pam_getenv(handle, "XDG_SEAT");
a8573ccc
LP
260 if (isempty(seat))
261 seat = getenv("XDG_SEAT");
262
bbc73283 263 cvtnr = pam_getenv(handle, "XDG_VTNR");
a8573ccc
LP
264 if (isempty(cvtnr))
265 cvtnr = getenv("XDG_VTNR");
98a28fef 266
ed18b08b
LP
267 service = strempty(service);
268 tty = strempty(tty);
269 display = strempty(display);
270 remote_user = strempty(remote_user);
271 remote_host = strempty(remote_host);
272 seat = strempty(seat);
98a28fef 273
ee8545b0 274 if (strchr(tty, ':')) {
e2acb67b
LP
275 /* A tty with a colon is usually an X11 display,
276 * placed there to show up in utmp. We rearrange
277 * things and don't pretend that an X display was a
278 * tty. */
ee8545b0
LP
279
280 if (isempty(display))
281 display = tty;
cd58752a 282 tty = "";
1a4459d6 283 } else if (streq(tty, "cron")) {
0ad1271f
LP
284 /* cron has been setting PAM_TTY to "cron" for a very
285 * long time and it probably shouldn't stop doing that
286 * for compatibility reasons. */
1a4459d6 287 tty = "";
0ad1271f
LP
288 type = "unspecified";
289 } else if (streq(tty, "ssh")) {
290 /* ssh has been setting PAM_TTY to "ssh" for a very
291 * long time and probably shouldn't stop doing that
292 * for compatibility reasons. */
293 tty = "";
294 type ="tty";
ee8545b0
LP
295 }
296
8e7705e5 297 /* If this fails vtnr will be 0, that's intended */
4d6d6518
LP
298 if (!isempty(cvtnr))
299 safe_atou32(cvtnr, &vtnr);
300
fc7985ed
LP
301 if (!isempty(display) && vtnr <= 0) {
302 if (isempty(seat))
6ef25fb6 303 get_seat_from_display(display, &seat, &vtnr);
fc7985ed 304 else if (streq(seat, "seat0"))
6ef25fb6 305 get_seat_from_display(display, NULL, &vtnr);
fc7985ed 306 }
4d6d6518 307
0ad1271f
LP
308 if (!type)
309 type = !isempty(display) ? "x11" :
310 !isempty(tty) ? "tty" : "unspecified";
98a28fef 311
55efac6c 312 class = pam_getenv(handle, "XDG_SESSION_CLASS");
a8573ccc
LP
313 if (isempty(class))
314 class = getenv("XDG_SESSION_CLASS");
485507b8
MM
315 if (isempty(class))
316 class = class_pam;
55efac6c 317 if (isempty(class))
e2acb67b 318 class = streq(type, "unspecified") ? "background" : "user";
55efac6c
LP
319
320 remote = !isempty(remote_host) &&
321 !streq(remote_host, "localhost") &&
322 !streq(remote_host, "localhost.localdomain");
98a28fef 323
4d49b48c 324 /* Talk to logind over the message bus */
5a330cda 325
ffcfcb6b
ZJS
326 r = sd_bus_open_system(&bus);
327 if (r < 0) {
328 pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror(-r));
329 return PAM_SESSION_ERR;
cc377381
LP
330 }
331
ce959314
MS
332 if (debug)
333 pam_syslog(handle, LOG_DEBUG, "Asking logind to create session: "
f904bdf2
CG
334 "uid=%u pid=%u service=%s type=%s class=%s seat=%s vtnr=%u tty=%s display=%s remote=%s remote_user=%s remote_host=%s",
335 uid, pid, service, type, class, seat, vtnr, tty, display, yes_no(remote), remote_user, remote_host);
ce959314 336
ffcfcb6b
ZJS
337 r = sd_bus_call_method(bus,
338 "org.freedesktop.login1",
339 "/org/freedesktop/login1",
340 "org.freedesktop.login1.Manager",
341 "CreateSession",
342 &error,
343 &reply,
344 "uussssussbssa(sv)",
345 uid,
346 pid,
347 service,
348 type,
349 class,
350 seat,
351 vtnr,
352 tty,
353 display,
354 remote,
355 remote_user,
356 remote_host,
357 0);
358 if (r < 0) {
8159d91a 359 pam_syslog(handle, LOG_ERR, "Failed to create session: %s", bus_error_message(&error, r));
ffcfcb6b 360 return PAM_SYSTEM_ERR;
98a28fef 361 }
74fe1fe3 362
ffcfcb6b
ZJS
363 r = sd_bus_message_read(reply,
364 "soshsub",
365 &id,
366 &object_path,
367 &runtime_path,
368 &session_fd,
369 &seat,
370 &vtnr,
371 &existing);
372 if (r < 0) {
373 pam_syslog(handle, LOG_ERR, "Failed to parse message: %s", strerror(-r));
5a330cda 374 return PAM_SESSION_ERR;
98a28fef 375 }
74fe1fe3 376
ce959314
MS
377 if (debug)
378 pam_syslog(handle, LOG_DEBUG, "Reply from logind: "
379 "id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u",
380 id, object_path, runtime_path, session_fd, seat, vtnr);
381
98a28fef
LP
382 r = pam_misc_setenv(handle, "XDG_SESSION_ID", id, 0);
383 if (r != PAM_SUCCESS) {
384 pam_syslog(handle, LOG_ERR, "Failed to set session id.");
5a330cda 385 return r;
8c6db833
LP
386 }
387
98a28fef
LP
388 r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", runtime_path, 0);
389 if (r != PAM_SUCCESS) {
390 pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
5a330cda 391 return r;
98a28fef 392 }
8c6db833 393
bbc73283
LP
394 if (!isempty(seat)) {
395 r = pam_misc_setenv(handle, "XDG_SEAT", seat, 0);
396 if (r != PAM_SUCCESS) {
397 pam_syslog(handle, LOG_ERR, "Failed to set seat.");
5a330cda 398 return r;
bbc73283
LP
399 }
400 }
401
402 if (vtnr > 0) {
29d230f6 403 char buf[DECIMAL_STR_MAX(vtnr)];
bbc73283 404 snprintf(buf, sizeof(buf), "%u", vtnr);
bbc73283
LP
405
406 r = pam_misc_setenv(handle, "XDG_VTNR", buf, 0);
407 if (r != PAM_SUCCESS) {
408 pam_syslog(handle, LOG_ERR, "Failed to set virtual terminal number.");
5a330cda 409 return r;
bbc73283
LP
410 }
411 }
412
77085881
LP
413 r = pam_set_data(handle, "systemd.existing", INT_TO_PTR(!!existing), NULL);
414 if (r != PAM_SUCCESS) {
415 pam_syslog(handle, LOG_ERR, "Failed to install existing flag.");
5a330cda 416 return r;
77085881
LP
417 }
418
21c390cc 419 if (session_fd >= 0) {
5a330cda
ZJS
420 session_fd = dup(session_fd);
421 if (session_fd < 0) {
422 pam_syslog(handle, LOG_ERR, "Failed to dup session fd: %m");
423 return PAM_SESSION_ERR;
424 }
425
21c390cc
LP
426 r = pam_set_data(handle, "systemd.session-fd", INT_TO_PTR(session_fd+1), NULL);
427 if (r != PAM_SUCCESS) {
428 pam_syslog(handle, LOG_ERR, "Failed to install session fd.");
5a330cda
ZJS
429 close_nointr_nofail(session_fd);
430 return r;
21c390cc 431 }
98a28fef 432 }
8c6db833 433
ffcfcb6b 434 return PAM_SUCCESS;
98a28fef 435}
8c6db833 436
98a28fef
LP
437_public_ PAM_EXTERN int pam_sm_close_session(
438 pam_handle_t *handle,
439 int flags,
440 int argc, const char **argv) {
8c6db833 441
29d230f6 442 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
29d230f6 443 _cleanup_bus_unref_ sd_bus *bus = NULL;
77085881 444 const void *p = NULL, *existing = NULL;
75c8e3cf 445 const char *id;
75c8e3cf 446 int r;
8c6db833 447
ffcfcb6b 448 assert(handle);
75c8e3cf 449
77085881
LP
450 /* Only release session if it wasn't pre-existing when we
451 * tried to create it */
452 pam_get_data(handle, "systemd.existing", &existing);
453
75c8e3cf 454 id = pam_getenv(handle, "XDG_SESSION_ID");
77085881 455 if (id && !existing) {
75c8e3cf
LP
456
457 /* Before we go and close the FIFO we need to tell
458 * logind that this is a clean session shutdown, so
459 * that it doesn't just go and slaughter us
460 * immediately after closing the fd */
461
ffcfcb6b
ZJS
462 r = sd_bus_open_system(&bus);
463 if (r < 0) {
464 pam_syslog(handle, LOG_ERR,
465 "Failed to connect to system bus: %s", strerror(-r));
75c8e3cf
LP
466 r = PAM_SESSION_ERR;
467 goto finish;
468 }
469
ffcfcb6b
ZJS
470 r = sd_bus_call_method(bus,
471 "org.freedesktop.login1",
472 "/org/freedesktop/login1",
473 "org.freedesktop.login1.Manager",
474 "ReleaseSession",
475 &error,
476 NULL,
477 "s",
478 id);
479 if (r < 0) {
480 pam_syslog(handle, LOG_ERR,
8159d91a 481 "Failed to release session: %s", bus_error_message(&error, r));
74fe1fe3 482
75c8e3cf
LP
483 r = PAM_SESSION_ERR;
484 goto finish;
485 }
486 }
487
488 r = PAM_SUCCESS;
489
490finish:
491 pam_get_data(handle, "systemd.session-fd", &p);
98a28fef
LP
492 if (p)
493 close_nointr(PTR_TO_INT(p) - 1);
1f73f0f1 494
75c8e3cf 495 return r;
8c6db833 496}