1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
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/sysmacros.h>
15 #include <sys/types.h>
16 #include <unistd.h>
17 
18 #include "alloc-util.h"
19 #include "audit-util.h"
20 #include "bus-common-errors.h"
21 #include "bus-error.h"
22 #include "bus-internal.h"
23 #include "bus-locator.h"
24 #include "cgroup-setup.h"
25 #include "errno-util.h"
26 #include "fd-util.h"
27 #include "fileio.h"
28 #include "format-util.h"
29 #include "fs-util.h"
30 #include "hostname-util.h"
31 #include "locale-util.h"
32 #include "login-util.h"
33 #include "macro.h"
34 #include "pam-util.h"
35 #include "parse-util.h"
36 #include "path-util.h"
37 #include "percent-util.h"
38 #include "process-util.h"
39 #include "rlimit-util.h"
40 #include "socket-util.h"
41 #include "stdio-util.h"
42 #include "strv.h"
43 #include "terminal-util.h"
44 #include "user-util.h"
45 #include "userdb.h"
46 
47 #define LOGIN_SLOW_BUS_CALL_TIMEOUT_USEC (2*USEC_PER_MINUTE)
48 
parse_argv(pam_handle_t * handle,int argc,const char ** argv,const char ** class,const char ** type,const char ** desktop,bool * debug)49 static int parse_argv(
50                 pam_handle_t *handle,
51                 int argc, const char **argv,
52                 const char **class,
53                 const char **type,
54                 const char **desktop,
55                 bool *debug) {
56 
57         unsigned i;
58 
59         assert(argc >= 0);
60         assert(argc == 0 || argv);
61 
62         for (i = 0; i < (unsigned) argc; i++) {
63                 const char *p;
64 
65                 if ((p = startswith(argv[i], "class="))) {
66                         if (class)
67                                 *class = p;
68 
69                 } else if ((p = startswith(argv[i], "type="))) {
70                         if (type)
71                                 *type = p;
72 
73                 } else if ((p = startswith(argv[i], "desktop="))) {
74                         if (desktop)
75                                 *desktop = p;
76 
77                 } else if (streq(argv[i], "debug")) {
78                         if (debug)
79                                 *debug = true;
80 
81                 } else if ((p = startswith(argv[i], "debug="))) {
82                         int k;
83 
84                         k = parse_boolean(p);
85                         if (k < 0)
86                                 pam_syslog(handle, LOG_WARNING, "Failed to parse debug= argument, ignoring: %s", p);
87                         else if (debug)
88                                 *debug = k;
89 
90                 } else
91                         pam_syslog(handle, LOG_WARNING, "Unknown parameter '%s', ignoring", argv[i]);
92         }
93 
94         return 0;
95 }
96 
acquire_user_record(pam_handle_t * handle,UserRecord ** ret_record)97 static int acquire_user_record(
98                 pam_handle_t *handle,
99                 UserRecord **ret_record) {
100 
101         _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
102         const char *username = NULL, *json = NULL;
103         _cleanup_free_ char *field = NULL;
104         int r;
105 
106         assert(handle);
107 
108         r = pam_get_user(handle, &username, NULL);
109         if (r != PAM_SUCCESS) {
110                 pam_syslog(handle, LOG_ERR, "Failed to get user name: %s", pam_strerror(handle, r));
111                 return r;
112         }
113 
114         if (isempty(username)) {
115                 pam_syslog(handle, LOG_ERR, "User name not valid.");
116                 return PAM_SERVICE_ERR;
117         }
118 
119         /* If pam_systemd_homed (or some other module) already acquired the user record we can reuse it
120          * here. */
121         field = strjoin("systemd-user-record-", username);
122         if (!field)
123                 return pam_log_oom(handle);
124 
125         r = pam_get_data(handle, field, (const void**) &json);
126         if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
127                 pam_syslog(handle, LOG_ERR, "Failed to get PAM user record data: %s", pam_strerror(handle, r));
128                 return r;
129         }
130         if (r == PAM_SUCCESS && json) {
131                 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
132 
133                 /* Parse cached record */
134                 r = json_parse(json, JSON_PARSE_SENSITIVE, &v, NULL, NULL);
135                 if (r < 0) {
136                         pam_syslog(handle, LOG_ERR, "Failed to parse JSON user record: %s", strerror_safe(r));
137                         return PAM_SERVICE_ERR;
138                 }
139 
140                 ur = user_record_new();
141                 if (!ur)
142                         return pam_log_oom(handle);
143 
144                 r = user_record_load(ur, v, USER_RECORD_LOAD_REFUSE_SECRET|USER_RECORD_PERMISSIVE);
145                 if (r < 0) {
146                         pam_syslog(handle, LOG_ERR, "Failed to load user record: %s", strerror_safe(r));
147                         return PAM_SERVICE_ERR;
148                 }
149 
150                 /* Safety check if cached record actually matches what we are looking for */
151                 if (!streq_ptr(username, ur->user_name)) {
152                         pam_syslog(handle, LOG_ERR, "Acquired user record does not match user name.");
153                         return PAM_SERVICE_ERR;
154                 }
155         } else {
156                 _cleanup_free_ char *formatted = NULL;
157 
158                 /* Request the record ourselves */
159                 r = userdb_by_name(username, 0, &ur);
160                 if (r < 0) {
161                         pam_syslog(handle, LOG_ERR, "Failed to get user record: %s", strerror_safe(r));
162                         return PAM_USER_UNKNOWN;
163                 }
164 
165                 r = json_variant_format(ur->json, 0, &formatted);
166                 if (r < 0) {
167                         pam_syslog(handle, LOG_ERR, "Failed to format user JSON: %s", strerror_safe(r));
168                         return PAM_SERVICE_ERR;
169                 }
170 
171                 /* And cache it for everyone else */
172                 r = pam_set_data(handle, field, formatted, pam_cleanup_free);
173                 if (r < 0) {
174                         pam_syslog(handle, LOG_ERR, "Failed to set PAM user record data '%s': %s",
175                                    field, pam_strerror(handle, r));
176                         return r;
177                 }
178 
179                 TAKE_PTR(formatted);
180         }
181 
182         if (!uid_is_valid(ur->uid)) {
183                 pam_syslog(handle, LOG_ERR, "Acquired user record does not have a UID.");
184                 return PAM_SERVICE_ERR;
185         }
186 
187         if (ret_record)
188                 *ret_record = TAKE_PTR(ur);
189 
190         return PAM_SUCCESS;
191 }
192 
display_is_local(const char * display)193 static bool display_is_local(const char *display) {
194         assert(display);
195 
196         return
197                 display[0] == ':' &&
198                 display[1] >= '0' &&
199                 display[1] <= '9';
200 }
201 
socket_from_display(const char * display)202 static int socket_from_display(const char *display) {
203         _cleanup_free_ char *f = NULL;
204         size_t k;
205         char *c;
206         union sockaddr_union sa;
207         socklen_t sa_len;
208         _cleanup_close_ int fd = -1;
209         int r;
210 
211         assert(display);
212 
213         if (!display_is_local(display))
214                 return -EINVAL;
215 
216         k = strspn(display+1, "0123456789");
217 
218         /* Try abstract socket first. */
219         f = new(char, STRLEN("@/tmp/.X11-unix/X") + k + 1);
220         if (!f)
221                 return -ENOMEM;
222 
223         c = stpcpy(f, "@/tmp/.X11-unix/X");
224         memcpy(c, display+1, k);
225         c[k] = 0;
226 
227         r = sockaddr_un_set_path(&sa.un, f);
228         if (r < 0)
229                 return r;
230         sa_len = r;
231 
232         fd = RET_NERRNO(socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0));
233         if (fd < 0)
234                 return fd;
235 
236         r = RET_NERRNO(connect(fd, &sa.sa, sa_len));
237         if (r >= 0)
238                 return TAKE_FD(fd);
239         if (r != -ECONNREFUSED)
240                 return r;
241 
242         /* Try also non-abstract socket. */
243         r = sockaddr_un_set_path(&sa.un, f + 1);
244         if (r < 0)
245                 return r;
246         sa_len = r;
247 
248         r = RET_NERRNO(connect(fd, &sa.sa, sa_len));
249         if (r >= 0)
250                 return TAKE_FD(fd);
251         return r;
252 }
253 
get_seat_from_display(const char * display,const char ** seat,uint32_t * vtnr)254 static int get_seat_from_display(const char *display, const char **seat, uint32_t *vtnr) {
255         _cleanup_free_ char *sys_path = NULL, *tty = NULL;
256         _cleanup_close_ int fd = -1;
257         struct ucred ucred;
258         int v, r;
259         dev_t display_ctty;
260 
261         assert(display);
262         assert(vtnr);
263 
264         /* We deduce the X11 socket from the display name, then use
265          * SO_PEERCRED to determine the X11 server process, ask for
266          * the controlling tty of that and if it's a VC then we know
267          * the seat and the virtual terminal. Sounds ugly, is only
268          * semi-ugly. */
269 
270         fd = socket_from_display(display);
271         if (fd < 0)
272                 return fd;
273 
274         r = getpeercred(fd, &ucred);
275         if (r < 0)
276                 return r;
277 
278         r = get_ctty_devnr(ucred.pid, &display_ctty);
279         if (r < 0)
280                 return r;
281 
282         if (asprintf(&sys_path, "/sys/dev/char/%d:%d", major(display_ctty), minor(display_ctty)) < 0)
283                 return -ENOMEM;
284         r = readlink_value(sys_path, &tty);
285         if (r < 0)
286                 return r;
287 
288         v = vtnr_from_tty(tty);
289         if (v < 0)
290                 return v;
291         else if (v == 0)
292                 return -ENOENT;
293 
294         if (seat)
295                 *seat = "seat0";
296         *vtnr = (uint32_t) v;
297 
298         return 0;
299 }
300 
export_legacy_dbus_address(pam_handle_t * handle,const char * runtime)301 static int export_legacy_dbus_address(
302                 pam_handle_t *handle,
303                 const char *runtime) {
304 
305         const char *s;
306         _cleanup_free_ char *t = NULL;
307         int r = PAM_BUF_ERR;
308 
309         /* We need to export $DBUS_SESSION_BUS_ADDRESS because various applications will not connect
310          * correctly to the bus without it. This setting matches what dbus.socket does for the user
311          * session using 'systemctl --user set-environment'. We want to have the same configuration
312          * in processes started from the PAM session.
313          *
314          * The setting of the address is guarded by the access() check because it is also possible to compile
315          * dbus without --enable-user-session, in which case this socket is not used, and
316          * $DBUS_SESSION_BUS_ADDRESS should not be set. An alternative approach would to not do the access()
317          * check here, and let applications try on their own, by using "unix:path=%s/bus;autolaunch:". But we
318          * expect the socket to be present by the time we do this check, so we can just as well check once
319          * here. */
320 
321         s = strjoina(runtime, "/bus");
322         if (access(s, F_OK) < 0)
323                 return PAM_SUCCESS;
324 
325         if (asprintf(&t, DEFAULT_USER_BUS_ADDRESS_FMT, runtime) < 0)
326                 return pam_log_oom(handle);
327 
328         r = pam_misc_setenv(handle, "DBUS_SESSION_BUS_ADDRESS", t, 0);
329         if (r != PAM_SUCCESS) {
330                 pam_syslog(handle, LOG_ERR, "Failed to set bus variable: %s", pam_strerror(handle, r));
331                 return r;
332         }
333 
334         return PAM_SUCCESS;
335 }
336 
append_session_memory_max(pam_handle_t * handle,sd_bus_message * m,const char * limit)337 static int append_session_memory_max(pam_handle_t *handle, sd_bus_message *m, const char *limit) {
338         uint64_t val;
339         int r;
340 
341         if (isempty(limit))
342                 return PAM_SUCCESS;
343 
344         if (streq(limit, "infinity")) {
345                 r = sd_bus_message_append(m, "(sv)", "MemoryMax", "t", UINT64_MAX);
346                 if (r < 0)
347                         return pam_bus_log_create_error(handle, r);
348 
349                 return PAM_SUCCESS;
350         }
351 
352         r = parse_permyriad(limit);
353         if (r >= 0) {
354                 r = sd_bus_message_append(m, "(sv)", "MemoryMaxScale", "u", UINT32_SCALE_FROM_PERMYRIAD(r));
355                 if (r < 0)
356                         return pam_bus_log_create_error(handle, r);
357 
358                 return PAM_SUCCESS;
359         }
360 
361         r = parse_size(limit, 1024, &val);
362         if (r >= 0) {
363                 r = sd_bus_message_append(m, "(sv)", "MemoryMax", "t", val);
364                 if (r < 0)
365                         return pam_bus_log_create_error(handle, r);
366 
367                 return PAM_SUCCESS;
368         }
369 
370         pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.memory_max, ignoring: %s", limit);
371         return PAM_SUCCESS;
372 }
373 
append_session_runtime_max_sec(pam_handle_t * handle,sd_bus_message * m,const char * limit)374 static int append_session_runtime_max_sec(pam_handle_t *handle, sd_bus_message *m, const char *limit) {
375         usec_t val;
376         int r;
377 
378         /* No need to parse "infinity" here, it will be set by default later in scope_init() */
379         if (isempty(limit) || streq(limit, "infinity"))
380                 return PAM_SUCCESS;
381 
382         r = parse_sec(limit, &val);
383         if (r >= 0) {
384                 r = sd_bus_message_append(m, "(sv)", "RuntimeMaxUSec", "t", (uint64_t) val);
385                 if (r < 0)
386                         return pam_bus_log_create_error(handle, r);
387         } else
388                 pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.runtime_max_sec: %s, ignoring.", limit);
389 
390         return PAM_SUCCESS;
391 }
392 
append_session_tasks_max(pam_handle_t * handle,sd_bus_message * m,const char * limit)393 static int append_session_tasks_max(pam_handle_t *handle, sd_bus_message *m, const char *limit) {
394         uint64_t val;
395         int r;
396 
397         /* No need to parse "infinity" here, it will be set unconditionally later in manager_start_scope() */
398         if (isempty(limit) || streq(limit, "infinity"))
399                 return PAM_SUCCESS;
400 
401         r = safe_atou64(limit, &val);
402         if (r >= 0) {
403                 r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", val);
404                 if (r < 0)
405                         return pam_bus_log_create_error(handle, r);
406         } else
407                 pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.tasks_max, ignoring: %s", limit);
408 
409         return PAM_SUCCESS;
410 }
411 
append_session_cg_weight(pam_handle_t * handle,sd_bus_message * m,const char * limit,const char * field)412 static int append_session_cg_weight(pam_handle_t *handle, sd_bus_message *m, const char *limit, const char *field) {
413         uint64_t val;
414         int r;
415 
416         if (isempty(limit))
417                 return PAM_SUCCESS;
418 
419         r = cg_weight_parse(limit, &val);
420         if (r >= 0) {
421                 r = sd_bus_message_append(m, "(sv)", field, "t", val);
422                 if (r < 0)
423                         return pam_bus_log_create_error(handle, r);
424         } else if (streq(field, "CPUWeight"))
425                 pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.cpu_weight, ignoring: %s", limit);
426         else
427                 pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.io_weight, ignoring: %s", limit);
428 
429         return PAM_SUCCESS;
430 }
431 
getenv_harder(pam_handle_t * handle,const char * key,const char * fallback)432 static const char* getenv_harder(pam_handle_t *handle, const char *key, const char *fallback) {
433         const char *v;
434 
435         assert(handle);
436         assert(key);
437 
438         /* Looks for an environment variable, preferably in the environment block associated with the
439          * specified PAM handle, falling back to the process' block instead. Why check both? Because we want
440          * to permit configuration of session properties from unit files that invoke PAM services, so that
441          * PAM services don't have to be reworked to set systemd-specific properties, but these properties
442          * can still be set from the unit file Environment= block. */
443 
444         v = pam_getenv(handle, key);
445         if (!isempty(v))
446                 return v;
447 
448         /* We use secure_getenv() here, since we might get loaded into su/sudo, which are SUID. Ideally
449          * they'd clean up the environment before invoking foreign code (such as PAM modules), but alas they
450          * currently don't (to be precise, they clean up the environment they pass to their children, but
451          * not their own environ[]). */
452         v = secure_getenv(key);
453         if (!isempty(v))
454                 return v;
455 
456         return fallback;
457 }
458 
update_environment(pam_handle_t * handle,const char * key,const char * value)459 static int update_environment(pam_handle_t *handle, const char *key, const char *value) {
460         int r;
461 
462         assert(handle);
463         assert(key);
464 
465         /* Updates the environment, but only if there's actually a value set. Also, log about errors */
466 
467         if (isempty(value))
468                 return PAM_SUCCESS;
469 
470         r = pam_misc_setenv(handle, key, value, 0);
471         if (r != PAM_SUCCESS)
472                 pam_syslog(handle, LOG_ERR, "Failed to set environment variable %s: %s", key, pam_strerror(handle, r));
473 
474         return r;
475 }
476 
validate_runtime_directory(pam_handle_t * handle,const char * path,uid_t uid)477 static bool validate_runtime_directory(pam_handle_t *handle, const char *path, uid_t uid) {
478         struct stat st;
479 
480         assert(handle);
481         assert(path);
482 
483         /* Some extra paranoia: let's not set $XDG_RUNTIME_DIR if the directory we'd set it to isn't actually
484          * set up properly for us. This is supposed to provide a careful safety net for supporting su/sudo
485          * type transitions: in that case the UID changes, but the session and thus the user owning it
486          * doesn't change. Since the $XDG_RUNTIME_DIR lifecycle is bound to the session's user being logged
487          * in at least once we should be particularly careful when setting the environment variable, since
488          * otherwise we might end up setting $XDG_RUNTIME_DIR to some directory owned by the wrong user. */
489 
490         if (!path_is_absolute(path)) {
491                 pam_syslog(handle, LOG_ERR, "Provided runtime directory '%s' is not absolute.", path);
492                 goto fail;
493         }
494 
495         if (lstat(path, &st) < 0) {
496                 pam_syslog(handle, LOG_ERR, "Failed to stat() runtime directory '%s': %s", path, strerror_safe(errno));
497                 goto fail;
498         }
499 
500         if (!S_ISDIR(st.st_mode)) {
501                 pam_syslog(handle, LOG_ERR, "Runtime directory '%s' is not actually a directory.", path);
502                 goto fail;
503         }
504 
505         if (st.st_uid != uid) {
506                 pam_syslog(handle, LOG_ERR, "Runtime directory '%s' is not owned by UID " UID_FMT ", as it should.", path, uid);
507                 goto fail;
508         }
509 
510         return true;
511 
512 fail:
513         pam_syslog(handle, LOG_WARNING, "Not setting $XDG_RUNTIME_DIR, as the directory is not in order.");
514         return false;
515 }
516 
pam_putenv_and_log(pam_handle_t * handle,const char * e,bool debug)517 static int pam_putenv_and_log(pam_handle_t *handle, const char *e, bool debug) {
518         int r;
519 
520         assert(handle);
521         assert(e);
522 
523         r = pam_putenv(handle, e);
524         if (r != PAM_SUCCESS) {
525                 pam_syslog(handle, LOG_ERR, "Failed to set PAM environment variable %s: %s", e, pam_strerror(handle, r));
526                 return r;
527         }
528 
529         if (debug)
530                 pam_syslog(handle, LOG_DEBUG, "PAM environment variable %s set based on user record.", e);
531 
532         return PAM_SUCCESS;
533 }
534 
apply_user_record_settings(pam_handle_t * handle,UserRecord * ur,bool debug)535 static int apply_user_record_settings(pam_handle_t *handle, UserRecord *ur, bool debug) {
536         int r;
537 
538         assert(handle);
539         assert(ur);
540 
541         if (ur->umask != MODE_INVALID) {
542                 umask(ur->umask);
543 
544                 if (debug)
545                         pam_syslog(handle, LOG_DEBUG, "Set user umask to %04o based on user record.", ur->umask);
546         }
547 
548         STRV_FOREACH(i, ur->environment) {
549                 _cleanup_free_ char *n = NULL;
550                 const char *e;
551 
552                 assert_se(e = strchr(*i, '=')); /* environment was already validated while parsing JSON record, this thus must hold */
553 
554                 n = strndup(*i, e - *i);
555                 if (!n)
556                         return pam_log_oom(handle);
557 
558                 if (pam_getenv(handle, n)) {
559                         if (debug)
560                                 pam_syslog(handle, LOG_DEBUG, "PAM environment variable $%s already set, not changing based on record.", *i);
561                         continue;
562                 }
563 
564                 r = pam_putenv_and_log(handle, *i, debug);
565                 if (r != PAM_SUCCESS)
566                         return r;
567         }
568 
569         if (ur->email_address) {
570                 if (pam_getenv(handle, "EMAIL")) {
571                         if (debug)
572                                 pam_syslog(handle, LOG_DEBUG, "PAM environment variable $EMAIL already set, not changing based on user record.");
573                 } else {
574                         _cleanup_free_ char *joined = NULL;
575 
576                         joined = strjoin("EMAIL=", ur->email_address);
577                         if (!joined)
578                                 return pam_log_oom(handle);
579 
580                         r = pam_putenv_and_log(handle, joined, debug);
581                         if (r != PAM_SUCCESS)
582                                 return r;
583                 }
584         }
585 
586         if (ur->time_zone) {
587                 if (pam_getenv(handle, "TZ")) {
588                         if (debug)
589                                 pam_syslog(handle, LOG_DEBUG, "PAM environment variable $TZ already set, not changing based on user record.");
590                 } else if (!timezone_is_valid(ur->time_zone, LOG_DEBUG)) {
591                         if (debug)
592                                 pam_syslog(handle, LOG_DEBUG, "Time zone specified in user record is not valid locally, not setting $TZ.");
593                 } else {
594                         _cleanup_free_ char *joined = NULL;
595 
596                         joined = strjoin("TZ=:", ur->time_zone);
597                         if (!joined)
598                                 return pam_log_oom(handle);
599 
600                         r = pam_putenv_and_log(handle, joined, debug);
601                         if (r != PAM_SUCCESS)
602                                 return r;
603                 }
604         }
605 
606         if (ur->preferred_language) {
607                 if (pam_getenv(handle, "LANG")) {
608                         if (debug)
609                                 pam_syslog(handle, LOG_DEBUG, "PAM environment variable $LANG already set, not changing based on user record.");
610                 } else if (locale_is_installed(ur->preferred_language) <= 0) {
611                         if (debug)
612                                 pam_syslog(handle, LOG_DEBUG, "Preferred language specified in user record is not valid or not installed, not setting $LANG.");
613                 } else {
614                         _cleanup_free_ char *joined = NULL;
615 
616                         joined = strjoin("LANG=", ur->preferred_language);
617                         if (!joined)
618                                 return pam_log_oom(handle);
619 
620                         r = pam_putenv_and_log(handle, joined, debug);
621                         if (r != PAM_SUCCESS)
622                                 return r;
623                 }
624         }
625 
626         if (nice_is_valid(ur->nice_level)) {
627                 if (nice(ur->nice_level) < 0)
628                         pam_syslog(handle, LOG_ERR, "Failed to set nice level to %i, ignoring: %s", ur->nice_level, strerror_safe(errno));
629                 else if (debug)
630                         pam_syslog(handle, LOG_DEBUG, "Nice level set, based on user record.");
631         }
632 
633         for (int rl = 0; rl < _RLIMIT_MAX; rl++) {
634 
635                 if (!ur->rlimits[rl])
636                         continue;
637 
638                 r = setrlimit_closest(rl, ur->rlimits[rl]);
639                 if (r < 0)
640                         pam_syslog(handle, LOG_ERR, "Failed to set resource limit %s, ignoring: %s", rlimit_to_string(rl), strerror_safe(r));
641                 else if (debug)
642                         pam_syslog(handle, LOG_DEBUG, "Resource limit %s set, based on user record.", rlimit_to_string(rl));
643         }
644 
645         return PAM_SUCCESS;
646 }
647 
configure_runtime_directory(pam_handle_t * handle,UserRecord * ur,const char * rt)648 static int configure_runtime_directory(
649                 pam_handle_t *handle,
650                 UserRecord *ur,
651                 const char *rt) {
652 
653         int r;
654 
655         assert(handle);
656         assert(ur);
657         assert(rt);
658 
659         if (!validate_runtime_directory(handle, rt, ur->uid))
660                 return PAM_SUCCESS;
661 
662         r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0);
663         if (r != PAM_SUCCESS) {
664                 pam_syslog(handle, LOG_ERR, "Failed to set runtime dir: %s", pam_strerror(handle, r));
665                 return r;
666         }
667 
668         return export_legacy_dbus_address(handle, rt);
669 }
670 
pam_sm_open_session(pam_handle_t * handle,int flags,int argc,const char ** argv)671 _public_ PAM_EXTERN int pam_sm_open_session(
672                 pam_handle_t *handle,
673                 int flags,
674                 int argc, const char **argv) {
675 
676         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
677         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
678         const char
679                 *id, *object_path, *runtime_path,
680                 *service = NULL,
681                 *tty = NULL, *display = NULL,
682                 *remote_user = NULL, *remote_host = NULL,
683                 *seat = NULL,
684                 *type = NULL, *class = NULL,
685                 *class_pam = NULL, *type_pam = NULL, *cvtnr = NULL, *desktop = NULL, *desktop_pam = NULL,
686                 *memory_max = NULL, *tasks_max = NULL, *cpu_weight = NULL, *io_weight = NULL, *runtime_max_sec = NULL;
687         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
688         _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
689         int session_fd = -1, existing, r;
690         bool debug = false, remote;
691         uint32_t vtnr = 0;
692         uid_t original_uid;
693 
694         assert(handle);
695 
696         if (parse_argv(handle,
697                        argc, argv,
698                        &class_pam,
699                        &type_pam,
700                        &desktop_pam,
701                        &debug) < 0)
702                 return PAM_SESSION_ERR;
703 
704         if (debug)
705                 pam_syslog(handle, LOG_DEBUG, "pam-systemd initializing");
706 
707         r = acquire_user_record(handle, &ur);
708         if (r != PAM_SUCCESS)
709                 return r;
710 
711         /* Make most of this a NOP on non-logind systems */
712         if (!logind_running())
713                 goto success;
714 
715         /* Make sure we don't enter a loop by talking to
716          * systemd-logind when it is actually waiting for the
717          * background to finish start-up. If the service is
718          * "systemd-user" we simply set XDG_RUNTIME_DIR and
719          * leave. */
720 
721         r = pam_get_item(handle, PAM_SERVICE, (const void**) &service);
722         if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) {
723                 pam_syslog(handle, LOG_ERR, "Failed to get PAM service: %s", pam_strerror(handle, r));
724                 return r;
725         }
726         if (streq_ptr(service, "systemd-user")) {
727                 char rt[STRLEN("/run/user/") + DECIMAL_STR_MAX(uid_t)];
728 
729                 xsprintf(rt, "/run/user/"UID_FMT, ur->uid);
730                 r = configure_runtime_directory(handle, ur, rt);
731                 if (r != PAM_SUCCESS)
732                         return r;
733 
734                 goto success;
735         }
736 
737         /* Otherwise, we ask logind to create a session for us */
738 
739         r = pam_get_item(handle, PAM_XDISPLAY, (const void**) &display);
740         if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) {
741                 pam_syslog(handle, LOG_ERR, "Failed to get PAM XDISPLAY: %s", pam_strerror(handle, r));
742                 return r;
743         }
744         r = pam_get_item(handle, PAM_TTY, (const void**) &tty);
745         if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) {
746                 pam_syslog(handle, LOG_ERR, "Failed to get PAM TTY: %s", pam_strerror(handle, r));
747                 return r;
748         }
749         r = pam_get_item(handle, PAM_RUSER, (const void**) &remote_user);
750         if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) {
751                 pam_syslog(handle, LOG_ERR, "Failed to get PAM RUSER: %s", pam_strerror(handle, r));
752                 return r;
753         }
754         r = pam_get_item(handle, PAM_RHOST, (const void**) &remote_host);
755         if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) {
756                 pam_syslog(handle, LOG_ERR, "Failed to get PAM RHOST: %s", pam_strerror(handle, r));
757                 return r;
758         }
759 
760         seat = getenv_harder(handle, "XDG_SEAT", NULL);
761         cvtnr = getenv_harder(handle, "XDG_VTNR", NULL);
762         type = getenv_harder(handle, "XDG_SESSION_TYPE", type_pam);
763         class = getenv_harder(handle, "XDG_SESSION_CLASS", class_pam);
764         desktop = getenv_harder(handle, "XDG_SESSION_DESKTOP", desktop_pam);
765 
766         tty = strempty(tty);
767 
768         if (strchr(tty, ':')) {
769                 /* A tty with a colon is usually an X11 display, placed there to show up in utmp. We rearrange things
770                  * and don't pretend that an X display was a tty. */
771                 if (isempty(display))
772                         display = tty;
773                 tty = NULL;
774 
775         } else if (streq(tty, "cron")) {
776                 /* cron is setting PAM_TTY to "cron" for some reason (the commit carries no information why, but
777                  * probably because it wants to set it to something as pam_time/pam_access/… require PAM_TTY to be set
778                  * (as they otherwise even try to update it!) — but cron doesn't actually allocate a TTY for its forked
779                  * off processes.) */
780                 type = "unspecified";
781                 class = "background";
782                 tty = NULL;
783 
784         } else if (streq(tty, "ssh")) {
785                 /* ssh has been setting PAM_TTY to "ssh" (for the same reason as cron does this, see above. For further
786                  * details look for "PAM_TTY_KLUDGE" in the openssh sources). */
787                 type ="tty";
788                 class = "user";
789                 tty = NULL; /* This one is particularly sad, as this means that ssh sessions — even though usually
790                              * associated with a pty — won't be tracked by their tty in logind. This is because ssh
791                              * does the PAM session registration early for new connections, and registers a pty only
792                              * much later (this is because it doesn't know yet if it needs one at all, as whether to
793                              * register a pty or not is negotiated much later in the protocol). */
794 
795         } else
796                 /* Chop off leading /dev prefix that some clients specify, but others do not. */
797                 tty = skip_dev_prefix(tty);
798 
799         /* If this fails vtnr will be 0, that's intended */
800         if (!isempty(cvtnr))
801                 (void) safe_atou32(cvtnr, &vtnr);
802 
803         if (!isempty(display) && !vtnr) {
804                 if (isempty(seat))
805                         (void) get_seat_from_display(display, &seat, &vtnr);
806                 else if (streq(seat, "seat0"))
807                         (void) get_seat_from_display(display, NULL, &vtnr);
808         }
809 
810         if (seat && !streq(seat, "seat0") && vtnr != 0) {
811                 if (debug)
812                         pam_syslog(handle, LOG_DEBUG, "Ignoring vtnr %"PRIu32" for %s which is not seat0", vtnr, seat);
813                 vtnr = 0;
814         }
815 
816         if (isempty(type))
817                 type = !isempty(display) ? "x11" :
818                            !isempty(tty) ? "tty" : "unspecified";
819 
820         if (isempty(class))
821                 class = streq(type, "unspecified") ? "background" : "user";
822 
823         remote = !isempty(remote_host) && !is_localhost(remote_host);
824 
825         r = pam_get_data(handle, "systemd.memory_max", (const void **)&memory_max);
826         if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
827                 pam_syslog(handle, LOG_ERR, "Failed to get PAM systemd.memory_max data: %s", pam_strerror(handle, r));
828                 return r;
829         }
830         r = pam_get_data(handle, "systemd.tasks_max",  (const void **)&tasks_max);
831         if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
832                 pam_syslog(handle, LOG_ERR, "Failed to get PAM systemd.tasks_max data: %s", pam_strerror(handle, r));
833                 return r;
834         }
835         r = pam_get_data(handle, "systemd.cpu_weight", (const void **)&cpu_weight);
836         if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
837                 pam_syslog(handle, LOG_ERR, "Failed to get PAM systemd.cpu_weight data: %s", pam_strerror(handle, r));
838                 return r;
839         }
840         r = pam_get_data(handle, "systemd.io_weight",  (const void **)&io_weight);
841         if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
842                 pam_syslog(handle, LOG_ERR, "Failed to get PAM systemd.io_weight data: %s", pam_strerror(handle, r));
843                 return r;
844         }
845         r = pam_get_data(handle, "systemd.runtime_max_sec", (const void **)&runtime_max_sec);
846         if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
847                 pam_syslog(handle, LOG_ERR, "Failed to get PAM systemd.runtime_max_sec data: %s", pam_strerror(handle, r));
848                 return r;
849         }
850 
851         /* Talk to logind over the message bus */
852 
853         r = pam_acquire_bus_connection(handle, &bus);
854         if (r != PAM_SUCCESS)
855                 return r;
856 
857         if (debug) {
858                 pam_syslog(handle, LOG_DEBUG, "Asking logind to create session: "
859                            "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",
860                            ur->uid, getpid_cached(),
861                            strempty(service),
862                            type, class, strempty(desktop),
863                            strempty(seat), vtnr, strempty(tty), strempty(display),
864                            yes_no(remote), strempty(remote_user), strempty(remote_host));
865                 pam_syslog(handle, LOG_DEBUG, "Session limits: "
866                            "memory_max=%s tasks_max=%s cpu_weight=%s io_weight=%s runtime_max_sec=%s",
867                            strna(memory_max), strna(tasks_max), strna(cpu_weight), strna(io_weight), strna(runtime_max_sec));
868         }
869 
870         r = bus_message_new_method_call(bus, &m, bus_login_mgr, "CreateSession");
871         if (r < 0)
872                 return pam_bus_log_create_error(handle, r);
873 
874         r = sd_bus_message_append(m, "uusssssussbss",
875                         (uint32_t) ur->uid,
876                         0,
877                         service,
878                         type,
879                         class,
880                         desktop,
881                         seat,
882                         vtnr,
883                         tty,
884                         display,
885                         remote,
886                         remote_user,
887                         remote_host);
888         if (r < 0)
889                 return pam_bus_log_create_error(handle, r);
890 
891         r = sd_bus_message_open_container(m, 'a', "(sv)");
892         if (r < 0)
893                 return pam_bus_log_create_error(handle, r);
894 
895         r = append_session_memory_max(handle, m, memory_max);
896         if (r != PAM_SUCCESS)
897                 return r;
898 
899         r = append_session_runtime_max_sec(handle, m, runtime_max_sec);
900         if (r != PAM_SUCCESS)
901                 return r;
902 
903         r = append_session_tasks_max(handle, m, tasks_max);
904         if (r != PAM_SUCCESS)
905                 return r;
906 
907         r = append_session_cg_weight(handle, m, cpu_weight, "CPUWeight");
908         if (r != PAM_SUCCESS)
909                 return r;
910 
911         r = append_session_cg_weight(handle, m, io_weight, "IOWeight");
912         if (r != PAM_SUCCESS)
913                 return r;
914 
915         r = sd_bus_message_close_container(m);
916         if (r < 0)
917                 return pam_bus_log_create_error(handle, r);
918 
919         r = sd_bus_call(bus, m, LOGIN_SLOW_BUS_CALL_TIMEOUT_USEC, &error, &reply);
920         if (r < 0) {
921                 if (sd_bus_error_has_name(&error, BUS_ERROR_SESSION_BUSY)) {
922                         if (debug)
923                                 pam_syslog(handle, LOG_DEBUG, "Not creating session: %s", bus_error_message(&error, r));
924 
925                         /* We are already in a session, don't do anything */
926                         goto success;
927                 } else {
928                         pam_syslog(handle, LOG_ERR, "Failed to create session: %s", bus_error_message(&error, r));
929                         return PAM_SESSION_ERR;
930                 }
931         }
932 
933         r = sd_bus_message_read(reply,
934                                 "soshusub",
935                                 &id,
936                                 &object_path,
937                                 &runtime_path,
938                                 &session_fd,
939                                 &original_uid,
940                                 &seat,
941                                 &vtnr,
942                                 &existing);
943         if (r < 0)
944                 return pam_bus_log_parse_error(handle, r);
945 
946         if (debug)
947                 pam_syslog(handle, LOG_DEBUG, "Reply from logind: "
948                            "id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u original_uid=%u",
949                            id, object_path, runtime_path, session_fd, seat, vtnr, original_uid);
950 
951         r = update_environment(handle, "XDG_SESSION_ID", id);
952         if (r != PAM_SUCCESS)
953                 return r;
954 
955         if (original_uid == ur->uid) {
956                 /* Don't set $XDG_RUNTIME_DIR if the user we now authenticated for does not match the
957                  * original user of the session. We do this in order not to result in privileged apps
958                  * clobbering the runtime directory unnecessarily. */
959 
960                 r = configure_runtime_directory(handle, ur, runtime_path);
961                 if (r != PAM_SUCCESS)
962                         return r;
963         }
964 
965         /* Most likely we got the session/type/class from environment variables, but might have gotten the data
966          * somewhere else (for example PAM module parameters). Let's now update the environment variables, so that this
967          * data is inherited into the session processes, and programs can rely on them to be initialized. */
968 
969         r = update_environment(handle, "XDG_SESSION_TYPE", type);
970         if (r != PAM_SUCCESS)
971                 return r;
972 
973         r = update_environment(handle, "XDG_SESSION_CLASS", class);
974         if (r != PAM_SUCCESS)
975                 return r;
976 
977         r = update_environment(handle, "XDG_SESSION_DESKTOP", desktop);
978         if (r != PAM_SUCCESS)
979                 return r;
980 
981         r = update_environment(handle, "XDG_SEAT", seat);
982         if (r != PAM_SUCCESS)
983                 return r;
984 
985         if (vtnr > 0) {
986                 char buf[DECIMAL_STR_MAX(vtnr)];
987                 sprintf(buf, "%u", vtnr);
988 
989                 r = update_environment(handle, "XDG_VTNR", buf);
990                 if (r != PAM_SUCCESS)
991                         return r;
992         }
993 
994         r = pam_set_data(handle, "systemd.existing", INT_TO_PTR(!!existing), NULL);
995         if (r != PAM_SUCCESS) {
996                 pam_syslog(handle, LOG_ERR, "Failed to install existing flag: %s", pam_strerror(handle, r));
997                 return r;
998         }
999 
1000         if (session_fd >= 0) {
1001                 session_fd = fcntl(session_fd, F_DUPFD_CLOEXEC, 3);
1002                 if (session_fd < 0) {
1003                         pam_syslog(handle, LOG_ERR, "Failed to dup session fd: %m");
1004                         return PAM_SESSION_ERR;
1005                 }
1006 
1007                 r = pam_set_data(handle, "systemd.session-fd", FD_TO_PTR(session_fd), NULL);
1008                 if (r != PAM_SUCCESS) {
1009                         pam_syslog(handle, LOG_ERR, "Failed to install session fd: %s", pam_strerror(handle, r));
1010                         safe_close(session_fd);
1011                         return r;
1012                 }
1013         }
1014 
1015 success:
1016         r = apply_user_record_settings(handle, ur, debug);
1017         if (r != PAM_SUCCESS)
1018                 return r;
1019 
1020         /* Let's release the D-Bus connection, after all the session might live quite a long time, and we are
1021          * not going to use the bus connection in that time, so let's better close before the daemon kicks us
1022          * off because we are not processing anything. */
1023         (void) pam_release_bus_connection(handle);
1024         return PAM_SUCCESS;
1025 }
1026 
pam_sm_close_session(pam_handle_t * handle,int flags,int argc,const char ** argv)1027 _public_ PAM_EXTERN int pam_sm_close_session(
1028                 pam_handle_t *handle,
1029                 int flags,
1030                 int argc, const char **argv) {
1031 
1032         const void *existing = NULL;
1033         bool debug = false;
1034         const char *id;
1035         int r;
1036 
1037         assert(handle);
1038 
1039         if (parse_argv(handle,
1040                        argc, argv,
1041                        NULL,
1042                        NULL,
1043                        NULL,
1044                        &debug) < 0)
1045                 return PAM_SESSION_ERR;
1046 
1047         if (debug)
1048                 pam_syslog(handle, LOG_DEBUG, "pam-systemd shutting down");
1049 
1050         /* Only release session if it wasn't pre-existing when we
1051          * tried to create it */
1052         r = pam_get_data(handle, "systemd.existing", &existing);
1053         if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
1054                 pam_syslog(handle, LOG_ERR, "Failed to get PAM systemd.existing data: %s", pam_strerror(handle, r));
1055                 return r;
1056         }
1057 
1058         id = pam_getenv(handle, "XDG_SESSION_ID");
1059         if (id && !existing) {
1060                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1061                 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1062 
1063                 /* Before we go and close the FIFO we need to tell logind that this is a clean session
1064                  * shutdown, so that it doesn't just go and slaughter us immediately after closing the fd */
1065 
1066                 r = pam_acquire_bus_connection(handle, &bus);
1067                 if (r != PAM_SUCCESS)
1068                         return r;
1069 
1070                 r = bus_call_method(bus, bus_login_mgr, "ReleaseSession", &error, NULL, "s", id);
1071                 if (r < 0) {
1072                         pam_syslog(handle, LOG_ERR, "Failed to release session: %s", bus_error_message(&error, r));
1073                         return PAM_SESSION_ERR;
1074                 }
1075         }
1076 
1077         /* Note that we are knowingly leaking the FIFO fd here. This way, logind can watch us die. If we
1078          * closed it here it would not have any clue when that is completed. Given that one cannot really
1079          * have multiple PAM sessions open from the same process this means we will leak one FD at max. */
1080 
1081         return PAM_SUCCESS;
1082 }
1083