From: Jin Liu Date: Tue, 31 Oct 2023 04:48:24 +0000 (+0800) Subject: New PAM module: pam_systemd_loadkey X-Git-Tag: v255-rc1~90^2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F29776%2Fhead;p=thirdparty%2Fsystemd.git New PAM module: pam_systemd_loadkey This module reads password from kernel keyring and sets it as PAM authtok. It's inspired by gdm's pam_gdm, which reads the LUKS password stored by systemd-cryptsetup, so Gnome Keyring can be automatically unlocked if set to the same password (when autologin is enabled so the user doesn't enter a password in gdm). --- diff --git a/man/pam_systemd_loadkey.xml b/man/pam_systemd_loadkey.xml new file mode 100644 index 00000000000..afb41f318d1 --- /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 106b80f3149..137696d7cf5 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 6fb09b48e2a..b5bb1502584 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 00000000000..3b4e91124a2 --- /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 00000000000..d611dc1022e --- /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: *; +};