diff --git a/man/crypttab.xml b/man/crypttab.xml index 8f0dbe06e50..8ffeaf7fcba 100644 --- a/man/crypttab.xml +++ b/man/crypttab.xml @@ -673,6 +673,26 @@ + + + + Controls whether to use cache for passwords or security token PINs. + Takes a boolean or the special string read-only. Defaults to + yes. + + If set to read-only, the kernel keyring is checked for a + password/PIN before requesting one interactively. If set to yes, + in addition to checking the keyring, any password/PIN entered interactively is cached + in the keyring with a 2.5-minute timeout before being purged. + + Note that this option is not permitted for PKCS#11 security tokens. The reasoning + behind this is that PKCS#11 security tokens are usually configured to lock after being + supplied an invalid PIN multiple times, so using the cache might inadvertently lock the + token. + + + + diff --git a/man/systemd-cryptsetup.xml b/man/systemd-cryptsetup.xml index 676a38a763f..1c2db11a45c 100644 --- a/man/systemd-cryptsetup.xml +++ b/man/systemd-cryptsetup.xml @@ -94,8 +94,9 @@ If the try-empty-password option is specified then unlocking the volume with an empty password is attempted. - The kernel keyring is then checked for a suitable cached password from previous - attempts. + If the password-cache= option is set to yes or + read-only, the kernel keyring is then checked for a suitable cached password from + previous attempts. Finally, the user is queried for a password, possibly multiple times, unless the headless option is set. diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index 9306d771b99..4eec4c450a7 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -84,7 +84,8 @@ static char *arg_header = NULL; static unsigned arg_tries = 3; static bool arg_readonly = false; static bool arg_verify = false; -static AskPasswordFlags arg_ask_password_flags = 0; +static bool arg_password_cache_set = false; /* Not the actual argument value, just an indicator that some value is set */ +static AskPasswordFlags arg_ask_password_flags = ASK_PASSWORD_ACCEPT_CACHED | ASK_PASSWORD_PUSH_CACHE; static bool arg_discards = false; static bool arg_same_cpu_crypt = false; static bool arg_submit_from_crypt_cpus = false; @@ -299,6 +300,21 @@ static int parse_one_option(const char *option) { SET_FLAG(arg_ask_password_flags, ASK_PASSWORD_ECHO, r); SET_FLAG(arg_ask_password_flags, ASK_PASSWORD_SILENT, !r); } + } else if ((val = startswith(option, "password-cache="))) { + arg_password_cache_set = true; + + if (streq(val, "read-only")) { + arg_ask_password_flags |= ASK_PASSWORD_ACCEPT_CACHED; + arg_ask_password_flags &= ~ASK_PASSWORD_PUSH_CACHE; + } else { + r = parse_boolean(val); + if (r < 0) { + log_warning_errno(r, "Invalid password-cache= option \"%s\", ignoring.", val); + return 0; + } + + SET_FLAG(arg_ask_password_flags, ASK_PASSWORD_ACCEPT_CACHED|ASK_PASSWORD_PUSH_CACHE, r); + } } else if (STR_IN_SET(option, "allow-discards", "discard")) arg_discards = true; else if (streq(option, "same-cpu-crypt")) @@ -649,6 +665,17 @@ static int parse_crypt_config(const char *options) { log_warning("skip= ignored with type %s", arg_type); } + if (arg_pkcs11_uri || arg_pkcs11_uri_auto) { + /* If password-cache was not configured explicitly, default to no cache for PKCS#11 */ + if (!arg_password_cache_set) + arg_ask_password_flags &= ~(ASK_PASSWORD_ACCEPT_CACHED|ASK_PASSWORD_PUSH_CACHE); + + /* This prevents future backward-compatibility issues if we decide to allow caching for PKCS#11 */ + if (FLAGS_SET(arg_ask_password_flags, ASK_PASSWORD_ACCEPT_CACHED)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Password cache is not supported for PKCS#11 security tokens."); + } + return 0; } @@ -847,13 +874,13 @@ static int get_password( const char *vol, const char *src, usec_t until, - bool accept_cached, + bool ignore_cached, PassphraseType passphrase_type, char ***ret) { _cleanup_free_ char *friendly = NULL, *text = NULL, *disk_path = NULL, *id = NULL; _cleanup_strv_free_erase_ char **passwords = NULL; - AskPasswordFlags flags = arg_ask_password_flags | ASK_PASSWORD_PUSH_CACHE; + AskPasswordFlags flags = arg_ask_password_flags; int r; assert(vol); @@ -886,11 +913,10 @@ static int get_password( .credential = "cryptsetup.passphrase", }; - r = ask_password_auto( - &req, - until, - flags | (accept_cached*ASK_PASSWORD_ACCEPT_CACHED), - &passwords); + if (ignore_cached) + flags &= ~ASK_PASSWORD_ACCEPT_CACHED; + + r = ask_password_auto(&req, until, flags, &passwords); if (r < 0) return log_error_errno(r, "Failed to query password: %m"); @@ -1349,7 +1375,7 @@ static int crypt_activate_by_token_pin_ask_password( const char *credential) { #if HAVE_LIBCRYPTSETUP_PLUGINS - AskPasswordFlags flags = arg_ask_password_flags | ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_ACCEPT_CACHED; + AskPasswordFlags flags = arg_ask_password_flags; _cleanup_strv_free_erase_ char **pins = NULL; int r; @@ -2521,7 +2547,13 @@ static int run(int argc, char *argv[]) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No passphrase or recovery key registered."); } - r = get_password(volume, source, until, use_cached_passphrase && !arg_verify, passphrase_type, &passwords); + r = get_password( + volume, + source, + until, + /* ignore_cached= */ !use_cached_passphrase || arg_verify, + passphrase_type, + &passwords); use_cached_passphrase = false; if (r == -EAGAIN) continue; diff --git a/src/shared/cryptsetup-fido2.c b/src/shared/cryptsetup-fido2.c index 001285efd1c..8a5d42babab 100644 --- a/src/shared/cryptsetup-fido2.c +++ b/src/shared/cryptsetup-fido2.c @@ -44,8 +44,6 @@ int acquire_fido2_key( return log_error_errno(SYNTHETIC_ERRNO(ENOPKG), "Local verification is required to unlock this volume, but the 'headless' parameter was set."); - askpw_flags |= ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_ACCEPT_CACHED; - assert(cid); assert(key_file || key_data); diff --git a/src/shared/cryptsetup-tpm2.c b/src/shared/cryptsetup-tpm2.c index d029f101add..95c01678aa9 100644 --- a/src/shared/cryptsetup-tpm2.c +++ b/src/shared/cryptsetup-tpm2.c @@ -178,6 +178,8 @@ int acquire_tpm2_key( if (r < 0) return r; + askpw_flags &= ~ASK_PASSWORD_ACCEPT_CACHED; + if (iovec_is_set(salt)) { uint8_t salted_pin[SHA256_DIGEST_SIZE] = {}; CLEANUP_ERASE(salted_pin);