diff --git a/man/pam_systemd_loadkey.xml b/man/pam_systemd_loadkey.xml
new file mode 100644
index 0000000000..afb41f318d
--- /dev/null
+++ b/man/pam_systemd_loadkey.xml
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+ pam_systemd_loadkey
+ systemd
+
+
+
+ pam_systemd_loadkey
+ 8
+
+
+
+ pam_systemd_loadkey
+ Read password from kernel keyring and set it as PAM authtok
+
+
+
+ pam_systemd_loadkey.so
+
+
+
+ Description
+
+ pam_systemd_loadkey reads a NUL-separated password list from the kernel keyring,
+ and sets the last password in the list as the PAM authtok.
+
+ The password list is supposed to be stored in the "user" keyring of the root user,
+ by an earlier call to
+ systemd-ask-password1
+ with .
+ You can pass the keyname to pam_systemd_loadkey via the option.
+
+
+
+
+ Options
+
+ The following options are understood:
+
+
+
+
+ keyname=
+
+ Takes a string argument which sets the keyname to read.
+ The default is cryptsetup, which is used by
+ systemd-cryptsetup@.service8
+ to store LUKS passphrase during boot.
+
+
+
+
+
+ debug
+
+ The module will log debugging information as it operates.
+
+
+
+
+
+
+
+ Example
+
+ This module is intended to be used when you use LUKS with a passphrase, enable autologin in the display
+ manager, and want to unlock Gnome Keyring / KDE KWallet automatically. So in total, you only enter one password
+ during boot.
+
+ You need to set the password of your Gnome Keyring/KWallet to the same as your LUKS passphrase.
+ Then add the following lines to your display manager's PAM config under /etc/pam.d/ (e.g. sddm-autologin):
+
+
+-auth optional pam_systemd_loadkey.so
+-session optional pam_gnome_keyring.so auto_start
+-session optional pam_kwallet5.so auto_start
+
+
+ And add the following lines to your display manager's systemd service file, so it can access root's keyring:
+
+
+[Service]
+KeyringMode=inherit
+
+
+ In this setup, early during the boot process,
+ systemd-cryptsetup@.service8
+ will ask for the passphrase and store it in the kernel keyring with the keyname cryptsetup.
+ Then when the display manager does the autologin, pam_systemd_loadkey will read the passphrase from the kernel keyring,
+ set it as the PAM authtok, and then pam_gnome_keyring and pam_kwallet5 will unlock with the same passphrase.
+
+
+
diff --git a/man/rules/meson.build b/man/rules/meson.build
index 106b80f314..137696d7cf 100644
--- a/man/rules/meson.build
+++ b/man/rules/meson.build
@@ -65,6 +65,7 @@ manpages = [
['org.freedesktop.systemd1', '5', [], ''],
['org.freedesktop.timedate1', '5', [], 'ENABLE_TIMEDATED'],
['os-release', '5', ['extension-release', 'initrd-release'], ''],
+ ['pam_systemd_loadkey', '8', [], 'HAVE_PAM'],
['pam_systemd', '8', [], 'HAVE_PAM'],
['pam_systemd_home', '8', [], 'ENABLE_PAM_HOME'],
['portablectl', '1', [], 'ENABLE_PORTABLED'],
diff --git a/src/login/meson.build b/src/login/meson.build
index 6fb09b48e2..b5bb150258 100644
--- a/src/login/meson.build
+++ b/src/login/meson.build
@@ -114,6 +114,14 @@ modules += [
'sources' : files('pam_systemd.c'),
'version-script' : meson.current_source_dir() / 'pam_systemd.sym',
},
+ pam_template + {
+ 'name' : 'pam_systemd_loadkey',
+ 'conditions' : [
+ 'HAVE_PAM',
+ ],
+ 'sources' : files('pam_systemd_loadkey.c'),
+ 'version-script' : meson.current_source_dir() / 'pam_systemd_loadkey.sym',
+ },
]
enable_logind = conf.get('ENABLE_LOGIND') == 1
diff --git a/src/login/pam_systemd_loadkey.c b/src/login/pam_systemd_loadkey.c
new file mode 100644
index 0000000000..3b4e91124a
--- /dev/null
+++ b/src/login/pam_systemd_loadkey.c
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "keyring-util.h"
+#include "macro.h"
+#include "missing_syscall.h"
+#include "nulstr-util.h"
+#include "pam-util.h"
+#include "strv.h"
+
+/* By default, this module retrieves the key stored by systemd-cryptsetup.
+ * This can be overridden by the keyname= parameter. */
+static const char DEFAULT_KEYNAME[] = "cryptsetup";
+
+_public_ int pam_sm_authenticate(
+ pam_handle_t *handle,
+ int flags,
+ int argc, const char **argv) {
+
+ assert(handle);
+
+ /* Parse argv. */
+
+ assert(argc >= 0);
+ assert(argc == 0 || argv);
+
+ const char *keyname = DEFAULT_KEYNAME;
+ bool debug = false;
+
+ for (int i = 0; i < argc; i++) {
+ const char *p;
+
+ if ((p = startswith(argv[i], "keyname=")))
+ keyname = p;
+ else if (streq(argv[i], "debug"))
+ debug = true;
+ else
+ pam_syslog(handle, LOG_WARNING, "Unknown parameter '%s', ignoring.", argv[i]);
+ }
+
+ pam_debug_syslog(handle, debug, "pam-systemd-loadkey initializing");
+
+ /* Retrieve the key. */
+
+ key_serial_t serial;
+ serial = request_key("user", keyname, NULL, 0);
+ if (serial < 0) {
+ if (errno == ENOKEY) {
+ pam_debug_syslog(handle, debug, "Key not found: %s", keyname);
+ return PAM_AUTHINFO_UNAVAIL;
+ } else if (errno == EKEYEXPIRED) {
+ pam_debug_syslog(handle, debug, "Key expired: %s", keyname);
+ return PAM_AUTHINFO_UNAVAIL;
+ } else
+ return pam_syslog_errno(handle, LOG_ERR, errno, "Failed to look up the key: %m");
+ }
+
+ _cleanup_(erase_and_freep) void *p = NULL;
+ size_t n;
+ int r;
+
+ r = keyring_read(serial, &p, &n);
+ if (r < 0)
+ return pam_syslog_errno(handle, LOG_ERR, r, "Failed to read the key: %m");
+
+ /* Split the key by NUL. Set the last item as authtok. */
+
+ _cleanup_(strv_free_erasep) char **passwords = strv_parse_nulstr(p, n);
+ if (!passwords)
+ return pam_log_oom(handle);
+
+ size_t passwords_len = strv_length(passwords);
+ if (passwords_len == 0) {
+ pam_debug_syslog(handle, debug, "Key is empty");
+ return PAM_AUTHINFO_UNAVAIL;
+ } else if (passwords_len > 1)
+ pam_debug_syslog(handle, debug, "Multiple passwords found in the key. Using the last one");
+
+ r = pam_set_item(handle, PAM_AUTHTOK, passwords[passwords_len - 1]);
+ if (r != PAM_SUCCESS)
+ return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to set PAM auth token: @PAMERR@");
+
+ return PAM_SUCCESS;
+}
+
+_public_ int pam_sm_setcred(
+ pam_handle_t *handle,
+ int flags,
+ int argc, const char **argv) {
+
+ return PAM_SUCCESS;
+}
diff --git a/src/login/pam_systemd_loadkey.sym b/src/login/pam_systemd_loadkey.sym
new file mode 100644
index 0000000000..d611dc1022
--- /dev/null
+++ b/src/login/pam_systemd_loadkey.sym
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+{
+global:
+ pam_sm_authenticate;
+ pam_sm_setcred;
+local: *;
+};