Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1+ */
2 :
3 : #include <endian.h>
4 : #include <errno.h>
5 : #include <fcntl.h>
6 : #include <pwd.h>
7 : #include <security/_pam_macros.h>
8 : #include <security/pam_ext.h>
9 : #include <security/pam_misc.h>
10 : #include <security/pam_modules.h>
11 : #include <security/pam_modutil.h>
12 : #include <sys/file.h>
13 : #include <sys/stat.h>
14 : #include <sys/types.h>
15 : #include <unistd.h>
16 :
17 : #include "alloc-util.h"
18 : #include "audit-util.h"
19 : #include "bus-common-errors.h"
20 : #include "bus-error.h"
21 : #include "bus-internal.h"
22 : #include "bus-util.h"
23 : #include "cgroup-util.h"
24 : #include "errno-util.h"
25 : #include "fd-util.h"
26 : #include "fileio.h"
27 : #include "format-util.h"
28 : #include "hostname-util.h"
29 : #include "login-util.h"
30 : #include "macro.h"
31 : #include "parse-util.h"
32 : #include "path-util.h"
33 : #include "process-util.h"
34 : #include "socket-util.h"
35 : #include "stdio-util.h"
36 : #include "strv.h"
37 : #include "terminal-util.h"
38 :
39 0 : static int parse_argv(
40 : pam_handle_t *handle,
41 : int argc, const char **argv,
42 : const char **class,
43 : const char **type,
44 : const char **desktop,
45 : bool *debug) {
46 :
47 : unsigned i;
48 :
49 0 : assert(argc >= 0);
50 0 : assert(argc == 0 || argv);
51 :
52 0 : for (i = 0; i < (unsigned) argc; i++) {
53 0 : if (startswith(argv[i], "class=")) {
54 0 : if (class)
55 0 : *class = argv[i] + 6;
56 :
57 0 : } else if (startswith(argv[i], "type=")) {
58 0 : if (type)
59 0 : *type = argv[i] + 5;
60 :
61 0 : } else if (startswith(argv[i], "desktop=")) {
62 0 : if (desktop)
63 0 : *desktop = argv[i] + 8;
64 :
65 0 : } else if (streq(argv[i], "debug")) {
66 0 : if (debug)
67 0 : *debug = true;
68 :
69 0 : } else if (startswith(argv[i], "debug=")) {
70 : int k;
71 :
72 0 : k = parse_boolean(argv[i] + 6);
73 0 : if (k < 0)
74 0 : pam_syslog(handle, LOG_WARNING, "Failed to parse debug= argument, ignoring.");
75 0 : else if (debug)
76 0 : *debug = k;
77 :
78 : } else
79 0 : pam_syslog(handle, LOG_WARNING, "Unknown parameter '%s', ignoring", argv[i]);
80 : }
81 :
82 0 : return 0;
83 : }
84 :
85 0 : static int get_user_data(
86 : pam_handle_t *handle,
87 : const char **ret_username,
88 : struct passwd **ret_pw) {
89 :
90 0 : const char *username = NULL;
91 0 : struct passwd *pw = NULL;
92 : int r;
93 :
94 0 : assert(handle);
95 0 : assert(ret_username);
96 0 : assert(ret_pw);
97 :
98 0 : r = pam_get_user(handle, &username, NULL);
99 0 : if (r != PAM_SUCCESS) {
100 0 : pam_syslog(handle, LOG_ERR, "Failed to get user name.");
101 0 : return r;
102 : }
103 :
104 0 : if (isempty(username)) {
105 0 : pam_syslog(handle, LOG_ERR, "User name not valid.");
106 0 : return PAM_AUTH_ERR;
107 : }
108 :
109 0 : pw = pam_modutil_getpwnam(handle, username);
110 0 : if (!pw) {
111 0 : pam_syslog(handle, LOG_ERR, "Failed to get user data.");
112 0 : return PAM_USER_UNKNOWN;
113 : }
114 :
115 0 : *ret_pw = pw;
116 0 : *ret_username = username;
117 :
118 0 : return PAM_SUCCESS;
119 : }
120 :
121 0 : static bool display_is_local(const char *display) {
122 0 : assert(display);
123 :
124 : return
125 0 : display[0] == ':' &&
126 0 : display[1] >= '0' &&
127 0 : display[1] <= '9';
128 : }
129 :
130 0 : static int socket_from_display(const char *display, char **path) {
131 : size_t k;
132 : char *f, *c;
133 :
134 0 : assert(display);
135 0 : assert(path);
136 :
137 0 : if (!display_is_local(display))
138 0 : return -EINVAL;
139 :
140 0 : k = strspn(display+1, "0123456789");
141 :
142 0 : f = new(char, STRLEN("/tmp/.X11-unix/X") + k + 1);
143 0 : if (!f)
144 0 : return -ENOMEM;
145 :
146 0 : c = stpcpy(f, "/tmp/.X11-unix/X");
147 0 : memcpy(c, display+1, k);
148 0 : c[k] = 0;
149 :
150 0 : *path = f;
151 :
152 0 : return 0;
153 : }
154 :
155 0 : static int get_seat_from_display(const char *display, const char **seat, uint32_t *vtnr) {
156 0 : union sockaddr_union sa = {};
157 0 : _cleanup_free_ char *p = NULL, *tty = NULL;
158 0 : _cleanup_close_ int fd = -1;
159 : struct ucred ucred;
160 : int v, r, salen;
161 :
162 0 : assert(display);
163 0 : assert(vtnr);
164 :
165 : /* We deduce the X11 socket from the display name, then use
166 : * SO_PEERCRED to determine the X11 server process, ask for
167 : * the controlling tty of that and if it's a VC then we know
168 : * the seat and the virtual terminal. Sounds ugly, is only
169 : * semi-ugly. */
170 :
171 0 : r = socket_from_display(display, &p);
172 0 : if (r < 0)
173 0 : return r;
174 0 : salen = sockaddr_un_set_path(&sa.un, p);
175 0 : if (salen < 0)
176 0 : return salen;
177 :
178 0 : fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
179 0 : if (fd < 0)
180 0 : return -errno;
181 :
182 0 : if (connect(fd, &sa.sa, salen) < 0)
183 0 : return -errno;
184 :
185 0 : r = getpeercred(fd, &ucred);
186 0 : if (r < 0)
187 0 : return r;
188 :
189 0 : r = get_ctty(ucred.pid, NULL, &tty);
190 0 : if (r < 0)
191 0 : return r;
192 :
193 0 : v = vtnr_from_tty(tty);
194 0 : if (v < 0)
195 0 : return v;
196 0 : else if (v == 0)
197 0 : return -ENOENT;
198 :
199 0 : if (seat)
200 0 : *seat = "seat0";
201 0 : *vtnr = (uint32_t) v;
202 :
203 0 : return 0;
204 : }
205 :
206 0 : static int export_legacy_dbus_address(
207 : pam_handle_t *handle,
208 : uid_t uid,
209 : const char *runtime) {
210 :
211 : const char *s;
212 0 : _cleanup_free_ char *t = NULL;
213 0 : int r = PAM_BUF_ERR;
214 :
215 : /* We need to export $DBUS_SESSION_BUS_ADDRESS because various applications will not connect
216 : * correctly to the bus without it. This setting matches what dbus.socket does for the user
217 : * session using 'systemctl --user set-environment'. We want to have the same configuration
218 : * in processes started from the PAM session.
219 : *
220 : * The setting of the address is guarded by the access() check because it is also possible to compile
221 : * dbus without --enable-user-session, in which case this socket is not used, and
222 : * $DBUS_SESSION_BUS_ADDRESS should not be set. An alternative approach would to not do the access()
223 : * check here, and let applications try on their own, by using "unix:path=%s/bus;autolaunch:". But we
224 : * expect the socket to be present by the time we do this check, so we can just as well check once
225 : * here. */
226 :
227 0 : s = strjoina(runtime, "/bus");
228 0 : if (access(s, F_OK) < 0)
229 0 : return PAM_SUCCESS;
230 :
231 0 : if (asprintf(&t, DEFAULT_USER_BUS_ADDRESS_FMT, runtime) < 0)
232 0 : goto error;
233 :
234 0 : r = pam_misc_setenv(handle, "DBUS_SESSION_BUS_ADDRESS", t, 0);
235 0 : if (r != PAM_SUCCESS)
236 0 : goto error;
237 :
238 0 : return PAM_SUCCESS;
239 :
240 0 : error:
241 0 : pam_syslog(handle, LOG_ERR, "Failed to set bus variable.");
242 0 : return r;
243 : }
244 :
245 0 : static int append_session_memory_max(pam_handle_t *handle, sd_bus_message *m, const char *limit) {
246 : uint64_t val;
247 : int r;
248 :
249 0 : if (isempty(limit))
250 0 : return 0;
251 :
252 0 : if (streq(limit, "infinity")) {
253 0 : r = sd_bus_message_append(m, "(sv)", "MemoryMax", "t", (uint64_t)-1);
254 0 : if (r < 0) {
255 0 : pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
256 0 : return r;
257 : }
258 : } else {
259 0 : r = parse_permille(limit);
260 0 : if (r >= 0) {
261 0 : r = sd_bus_message_append(m, "(sv)", "MemoryMaxScale", "u", (uint32_t) (((uint64_t) r * UINT32_MAX) / 1000U));
262 0 : if (r < 0) {
263 0 : pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
264 0 : return r;
265 : }
266 : } else {
267 0 : r = parse_size(limit, 1024, &val);
268 0 : if (r >= 0) {
269 0 : r = sd_bus_message_append(m, "(sv)", "MemoryMax", "t", val);
270 0 : if (r < 0) {
271 0 : pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
272 0 : return r;
273 : }
274 : } else
275 0 : pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.memory_max: %s, ignoring.", limit);
276 : }
277 : }
278 :
279 0 : return 0;
280 : }
281 :
282 0 : static int append_session_tasks_max(pam_handle_t *handle, sd_bus_message *m, const char *limit) {
283 : uint64_t val;
284 : int r;
285 :
286 : /* No need to parse "infinity" here, it will be set unconditionally later in manager_start_scope() */
287 0 : if (isempty(limit) || streq(limit, "infinity"))
288 0 : return 0;
289 :
290 0 : r = safe_atou64(limit, &val);
291 0 : if (r >= 0) {
292 0 : r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", val);
293 0 : if (r < 0) {
294 0 : pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
295 0 : return r;
296 : }
297 : } else
298 0 : pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.tasks_max: %s, ignoring.", limit);
299 :
300 0 : return 0;
301 : }
302 :
303 0 : static int append_session_cg_weight(pam_handle_t *handle, sd_bus_message *m, const char *limit, const char *field) {
304 : uint64_t val;
305 : int r;
306 :
307 0 : if (isempty(limit))
308 0 : return 0;
309 :
310 0 : r = cg_weight_parse(limit, &val);
311 0 : if (r >= 0) {
312 0 : r = sd_bus_message_append(m, "(sv)", field, "t", val);
313 0 : if (r < 0) {
314 0 : pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
315 0 : return r;
316 : }
317 0 : } else if (streq(field, "CPUWeight"))
318 0 : pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.cpu_weight: %s, ignoring.", limit);
319 : else
320 0 : pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.io_weight: %s, ignoring.", limit);
321 :
322 0 : return 0;
323 : }
324 :
325 0 : static const char* getenv_harder(pam_handle_t *handle, const char *key, const char *fallback) {
326 : const char *v;
327 :
328 0 : assert(handle);
329 0 : assert(key);
330 :
331 : /* Looks for an environment variable, preferably in the environment block associated with the
332 : * specified PAM handle, falling back to the process' block instead. Why check both? Because we want
333 : * to permit configuration of session properties from unit files that invoke PAM services, so that
334 : * PAM services don't have to be reworked to set systemd-specific properties, but these properties
335 : * can still be set from the unit file Environment= block. */
336 :
337 0 : v = pam_getenv(handle, key);
338 0 : if (!isempty(v))
339 0 : return v;
340 :
341 : /* We use secure_getenv() here, since we might get loaded into su/sudo, which are SUID. Ideally
342 : * they'd clean up the environment before invoking foreign code (such as PAM modules), but alas they
343 : * currently don't (to be precise, they clean up the environment they pass to their children, but
344 : * not their own environ[]). */
345 0 : v = secure_getenv(key);
346 0 : if (!isempty(v))
347 0 : return v;
348 :
349 0 : return fallback;
350 : }
351 :
352 0 : static int update_environment(pam_handle_t *handle, const char *key, const char *value) {
353 : int r;
354 :
355 0 : assert(handle);
356 0 : assert(key);
357 :
358 : /* Updates the environment, but only if there's actually a value set. Also, log about errors */
359 :
360 0 : if (isempty(value))
361 0 : return PAM_SUCCESS;
362 :
363 0 : r = pam_misc_setenv(handle, key, value, 0);
364 0 : if (r != PAM_SUCCESS)
365 0 : pam_syslog(handle, LOG_ERR, "Failed to set environment variable %s.", key);
366 :
367 0 : return r;
368 : }
369 :
370 0 : static bool validate_runtime_directory(pam_handle_t *handle, const char *path, uid_t uid) {
371 : struct stat st;
372 :
373 0 : assert(path);
374 :
375 : /* Just some extra paranoia: let's not set $XDG_RUNTIME_DIR if the directory we'd set it to isn't actually set
376 : * up properly for us. */
377 :
378 0 : if (lstat(path, &st) < 0) {
379 0 : pam_syslog(handle, LOG_ERR, "Failed to stat() runtime directory '%s': %s", path, strerror_safe(errno));
380 0 : goto fail;
381 : }
382 :
383 0 : if (!S_ISDIR(st.st_mode)) {
384 0 : pam_syslog(handle, LOG_ERR, "Runtime directory '%s' is not actually a directory.", path);
385 0 : goto fail;
386 : }
387 :
388 0 : if (st.st_uid != uid) {
389 0 : pam_syslog(handle, LOG_ERR, "Runtime directory '%s' is not owned by UID " UID_FMT ", as it should.", path, uid);
390 0 : goto fail;
391 : }
392 :
393 0 : return true;
394 :
395 0 : fail:
396 0 : pam_syslog(handle, LOG_WARNING, "Not setting $XDG_RUNTIME_DIR, as the directory is not in order.");
397 0 : return false;
398 : }
399 :
400 : _public_ PAM_EXTERN int pam_sm_open_session(
401 : pam_handle_t *handle,
402 : int flags,
403 : int argc, const char **argv) {
404 :
405 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
406 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
407 : const char
408 : *username, *id, *object_path, *runtime_path,
409 0 : *service = NULL,
410 0 : *tty = NULL, *display = NULL,
411 0 : *remote_user = NULL, *remote_host = NULL,
412 0 : *seat = NULL,
413 0 : *type = NULL, *class = NULL,
414 0 : *class_pam = NULL, *type_pam = NULL, *cvtnr = NULL, *desktop = NULL, *desktop_pam = NULL,
415 0 : *memory_max = NULL, *tasks_max = NULL, *cpu_weight = NULL, *io_weight = NULL;
416 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
417 0 : int session_fd = -1, existing, r;
418 0 : bool debug = false, remote;
419 : struct passwd *pw;
420 0 : uint32_t vtnr = 0;
421 : uid_t original_uid;
422 :
423 0 : assert(handle);
424 :
425 : /* Make this a NOP on non-logind systems */
426 0 : if (!logind_running())
427 0 : return PAM_SUCCESS;
428 :
429 0 : if (parse_argv(handle,
430 : argc, argv,
431 : &class_pam,
432 : &type_pam,
433 : &desktop_pam,
434 : &debug) < 0)
435 0 : return PAM_SESSION_ERR;
436 :
437 0 : if (debug)
438 0 : pam_syslog(handle, LOG_DEBUG, "pam-systemd initializing");
439 :
440 0 : r = get_user_data(handle, &username, &pw);
441 0 : if (r != PAM_SUCCESS) {
442 0 : pam_syslog(handle, LOG_ERR, "Failed to get user data.");
443 0 : return r;
444 : }
445 :
446 : /* Make sure we don't enter a loop by talking to
447 : * systemd-logind when it is actually waiting for the
448 : * background to finish start-up. If the service is
449 : * "systemd-user" we simply set XDG_RUNTIME_DIR and
450 : * leave. */
451 :
452 0 : pam_get_item(handle, PAM_SERVICE, (const void**) &service);
453 0 : if (streq_ptr(service, "systemd-user")) {
454 : char rt[STRLEN("/run/user/") + DECIMAL_STR_MAX(uid_t)];
455 :
456 0 : xsprintf(rt, "/run/user/"UID_FMT, pw->pw_uid);
457 0 : if (validate_runtime_directory(handle, rt, pw->pw_uid)) {
458 0 : r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0);
459 0 : if (r != PAM_SUCCESS) {
460 0 : pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
461 0 : return r;
462 : }
463 : }
464 :
465 0 : r = export_legacy_dbus_address(handle, pw->pw_uid, rt);
466 0 : if (r != PAM_SUCCESS)
467 0 : return r;
468 :
469 0 : return PAM_SUCCESS;
470 : }
471 :
472 : /* Otherwise, we ask logind to create a session for us */
473 :
474 0 : pam_get_item(handle, PAM_XDISPLAY, (const void**) &display);
475 0 : pam_get_item(handle, PAM_TTY, (const void**) &tty);
476 0 : pam_get_item(handle, PAM_RUSER, (const void**) &remote_user);
477 0 : pam_get_item(handle, PAM_RHOST, (const void**) &remote_host);
478 :
479 0 : seat = getenv_harder(handle, "XDG_SEAT", NULL);
480 0 : cvtnr = getenv_harder(handle, "XDG_VTNR", NULL);
481 0 : type = getenv_harder(handle, "XDG_SESSION_TYPE", type_pam);
482 0 : class = getenv_harder(handle, "XDG_SESSION_CLASS", class_pam);
483 0 : desktop = getenv_harder(handle, "XDG_SESSION_DESKTOP", desktop_pam);
484 :
485 0 : tty = strempty(tty);
486 :
487 0 : if (strchr(tty, ':')) {
488 : /* A tty with a colon is usually an X11 display, placed there to show up in utmp. We rearrange things
489 : * and don't pretend that an X display was a tty. */
490 0 : if (isempty(display))
491 0 : display = tty;
492 0 : tty = NULL;
493 :
494 0 : } else if (streq(tty, "cron")) {
495 : /* cron is setting PAM_TTY to "cron" for some reason (the commit carries no information why, but
496 : * probably because it wants to set it to something as pam_time/pam_access/… require PAM_TTY to be set
497 : * (as they otherwise even try to update it!) — but cron doesn't actually allocate a TTY for its forked
498 : * off processes.) */
499 0 : type = "unspecified";
500 0 : class = "background";
501 0 : tty = NULL;
502 :
503 0 : } else if (streq(tty, "ssh")) {
504 : /* ssh has been setting PAM_TTY to "ssh" (for the same reason as cron does this, see above. For further
505 : * details look for "PAM_TTY_KLUDGE" in the openssh sources). */
506 0 : type ="tty";
507 0 : class = "user";
508 0 : tty = NULL; /* This one is particularly sad, as this means that ssh sessions — even though usually
509 : * associated with a pty — won't be tracked by their tty in logind. This is because ssh
510 : * does the PAM session registration early for new connections, and registers a pty only
511 : * much later (this is because it doesn't know yet if it needs one at all, as whether to
512 : * register a pty or not is negotiated much later in the protocol). */
513 :
514 : } else
515 : /* Chop off leading /dev prefix that some clients specify, but others do not. */
516 0 : tty = skip_dev_prefix(tty);
517 :
518 : /* If this fails vtnr will be 0, that's intended */
519 0 : if (!isempty(cvtnr))
520 0 : (void) safe_atou32(cvtnr, &vtnr);
521 :
522 0 : if (!isempty(display) && !vtnr) {
523 0 : if (isempty(seat))
524 0 : (void) get_seat_from_display(display, &seat, &vtnr);
525 0 : else if (streq(seat, "seat0"))
526 0 : (void) get_seat_from_display(display, NULL, &vtnr);
527 : }
528 :
529 0 : if (seat && !streq(seat, "seat0") && vtnr != 0) {
530 0 : if (debug)
531 0 : pam_syslog(handle, LOG_DEBUG, "Ignoring vtnr %"PRIu32" for %s which is not seat0", vtnr, seat);
532 0 : vtnr = 0;
533 : }
534 :
535 0 : if (isempty(type))
536 0 : type = !isempty(display) ? "x11" :
537 0 : !isempty(tty) ? "tty" : "unspecified";
538 :
539 0 : if (isempty(class))
540 0 : class = streq(type, "unspecified") ? "background" : "user";
541 :
542 0 : remote = !isempty(remote_host) && !is_localhost(remote_host);
543 :
544 0 : (void) pam_get_data(handle, "systemd.memory_max", (const void **)&memory_max);
545 0 : (void) pam_get_data(handle, "systemd.tasks_max", (const void **)&tasks_max);
546 0 : (void) pam_get_data(handle, "systemd.cpu_weight", (const void **)&cpu_weight);
547 0 : (void) pam_get_data(handle, "systemd.io_weight", (const void **)&io_weight);
548 :
549 : /* Talk to logind over the message bus */
550 :
551 0 : r = sd_bus_open_system(&bus);
552 0 : if (r < 0) {
553 0 : pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror_safe(r));
554 0 : return PAM_SESSION_ERR;
555 : }
556 :
557 0 : if (debug) {
558 0 : pam_syslog(handle, LOG_DEBUG, "Asking logind to create session: "
559 : "uid="UID_FMT" pid="PID_FMT" service=%s type=%s class=%s desktop=%s seat=%s vtnr=%"PRIu32" tty=%s display=%s remote=%s remote_user=%s remote_host=%s",
560 0 : pw->pw_uid, getpid_cached(),
561 : strempty(service),
562 : type, class, strempty(desktop),
563 : strempty(seat), vtnr, strempty(tty), strempty(display),
564 : yes_no(remote), strempty(remote_user), strempty(remote_host));
565 0 : pam_syslog(handle, LOG_DEBUG, "Session limits: "
566 : "memory_max=%s tasks_max=%s cpu_weight=%s io_weight=%s",
567 : strna(memory_max), strna(tasks_max), strna(cpu_weight), strna(io_weight));
568 : }
569 :
570 0 : r = sd_bus_message_new_method_call(
571 : bus,
572 : &m,
573 : "org.freedesktop.login1",
574 : "/org/freedesktop/login1",
575 : "org.freedesktop.login1.Manager",
576 : "CreateSession");
577 0 : if (r < 0) {
578 0 : pam_syslog(handle, LOG_ERR, "Failed to create CreateSession method call: %s", strerror_safe(r));
579 0 : return PAM_SESSION_ERR;
580 : }
581 :
582 0 : r = sd_bus_message_append(m, "uusssssussbss",
583 0 : (uint32_t) pw->pw_uid,
584 : 0,
585 : service,
586 : type,
587 : class,
588 : desktop,
589 : seat,
590 : vtnr,
591 : tty,
592 : display,
593 : remote,
594 : remote_user,
595 : remote_host);
596 0 : if (r < 0) {
597 0 : pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
598 0 : return PAM_SESSION_ERR;
599 : }
600 :
601 0 : r = sd_bus_message_open_container(m, 'a', "(sv)");
602 0 : if (r < 0) {
603 0 : pam_syslog(handle, LOG_ERR, "Failed to open message container: %s", strerror_safe(r));
604 0 : return PAM_SYSTEM_ERR;
605 : }
606 :
607 0 : r = append_session_memory_max(handle, m, memory_max);
608 0 : if (r < 0)
609 0 : return PAM_SESSION_ERR;
610 :
611 0 : r = append_session_tasks_max(handle, m, tasks_max);
612 0 : if (r < 0)
613 0 : return PAM_SESSION_ERR;
614 :
615 0 : r = append_session_cg_weight(handle, m, cpu_weight, "CPUWeight");
616 0 : if (r < 0)
617 0 : return PAM_SESSION_ERR;
618 :
619 0 : r = append_session_cg_weight(handle, m, io_weight, "IOWeight");
620 0 : if (r < 0)
621 0 : return PAM_SESSION_ERR;
622 :
623 0 : r = sd_bus_message_close_container(m);
624 0 : if (r < 0) {
625 0 : pam_syslog(handle, LOG_ERR, "Failed to close message container: %s", strerror_safe(r));
626 0 : return PAM_SYSTEM_ERR;
627 : }
628 :
629 0 : r = sd_bus_call(bus, m, 0, &error, &reply);
630 0 : if (r < 0) {
631 0 : if (sd_bus_error_has_name(&error, BUS_ERROR_SESSION_BUSY)) {
632 0 : if (debug)
633 0 : pam_syslog(handle, LOG_DEBUG, "Not creating session: %s", bus_error_message(&error, r));
634 0 : return PAM_SUCCESS;
635 : } else {
636 0 : pam_syslog(handle, LOG_ERR, "Failed to create session: %s", bus_error_message(&error, r));
637 0 : return PAM_SYSTEM_ERR;
638 : }
639 : }
640 :
641 0 : r = sd_bus_message_read(reply,
642 : "soshusub",
643 : &id,
644 : &object_path,
645 : &runtime_path,
646 : &session_fd,
647 : &original_uid,
648 : &seat,
649 : &vtnr,
650 : &existing);
651 0 : if (r < 0) {
652 0 : pam_syslog(handle, LOG_ERR, "Failed to parse message: %s", strerror_safe(r));
653 0 : return PAM_SESSION_ERR;
654 : }
655 :
656 0 : if (debug)
657 0 : pam_syslog(handle, LOG_DEBUG, "Reply from logind: "
658 : "id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u original_uid=%u",
659 : id, object_path, runtime_path, session_fd, seat, vtnr, original_uid);
660 :
661 0 : r = update_environment(handle, "XDG_SESSION_ID", id);
662 0 : if (r != PAM_SUCCESS)
663 0 : return r;
664 :
665 0 : if (original_uid == pw->pw_uid) {
666 : /* Don't set $XDG_RUNTIME_DIR if the user we now
667 : * authenticated for does not match the original user
668 : * of the session. We do this in order not to result
669 : * in privileged apps clobbering the runtime directory
670 : * unnecessarily. */
671 :
672 0 : if (validate_runtime_directory(handle, runtime_path, pw->pw_uid)) {
673 0 : r = update_environment(handle, "XDG_RUNTIME_DIR", runtime_path);
674 0 : if (r != PAM_SUCCESS)
675 0 : return r;
676 : }
677 :
678 0 : r = export_legacy_dbus_address(handle, pw->pw_uid, runtime_path);
679 0 : if (r != PAM_SUCCESS)
680 0 : return r;
681 : }
682 :
683 : /* Most likely we got the session/type/class from environment variables, but might have gotten the data
684 : * somewhere else (for example PAM module parameters). Let's now update the environment variables, so that this
685 : * data is inherited into the session processes, and programs can rely on them to be initialized. */
686 :
687 0 : r = update_environment(handle, "XDG_SESSION_TYPE", type);
688 0 : if (r != PAM_SUCCESS)
689 0 : return r;
690 :
691 0 : r = update_environment(handle, "XDG_SESSION_CLASS", class);
692 0 : if (r != PAM_SUCCESS)
693 0 : return r;
694 :
695 0 : r = update_environment(handle, "XDG_SESSION_DESKTOP", desktop);
696 0 : if (r != PAM_SUCCESS)
697 0 : return r;
698 :
699 0 : r = update_environment(handle, "XDG_SEAT", seat);
700 0 : if (r != PAM_SUCCESS)
701 0 : return r;
702 :
703 0 : if (vtnr > 0) {
704 : char buf[DECIMAL_STR_MAX(vtnr)];
705 0 : sprintf(buf, "%u", vtnr);
706 :
707 0 : r = update_environment(handle, "XDG_VTNR", buf);
708 0 : if (r != PAM_SUCCESS)
709 0 : return r;
710 : }
711 :
712 0 : r = pam_set_data(handle, "systemd.existing", INT_TO_PTR(!!existing), NULL);
713 0 : if (r != PAM_SUCCESS) {
714 0 : pam_syslog(handle, LOG_ERR, "Failed to install existing flag.");
715 0 : return r;
716 : }
717 :
718 0 : if (session_fd >= 0) {
719 0 : session_fd = fcntl(session_fd, F_DUPFD_CLOEXEC, 3);
720 0 : if (session_fd < 0) {
721 0 : pam_syslog(handle, LOG_ERR, "Failed to dup session fd: %m");
722 0 : return PAM_SESSION_ERR;
723 : }
724 :
725 0 : r = pam_set_data(handle, "systemd.session-fd", FD_TO_PTR(session_fd), NULL);
726 0 : if (r != PAM_SUCCESS) {
727 0 : pam_syslog(handle, LOG_ERR, "Failed to install session fd.");
728 0 : safe_close(session_fd);
729 0 : return r;
730 : }
731 : }
732 :
733 0 : return PAM_SUCCESS;
734 : }
735 :
736 : _public_ PAM_EXTERN int pam_sm_close_session(
737 : pam_handle_t *handle,
738 : int flags,
739 : int argc, const char **argv) {
740 :
741 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
742 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
743 0 : const void *existing = NULL;
744 : const char *id;
745 : int r;
746 :
747 0 : assert(handle);
748 :
749 : /* Only release session if it wasn't pre-existing when we
750 : * tried to create it */
751 0 : (void) pam_get_data(handle, "systemd.existing", &existing);
752 :
753 0 : id = pam_getenv(handle, "XDG_SESSION_ID");
754 0 : if (id && !existing) {
755 :
756 : /* Before we go and close the FIFO we need to tell
757 : * logind that this is a clean session shutdown, so
758 : * that it doesn't just go and slaughter us
759 : * immediately after closing the fd */
760 :
761 0 : r = sd_bus_open_system(&bus);
762 0 : if (r < 0) {
763 0 : pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror_safe(r));
764 0 : return PAM_SESSION_ERR;
765 : }
766 :
767 0 : r = sd_bus_call_method(bus,
768 : "org.freedesktop.login1",
769 : "/org/freedesktop/login1",
770 : "org.freedesktop.login1.Manager",
771 : "ReleaseSession",
772 : &error,
773 : NULL,
774 : "s",
775 : id);
776 0 : if (r < 0) {
777 0 : pam_syslog(handle, LOG_ERR, "Failed to release session: %s", bus_error_message(&error, r));
778 0 : return PAM_SESSION_ERR;
779 : }
780 : }
781 :
782 : /* Note that we are knowingly leaking the FIFO fd here. This
783 : * way, logind can watch us die. If we closed it here it would
784 : * not have any clue when that is completed. Given that one
785 : * cannot really have multiple PAM sessions open from the same
786 : * process this means we will leak one FD at max. */
787 :
788 0 : return PAM_SUCCESS;
789 : }
|