]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/pam-module.c
activate: port to use new getpeername_pretty()/getsockname_pretty() calls
[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
4d49b48c 321 /* Talk to logind over the message bus */
ffcfcb6b
ZJS
322 r = sd_bus_open_system(&bus);
323 if (r < 0) {
324 pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror(-r));
325 return PAM_SESSION_ERR;
cc377381
LP
326 }
327
ce959314
MS
328 if (debug)
329 pam_syslog(handle, LOG_DEBUG, "Asking logind to create session: "
f904bdf2
CG
330 "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",
331 uid, pid, service, type, class, seat, vtnr, tty, display, yes_no(remote), remote_user, remote_host);
ce959314 332
ffcfcb6b
ZJS
333 r = sd_bus_call_method(bus,
334 "org.freedesktop.login1",
335 "/org/freedesktop/login1",
336 "org.freedesktop.login1.Manager",
337 "CreateSession",
338 &error,
339 &reply,
340 "uussssussbssa(sv)",
341 uid,
342 pid,
343 service,
344 type,
345 class,
346 seat,
347 vtnr,
348 tty,
349 display,
350 remote,
351 remote_user,
352 remote_host,
353 0);
354 if (r < 0) {
355 pam_syslog(handle, LOG_ERR, "Failed to communicate with systemd-logind: %s", strerror(-r));
356 if (error.name || error.message)
357 pam_syslog(handle, LOG_ERR, "systemd-logind returned %s: %s",
358 error.name ?: "unknown error",
359 error.message ?: "no message");
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));
98a28fef
LP
374 r = PAM_SESSION_ERR;
375 goto finish;
376 }
74fe1fe3 377
ce959314
MS
378 if (debug)
379 pam_syslog(handle, LOG_DEBUG, "Reply from logind: "
380 "id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u",
381 id, object_path, runtime_path, session_fd, seat, vtnr);
382
98a28fef
LP
383 r = pam_misc_setenv(handle, "XDG_SESSION_ID", id, 0);
384 if (r != PAM_SUCCESS) {
385 pam_syslog(handle, LOG_ERR, "Failed to set session id.");
386 goto finish;
8c6db833
LP
387 }
388
98a28fef
LP
389 r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", runtime_path, 0);
390 if (r != PAM_SUCCESS) {
391 pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
392 goto finish;
393 }
8c6db833 394
bbc73283
LP
395 if (!isempty(seat)) {
396 r = pam_misc_setenv(handle, "XDG_SEAT", seat, 0);
397 if (r != PAM_SUCCESS) {
398 pam_syslog(handle, LOG_ERR, "Failed to set seat.");
399 goto finish;
400 }
401 }
402
403 if (vtnr > 0) {
404 char buf[11];
405 snprintf(buf, sizeof(buf), "%u", vtnr);
406 char_array_0(buf);
407
408 r = pam_misc_setenv(handle, "XDG_VTNR", buf, 0);
409 if (r != PAM_SUCCESS) {
410 pam_syslog(handle, LOG_ERR, "Failed to set virtual terminal number.");
411 goto finish;
412 }
413 }
414
77085881
LP
415 r = pam_set_data(handle, "systemd.existing", INT_TO_PTR(!!existing), NULL);
416 if (r != PAM_SUCCESS) {
417 pam_syslog(handle, LOG_ERR, "Failed to install existing flag.");
ffcfcb6b 418 goto finish;
77085881
LP
419 }
420
21c390cc
LP
421 if (session_fd >= 0) {
422 r = pam_set_data(handle, "systemd.session-fd", INT_TO_PTR(session_fd+1), NULL);
423 if (r != PAM_SUCCESS) {
424 pam_syslog(handle, LOG_ERR, "Failed to install session fd.");
ffcfcb6b 425 goto finish;
21c390cc 426 }
98a28fef 427 }
8c6db833 428
ffcfcb6b 429 return PAM_SUCCESS;
8c6db833 430
98a28fef 431finish:
98a28fef
LP
432 if (session_fd >= 0)
433 close_nointr_nofail(session_fd);
672c48cc 434
98a28fef
LP
435 return r;
436}
8c6db833 437
98a28fef
LP
438_public_ PAM_EXTERN int pam_sm_close_session(
439 pam_handle_t *handle,
440 int flags,
441 int argc, const char **argv) {
8c6db833 442
77085881 443 const void *p = NULL, *existing = NULL;
75c8e3cf 444 const char *id;
75c8e3cf 445 int r;
8c6db833 446
ffcfcb6b
ZJS
447 _cleanup_bus_unref_ sd_bus *bus = NULL;
448 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
449 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
75c8e3cf 450
ffcfcb6b 451 assert(handle);
75c8e3cf 452
77085881
LP
453 /* Only release session if it wasn't pre-existing when we
454 * tried to create it */
455 pam_get_data(handle, "systemd.existing", &existing);
456
75c8e3cf 457 id = pam_getenv(handle, "XDG_SESSION_ID");
77085881 458 if (id && !existing) {
75c8e3cf
LP
459
460 /* Before we go and close the FIFO we need to tell
461 * logind that this is a clean session shutdown, so
462 * that it doesn't just go and slaughter us
463 * immediately after closing the fd */
464
ffcfcb6b
ZJS
465 r = sd_bus_open_system(&bus);
466 if (r < 0) {
467 pam_syslog(handle, LOG_ERR,
468 "Failed to connect to system bus: %s", strerror(-r));
75c8e3cf
LP
469 r = PAM_SESSION_ERR;
470 goto finish;
471 }
472
ffcfcb6b
ZJS
473 r = sd_bus_call_method(bus,
474 "org.freedesktop.login1",
475 "/org/freedesktop/login1",
476 "org.freedesktop.login1.Manager",
477 "ReleaseSession",
478 &error,
479 NULL,
480 "s",
481 id);
482 if (r < 0) {
483 pam_syslog(handle, LOG_ERR,
484 "Failed to release session: %s", strerror(-r));
485 if (error.name || error.message)
486 pam_syslog(handle, LOG_ERR, "systemd-logind returned %s: %s",
487 error.name ?: "unknown error",
488 error.message ?: "no message");
74fe1fe3 489
75c8e3cf
LP
490 r = PAM_SESSION_ERR;
491 goto finish;
492 }
493 }
494
495 r = PAM_SUCCESS;
496
497finish:
498 pam_get_data(handle, "systemd.session-fd", &p);
98a28fef
LP
499 if (p)
500 close_nointr(PTR_TO_INT(p) - 1);
1f73f0f1 501
75c8e3cf 502 return r;
8c6db833 503}