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