From: Luca Boccassi Date: Tue, 13 Apr 2021 12:12:46 +0000 (+0100) Subject: FIDO2: ask and record whether user verification was used to lock the volume X-Git-Tag: v249-rc1~264^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=896cc0da986f85980c4377d3f7073ce1f1cae778;p=thirdparty%2Fsystemd.git FIDO2: ask and record whether user verification was used to lock the volume Some tokens support authorization via fingerprint or other biometric ID. Add support for "user verification" to cryptenroll and cryptsetup. Disable by default, as it is still quite uncommon. --- diff --git a/man/systemd-cryptenroll.xml b/man/systemd-cryptenroll.xml index 5b1b60db645..c7f4e63f600 100644 --- a/man/systemd-cryptenroll.xml +++ b/man/systemd-cryptenroll.xml @@ -141,6 +141,14 @@ + + BOOL + + When enrolling a FIDO2 security token, controls whether to require user verification + when unlocking the volume (the FIDO2 uv feature)). Defaults to no. + + + PATH diff --git a/src/cryptenroll/cryptenroll-fido2.c b/src/cryptenroll/cryptenroll-fido2.c index eab8f220e4d..3ba7866738e 100644 --- a/src/cryptenroll/cryptenroll-fido2.c +++ b/src/cryptenroll/cryptenroll-fido2.c @@ -79,7 +79,8 @@ int enroll_fido2( JSON_BUILD_PAIR("fido2-salt", JSON_BUILD_BASE64(salt, salt_size)), JSON_BUILD_PAIR("fido2-rp", JSON_BUILD_STRING("io.systemd.cryptsetup")), JSON_BUILD_PAIR("fido2-clientPin-required", JSON_BUILD_BOOLEAN(FLAGS_SET(lock_with, FIDO2ENROLL_PIN))), - JSON_BUILD_PAIR("fido2-up-required", JSON_BUILD_BOOLEAN(FLAGS_SET(lock_with, FIDO2ENROLL_UP))))); + JSON_BUILD_PAIR("fido2-up-required", JSON_BUILD_BOOLEAN(FLAGS_SET(lock_with, FIDO2ENROLL_UP))), + JSON_BUILD_PAIR("fido2-uv-required", JSON_BUILD_BOOLEAN(FLAGS_SET(lock_with, FIDO2ENROLL_UV))))); if (r < 0) return log_error_errno(r, "Failed to prepare PKCS#11 JSON token object: %m"); diff --git a/src/cryptenroll/cryptenroll.c b/src/cryptenroll/cryptenroll.c index 5eca69f8516..559a3468043 100644 --- a/src/cryptenroll/cryptenroll.c +++ b/src/cryptenroll/cryptenroll.c @@ -93,6 +93,8 @@ static int help(void) { " Whether to require entering a PIN to unlock the volume\n" " --fido2-with-user-presence=BOOL\n" " Whether to require user presence to unlock the volume\n" + " --fido2-with-user-verification=BOOL\n" + " Whether to require user verification to unlock the volume\n" " --tpm2-device=PATH\n" " Enroll a TPM2 device\n" " --tpm2-pcrs=PCR1,PCR2,PCR3,…\n" @@ -121,6 +123,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_WIPE_SLOT, ARG_FIDO2_WITH_PIN, ARG_FIDO2_WITH_UP, + ARG_FIDO2_WITH_UV, }; static const struct option options[] = { @@ -132,6 +135,7 @@ static int parse_argv(int argc, char *argv[]) { { "fido2-device", required_argument, NULL, ARG_FIDO2_DEVICE }, { "fido2-with-client-pin", required_argument, NULL, ARG_FIDO2_WITH_PIN }, { "fido2-with-user-presence", required_argument, NULL, ARG_FIDO2_WITH_UP }, + { "fido2-with-user-verification", required_argument, NULL, ARG_FIDO2_WITH_UV }, { "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE }, { "tpm2-pcrs", required_argument, NULL, ARG_TPM2_PCRS }, { "wipe-slot", required_argument, NULL, ARG_WIPE_SLOT }, @@ -177,6 +181,18 @@ static int parse_argv(int argc, char *argv[]) { break; } + case ARG_FIDO2_WITH_UV: { + bool lock_with_uv; + + r = parse_boolean_argument("--fido2-with-user-verification=", optarg, &lock_with_uv); + if (r < 0) + return r; + + SET_FLAG(arg_fido2_lock_with, FIDO2ENROLL_UV, lock_with_uv); + + break; + } + case ARG_PASSWORD: if (arg_enroll_type >= 0) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), diff --git a/src/cryptsetup/cryptsetup-fido2.c b/src/cryptsetup/cryptsetup-fido2.c index 9a3af2d8ff3..b21f970db77 100644 --- a/src/cryptsetup/cryptsetup-fido2.c +++ b/src/cryptsetup/cryptsetup-fido2.c @@ -205,6 +205,17 @@ int find_fido2_auto_data( SET_FLAG(required, FIDO2ENROLL_UP, json_variant_boolean(w)); } + + w = json_variant_by_key(v, "fido2-uv-required"); + if (w) { + /* The "fido2-uv-required" field is optional. */ + + if (!json_variant_is_boolean(w)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "FIDO2 token data's 'fido2-uv-required' field is not a boolean."); + + SET_FLAG(required, FIDO2ENROLL_UV, json_variant_boolean(w)); + } } if (!cid) diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index d47e758cd7f..e8e5b6dbfc2 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -769,7 +769,7 @@ static int attach_luks_or_plain_or_bitlk_by_fido2( if (r < 0) return r; - if (FLAGS_SET(required, FIDO2ENROLL_PIN | FIDO2ENROLL_UP) && arg_headless) + if (FLAGS_SET(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."); diff --git a/src/shared/libfido2-util.c b/src/shared/libfido2-util.c index 50e1efb2dce..ec69793f7cf 100644 --- a/src/shared/libfido2-util.c +++ b/src/shared/libfido2-util.c @@ -25,6 +25,7 @@ int (*sym_fido_assert_set_extensions)(fido_assert_t *, int) = NULL; int (*sym_fido_assert_set_hmac_salt)(fido_assert_t *, const unsigned char *, size_t) = NULL; int (*sym_fido_assert_set_rp)(fido_assert_t *, const char *) = NULL; int (*sym_fido_assert_set_up)(fido_assert_t *, fido_opt_t) = NULL; +int (*sym_fido_assert_set_uv)(fido_assert_t *, fido_opt_t) = NULL; size_t (*sym_fido_cbor_info_extensions_len)(const fido_cbor_info_t *) = NULL; char **(*sym_fido_cbor_info_extensions_ptr)(const fido_cbor_info_t *) = NULL; void (*sym_fido_cbor_info_free)(fido_cbor_info_t **) = NULL; @@ -84,6 +85,7 @@ int dlopen_libfido2(void) { DLSYM_ARG(fido_assert_set_hmac_salt), DLSYM_ARG(fido_assert_set_rp), DLSYM_ARG(fido_assert_set_up), + DLSYM_ARG(fido_assert_set_uv), DLSYM_ARG(fido_cbor_info_extensions_len), DLSYM_ARG(fido_cbor_info_extensions_ptr), DLSYM_ARG(fido_cbor_info_free), @@ -225,7 +227,7 @@ static int fido2_use_hmac_hash_specific_token( _cleanup_(fido_assert_free_wrapper) fido_assert_t *a = NULL; _cleanup_(fido_dev_free_wrapper) fido_dev_t *d = NULL; _cleanup_(erase_and_freep) void *hmac_copy = NULL; - bool has_up, has_client_pin; + bool has_up, has_client_pin, has_uv; size_t hmac_size; const void *hmac; int r; @@ -246,7 +248,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, NULL); + r = verify_features(d, path, LOG_ERR, NULL, &has_client_pin, &has_up, &has_uv); if (r < 0) return r; @@ -260,6 +262,11 @@ static int fido2_use_hmac_hash_specific_token( "User presence test required to unlock, but FIDO2 device %s does not support it.", path); + if (!has_uv && FLAGS_SET(required, FIDO2ENROLL_UV)) + return log_error_errno(SYNTHETIC_ERRNO(EHWPOISON), + "User verification required to unlock, but FIDO2 device %s does not support it.", + path); + a = sym_fido_assert_new(); if (!a) return log_oom(); @@ -303,6 +310,18 @@ static int fido2_use_hmac_hash_specific_token( log_info("User presence required to unlock."); } + if (has_uv) { + 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), + "Failed to %s FIDO2 user verification: %s", + enable_disable(FLAGS_SET(required, FIDO2ENROLL_UV)), + sym_fido_strerr(r)); + + if (FLAGS_SET(required, FIDO2ENROLL_UV)) + log_info("User verification required to unlock."); + } + if (FLAGS_SET(required, FIDO2ENROLL_PIN)) { char **i; @@ -515,6 +534,11 @@ int fido2_generate_hmac_hash( "Locking with user presence test requested, but FIDO2 device %s does not support it.", device); + if (!has_uv && FLAGS_SET(lock_with, FIDO2ENROLL_UV)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Locking with user verification requested, but FIDO2 device %s does not support it.", + device); + c = sym_fido_cred_new(); if (!c) return log_oom(); @@ -667,6 +691,20 @@ int fido2_generate_hmac_hash( emoji_enabled() ? " " : ""); } + if (has_uv) { + r = sym_fido_assert_set_uv(a, FLAGS_SET(lock_with, FIDO2ENROLL_UV) ? FIDO_OPT_TRUE : FIDO_OPT_FALSE); + if (r != FIDO_OK) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to %s FIDO user verification: %s", + enable_disable(FLAGS_SET(lock_with, FIDO2ENROLL_UV)), + sym_fido_strerr(r)); + + if (FLAGS_SET(lock_with, FIDO2ENROLL_UV)) + log_notice("%s%sIn order to allow secret key generation, please verify user on security token.", + emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "", + emoji_enabled() ? " " : ""); + } + r = sym_fido_dev_get_assert(d, a, FLAGS_SET(lock_with, FIDO2ENROLL_PIN) ? used_pin : NULL); if (r == FIDO_ERR_UP_REQUIRED && !FLAGS_SET(lock_with, FIDO2ENROLL_UP)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), diff --git a/src/shared/libfido2-util.h b/src/shared/libfido2-util.h index 9eddf5ca78e..1b31577e06c 100644 --- a/src/shared/libfido2-util.h +++ b/src/shared/libfido2-util.h @@ -6,6 +6,7 @@ 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_TYPE_MAX, _FIDO2ENROLL_TYPE_INVALID = -EINVAL, } Fido2EnrollFlags; @@ -23,6 +24,7 @@ extern int (*sym_fido_assert_set_extensions)(fido_assert_t *, int); extern int (*sym_fido_assert_set_hmac_salt)(fido_assert_t *, const unsigned char *, size_t); extern int (*sym_fido_assert_set_rp)(fido_assert_t *, const char *); extern int (*sym_fido_assert_set_up)(fido_assert_t *, fido_opt_t); +extern int (*sym_fido_assert_set_uv)(fido_assert_t *, fido_opt_t); extern size_t (*sym_fido_cbor_info_extensions_len)(const fido_cbor_info_t *); extern char **(*sym_fido_cbor_info_extensions_ptr)(const fido_cbor_info_t *); extern void (*sym_fido_cbor_info_free)(fido_cbor_info_t **);