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