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