summaryrefslogtreecommitdiff
path: root/src/cryptsetup
diff options
context:
space:
mode:
authorPeter Cai <peter@typeblog.net>2022-10-29 19:00:53 -0400
committerLuca Boccassi <luca.boccassi@gmail.com>2022-12-12 21:48:39 +0100
commite6319a102e5b6f7c1588ca851d66db7c3ade1665 (patch)
tree810ebf6bd99149f2190bd67ffdefee836b523fb4 /src/cryptsetup
parentd41789edc5896dde227772e2812a86cb7e568367 (diff)
downloadsystemd-e6319a102e5b6f7c1588ca851d66db7c3ade1665.tar.gz
cryptsetup-fido2: Try all FIDO2 key slots when opening LUKS volume
After #25268, it is now possible to check whether a credential is present on a FIDO2 token without actually attempting to retrieve said credential. However, when cryptsetup plugins are not enabled, the fallback unlock routines are not able to make multiple attempts with multiple different FIDO2 key slots. Instead of looking for one FIDO2 key slot when trying to unlock, we now attempt to use all key slots applicable. Fixes #19208.
Diffstat (limited to 'src/cryptsetup')
-rw-r--r--src/cryptsetup/cryptsetup-fido2.c129
-rw-r--r--src/cryptsetup/cryptsetup-fido2.h40
-rw-r--r--src/cryptsetup/cryptsetup.c69
3 files changed, 123 insertions, 115 deletions
diff --git a/src/cryptsetup/cryptsetup-fido2.c b/src/cryptsetup/cryptsetup-fido2.c
index 74053b8ce3..a3bdedba1d 100644
--- a/src/cryptsetup/cryptsetup-fido2.c
+++ b/src/cryptsetup/cryptsetup-fido2.c
@@ -38,6 +38,10 @@ int acquire_fido2_key(
size_t salt_size;
int r;
+ if ((required & (FIDO2ENROLL_PIN | FIDO2ENROLL_UP | FIDO2ENROLL_UV)) && headless)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOPKG),
+ "Local verification is required to unlock this volume, but the 'headless' parameter was set.");
+
ask_password_flags |= ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_ACCEPT_CACHED;
assert(cid);
@@ -76,28 +80,6 @@ int acquire_fido2_key(
}
for (;;) {
- if (!FLAGS_SET(required, FIDO2ENROLL_PIN) || pins) {
- r = fido2_use_hmac_hash(
- device,
- rp_id ?: "io.systemd.cryptsetup",
- salt, salt_size,
- cid, cid_size,
- pins,
- required,
- ret_decrypted_key,
- ret_decrypted_key_size);
- if (!IN_SET(r,
- -ENOANO, /* needs pin */
- -ENOLCK)) /* pin incorrect */
- return r;
-
- device_exists = true; /* that a PIN is needed/wasn't correct means that we managed to
- * talk to a device */
- }
-
- if (headless)
- return log_error_errno(SYNTHETIC_ERRNO(ENOPKG), "PIN querying disabled via 'headless' option. Use the '$PIN' environment variable.");
-
if (!device_exists) {
/* Before we inquire for the PIN we'll need, if we never talked to the device, check
* if the device actually is plugged in. Otherwise we'll ask for the PIN already when
@@ -112,6 +94,30 @@ int acquire_fido2_key(
device_exists = true; /* now we know for sure, a device exists, no need to ask again */
}
+ /* Always make an attempt before asking for PIN.
+ * fido2_use_hmac_hash() will perform a pre-flight check for whether the credential for
+ * can be found on one of the connected devices. This way, we can avoid prompting the user
+ * for a PIN when we are sure that no device can be used. */
+ r = fido2_use_hmac_hash(
+ device,
+ rp_id ?: "io.systemd.cryptsetup",
+ salt, salt_size,
+ cid, cid_size,
+ pins,
+ required,
+ ret_decrypted_key,
+ ret_decrypted_key_size);
+ if (!IN_SET(r,
+ -ENOANO, /* needs pin */
+ -ENOLCK)) /* pin incorrect */
+ return r;
+
+ device_exists = true; /* that a PIN is needed/wasn't correct means that we managed to
+ * talk to a device */
+
+ if (headless)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOPKG), "PIN querying disabled via 'headless' option. Use the '$PIN' environment variable.");
+
pins = strv_free_erase(pins);
r = ask_password_auto("Please enter security token PIN:", "drive-harddisk", NULL, "fido2-pin", "cryptsetup.fido2-pin", until, ask_password_flags, &pins);
if (r < 0)
@@ -121,35 +127,38 @@ int acquire_fido2_key(
}
}
-int find_fido2_auto_data(
+int acquire_fido2_key_auto(
struct crypt_device *cd,
- char **ret_rp_id,
- void **ret_salt,
- size_t *ret_salt_size,
- void **ret_cid,
- size_t *ret_cid_size,
- int *ret_keyslot,
- Fido2EnrollFlags *ret_required) {
-
- _cleanup_free_ void *cid = NULL, *salt = NULL;
- size_t cid_size = 0, salt_size = 0;
- _cleanup_free_ char *rp = NULL;
- int r, keyslot = -1;
+ const char *name,
+ const char *friendly_name,
+ const char *fido2_device,
+ const char *key_file,
+ size_t key_file_size,
+ uint64_t key_file_offset,
+ usec_t until,
+ bool headless,
+ void **ret_decrypted_key,
+ size_t *ret_decrypted_key_size,
+ AskPasswordFlags ask_password_flags) {
+
+ _cleanup_free_ void *cid = NULL;
+ size_t cid_size = 0;
+ int r, ret = -ENOENT;
Fido2EnrollFlags required = 0;
assert(cd);
- assert(ret_salt);
- assert(ret_salt_size);
- assert(ret_cid);
- assert(ret_cid_size);
- assert(ret_keyslot);
- assert(ret_required);
+ assert(name);
+ assert(ret_decrypted_key);
+ assert(ret_decrypted_key_size);
/* Loads FIDO2 metadata from LUKS2 JSON token headers. */
for (int token = 0; token < sym_crypt_token_max(CRYPT_LUKS2); token ++) {
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
JsonVariant *w;
+ _cleanup_free_ void *salt = NULL;
+ _cleanup_free_ char *rp = NULL;
+ size_t salt_size = 0;
int ks;
r = cryptsetup_get_token_as_json(cd, token, "systemd-fido2", &v);
@@ -166,13 +175,6 @@ int find_fido2_auto_data(
continue;
}
- if (cid)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
- "Multiple FIDO2 tokens enrolled, cannot automatically determine token.");
-
- assert(keyslot < 0);
- keyslot = ks;
-
w = json_variant_by_key(v, "fido2-credential");
if (!w || !json_variant_is_string(w))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
@@ -243,20 +245,33 @@ int find_fido2_auto_data(
SET_FLAG(required, FIDO2ENROLL_UV, json_variant_boolean(w));
} else
required |= FIDO2ENROLL_UV_OMIT; /* compat with 248 */
+
+ ret = acquire_fido2_key(
+ name,
+ friendly_name,
+ fido2_device,
+ rp,
+ cid, cid_size,
+ key_file, key_file_size, key_file_offset,
+ salt, salt_size,
+ until,
+ headless,
+ required,
+ ret_decrypted_key, ret_decrypted_key_size,
+ ask_password_flags);
+ if (ret == 0)
+ break;
}
if (!cid)
return log_error_errno(SYNTHETIC_ERRNO(ENXIO),
"No valid FIDO2 token data found.");
- log_info("Automatically discovered security FIDO2 token unlocks volume.");
+ if (ret == -EAGAIN) /* fido2 device does not exist, or UV is blocked; caller will prompt for retry */
+ return log_debug_errno(ret, "FIDO2 token does not exist, or UV is blocked.");
+ if (ret < 0)
+ return log_error_errno(ret, "Failed to unlock LUKS volume with FIDO2 token: %m");
- *ret_rp_id = TAKE_PTR(rp);
- *ret_cid = TAKE_PTR(cid);
- *ret_cid_size = cid_size;
- *ret_salt = TAKE_PTR(salt);
- *ret_salt_size = salt_size;
- *ret_keyslot = keyslot;
- *ret_required = required;
- return 0;
+ log_info("Unlocked volume via automatically discovered security FIDO2 token.");
+ return ret;
}
diff --git a/src/cryptsetup/cryptsetup-fido2.h b/src/cryptsetup/cryptsetup-fido2.h
index 204f1e06c6..371bf21f0e 100644
--- a/src/cryptsetup/cryptsetup-fido2.h
+++ b/src/cryptsetup/cryptsetup-fido2.h
@@ -29,15 +29,19 @@ int acquire_fido2_key(
size_t *ret_decrypted_key_size,
AskPasswordFlags ask_password_flags);
-int find_fido2_auto_data(
+int acquire_fido2_key_auto(
struct crypt_device *cd,
- char **ret_rp_id,
- void **ret_salt,
- size_t *ret_salt_size,
- void **ret_cid,
- size_t *ret_cid_size,
- int *ret_keyslot,
- Fido2EnrollFlags *ret_required);
+ const char *name,
+ const char *friendly_name,
+ const char *fido2_device,
+ const char *key_file,
+ size_t key_file_size,
+ uint64_t key_file_offset,
+ usec_t until,
+ bool headless,
+ void **ret_decrypted_key,
+ size_t *ret_decrypted_key_size,
+ AskPasswordFlags ask_password_flags);
#else
@@ -64,15 +68,19 @@ static inline int acquire_fido2_key(
"FIDO2 token support not available.");
}
-static inline int find_fido2_auto_data(
+static inline int acquire_fido2_key_auto(
struct crypt_device *cd,
- char **ret_rp_id,
- void **ret_salt,
- size_t *ret_salt_size,
- void **ret_cid,
- size_t *ret_cid_size,
- int *ret_keyslot,
- Fido2EnrollFlags *ret_required) {
+ const char *name,
+ const char *friendly_name,
+ const char *fido2_device,
+ const char *key_file,
+ size_t key_file_size,
+ uint64_t key_file_offset,
+ usec_t until,
+ bool headless,
+ void **ret_decrypted_key,
+ size_t *ret_decrypted_key_size,
+ AskPasswordFlags ask_password_flags) {
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"FIDO2 token support not available.");
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index 90b5ab21a9..389bf4fdcf 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -1061,9 +1061,8 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
_cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL;
_cleanup_(erase_and_freep) void *decrypted_key = NULL;
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
- _cleanup_free_ void *discovered_salt = NULL, *discovered_cid = NULL;
- size_t discovered_salt_size, discovered_cid_size, decrypted_key_size, cid_size = 0;
- _cleanup_free_ char *friendly = NULL, *discovered_rp_id = NULL;
+ size_t decrypted_key_size, cid_size = 0;
+ _cleanup_free_ char *friendly = NULL;
int keyslot = arg_key_slot, r;
const char *rp_id = NULL;
const void *cid = NULL;
@@ -1088,32 +1087,6 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
* use PIN + UP when needed, and do not configure UV at all. Eventually, we should make this
* explicitly configurable. */
required = FIDO2ENROLL_PIN_IF_NEEDED | FIDO2ENROLL_UP_IF_NEEDED | FIDO2ENROLL_UV_OMIT;
- } else if (!use_libcryptsetup_plugin) {
- r = find_fido2_auto_data(
- cd,
- &discovered_rp_id,
- &discovered_salt,
- &discovered_salt_size,
- &discovered_cid,
- &discovered_cid_size,
- &keyslot,
- &required);
-
- if (IN_SET(r, -ENOTUNIQ, -ENXIO))
- return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
- "Automatic FIDO2 metadata discovery was not possible because missing or not unique, falling back to traditional unlocking.");
- if (r < 0)
- return r;
-
- if ((required & (FIDO2ENROLL_PIN | FIDO2ENROLL_UP | FIDO2ENROLL_UV)) && arg_headless)
- return log_error_errno(SYNTHETIC_ERRNO(ENOPKG),
- "Local verification is required to unlock this volume, but the 'headless' parameter was set.");
-
- rp_id = discovered_rp_id;
- key_data = discovered_salt;
- key_data_size = discovered_salt_size;
- cid = discovered_cid;
- cid_size = discovered_cid_size;
}
friendly = friendly_disk_name(crypt_get_device_name(cd), name);
@@ -1128,19 +1101,31 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
"Automatic FIDO2 metadata discovery was not possible because missing or not unique, falling back to traditional unlocking.");
} else {
- r = acquire_fido2_key(
- name,
- friendly,
- arg_fido2_device,
- rp_id,
- cid, cid_size,
- key_file, arg_keyfile_size, arg_keyfile_offset,
- key_data, key_data_size,
- until,
- arg_headless,
- required,
- &decrypted_key, &decrypted_key_size,
- arg_ask_password_flags);
+ if (cid)
+ r = acquire_fido2_key(
+ name,
+ friendly,
+ arg_fido2_device,
+ rp_id,
+ cid, cid_size,
+ key_file, arg_keyfile_size, arg_keyfile_offset,
+ key_data, key_data_size,
+ until,
+ arg_headless,
+ required,
+ &decrypted_key, &decrypted_key_size,
+ arg_ask_password_flags);
+ else
+ r = acquire_fido2_key_auto(
+ cd,
+ name,
+ friendly,
+ arg_fido2_device,
+ key_file, arg_keyfile_size, arg_keyfile_offset,
+ until,
+ arg_headless,
+ &decrypted_key, &decrypted_key_size,
+ arg_ask_password_flags);
if (r >= 0)
break;
}