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);