mirror of
https://github.com/systemd/systemd.git
synced 2024-12-22 17:35:35 +03:00
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:
parent
06f087192d
commit
896cc0da98
@ -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>
|
||||
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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)
|
||||
|
@ -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.");
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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 **);
|
||||
|
Loading…
Reference in New Issue
Block a user