]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/pam-module.c
systemd_pam: treat debug as debug=1 and parse all params
[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
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
LP
184 DBusError error;
185 uint32_t uid, pid;
cc377381 186 DBusMessageIter iter, sub;
98a28fef
LP
187 int session_fd = -1;
188 DBusConnection *bus = NULL;
189 DBusMessage *m = NULL, *reply = NULL;
77085881 190 dbus_bool_t remote, existing;
ed18b08b 191 int r;
4d6d6518 192 uint32_t vtnr = 0;
8c6db833
LP
193
194 assert(handle);
195
98a28fef
LP
196 dbus_error_init(&error);
197
970edce6
ZJS
198 if (debug)
199 pam_syslog(handle, LOG_INFO, "pam-systemd initializing");
98a28fef 200
79d860fe
MP
201 /* Make this a NOP on non-logind systems */
202 if (!logind_running())
8c6db833
LP
203 return PAM_SUCCESS;
204
e9fbc77c
LP
205 if (parse_argv(handle,
206 argc, argv,
fb6becb4
LP
207 &class_pam,
208 &debug) < 0) {
ed18b08b
LP
209 r = PAM_SESSION_ERR;
210 goto finish;
211 }
74fe1fe3 212
98a28fef
LP
213 r = get_user_data(handle, &username, &pw);
214 if (r != PAM_SUCCESS)
8c6db833
LP
215 goto finish;
216
30b2c336
LP
217 /* Make sure we don't enter a loop by talking to
218 * systemd-logind when it is actually waiting for the
219 * background to finish start-up. If the service is
5c390a4a 220 * "systemd-user" we simply set XDG_RUNTIME_DIR and
30b2c336
LP
221 * leave. */
222
223 pam_get_item(handle, PAM_SERVICE, (const void**) &service);
5c390a4a 224 if (streq_ptr(service, "systemd-user")) {
05a049cc 225 _cleanup_free_ char *p = NULL, *rt = NULL;
30b2c336
LP
226
227 if (asprintf(&p, "/run/systemd/users/%lu", (unsigned long) pw->pw_uid) < 0) {
228 r = PAM_BUF_ERR;
229 goto finish;
230 }
231
232 r = parse_env_file(p, NEWLINE,
233 "RUNTIME", &rt,
234 NULL);
30b2c336
LP
235 if (r < 0 && r != -ENOENT) {
236 r = PAM_SESSION_ERR;
30b2c336
LP
237 goto finish;
238 }
239
240 if (rt) {
241 r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0);
30b2c336
LP
242 if (r != PAM_SUCCESS) {
243 pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
244 goto finish;
245 }
246 }
247
248 r = PAM_SUCCESS;
249 goto finish;
250 }
251
ed18b08b
LP
252 dbus_connection_set_change_sigpipe(FALSE);
253
98a28fef
LP
254 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
255 if (!bus) {
256 pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", bus_error_message(&error));
257 r = PAM_SESSION_ERR;
8c6db833
LP
258 goto finish;
259 }
260
98a28fef
LP
261 m = dbus_message_new_method_call(
262 "org.freedesktop.login1",
263 "/org/freedesktop/login1",
264 "org.freedesktop.login1.Manager",
265 "CreateSession");
98a28fef
LP
266 if (!m) {
267 pam_syslog(handle, LOG_ERR, "Could not allocate create session message.");
8c6db833
LP
268 r = PAM_BUF_ERR;
269 goto finish;
270 }
271
98a28fef
LP
272 uid = pw->pw_uid;
273 pid = getpid();
274
98a28fef
LP
275 pam_get_item(handle, PAM_XDISPLAY, (const void**) &display);
276 pam_get_item(handle, PAM_TTY, (const void**) &tty);
277 pam_get_item(handle, PAM_RUSER, (const void**) &remote_user);
278 pam_get_item(handle, PAM_RHOST, (const void**) &remote_host);
a8573ccc 279
bbc73283 280 seat = pam_getenv(handle, "XDG_SEAT");
a8573ccc
LP
281 if (isempty(seat))
282 seat = getenv("XDG_SEAT");
283
bbc73283 284 cvtnr = pam_getenv(handle, "XDG_VTNR");
a8573ccc
LP
285 if (isempty(cvtnr))
286 cvtnr = getenv("XDG_VTNR");
98a28fef 287
ed18b08b
LP
288 service = strempty(service);
289 tty = strempty(tty);
290 display = strempty(display);
291 remote_user = strempty(remote_user);
292 remote_host = strempty(remote_host);
293 seat = strempty(seat);
98a28fef 294
ee8545b0 295 if (strchr(tty, ':')) {
e2acb67b
LP
296 /* A tty with a colon is usually an X11 display,
297 * placed there to show up in utmp. We rearrange
298 * things and don't pretend that an X display was a
299 * tty. */
ee8545b0
LP
300
301 if (isempty(display))
302 display = tty;
cd58752a 303 tty = "";
1a4459d6 304 } else if (streq(tty, "cron")) {
0ad1271f
LP
305 /* cron has been setting PAM_TTY to "cron" for a very
306 * long time and it probably shouldn't stop doing that
307 * for compatibility reasons. */
1a4459d6 308 tty = "";
0ad1271f
LP
309 type = "unspecified";
310 } else if (streq(tty, "ssh")) {
311 /* ssh has been setting PAM_TTY to "ssh" for a very
312 * long time and probably shouldn't stop doing that
313 * for compatibility reasons. */
314 tty = "";
315 type ="tty";
ee8545b0
LP
316 }
317
8e7705e5 318 /* If this fails vtnr will be 0, that's intended */
4d6d6518
LP
319 if (!isempty(cvtnr))
320 safe_atou32(cvtnr, &vtnr);
321
fc7985ed
LP
322 if (!isempty(display) && vtnr <= 0) {
323 if (isempty(seat))
6ef25fb6 324 get_seat_from_display(display, &seat, &vtnr);
fc7985ed 325 else if (streq(seat, "seat0"))
6ef25fb6 326 get_seat_from_display(display, NULL, &vtnr);
fc7985ed 327 }
4d6d6518 328
0ad1271f
LP
329 if (!type)
330 type = !isempty(display) ? "x11" :
331 !isempty(tty) ? "tty" : "unspecified";
98a28fef 332
55efac6c 333 class = pam_getenv(handle, "XDG_SESSION_CLASS");
a8573ccc
LP
334 if (isempty(class))
335 class = getenv("XDG_SESSION_CLASS");
485507b8
MM
336 if (isempty(class))
337 class = class_pam;
55efac6c 338 if (isempty(class))
e2acb67b 339 class = streq(type, "unspecified") ? "background" : "user";
55efac6c
LP
340
341 remote = !isempty(remote_host) &&
342 !streq(remote_host, "localhost") &&
343 !streq(remote_host, "localhost.localdomain");
98a28fef
LP
344
345 if (!dbus_message_append_args(m,
346 DBUS_TYPE_UINT32, &uid,
347 DBUS_TYPE_UINT32, &pid,
348 DBUS_TYPE_STRING, &service,
349 DBUS_TYPE_STRING, &type,
55efac6c 350 DBUS_TYPE_STRING, &class,
98a28fef 351 DBUS_TYPE_STRING, &seat,
4d6d6518 352 DBUS_TYPE_UINT32, &vtnr,
98a28fef
LP
353 DBUS_TYPE_STRING, &tty,
354 DBUS_TYPE_STRING, &display,
355 DBUS_TYPE_BOOLEAN, &remote,
356 DBUS_TYPE_STRING, &remote_user,
357 DBUS_TYPE_STRING, &remote_host,
358 DBUS_TYPE_INVALID)) {
359 pam_syslog(handle, LOG_ERR, "Could not attach parameters to message.");
360 r = PAM_BUF_ERR;
361 goto finish;
362 }
8c6db833 363
98a28fef 364 dbus_message_iter_init_append(m, &iter);
8c6db833 365
cc377381
LP
366 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sv)", &sub) ||
367 !dbus_message_iter_close_container(&iter, &sub)) {
368 pam_syslog(handle, LOG_ERR, "Could not attach parameters to message.");
369 r = PAM_BUF_ERR;
370 goto finish;
371 }
372
ce959314
MS
373 if (debug)
374 pam_syslog(handle, LOG_DEBUG, "Asking logind to create session: "
f904bdf2
CG
375 "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",
376 uid, pid, service, type, class, seat, vtnr, tty, display, yes_no(remote), remote_user, remote_host);
ce959314 377
98a28fef
LP
378 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
379 if (!reply) {
380 pam_syslog(handle, LOG_ERR, "Failed to create session: %s", bus_error_message(&error));
381 r = PAM_SESSION_ERR;
382 goto finish;
383 }
74fe1fe3 384
98a28fef
LP
385 if (!dbus_message_get_args(reply, &error,
386 DBUS_TYPE_STRING, &id,
387 DBUS_TYPE_OBJECT_PATH, &object_path,
388 DBUS_TYPE_STRING, &runtime_path,
389 DBUS_TYPE_UNIX_FD, &session_fd,
bbc73283
LP
390 DBUS_TYPE_STRING, &seat,
391 DBUS_TYPE_UINT32, &vtnr,
77085881 392 DBUS_TYPE_BOOLEAN, &existing,
98a28fef
LP
393 DBUS_TYPE_INVALID)) {
394 pam_syslog(handle, LOG_ERR, "Failed to parse message: %s", bus_error_message(&error));
395 r = PAM_SESSION_ERR;
396 goto finish;
397 }
74fe1fe3 398
ce959314
MS
399 if (debug)
400 pam_syslog(handle, LOG_DEBUG, "Reply from logind: "
401 "id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u",
402 id, object_path, runtime_path, session_fd, seat, vtnr);
403
98a28fef
LP
404 r = pam_misc_setenv(handle, "XDG_SESSION_ID", id, 0);
405 if (r != PAM_SUCCESS) {
406 pam_syslog(handle, LOG_ERR, "Failed to set session id.");
407 goto finish;
8c6db833
LP
408 }
409
98a28fef
LP
410 r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", runtime_path, 0);
411 if (r != PAM_SUCCESS) {
412 pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
413 goto finish;
414 }
8c6db833 415
bbc73283
LP
416 if (!isempty(seat)) {
417 r = pam_misc_setenv(handle, "XDG_SEAT", seat, 0);
418 if (r != PAM_SUCCESS) {
419 pam_syslog(handle, LOG_ERR, "Failed to set seat.");
420 goto finish;
421 }
422 }
423
424 if (vtnr > 0) {
425 char buf[11];
426 snprintf(buf, sizeof(buf), "%u", vtnr);
427 char_array_0(buf);
428
429 r = pam_misc_setenv(handle, "XDG_VTNR", buf, 0);
430 if (r != PAM_SUCCESS) {
431 pam_syslog(handle, LOG_ERR, "Failed to set virtual terminal number.");
432 goto finish;
433 }
434 }
435
77085881
LP
436 r = pam_set_data(handle, "systemd.existing", INT_TO_PTR(!!existing), NULL);
437 if (r != PAM_SUCCESS) {
438 pam_syslog(handle, LOG_ERR, "Failed to install existing flag.");
439 return r;
440 }
441
21c390cc
LP
442 if (session_fd >= 0) {
443 r = pam_set_data(handle, "systemd.session-fd", INT_TO_PTR(session_fd+1), NULL);
444 if (r != PAM_SUCCESS) {
445 pam_syslog(handle, LOG_ERR, "Failed to install session fd.");
446 return r;
447 }
98a28fef 448 }
8c6db833 449
98a28fef 450 session_fd = -1;
8c6db833 451
98a28fef 452 r = PAM_SUCCESS;
8c6db833 453
98a28fef 454finish:
98a28fef 455 dbus_error_free(&error);
35d2e7ec 456
98a28fef
LP
457 if (bus) {
458 dbus_connection_close(bus);
459 dbus_connection_unref(bus);
8c6db833
LP
460 }
461
98a28fef
LP
462 if (m)
463 dbus_message_unref(m);
8c6db833 464
ed18b08b
LP
465 if (reply)
466 dbus_message_unref(reply);
467
98a28fef
LP
468 if (session_fd >= 0)
469 close_nointr_nofail(session_fd);
672c48cc 470
98a28fef
LP
471 return r;
472}
8c6db833 473
98a28fef
LP
474_public_ PAM_EXTERN int pam_sm_close_session(
475 pam_handle_t *handle,
476 int flags,
477 int argc, const char **argv) {
8c6db833 478
77085881 479 const void *p = NULL, *existing = NULL;
75c8e3cf
LP
480 const char *id;
481 DBusConnection *bus = NULL;
482 DBusMessage *m = NULL, *reply = NULL;
483 DBusError error;
484 int r;
8c6db833 485
75c8e3cf
LP
486 assert(handle);
487
488 dbus_error_init(&error);
489
77085881
LP
490 /* Only release session if it wasn't pre-existing when we
491 * tried to create it */
492 pam_get_data(handle, "systemd.existing", &existing);
493
75c8e3cf 494 id = pam_getenv(handle, "XDG_SESSION_ID");
77085881 495 if (id && !existing) {
75c8e3cf
LP
496
497 /* Before we go and close the FIFO we need to tell
498 * logind that this is a clean session shutdown, so
499 * that it doesn't just go and slaughter us
500 * immediately after closing the fd */
501
502 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
503 if (!bus) {
504 pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", bus_error_message(&error));
505 r = PAM_SESSION_ERR;
506 goto finish;
507 }
508
509 m = dbus_message_new_method_call(
510 "org.freedesktop.login1",
511 "/org/freedesktop/login1",
512 "org.freedesktop.login1.Manager",
513 "ReleaseSession");
514 if (!m) {
515 pam_syslog(handle, LOG_ERR, "Could not allocate release session message.");
516 r = PAM_BUF_ERR;
517 goto finish;
518 }
519
520 if (!dbus_message_append_args(m,
521 DBUS_TYPE_STRING, &id,
522 DBUS_TYPE_INVALID)) {
523 pam_syslog(handle, LOG_ERR, "Could not attach parameters to message.");
524 r = PAM_BUF_ERR;
525 goto finish;
526 }
74fe1fe3 527
75c8e3cf
LP
528 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
529 if (!reply) {
530 pam_syslog(handle, LOG_ERR, "Failed to release session: %s", bus_error_message(&error));
531 r = PAM_SESSION_ERR;
532 goto finish;
533 }
534 }
535
536 r = PAM_SUCCESS;
537
538finish:
539 pam_get_data(handle, "systemd.session-fd", &p);
98a28fef
LP
540 if (p)
541 close_nointr(PTR_TO_INT(p) - 1);
1f73f0f1 542
75c8e3cf
LP
543 dbus_error_free(&error);
544
545 if (bus) {
546 dbus_connection_close(bus);
547 dbus_connection_unref(bus);
548 }
549
550 if (m)
551 dbus_message_unref(m);
552
553 if (reply)
554 dbus_message_unref(reply);
555
556 return r;
8c6db833 557}