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.
This commit is contained in:
Luca Boccassi 2021-04-13 13:12:46 +01:00 committed by Luca Boccassi
parent 06f087192d
commit 896cc0da98
7 changed files with 80 additions and 4 deletions

View file

@ -141,6 +141,14 @@
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--fido2-with-user-verification=</option><replaceable>BOOL</replaceable></term>
<listitem><para>When enrolling a FIDO2 security token, controls whether to require user verification
when unlocking the volume (the FIDO2 <literal>uv</literal> feature)). Defaults to <literal>no</literal>.
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--tpm2-device=</option><replaceable>PATH</replaceable></term>

View file

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

View file

@ -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),

View file

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

View file

@ -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.");

View file

@ -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),

View file

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