mirror of
https://github.com/systemd/systemd.git
synced 2026-08-13 17:08:14 +00:00
fido2: fix enrolling when UV is required ('alwaysUv')
When a Yubikey or other fido2 device has FIPS mode enabled, UV will
always be required and cannot be disabled. Unhelpfully, when it is not
sent down, the hardware token (not the library) returns a generic
FIDO_ERR_MISSING_PARAMETER:
$ systemd-cryptenroll --fido2-device=auto --fido2-with-client-pin=no foo
<...>
libfido2: rx: payload_len=1
libfido2: fido_rx: buf=0x55c9ce170940, len=1
libfido2: 0000: 14
libfido2: cbor_parse_reply: blob[0]=0x14
libfido2: fido_dev_get_assert_rx: adjust_assert_count
Failed to ask token for assertion: FIDO_ERR_MISSING_PARAMETER
This can be set even when the token doesn't support UV (as in, fingerprint
reader), in which case using the PIN is required.
Check if 'alwaysUv' is set, and if not already configured, force
either UV (if available) or PIN when enrolling.
(cherry picked from commit 9a34971421)
This commit is contained in:
@@ -158,13 +158,14 @@ static int verify_features(
|
||||
bool *ret_has_rk,
|
||||
bool *ret_has_client_pin,
|
||||
bool *ret_has_up,
|
||||
bool *ret_has_uv) {
|
||||
bool *ret_has_uv,
|
||||
bool *ret_has_always_uv) {
|
||||
|
||||
_cleanup_(fido_cbor_info_free_wrapper) fido_cbor_info_t *di = NULL;
|
||||
bool found_extension = false;
|
||||
char **e, **o;
|
||||
const bool *b;
|
||||
bool has_rk = false, has_client_pin = false, has_up = true, has_uv = false; /* Defaults are per table in 5.4 in FIDO2 spec */
|
||||
bool has_rk = false, has_client_pin = false, has_up = true, has_uv = false, has_always_uv = false; /* Defaults are per table in 5.4 in FIDO2 spec */
|
||||
size_t n;
|
||||
int r;
|
||||
|
||||
@@ -205,6 +206,8 @@ static int verify_features(
|
||||
has_up = b[i];
|
||||
if (streq(o[i], "uv"))
|
||||
has_uv = b[i];
|
||||
if (streq(o[i], "alwaysUv"))
|
||||
has_always_uv = b[i];
|
||||
}
|
||||
|
||||
if (!found_extension)
|
||||
@@ -215,11 +218,13 @@ static int verify_features(
|
||||
log_debug("Has rk ('Resident Key') support: %s\n"
|
||||
"Has clientPin support: %s\n"
|
||||
"Has up ('User Presence') support: %s\n"
|
||||
"Has uv ('User Verification') support: %s\n",
|
||||
"Has uv ('User Verification') support: %s\n"
|
||||
"Has alwaysUv ('User Verification' required): %s\n",
|
||||
yes_no(has_rk),
|
||||
yes_no(has_client_pin),
|
||||
yes_no(has_up),
|
||||
yes_no(has_uv));
|
||||
yes_no(has_uv),
|
||||
yes_no(has_always_uv));
|
||||
|
||||
if (ret_has_rk)
|
||||
*ret_has_rk = has_rk;
|
||||
@@ -229,6 +234,8 @@ static int verify_features(
|
||||
*ret_has_up = has_up;
|
||||
if (ret_has_uv)
|
||||
*ret_has_uv = has_uv;
|
||||
if (ret_has_always_uv)
|
||||
*ret_has_always_uv = has_always_uv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -320,7 +327,7 @@ static int fido2_is_cred_in_specific_token(
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||
"Failed to open FIDO2 device %s: %s", path, sym_fido_strerr(r));
|
||||
|
||||
r = verify_features(d, path, LOG_ERR, NULL, NULL, &has_up, &has_uv);
|
||||
r = verify_features(d, path, LOG_ERR, /* ret_has_rk= */ NULL, /* ret_has_client_pin= */ NULL, &has_up, &has_uv, /* ret_has_always_uv= */ NULL);
|
||||
if (r == -ENODEV) { /* Not a FIDO2 device or lacking HMAC-SECRET extension */
|
||||
log_debug_errno(r, "%s is not a FIDO2 device, or it lacks the hmac-secret extension", path);
|
||||
return false;
|
||||
@@ -432,7 +439,7 @@ static int fido2_use_hmac_hash_specific_token(
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||
"Failed to open FIDO2 device %s: %s", path, sym_fido_strerr(r));
|
||||
|
||||
r = verify_features(d, path, LOG_ERR, NULL, &has_client_pin, &has_up, &has_uv);
|
||||
r = verify_features(d, path, LOG_ERR, /* ret_has_rk= */ NULL, &has_client_pin, &has_up, &has_uv, /* ret_has_always_uv= */ NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -745,7 +752,7 @@ int fido2_generate_hmac_hash(
|
||||
_cleanup_(fido_cred_free_wrapper) fido_cred_t *c = NULL;
|
||||
_cleanup_(fido_dev_free_wrapper) fido_dev_t *d = NULL;
|
||||
_cleanup_(erase_and_freep) char *used_pin = NULL;
|
||||
bool has_rk, has_client_pin, has_up, has_uv;
|
||||
bool has_rk, has_client_pin, has_up, has_uv, has_always_uv;
|
||||
_cleanup_free_ char *cid_copy = NULL;
|
||||
size_t cid_size, secret_size;
|
||||
const void *cid, *secret;
|
||||
@@ -787,7 +794,7 @@ int fido2_generate_hmac_hash(
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||
"Failed to open FIDO2 device %s: %s", device, sym_fido_strerr(r));
|
||||
|
||||
r = verify_features(d, device, LOG_ERR, &has_rk, &has_client_pin, &has_up, &has_uv);
|
||||
r = verify_features(d, device, LOG_ERR, &has_rk, &has_client_pin, &has_up, &has_uv, &has_always_uv);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -807,6 +814,19 @@ int fido2_generate_hmac_hash(
|
||||
lock_with &= ~FIDO2ENROLL_UV;
|
||||
}
|
||||
|
||||
if (has_always_uv && !(FLAGS_SET(lock_with, FIDO2ENROLL_PIN) || FLAGS_SET(lock_with, FIDO2ENROLL_UV))) {
|
||||
if (has_uv) {
|
||||
log_notice("FIDO2 device %s enforces 'always user verification', forcing user verification.", device);
|
||||
lock_with |= FIDO2ENROLL_UV;
|
||||
} else if (has_client_pin) {
|
||||
log_notice("FIDO2 device %s enforces 'always user verification', but doesn't support user verification, forcing PIN.", device);
|
||||
lock_with |= FIDO2ENROLL_PIN;
|
||||
} else
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"FIDO2 device %s enforces 'always user verification', but doesn't support user verification or PIN, cannot proceed.",
|
||||
device);
|
||||
}
|
||||
|
||||
c = sym_fido_cred_new();
|
||||
if (!c)
|
||||
return log_oom();
|
||||
@@ -1124,7 +1144,8 @@ static int check_device_is_fido2_with_hmac_secret(
|
||||
bool *ret_has_rk,
|
||||
bool *ret_has_client_pin,
|
||||
bool *ret_has_up,
|
||||
bool *ret_has_uv) {
|
||||
bool *ret_has_uv,
|
||||
bool *ret_has_always_uv) {
|
||||
|
||||
_cleanup_(fido_dev_free_wrapper) fido_dev_t *d = NULL;
|
||||
int r;
|
||||
@@ -1138,9 +1159,9 @@ static int check_device_is_fido2_with_hmac_secret(
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||
"Failed to open FIDO2 device %s: %s", path, sym_fido_strerr(r));
|
||||
|
||||
r = verify_features(d, path, LOG_DEBUG, ret_has_rk, ret_has_client_pin, ret_has_up, ret_has_uv);
|
||||
r = verify_features(d, path, LOG_DEBUG, ret_has_rk, ret_has_client_pin, ret_has_up, ret_has_uv, ret_has_always_uv);
|
||||
if (r == -ENODEV) { /* Not a FIDO2 device, or not implementing 'hmac-secret' */
|
||||
*ret_has_rk = *ret_has_client_pin = *ret_has_up = *ret_has_uv = false;
|
||||
*ret_has_rk = *ret_has_client_pin = *ret_has_up = *ret_has_uv = *ret_has_always_uv = false;
|
||||
return false;
|
||||
}
|
||||
if (r < 0)
|
||||
@@ -1178,7 +1199,7 @@ int fido2_list_devices(void) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
t = table_new("path", "manufacturer", "product", "compatible", "rk", "clientpin", "up", "uv");
|
||||
t = table_new("path", "manufacturer", "product", "compatible", "rk", "clientpin", "up", "uv", "alwaysuv");
|
||||
if (!t) {
|
||||
r = log_oom();
|
||||
goto finish;
|
||||
@@ -1186,7 +1207,7 @@ int fido2_list_devices(void) {
|
||||
|
||||
for (size_t i = 0; i < found; i++) {
|
||||
const fido_dev_info_t *entry;
|
||||
bool has_rk, has_client_pin, has_up, has_uv;
|
||||
bool has_rk, has_client_pin, has_up, has_uv, has_always_uv;
|
||||
|
||||
entry = sym_fido_dev_info_ptr(di, i);
|
||||
if (!entry) {
|
||||
@@ -1195,7 +1216,7 @@ int fido2_list_devices(void) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = check_device_is_fido2_with_hmac_secret(sym_fido_dev_info_path(entry), &has_rk, &has_client_pin, &has_up, &has_uv);
|
||||
r = check_device_is_fido2_with_hmac_secret(sym_fido_dev_info_path(entry), &has_rk, &has_client_pin, &has_up, &has_uv, &has_always_uv);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
bool compatible = r > 0;
|
||||
@@ -1209,7 +1230,8 @@ int fido2_list_devices(void) {
|
||||
TABLE_BOOLEAN_CHECKMARK, has_rk,
|
||||
TABLE_BOOLEAN_CHECKMARK, has_client_pin,
|
||||
TABLE_BOOLEAN_CHECKMARK, has_up,
|
||||
TABLE_BOOLEAN_CHECKMARK, has_uv);
|
||||
TABLE_BOOLEAN_CHECKMARK, has_uv,
|
||||
TABLE_BOOLEAN_CHECKMARK, has_always_uv);
|
||||
if (r < 0) {
|
||||
table_log_add_error(r);
|
||||
goto finish;
|
||||
@@ -1227,7 +1249,8 @@ int fido2_list_devices(void) {
|
||||
"%1$sLegend: RK %2$s Resident key%3$s\n"
|
||||
"%1$s CLIENTPIN %2$s PIN request%3$s\n"
|
||||
"%1$s UP %2$s User presence%3$s\n"
|
||||
"%1$s UV %2$s User verification%3$s\n",
|
||||
"%1$s UV %2$s User verification%3$s\n"
|
||||
"%1$s AlwaysUV %2$s User verification Required%3$s\n",
|
||||
ansi_grey(),
|
||||
glyph(GLYPH_ARROW_RIGHT),
|
||||
ansi_normal());
|
||||
@@ -1287,7 +1310,8 @@ int fido2_find_device_auto(char **ret) {
|
||||
/* ret_has_rk= */ NULL,
|
||||
/* ret_has_client_pin= */ NULL,
|
||||
/* ret_has_up= */ NULL,
|
||||
/* ret_has_uv= */ NULL);
|
||||
/* ret_has_uv= */ NULL,
|
||||
/* ret_has_always_uv= */ NULL);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
if (!r) {
|
||||
|
||||
Reference in New Issue
Block a user