diff --git a/src/cryptsetup/cryptsetup-fido2.c b/src/cryptsetup/cryptsetup-fido2.c index b21f970db7..6e400e44e3 100644 --- a/src/cryptsetup/cryptsetup-fido2.c +++ b/src/cryptsetup/cryptsetup-fido2.c @@ -117,8 +117,7 @@ int find_fido2_auto_data( size_t cid_size = 0, salt_size = 0; _cleanup_free_ char *rp = NULL; int r, keyslot = -1; - /* For backward compatibility, require pin and presence by default */ - Fido2EnrollFlags required = FIDO2ENROLL_PIN | FIDO2ENROLL_UP; + Fido2EnrollFlags required = 0; assert(cd); assert(ret_salt); @@ -193,7 +192,8 @@ int find_fido2_auto_data( "FIDO2 token data's 'fido2-clientPin-required' field is not a boolean."); SET_FLAG(required, FIDO2ENROLL_PIN, json_variant_boolean(w)); - } + } else + required |= FIDO2ENROLL_PIN_IF_NEEDED; /* compat with 248, where the field was unset */ w = json_variant_by_key(v, "fido2-up-required"); if (w) { @@ -204,7 +204,8 @@ int find_fido2_auto_data( "FIDO2 token data's 'fido2-up-required' field is not a boolean."); SET_FLAG(required, FIDO2ENROLL_UP, json_variant_boolean(w)); - } + } else + required |= FIDO2ENROLL_UP_IF_NEEDED; /* compat with 248 */ w = json_variant_by_key(v, "fido2-uv-required"); if (w) { @@ -215,7 +216,8 @@ int find_fido2_auto_data( "FIDO2 token data's 'fido2-uv-required' field is not a boolean."); SET_FLAG(required, FIDO2ENROLL_UV, json_variant_boolean(w)); - } + } else + required |= FIDO2ENROLL_UV_OMIT; /* compat with 248 */ } if (!cid) diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index 74bd071c10..f0826c47e5 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -758,7 +758,11 @@ static int attach_luks_or_plain_or_bitlk_by_fido2( cid = arg_fido2_cid; cid_size = arg_fido2_cid_size; - required = FIDO2ENROLL_PIN | FIDO2ENROLL_UP; /* For backwards compatibility, PIN+presence is required by default. */ + /* For now and for compatibility, if the user explicitly configured FIDO2 support and we do + * not read FIDO2 metadata off the LUKS2 header, default to the systemd 248 logic, where we + * 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 { r = find_fido2_auto_data( cd, diff --git a/src/shared/libfido2-util.c b/src/shared/libfido2-util.c index 3da64b549a..0272d0bde7 100644 --- a/src/shared/libfido2-util.c +++ b/src/shared/libfido2-util.c @@ -310,7 +310,7 @@ static int fido2_use_hmac_hash_specific_token( log_info("User presence required to unlock."); } - if (has_uv) { + if (has_uv && !FLAGS_SET(required, FIDO2ENROLL_UV_OMIT)) { r = sym_fido_assert_set_uv(a, FLAGS_SET(required, FIDO2ENROLL_UV) ? FIDO_OPT_TRUE : FIDO_OPT_FALSE); if (r != FIDO_OK) return log_error_errno(SYNTHETIC_ERRNO(EIO), @@ -322,20 +322,98 @@ static int fido2_use_hmac_hash_specific_token( log_info("User verification required to unlock."); } - if (FLAGS_SET(required, FIDO2ENROLL_PIN)) { - char **i; + for (;;) { + bool retry_with_up = false, retry_with_pin = false; - /* OK, we need a pin, try with all pins in turn */ - if (strv_isempty(pins)) - r = FIDO_ERR_PIN_REQUIRED; - else - STRV_FOREACH(i, pins) { - r = sym_fido_dev_get_assert(d, a, *i); - if (r != FIDO_ERR_PIN_INVALID) - break; + if (FLAGS_SET(required, FIDO2ENROLL_PIN)) { + char **i; + + /* OK, we need a pin, try with all pins in turn */ + if (strv_isempty(pins)) + r = FIDO_ERR_PIN_REQUIRED; + else + STRV_FOREACH(i, pins) { + r = sym_fido_dev_get_assert(d, a, *i); + if (r != FIDO_ERR_PIN_INVALID) + break; + } + + } else + r = sym_fido_dev_get_assert(d, a, NULL); + + /* In some conditions, where a PIN or UP is required we might accept that. Let's check the + * conditions and if so try immediately again. */ + + switch (r) { + + case FIDO_ERR_UP_REQUIRED: + /* So the token asked for "up". Try to turn it on, for compat with systemd 248 and try again. */ + + if (!has_up) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Token asks for user presence check but doesn't advertise 'up' feature."); + + if (FLAGS_SET(required, FIDO2ENROLL_UP)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Token asks for user presence check but was already enabled."); + + if (FLAGS_SET(required, FIDO2ENROLL_UP_IF_NEEDED)) { + log_info("User presence required to unlock."); + retry_with_up = true; } - } else - r = sym_fido_dev_get_assert(d, a, NULL); + + break; + + case FIDO_ERR_UNSUPPORTED_OPTION: + /* AuthenTrend ATKey.Pro returns this instead of FIDO_ERR_UP_REQUIRED, let's handle + * it gracefully (also see below.) */ + + if (has_up && (required & (FIDO2ENROLL_UP|FIDO2ENROLL_UP_IF_NEEDED)) == FIDO2ENROLL_UP_IF_NEEDED) { + log_notice("Got unsupported option error when when user presence test is turned off. Trying with user presence test turned on."); + retry_with_up = true; + } + + break; + + case FIDO_ERR_PIN_REQUIRED: + /* A pin was requested. Maybe supply one, if we are configured to do so on request */ + + if (!has_client_pin) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Token asks for PIN but doesn't advertise 'clientPin' feature."); + + if (FLAGS_SET(required, FIDO2ENROLL_PIN) && !strv_isempty(pins)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Token asks for PIN but one was already supplied."); + + if ((required & (FIDO2ENROLL_PIN|FIDO2ENROLL_PIN_IF_NEEDED)) == FIDO2ENROLL_PIN_IF_NEEDED) { + /* If a PIN so far wasn't specified but is requested by the device, and + * FIDO2ENROLL_PIN_IF_NEEDED is set, then provide it */ + log_debug("Retrying to create credential with PIN."); + retry_with_pin = true; + } + + break; + + default: + break; + } + + if (!retry_with_up && !retry_with_pin) + break; + + if (retry_with_up) { + r = sym_fido_assert_set_up(a, FIDO_OPT_TRUE); + if (r != FIDO_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to enable FIDO2 user presence test: %s", sym_fido_strerr(r)); + + required |= FIDO2ENROLL_UP; + } + + if (retry_with_pin) + required |= FIDO2ENROLL_PIN; + } switch (r) { case FIDO_OK: diff --git a/src/shared/libfido2-util.h b/src/shared/libfido2-util.h index bcf3c73706..5640cca5e3 100644 --- a/src/shared/libfido2-util.h +++ b/src/shared/libfido2-util.h @@ -7,6 +7,9 @@ typedef enum Fido2EnrollFlags { FIDO2ENROLL_PIN = 1 << 0, FIDO2ENROLL_UP = 1 << 1, /* User presence (ie: touching token) */ FIDO2ENROLL_UV = 1 << 2, /* User verification (ie: fingerprint) */ + FIDO2ENROLL_PIN_IF_NEEDED = 1 << 3, /* If auth doesn't work without PIN ask for one, as in systemd 248 */ + FIDO2ENROLL_UP_IF_NEEDED = 1 << 4, /* If auth doesn't work without UP, enable it, as in systemd 248 */ + FIDO2ENROLL_UV_OMIT = 1 << 5, /* Leave "uv" untouched, as in systemd 248 */ _FIDO2ENROLL_TYPE_MAX, _FIDO2ENROLL_TYPE_INVALID = -EINVAL, } Fido2EnrollFlags;