diff --git a/man/pam_systemd.xml b/man/pam_systemd.xml
index 5482ced067..bb46455695 100644
--- a/man/pam_systemd.xml
+++ b/man/pam_systemd.xml
@@ -238,6 +238,33 @@
+
+ inhibit=
+
+ Takes a colon-separated list of inhibitor lock types to acquire for the duration of
+ the session. The XDG_SESSION_INHIBIT environment variable (see below) takes
+ precedence. Inhibitor locks prevent the system from performing certain actions, such as sleeping.
+ The supported lock types are the same as for the
+ systemd-inhibit1
+ command, e.g. shutdown, sleep, idle.
+ If not set, no inhibitor lock is acquired. The lock is taken in block mode, the requested
+ actions are fully inhibited as long as the session is active.
+
+
+
+
+
+ inhibit-why=
+
+ Takes a short human-readable string describing the reason for the inhibitor lock
+ acquired via inhibit=. The XDG_SESSION_INHIBIT_WHY
+ environment variable (see below) takes precedence. Defaults to Active PAM session
+ if not set. Only has an effect if inhibit= or
+ XDG_SESSION_INHIBIT is set.
+
+
+
+
default-capability-bounding-set=
default-capability-ambient-set=
@@ -414,6 +441,25 @@
+
+
+ $XDG_SESSION_INHIBIT
+
+ The inhibitor lock types to acquire for the duration of the session, as a
+ colon-separated list. This may be used instead of inhibit= on the module
+ parameter line.
+
+
+
+
+
+ $XDG_SESSION_INHIBIT_WHY
+
+ The reason string for the inhibitor lock. This may be used instead of
+ inhibit-why= on the module parameter line.
+
+
+
If not set, pam_systemd will initialize
@@ -528,6 +574,7 @@ session required pam_unix.so
logind.conf5
loginctl1
pam_systemd_home8
+ systemd-inhibit1
pam.conf5
pam.d5
pam8
diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c
index 9bdd147db4..06d19607b2 100644
--- a/src/login/pam_systemd.c
+++ b/src/login/pam_systemd.c
@@ -117,6 +117,8 @@ static int parse_argv(
const char **type,
const char **desktop,
const char **area,
+ const char **inhibit_what,
+ const char **inhibit_why,
bool *debug,
uint64_t *default_capability_bounding_set,
uint64_t *default_capability_ambient_set) {
@@ -148,6 +150,12 @@ static int parse_argv(
sym_pam_syslog(pamh, LOG_WARNING, "Area name specified among PAM module parameters is not valid, ignoring: %s", p);
else if (area)
*area = p;
+ } else if ((p = startswith(argv[i], "inhibit="))) {
+ if (inhibit_what)
+ *inhibit_what = p;
+ } else if ((p = startswith(argv[i], "inhibit-why="))) {
+ if (inhibit_why)
+ *inhibit_why = p;
} else if (streq(argv[i], "debug")) {
if (debug)
@@ -1739,6 +1747,57 @@ static int close_osc_context(pam_handle_t *pamh, bool debug) {
return PAM_SUCCESS;
}
+static int acquire_inhibit_lock(
+ pam_handle_t *pamh,
+ const char *inhibit_what,
+ const char *inhibit_why,
+ bool debug) {
+
+ _cleanup_(pam_bus_data_disconnectp) PamBusData *d = NULL;
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ const char *service = NULL;
+ int r;
+
+ assert(pamh);
+ assert(inhibit_what);
+
+ r = pam_acquire_bus_connection(pamh, "pam-systemd", debug, &bus, &d);
+ if (r != PAM_SUCCESS)
+ return r;
+
+ (void) sym_pam_get_item(pamh, PAM_SERVICE, (const void **) &service);
+
+ r = bus_call_method(bus, bus_login_mgr, "Inhibit", &error, &reply, "ssss",
+ inhibit_what,
+ service ?: "pam_systemd",
+ inhibit_why ?: "Active PAM session",
+ "block");
+ if (r < 0) {
+ sym_pam_syslog(pamh, LOG_WARNING, "Failed to acquire inhibit lock: %s", bus_error_message(&error, r));
+ return PAM_SESSION_ERR;
+ }
+
+ int raw_fd;
+ r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_UNIX_FD, &raw_fd);
+ if (r < 0)
+ return pam_bus_log_parse_error(pamh, r);
+
+ _cleanup_close_ int fd = fcntl(raw_fd, F_DUPFD_CLOEXEC, 3);
+ if (fd < 0)
+ return pam_syslog_errno(pamh, LOG_ERR, errno, "Failed to dup inhibit lock fd: %m");
+
+ r = sym_pam_set_data(pamh, "systemd.inhibit-fd", FD_TO_PTR(fd), pam_cleanup_close);
+ if (r != PAM_SUCCESS)
+ return pam_syslog_pam_error(pamh, LOG_ERR, r,
+ "Failed to store inhibit lock fd: @PAMERR@");
+ TAKE_FD(fd);
+
+ pam_debug_syslog(pamh, debug, "Acquired inhibit lock for '%s'.", inhibit_what);
+ return PAM_SUCCESS;
+}
+
_public_ PAM_EXTERN int pam_sm_open_session(
pam_handle_t *pamh,
int flags,
@@ -1755,7 +1814,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
pam_log_setup();
uint64_t default_capability_bounding_set = CAP_MASK_UNSET, default_capability_ambient_set = CAP_MASK_UNSET;
- const char *class_pam = NULL, *type_pam = NULL, *desktop_pam = NULL, *area_pam = NULL;
+ const char *class_pam = NULL, *type_pam = NULL, *desktop_pam = NULL, *area_pam = NULL, *inhibit_what = NULL, *inhibit_why = NULL;
bool debug = false;
if (parse_argv(pamh,
argc, argv,
@@ -1763,6 +1822,8 @@ _public_ PAM_EXTERN int pam_sm_open_session(
&type_pam,
&desktop_pam,
&area_pam,
+ &inhibit_what,
+ &inhibit_why,
&debug,
&default_capability_bounding_set,
&default_capability_ambient_set) < 0)
@@ -1793,6 +1854,8 @@ _public_ PAM_EXTERN int pam_sm_open_session(
c.desktop = getenv_harder(pamh, "XDG_SESSION_DESKTOP", desktop_pam);
c.area = getenv_harder(pamh, "XDG_AREA", area_pam);
c.incomplete = getenv_harder_bool(pamh, "XDG_SESSION_INCOMPLETE", false);
+ inhibit_what = getenv_harder(pamh, "XDG_SESSION_INHIBIT", inhibit_what);
+ inhibit_why = getenv_harder(pamh, "XDG_SESSION_INHIBIT_WHY", inhibit_why);
const char *extra_device_access = getenv_harder(pamh, "XDG_SESSION_EXTRA_DEVICE_ACCESS", NULL);
if (extra_device_access) {
@@ -1818,6 +1881,11 @@ _public_ PAM_EXTERN int pam_sm_open_session(
if (r != PAM_SUCCESS)
return r;
+ if (inhibit_what)
+ /* Failure to acquire the inhibit lock is not fatal, session setup should proceed
+ * regardless. */
+ (void) acquire_inhibit_lock(pamh, inhibit_what, inhibit_why, debug);
+
r = import_shell_credentials(pamh, debug);
if (r != PAM_SUCCESS)
return r;
@@ -1859,6 +1927,8 @@ _public_ PAM_EXTERN int pam_sm_close_session(
/* type= */ NULL,
/* desktop= */ NULL,
/* area= */ NULL,
+ /* inhibit_what= */ NULL,
+ /* inhibit_why= */ NULL,
&debug,
/* default_capability_bounding_set= */ NULL,
/* default_capability_ambient_set= */ NULL) < 0)
@@ -1868,6 +1938,8 @@ _public_ PAM_EXTERN int pam_sm_close_session(
(void) close_osc_context(pamh, debug);
+ (void) sym_pam_set_data(pamh, "systemd.inhibit-fd", NULL, NULL);
+
id = sym_pam_getenv(pamh, "XDG_SESSION_ID");
if (id) {
_cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;