1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
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>
14 #include <sys/sysmacros.h>
15 #include <sys/types.h>
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"
25 #include "capability-util.h"
26 #include "cgroup-setup.h"
27 #include "devnum-util.h"
28 #include "errno-util.h"
31 #include "format-util.h"
33 #include "hostname-util.h"
34 #include "locale-util.h"
35 #include "login-util.h"
37 #include "missing_syscall.h"
39 #include "parse-util.h"
40 #include "path-util.h"
41 #include "percent-util.h"
42 #include "process-util.h"
43 #include "rlimit-util.h"
44 #include "socket-util.h"
45 #include "stdio-util.h"
47 #include "terminal-util.h"
48 #include "user-util.h"
51 #define LOGIN_SLOW_BUS_CALL_TIMEOUT_USEC (2*USEC_PER_MINUTE)
53 static int parse_caps(
64 if (value
[0] == '~') {
71 _cleanup_free_
char *s
= NULL
;
75 /* We can't use spaces as separators here, as PAM's simplistic argument parser doesn't allow
76 * spaces inside of arguments. We use commas instead (which is similar to cap_from_text(),
77 * which also uses commas). */
78 r
= extract_first_word(&value
, &s
, ",", EXTRACT_DONT_COALESCE_SEPARATORS
);
84 c
= capability_from_name(s
);
86 pam_syslog(handle
, LOG_WARNING
, "Unknown capability, ignoring: %s", s
);
95 if (*caps
== UINT64_MAX
)
96 b
= subtract
? all_capabilities() : 0;
109 static int parse_argv(
110 pam_handle_t
*handle
,
111 int argc
, const char **argv
,
114 const char **desktop
,
116 uint64_t *default_capability_bounding_set
,
117 uint64_t *default_capability_ambient_set
) {
123 assert(argc
== 0 || argv
);
125 for (int i
= 0; i
< argc
; i
++) {
128 if ((p
= startswith(argv
[i
], "class="))) {
132 } else if ((p
= startswith(argv
[i
], "type="))) {
136 } else if ((p
= startswith(argv
[i
], "desktop="))) {
140 } else if (streq(argv
[i
], "debug")) {
144 } else if ((p
= startswith(argv
[i
], "debug="))) {
145 r
= parse_boolean(p
);
147 pam_syslog(handle
, LOG_WARNING
, "Failed to parse debug= argument, ignoring: %s", p
);
151 } else if ((p
= startswith(argv
[i
], "default-capability-bounding-set="))) {
152 r
= parse_caps(handle
, p
, default_capability_bounding_set
);
154 pam_syslog(handle
, LOG_WARNING
, "Failed to parse default-capability-bounding-set= argument, ignoring: %s", p
);
156 } else if ((p
= startswith(argv
[i
], "default-capability-ambient-set="))) {
157 r
= parse_caps(handle
, p
, default_capability_ambient_set
);
159 pam_syslog(handle
, LOG_WARNING
, "Failed to parse default-capability-ambient-set= argument, ignoring: %s", p
);
162 pam_syslog(handle
, LOG_WARNING
, "Unknown parameter '%s', ignoring.", argv
[i
]);
168 static int acquire_user_record(
169 pam_handle_t
*handle
,
170 UserRecord
**ret_record
) {
172 _cleanup_(user_record_unrefp
) UserRecord
*ur
= NULL
;
173 const char *username
= NULL
, *json
= NULL
;
174 _cleanup_free_
char *field
= NULL
;
179 r
= pam_get_user(handle
, &username
, NULL
);
180 if (r
!= PAM_SUCCESS
)
181 return pam_syslog_pam_error(handle
, LOG_ERR
, r
, "Failed to get user name: @PAMERR@");
183 if (isempty(username
))
184 return pam_syslog_pam_error(handle
, LOG_ERR
, PAM_SERVICE_ERR
, "User name not valid.");
186 /* If pam_systemd_homed (or some other module) already acquired the user record we can reuse it
188 field
= strjoin("systemd-user-record-", username
);
190 return pam_log_oom(handle
);
192 r
= pam_get_data(handle
, field
, (const void**) &json
);
193 if (!IN_SET(r
, PAM_SUCCESS
, PAM_NO_MODULE_DATA
))
194 return pam_syslog_pam_error(handle
, LOG_ERR
, r
, "Failed to get PAM user record data: @PAMERR@");
195 if (r
== PAM_SUCCESS
&& json
) {
196 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
198 /* Parse cached record */
199 r
= json_parse(json
, JSON_PARSE_SENSITIVE
, &v
, NULL
, NULL
);
201 return pam_syslog_errno(handle
, LOG_ERR
, r
, "Failed to parse JSON user record: %m");
203 ur
= user_record_new();
205 return pam_log_oom(handle
);
207 r
= user_record_load(ur
, v
, USER_RECORD_LOAD_REFUSE_SECRET
|USER_RECORD_PERMISSIVE
);
209 return pam_syslog_errno(handle
, LOG_ERR
, r
, "Failed to load user record: %m");
211 /* Safety check if cached record actually matches what we are looking for */
212 if (!streq_ptr(username
, ur
->user_name
))
213 return pam_syslog_pam_error(handle
, LOG_ERR
, PAM_SERVICE_ERR
,
214 "Acquired user record does not match user name.");
216 _cleanup_free_
char *formatted
= NULL
;
218 /* Request the record ourselves */
219 r
= userdb_by_name(username
, 0, &ur
);
221 pam_syslog_errno(handle
, LOG_ERR
, r
, "Failed to get user record: %m");
222 return PAM_USER_UNKNOWN
;
225 r
= json_variant_format(ur
->json
, 0, &formatted
);
227 return pam_syslog_errno(handle
, LOG_ERR
, r
, "Failed to format user JSON: %m");
229 /* And cache it for everyone else */
230 r
= pam_set_data(handle
, field
, formatted
, pam_cleanup_free
);
231 if (r
!= PAM_SUCCESS
)
232 return pam_syslog_pam_error(handle
, LOG_ERR
, r
,
233 "Failed to set PAM user record data '%s': @PAMERR@", field
);
237 if (!uid_is_valid(ur
->uid
))
238 return pam_syslog_pam_error(handle
, LOG_ERR
, PAM_SERVICE_ERR
,
239 "Acquired user record does not have a UID.");
242 *ret_record
= TAKE_PTR(ur
);
247 static bool display_is_local(const char *display
) {
252 ascii_isdigit(display
[1]);
255 static int socket_from_display(const char *display
) {
256 _cleanup_free_
char *f
= NULL
;
259 union sockaddr_union sa
;
261 _cleanup_close_
int fd
= -EBADF
;
266 if (!display_is_local(display
))
269 k
= strspn(display
+1, "0123456789");
271 /* Try abstract socket first. */
272 f
= new(char, STRLEN("@/tmp/.X11-unix/X") + k
+ 1);
276 c
= stpcpy(f
, "@/tmp/.X11-unix/X");
277 memcpy(c
, display
+1, k
);
280 r
= sockaddr_un_set_path(&sa
.un
, f
);
285 fd
= RET_NERRNO(socket(AF_UNIX
, SOCK_STREAM
|SOCK_CLOEXEC
, 0));
289 r
= RET_NERRNO(connect(fd
, &sa
.sa
, sa_len
));
292 if (r
!= -ECONNREFUSED
)
295 /* Try also non-abstract socket. */
296 r
= sockaddr_un_set_path(&sa
.un
, f
+ 1);
301 r
= RET_NERRNO(connect(fd
, &sa
.sa
, sa_len
));
307 static int get_seat_from_display(const char *display
, const char **seat
, uint32_t *vtnr
) {
308 _cleanup_free_
char *sys_path
= NULL
, *tty
= NULL
;
309 _cleanup_close_
int fd
= -EBADF
;
317 /* We deduce the X11 socket from the display name, then use
318 * SO_PEERCRED to determine the X11 server process, ask for
319 * the controlling tty of that and if it's a VC then we know
320 * the seat and the virtual terminal. Sounds ugly, is only
323 fd
= socket_from_display(display
);
327 r
= getpeercred(fd
, &ucred
);
331 r
= get_ctty_devnr(ucred
.pid
, &display_ctty
);
335 if (asprintf(&sys_path
, "/sys/dev/char/" DEVNUM_FORMAT_STR
, DEVNUM_FORMAT_VAL(display_ctty
)) < 0)
337 r
= readlink_value(sys_path
, &tty
);
341 v
= vtnr_from_tty(tty
);
349 *vtnr
= (uint32_t) v
;
354 static int export_legacy_dbus_address(
355 pam_handle_t
*handle
,
356 const char *runtime
) {
359 _cleanup_free_
char *t
= NULL
;
362 /* We need to export $DBUS_SESSION_BUS_ADDRESS because various applications will not connect
363 * correctly to the bus without it. This setting matches what dbus.socket does for the user
364 * session using 'systemctl --user set-environment'. We want to have the same configuration
365 * in processes started from the PAM session.
367 * The setting of the address is guarded by the access() check because it is also possible to compile
368 * dbus without --enable-user-session, in which case this socket is not used, and
369 * $DBUS_SESSION_BUS_ADDRESS should not be set. An alternative approach would to not do the access()
370 * check here, and let applications try on their own, by using "unix:path=%s/bus;autolaunch:". But we
371 * expect the socket to be present by the time we do this check, so we can just as well check once
374 s
= strjoina(runtime
, "/bus");
375 if (access(s
, F_OK
) < 0)
378 if (asprintf(&t
, DEFAULT_USER_BUS_ADDRESS_FMT
, runtime
) < 0)
379 return pam_log_oom(handle
);
381 r
= pam_misc_setenv(handle
, "DBUS_SESSION_BUS_ADDRESS", t
, 0);
382 if (r
!= PAM_SUCCESS
)
383 return pam_syslog_pam_error(handle
, LOG_ERR
, r
, "Failed to set bus variable: @PAMERR@");
388 static int append_session_memory_max(pam_handle_t
*handle
, sd_bus_message
*m
, const char *limit
) {
395 if (streq(limit
, "infinity")) {
396 r
= sd_bus_message_append(m
, "(sv)", "MemoryMax", "t", UINT64_MAX
);
398 return pam_bus_log_create_error(handle
, r
);
403 r
= parse_permyriad(limit
);
405 r
= sd_bus_message_append(m
, "(sv)", "MemoryMaxScale", "u", UINT32_SCALE_FROM_PERMYRIAD(r
));
407 return pam_bus_log_create_error(handle
, r
);
412 r
= parse_size(limit
, 1024, &val
);
414 r
= sd_bus_message_append(m
, "(sv)", "MemoryMax", "t", val
);
416 return pam_bus_log_create_error(handle
, r
);
421 pam_syslog(handle
, LOG_WARNING
, "Failed to parse systemd.memory_max, ignoring: %s", limit
);
425 static int append_session_runtime_max_sec(pam_handle_t
*handle
, sd_bus_message
*m
, const char *limit
) {
429 /* No need to parse "infinity" here, it will be set by default later in scope_init() */
430 if (isempty(limit
) || streq(limit
, "infinity"))
433 r
= parse_sec(limit
, &val
);
435 r
= sd_bus_message_append(m
, "(sv)", "RuntimeMaxUSec", "t", (uint64_t) val
);
437 return pam_bus_log_create_error(handle
, r
);
439 pam_syslog(handle
, LOG_WARNING
, "Failed to parse systemd.runtime_max_sec: %s, ignoring.", limit
);
444 static int append_session_tasks_max(pam_handle_t
*handle
, sd_bus_message
*m
, const char *limit
) {
448 /* No need to parse "infinity" here, it will be set unconditionally later in manager_start_scope() */
449 if (isempty(limit
) || streq(limit
, "infinity"))
452 r
= safe_atou64(limit
, &val
);
454 r
= sd_bus_message_append(m
, "(sv)", "TasksMax", "t", val
);
456 return pam_bus_log_create_error(handle
, r
);
458 pam_syslog(handle
, LOG_WARNING
, "Failed to parse systemd.tasks_max, ignoring: %s", limit
);
463 static int append_session_cpu_weight(pam_handle_t
*handle
, sd_bus_message
*m
, const char *limit
) {
470 r
= cg_cpu_weight_parse(limit
, &val
);
472 pam_syslog(handle
, LOG_WARNING
, "Failed to parse systemd.cpu_weight, ignoring: %s", limit
);
474 r
= sd_bus_message_append(m
, "(sv)", "CPUWeight", "t", val
);
476 return pam_bus_log_create_error(handle
, r
);
482 static int append_session_io_weight(pam_handle_t
*handle
, sd_bus_message
*m
, const char *limit
) {
489 r
= cg_weight_parse(limit
, &val
);
491 pam_syslog(handle
, LOG_WARNING
, "Failed to parse systemd.io_weight, ignoring: %s", limit
);
493 r
= sd_bus_message_append(m
, "(sv)", "IOWeight", "t", val
);
495 return pam_bus_log_create_error(handle
, r
);
501 static const char* getenv_harder(pam_handle_t
*handle
, const char *key
, const char *fallback
) {
507 /* Looks for an environment variable, preferably in the environment block associated with the
508 * specified PAM handle, falling back to the process' block instead. Why check both? Because we want
509 * to permit configuration of session properties from unit files that invoke PAM services, so that
510 * PAM services don't have to be reworked to set systemd-specific properties, but these properties
511 * can still be set from the unit file Environment= block. */
513 v
= pam_getenv(handle
, key
);
517 /* We use secure_getenv() here, since we might get loaded into su/sudo, which are SUID. Ideally
518 * they'd clean up the environment before invoking foreign code (such as PAM modules), but alas they
519 * currently don't (to be precise, they clean up the environment they pass to their children, but
520 * not their own environ[]). */
521 v
= secure_getenv(key
);
528 static bool getenv_harder_bool(pam_handle_t
*handle
, const char *key
, bool fallback
) {
535 v
= getenv_harder(handle
, key
, NULL
);
539 r
= parse_boolean(v
);
541 pam_syslog(handle
, LOG_ERR
, "Boolean environment variable value of '%s' is not valid: %s", key
, v
);
548 static int update_environment(pam_handle_t
*handle
, const char *key
, const char *value
) {
554 /* Updates the environment, but only if there's actually a value set. Also, log about errors */
559 r
= pam_misc_setenv(handle
, key
, value
, 0);
560 if (r
!= PAM_SUCCESS
)
561 return pam_syslog_pam_error(handle
, LOG_ERR
, r
,
562 "Failed to set environment variable %s: @PAMERR@", key
);
567 static bool validate_runtime_directory(pam_handle_t
*handle
, const char *path
, uid_t uid
) {
573 /* Some extra paranoia: let's not set $XDG_RUNTIME_DIR if the directory we'd set it to isn't actually
574 * set up properly for us. This is supposed to provide a careful safety net for supporting su/sudo
575 * type transitions: in that case the UID changes, but the session and thus the user owning it
576 * doesn't change. Since the $XDG_RUNTIME_DIR lifecycle is bound to the session's user being logged
577 * in at least once we should be particularly careful when setting the environment variable, since
578 * otherwise we might end up setting $XDG_RUNTIME_DIR to some directory owned by the wrong user. */
580 if (!path_is_absolute(path
)) {
581 pam_syslog(handle
, LOG_ERR
, "Provided runtime directory '%s' is not absolute.", path
);
585 if (lstat(path
, &st
) < 0) {
586 pam_syslog_errno(handle
, LOG_ERR
, errno
, "Failed to stat() runtime directory '%s': %m", path
);
590 if (!S_ISDIR(st
.st_mode
)) {
591 pam_syslog(handle
, LOG_ERR
, "Runtime directory '%s' is not actually a directory.", path
);
595 if (st
.st_uid
!= uid
) {
596 pam_syslog(handle
, LOG_ERR
, "Runtime directory '%s' is not owned by UID " UID_FMT
", as it should.", path
, uid
);
603 pam_syslog(handle
, LOG_WARNING
, "Not setting $XDG_RUNTIME_DIR, as the directory is not in order.");
607 static int pam_putenv_and_log(pam_handle_t
*handle
, const char *e
, bool debug
) {
613 r
= pam_putenv(handle
, e
);
614 if (r
!= PAM_SUCCESS
)
615 return pam_syslog_pam_error(handle
, LOG_ERR
, r
,
616 "Failed to set PAM environment variable %s: @PAMERR@", e
);
618 pam_debug_syslog(handle
, debug
, "PAM environment variable %s set based on user record.", e
);
623 static int apply_user_record_settings(
624 pam_handle_t
*handle
,
627 uint64_t default_capability_bounding_set
,
628 uint64_t default_capability_ambient_set
) {
629 _cleanup_strv_free_
char **langs
= NULL
;
635 if (ur
->umask
!= MODE_INVALID
) {
637 pam_debug_syslog(handle
, debug
, "Set user umask to %04o based on user record.", ur
->umask
);
640 STRV_FOREACH(i
, ur
->environment
) {
641 r
= pam_putenv_and_log(handle
, *i
, debug
);
642 if (r
!= PAM_SUCCESS
)
646 if (ur
->email_address
) {
647 _cleanup_free_
char *joined
= NULL
;
649 joined
= strjoin("EMAIL=", ur
->email_address
);
651 return pam_log_oom(handle
);
653 r
= pam_putenv_and_log(handle
, joined
, debug
);
654 if (r
!= PAM_SUCCESS
)
659 if (!timezone_is_valid(ur
->time_zone
, LOG_DEBUG
))
660 pam_debug_syslog(handle
, debug
,
661 "Time zone specified in user record is not valid locally, not setting $TZ.");
663 _cleanup_free_
char *joined
= NULL
;
665 joined
= strjoin("TZ=:", ur
->time_zone
);
667 return pam_log_oom(handle
);
669 r
= pam_putenv_and_log(handle
, joined
, debug
);
670 if (r
!= PAM_SUCCESS
)
675 r
= user_record_languages(ur
, &langs
);
677 pam_syslog_errno(handle
, LOG_ERR
, r
,
678 "Failed to acquire user's language preferences, ignoring: %m");
679 else if (strv_isempty(langs
))
680 ; /* User has no preference set so we do nothing */
681 else if (locale_is_installed(langs
[0]) <= 0)
682 pam_debug_syslog(handle
, debug
,
683 "Preferred languages specified in user record are not installed locally, not setting $LANG or $LANGUAGE.");
685 _cleanup_free_
char *lang
= NULL
;
687 lang
= strjoin("LANG=", langs
[0]);
689 return pam_log_oom(handle
);
691 r
= pam_putenv_and_log(handle
, lang
, debug
);
692 if (r
!= PAM_SUCCESS
)
695 if (strv_length(langs
) > 1) {
696 _cleanup_free_
char *joined
= NULL
, *language
= NULL
;
698 joined
= strv_join(langs
, ":");
700 return pam_log_oom(handle
);
702 language
= strjoin("LANGUAGE=", joined
);
704 return pam_log_oom(handle
);
706 r
= pam_putenv_and_log(handle
, language
, debug
);
707 if (r
!= PAM_SUCCESS
)
712 if (nice_is_valid(ur
->nice_level
)) {
713 if (nice(ur
->nice_level
) < 0)
714 pam_syslog_errno(handle
, LOG_ERR
, errno
,
715 "Failed to set nice level to %i, ignoring: %m", ur
->nice_level
);
717 pam_debug_syslog(handle
, debug
,
718 "Nice level set to %i, based on user record.", ur
->nice_level
);
721 for (int rl
= 0; rl
< _RLIMIT_MAX
; rl
++) {
723 if (!ur
->rlimits
[rl
])
726 r
= setrlimit_closest(rl
, ur
->rlimits
[rl
]);
728 pam_syslog_errno(handle
, LOG_ERR
, r
,
729 "Failed to set resource limit %s, ignoring: %m", rlimit_to_string(rl
));
731 pam_debug_syslog(handle
, debug
,
732 "Resource limit %s set, based on user record.", rlimit_to_string(rl
));
736 a
= user_record_capability_ambient_set(ur
);
738 a
= default_capability_ambient_set
;
740 b
= user_record_capability_bounding_set(ur
);
742 b
= default_capability_bounding_set
;
744 if (a
!= UINT64_MAX
&& a
!= 0) {
747 r
= capability_ambient_set_apply(a
, /* also_inherit= */ true);
749 pam_syslog_errno(handle
, LOG_ERR
, r
,
750 "Failed to set ambient capabilities, ignoring: %m");
753 if (b
!= UINT64_MAX
&& !cap_test_all(b
)) {
754 r
= capability_bounding_set_drop(b
, /* right_now= */ false);
756 pam_syslog_errno(handle
, LOG_ERR
, r
,
757 "Failed to set bounding capabilities, ignoring: %m");
763 static int configure_runtime_directory(
764 pam_handle_t
*handle
,
774 if (!validate_runtime_directory(handle
, rt
, ur
->uid
))
777 r
= pam_misc_setenv(handle
, "XDG_RUNTIME_DIR", rt
, 0);
778 if (r
!= PAM_SUCCESS
)
779 return pam_syslog_pam_error(handle
, LOG_ERR
, r
, "Failed to set runtime dir: @PAMERR@");
781 return export_legacy_dbus_address(handle
, rt
);
784 static uint64_t pick_default_capability_ambient_set(
789 /* If not configured otherwise, let's enable CAP_WAKE_ALARM for regular users when logging in on a
790 * seat (i.e. when they are present physically on the device), or when invoked for the systemd --user
791 * instances. This allows desktops to install CAP_WAKE_ALARM to implement alarm clock apps without
795 user_record_disposition(ur
) == USER_REGULAR
&&
796 (streq_ptr(service
, "systemd-user") || !isempty(seat
)) ? (UINT64_C(1) << CAP_WAKE_ALARM
) : UINT64_MAX
;
799 typedef struct SessionContext
{
811 const char *remote_user
;
812 const char *remote_host
;
813 const char *memory_max
;
814 const char *tasks_max
;
815 const char *cpu_weight
;
816 const char *io_weight
;
817 const char *runtime_max_sec
;
820 static int create_session_message(
822 pam_handle_t
*handle
,
823 const SessionContext
*context
,
825 sd_bus_message
**ret
) {
827 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
828 _cleanup_close_
int pidfd
= -EBADF
;
837 pidfd
= pidfd_open(getpid_cached(), 0);
838 if (pidfd
< 0 && !ERRNO_IS_NOT_SUPPORTED(errno
) && !ERRNO_IS_PRIVILEGE(errno
))
842 r
= bus_message_new_method_call(bus
, &m
, bus_login_mgr
, pidfd
>= 0 ? "CreateSessionWithPIDFD" : "CreateSession");
846 r
= sd_bus_message_append(m
,
847 pidfd
>= 0 ? "uhsssssussbss" : "uusssssussbss",
848 (uint32_t) context
->uid
,
849 pidfd
>= 0 ? pidfd
: context
->pid
,
859 context
->remote_user
,
860 context
->remote_host
);
865 r
= sd_bus_message_append(m
, "t", UINT64_C(0));
870 r
= sd_bus_message_open_container(m
, 'a', "(sv)");
874 r
= append_session_memory_max(handle
, m
, context
->memory_max
);
875 if (r
!= PAM_SUCCESS
)
878 r
= append_session_runtime_max_sec(handle
, m
, context
->runtime_max_sec
);
879 if (r
!= PAM_SUCCESS
)
882 r
= append_session_tasks_max(handle
, m
, context
->tasks_max
);
883 if (r
!= PAM_SUCCESS
)
886 r
= append_session_cpu_weight(handle
, m
, context
->cpu_weight
);
887 if (r
!= PAM_SUCCESS
)
890 r
= append_session_io_weight(handle
, m
, context
->io_weight
);
891 if (r
!= PAM_SUCCESS
)
894 r
= sd_bus_message_close_container(m
);
902 _public_ PAM_EXTERN
int pam_sm_open_session(
903 pam_handle_t
*handle
,
905 int argc
, const char **argv
) {
907 /* Let's release the D-Bus connection once this function exits, after all the session might live
908 * quite a long time, and we are not going to process the bus connection in that time, so let's
909 * better close before the daemon kicks us off because we are not processing anything. */
910 _cleanup_(pam_bus_data_disconnectp
) PamBusData
*d
= NULL
;
911 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
912 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
, *reply
= NULL
;
914 *id
, *object_path
, *runtime_path
,
916 *tty
= NULL
, *display
= NULL
,
917 *remote_user
= NULL
, *remote_host
= NULL
,
919 *type
= NULL
, *class = NULL
,
920 *class_pam
= NULL
, *type_pam
= NULL
, *cvtnr
= NULL
, *desktop
= NULL
, *desktop_pam
= NULL
,
921 *memory_max
= NULL
, *tasks_max
= NULL
, *cpu_weight
= NULL
, *io_weight
= NULL
, *runtime_max_sec
= NULL
;
922 uint64_t default_capability_bounding_set
= UINT64_MAX
, default_capability_ambient_set
= UINT64_MAX
;
923 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
924 _cleanup_(user_record_unrefp
) UserRecord
*ur
= NULL
;
925 int session_fd
= -EBADF
, existing
, r
;
926 bool debug
= false, remote
, incomplete
;
932 if (parse_argv(handle
,
938 &default_capability_bounding_set
,
939 &default_capability_ambient_set
) < 0)
940 return PAM_SESSION_ERR
;
942 pam_debug_syslog(handle
, debug
, "pam-systemd initializing");
944 r
= acquire_user_record(handle
, &ur
);
945 if (r
!= PAM_SUCCESS
)
948 /* Make most of this a NOP on non-logind systems */
949 if (!logind_running())
952 r
= pam_get_item_many(
954 PAM_SERVICE
, &service
,
955 PAM_XDISPLAY
, &display
,
957 PAM_RUSER
, &remote_user
,
958 PAM_RHOST
, &remote_host
);
959 if (r
!= PAM_SUCCESS
)
960 return pam_syslog_pam_error(handle
, LOG_ERR
, r
, "Failed to get PAM items: @PAMERR@");
962 seat
= getenv_harder(handle
, "XDG_SEAT", NULL
);
963 cvtnr
= getenv_harder(handle
, "XDG_VTNR", NULL
);
964 type
= getenv_harder(handle
, "XDG_SESSION_TYPE", type_pam
);
965 class = getenv_harder(handle
, "XDG_SESSION_CLASS", class_pam
);
966 desktop
= getenv_harder(handle
, "XDG_SESSION_DESKTOP", desktop_pam
);
967 incomplete
= getenv_harder_bool(handle
, "XDG_SESSION_INCOMPLETE", false);
969 if (streq_ptr(service
, "systemd-user")) {
970 /* If we detect that we are running in the "systemd-user" PAM stack, then let's patch the class to
971 * 'manager' if not set, simply for robustness reasons. */
972 type
= "unspecified";
973 class = IN_SET(user_record_disposition(ur
), USER_INTRINSIC
, USER_SYSTEM
, USER_DYNAMIC
) ?
974 "manager-early" : "manager";
977 } else if (tty
&& strchr(tty
, ':')) {
978 /* A tty with a colon is usually an X11 display, placed there to show up in utmp. We rearrange things
979 * and don't pretend that an X display was a tty. */
980 if (isempty(display
))
984 } else if (streq_ptr(tty
, "cron")) {
985 /* cron is setting PAM_TTY to "cron" for some reason (the commit carries no information why, but
986 * probably because it wants to set it to something as pam_time/pam_access/… require PAM_TTY to be set
987 * (as they otherwise even try to update it!) — but cron doesn't actually allocate a TTY for its forked
989 type
= "unspecified";
990 class = "background";
993 } else if (streq_ptr(tty
, "ssh")) {
994 /* ssh has been setting PAM_TTY to "ssh" (for the same reason as cron does this, see above. For further
995 * details look for "PAM_TTY_KLUDGE" in the openssh sources). */
998 tty
= NULL
; /* This one is particularly sad, as this means that ssh sessions — even though usually
999 * associated with a pty — won't be tracked by their tty in logind. This is because ssh
1000 * does the PAM session registration early for new connections, and registers a pty only
1001 * much later (this is because it doesn't know yet if it needs one at all, as whether to
1002 * register a pty or not is negotiated much later in the protocol). */
1005 /* Chop off leading /dev prefix that some clients specify, but others do not. */
1006 tty
= skip_dev_prefix(tty
);
1008 /* If this fails vtnr will be 0, that's intended */
1009 if (!isempty(cvtnr
))
1010 (void) safe_atou32(cvtnr
, &vtnr
);
1012 if (!isempty(display
) && !vtnr
) {
1014 (void) get_seat_from_display(display
, &seat
, &vtnr
);
1015 else if (streq(seat
, "seat0"))
1016 (void) get_seat_from_display(display
, NULL
, &vtnr
);
1019 if (seat
&& !streq(seat
, "seat0") && vtnr
!= 0) {
1020 pam_debug_syslog(handle
, debug
, "Ignoring vtnr %"PRIu32
" for %s which is not seat0", vtnr
, seat
);
1025 type
= !isempty(display
) ? "x11" :
1026 !isempty(tty
) ? "tty" : "unspecified";
1029 class = streq(type
, "unspecified") ? "background" :
1030 ((IN_SET(user_record_disposition(ur
), USER_INTRINSIC
, USER_SYSTEM
, USER_DYNAMIC
) &&
1031 streq(type
, "tty")) ? "user-early" : "user");
1034 if (streq(class, "user"))
1035 class = "user-incomplete";
1037 pam_syslog_pam_error(handle
, LOG_WARNING
, 0, "PAM session of class '%s' is incomplete, which is not supported, ignoring.", class);
1040 remote
= !isempty(remote_host
) && !is_localhost(remote_host
);
1042 r
= pam_get_data(handle
, "systemd.memory_max", (const void **)&memory_max
);
1043 if (!IN_SET(r
, PAM_SUCCESS
, PAM_NO_MODULE_DATA
))
1044 return pam_syslog_pam_error(handle
, LOG_ERR
, r
, "Failed to get PAM systemd.memory_max data: @PAMERR@");
1045 r
= pam_get_data(handle
, "systemd.tasks_max", (const void **)&tasks_max
);
1046 if (!IN_SET(r
, PAM_SUCCESS
, PAM_NO_MODULE_DATA
))
1047 return pam_syslog_pam_error(handle
, LOG_ERR
, r
, "Failed to get PAM systemd.tasks_max data: @PAMERR@");
1048 r
= pam_get_data(handle
, "systemd.cpu_weight", (const void **)&cpu_weight
);
1049 if (!IN_SET(r
, PAM_SUCCESS
, PAM_NO_MODULE_DATA
))
1050 return pam_syslog_pam_error(handle
, LOG_ERR
, r
, "Failed to get PAM systemd.cpu_weight data: @PAMERR@");
1051 r
= pam_get_data(handle
, "systemd.io_weight", (const void **)&io_weight
);
1052 if (!IN_SET(r
, PAM_SUCCESS
, PAM_NO_MODULE_DATA
))
1053 return pam_syslog_pam_error(handle
, LOG_ERR
, r
, "Failed to get PAM systemd.io_weight data: @PAMERR@");
1054 r
= pam_get_data(handle
, "systemd.runtime_max_sec", (const void **)&runtime_max_sec
);
1055 if (!IN_SET(r
, PAM_SUCCESS
, PAM_NO_MODULE_DATA
))
1056 return pam_syslog_pam_error(handle
, LOG_ERR
, r
, "Failed to get PAM systemd.runtime_max_sec data: @PAMERR@");
1058 /* Talk to logind over the message bus */
1059 r
= pam_acquire_bus_connection(handle
, "pam-systemd", &bus
, &d
);
1060 if (r
!= PAM_SUCCESS
)
1063 pam_debug_syslog(handle
, debug
,
1064 "Asking logind to create session: "
1065 "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",
1066 ur
->uid
, getpid_cached(),
1068 type
, class, strempty(desktop
),
1069 strempty(seat
), vtnr
, strempty(tty
), strempty(display
),
1070 yes_no(remote
), strempty(remote_user
), strempty(remote_host
));
1071 pam_debug_syslog(handle
, debug
,
1073 "memory_max=%s tasks_max=%s cpu_weight=%s io_weight=%s runtime_max_sec=%s",
1074 strna(memory_max
), strna(tasks_max
), strna(cpu_weight
), strna(io_weight
), strna(runtime_max_sec
));
1076 const SessionContext context
= {
1088 .remote_user
= remote_user
,
1089 .remote_host
= remote_host
,
1090 .memory_max
= memory_max
,
1091 .tasks_max
= tasks_max
,
1092 .cpu_weight
= cpu_weight
,
1093 .io_weight
= io_weight
,
1094 .runtime_max_sec
= runtime_max_sec
,
1097 r
= create_session_message(bus
,
1100 /* avoid_pidfd = */ false,
1103 return pam_bus_log_create_error(handle
, r
);
1105 r
= sd_bus_call(bus
, m
, LOGIN_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, &reply
);
1106 if (r
< 0 && sd_bus_error_has_name(&error
, SD_BUS_ERROR_UNKNOWN_METHOD
)) {
1107 sd_bus_error_free(&error
);
1108 pam_debug_syslog(handle
, debug
,
1109 "CreateSessionWithPIDFD() API is not available, retrying with CreateSession().");
1111 m
= sd_bus_message_unref(m
);
1112 r
= create_session_message(bus
,
1115 /* avoid_pidfd = */ true,
1118 return pam_bus_log_create_error(handle
, r
);
1120 r
= sd_bus_call(bus
, m
, LOGIN_SLOW_BUS_CALL_TIMEOUT_USEC
, &error
, &reply
);
1123 if (sd_bus_error_has_name(&error
, BUS_ERROR_SESSION_BUSY
)) {
1124 /* We are already in a session, don't do anything */
1125 pam_debug_syslog(handle
, debug
,
1126 "Not creating session: %s", bus_error_message(&error
, r
));
1130 pam_syslog(handle
, LOG_ERR
,
1131 "Failed to create session: %s", bus_error_message(&error
, r
));
1132 return PAM_SESSION_ERR
;
1135 r
= sd_bus_message_read(reply
,
1146 return pam_bus_log_parse_error(handle
, r
);
1148 pam_debug_syslog(handle
, debug
,
1149 "Reply from logind: "
1150 "id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u original_uid=%u",
1151 id
, object_path
, runtime_path
, session_fd
, seat
, vtnr
, original_uid
);
1153 /* Please update manager_default_environment() in core/manager.c accordingly if more session envvars
1154 * shall be added. */
1156 r
= update_environment(handle
, "XDG_SESSION_ID", id
);
1157 if (r
!= PAM_SUCCESS
)
1160 if (original_uid
== ur
->uid
) {
1161 /* Don't set $XDG_RUNTIME_DIR if the user we now authenticated for does not match the
1162 * original user of the session. We do this in order not to result in privileged apps
1163 * clobbering the runtime directory unnecessarily. */
1165 r
= configure_runtime_directory(handle
, ur
, runtime_path
);
1166 if (r
!= PAM_SUCCESS
)
1170 /* Most likely we got the session/type/class from environment variables, but might have gotten the data
1171 * somewhere else (for example PAM module parameters). Let's now update the environment variables, so that this
1172 * data is inherited into the session processes, and programs can rely on them to be initialized. */
1174 r
= update_environment(handle
, "XDG_SESSION_TYPE", type
);
1175 if (r
!= PAM_SUCCESS
)
1178 r
= update_environment(handle
, "XDG_SESSION_CLASS", class);
1179 if (r
!= PAM_SUCCESS
)
1182 r
= update_environment(handle
, "XDG_SESSION_DESKTOP", desktop
);
1183 if (r
!= PAM_SUCCESS
)
1186 r
= update_environment(handle
, "XDG_SEAT", seat
);
1187 if (r
!= PAM_SUCCESS
)
1191 char buf
[DECIMAL_STR_MAX(vtnr
)];
1192 sprintf(buf
, "%u", vtnr
);
1194 r
= update_environment(handle
, "XDG_VTNR", buf
);
1195 if (r
!= PAM_SUCCESS
)
1199 r
= pam_set_data(handle
, "systemd.existing", INT_TO_PTR(!!existing
), NULL
);
1200 if (r
!= PAM_SUCCESS
)
1201 return pam_syslog_pam_error(handle
, LOG_ERR
, r
, "Failed to install existing flag: @PAMERR@");
1203 if (session_fd
>= 0) {
1204 _cleanup_close_
int fd
= fcntl(session_fd
, F_DUPFD_CLOEXEC
, 3);
1206 return pam_syslog_errno(handle
, LOG_ERR
, errno
, "Failed to dup session fd: %m");
1208 r
= pam_set_data(handle
, "systemd.session-fd", FD_TO_PTR(fd
), NULL
);
1209 if (r
!= PAM_SUCCESS
)
1210 return pam_syslog_pam_error(handle
, LOG_ERR
, r
, "Failed to install session fd: @PAMERR@");
1215 if (default_capability_ambient_set
== UINT64_MAX
)
1216 default_capability_ambient_set
= pick_default_capability_ambient_set(ur
, service
, seat
);
1218 return apply_user_record_settings(handle
, ur
, debug
, default_capability_bounding_set
, default_capability_ambient_set
);
1221 _public_ PAM_EXTERN
int pam_sm_close_session(
1222 pam_handle_t
*handle
,
1224 int argc
, const char **argv
) {
1226 const void *existing
= NULL
;
1233 if (parse_argv(handle
,
1241 return PAM_SESSION_ERR
;
1243 pam_debug_syslog(handle
, debug
, "pam-systemd shutting down");
1245 /* Only release session if it wasn't pre-existing when we
1246 * tried to create it */
1247 r
= pam_get_data(handle
, "systemd.existing", &existing
);
1248 if (!IN_SET(r
, PAM_SUCCESS
, PAM_NO_MODULE_DATA
))
1249 return pam_syslog_pam_error(handle
, LOG_ERR
, r
,
1250 "Failed to get PAM systemd.existing data: @PAMERR@");
1252 id
= pam_getenv(handle
, "XDG_SESSION_ID");
1253 if (id
&& !existing
) {
1254 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1255 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1257 /* Before we go and close the FIFO we need to tell logind that this is a clean session
1258 * shutdown, so that it doesn't just go and slaughter us immediately after closing the fd */
1260 r
= pam_acquire_bus_connection(handle
, "pam-systemd", &bus
, NULL
);
1261 if (r
!= PAM_SUCCESS
)
1264 r
= bus_call_method(bus
, bus_login_mgr
, "ReleaseSession", &error
, NULL
, "s", id
);
1266 return pam_syslog_pam_error(handle
, LOG_ERR
, PAM_SESSION_ERR
,
1267 "Failed to release session: %s", bus_error_message(&error
, r
));
1270 /* Note that we are knowingly leaking the FIFO fd here. This way, logind can watch us die. If we
1271 * closed it here it would not have any clue when that is completed. Given that one cannot really
1272 * have multiple PAM sessions open from the same process this means we will leak one FD at max. */