diff options
-rw-r--r-- | man/systemd-cryptenroll.xml | 9 | ||||
-rw-r--r-- | src/cryptenroll/cryptenroll-fido2.c | 3 | ||||
-rw-r--r-- | src/cryptenroll/cryptenroll.c | 18 | ||||
-rw-r--r-- | src/cryptsetup/cryptsetup-fido2.c | 15 | ||||
-rw-r--r-- | src/cryptsetup/cryptsetup.c | 4 | ||||
-rw-r--r-- | src/home/homectl-fido2.c | 2 | ||||
-rw-r--r-- | src/home/homework-fido2.c | 3 | ||||
-rw-r--r-- | src/shared/libfido2-util.c | 84 | ||||
-rw-r--r-- | src/shared/libfido2-util.h | 2 |
9 files changed, 85 insertions, 55 deletions
diff --git a/man/systemd-cryptenroll.xml b/man/systemd-cryptenroll.xml index e1c5a41aac..5b1b60db64 100644 --- a/man/systemd-cryptenroll.xml +++ b/man/systemd-cryptenroll.xml @@ -133,6 +133,15 @@ </varlistentry> <varlistentry> + <term><option>--fido2-with-user-presence=</option><replaceable>BOOL</replaceable></term> + + <listitem><para>When enrolling a FIDO2 security token, controls whether to require the user to + verify presence (tap the token, the FIDO2 <literal>up</literal> feature) when unlocking the volume. + Defaults to <literal>yes</literal>. + </para></listitem> + </varlistentry> + + <varlistentry> <term><option>--tpm2-device=</option><replaceable>PATH</replaceable></term> <listitem><para>Enroll a TPM2 security chip. Expects a device node path referring to the TPM2 chip diff --git a/src/cryptenroll/cryptenroll-fido2.c b/src/cryptenroll/cryptenroll-fido2.c index 213b7795b6..eab8f220e4 100644 --- a/src/cryptenroll/cryptenroll-fido2.c +++ b/src/cryptenroll/cryptenroll-fido2.c @@ -78,7 +78,8 @@ int enroll_fido2( JSON_BUILD_PAIR("fido2-credential", JSON_BUILD_BASE64(cid, cid_size)), 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-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))))); 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 ef6031cb1f..5eca69f851 100644 --- a/src/cryptenroll/cryptenroll.c +++ b/src/cryptenroll/cryptenroll.c @@ -36,7 +36,7 @@ static int *arg_wipe_slots = NULL; static size_t arg_n_wipe_slots = 0; static WipeScope arg_wipe_slots_scope = WIPE_EXPLICIT; static unsigned arg_wipe_slots_mask = 0; /* Bitmask of (1U << EnrollType), for wiping all slots of specific types */ -static Fido2EnrollFlags arg_fido2_lock_with = FIDO2ENROLL_PIN; +static Fido2EnrollFlags arg_fido2_lock_with = FIDO2ENROLL_PIN | FIDO2ENROLL_UP; assert_cc(sizeof(arg_wipe_slots_mask) * 8 >= _ENROLL_TYPE_MAX); @@ -91,6 +91,8 @@ static int help(void) { " Enroll a FIDO2-HMAC security token\n" " --fido2-with-client-pin=BOOL\n" " 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" " --tpm2-device=PATH\n" " Enroll a TPM2 device\n" " --tpm2-pcrs=PCR1,PCR2,PCR3,…\n" @@ -118,6 +120,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_TPM2_PCRS, ARG_WIPE_SLOT, ARG_FIDO2_WITH_PIN, + ARG_FIDO2_WITH_UP, }; static const struct option options[] = { @@ -128,6 +131,7 @@ static int parse_argv(int argc, char *argv[]) { { "pkcs11-token-uri", required_argument, NULL, ARG_PKCS11_TOKEN_URI }, { "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 }, { "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE }, { "tpm2-pcrs", required_argument, NULL, ARG_TPM2_PCRS }, { "wipe-slot", required_argument, NULL, ARG_WIPE_SLOT }, @@ -161,6 +165,18 @@ static int parse_argv(int argc, char *argv[]) { break; } + case ARG_FIDO2_WITH_UP: { + bool lock_with_up; + + r = parse_boolean_argument("--fido2-with-user-presence=", optarg, &lock_with_up); + if (r < 0) + return r; + + SET_FLAG(arg_fido2_lock_with, FIDO2ENROLL_UP, lock_with_up); + + 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 339e245575..9a3af2d8ff 100644 --- a/src/cryptsetup/cryptsetup-fido2.c +++ b/src/cryptsetup/cryptsetup-fido2.c @@ -81,7 +81,6 @@ int acquire_fido2_key( salt, salt_size, cid, cid_size, pins, - /* up= */ true, required, ret_decrypted_key, ret_decrypted_key_size); @@ -118,7 +117,8 @@ int find_fido2_auto_data( size_t cid_size = 0, salt_size = 0; _cleanup_free_ char *rp = NULL; int r, keyslot = -1; - Fido2EnrollFlags required = FIDO2ENROLL_PIN; /* For backward compatibility, require pin by default */ + /* For backward compatibility, require pin and presence by default */ + Fido2EnrollFlags required = FIDO2ENROLL_PIN | FIDO2ENROLL_UP; assert(cd); assert(ret_salt); @@ -194,6 +194,17 @@ int find_fido2_auto_data( SET_FLAG(required, FIDO2ENROLL_PIN, json_variant_boolean(w)); } + + w = json_variant_by_key(v, "fido2-up-required"); + if (w) { + /* The "fido2-up-required" field is optional. */ + + if (!json_variant_is_boolean(w)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "FIDO2 token data's 'fido2-up-required' field is not a boolean."); + + SET_FLAG(required, FIDO2ENROLL_UP, json_variant_boolean(w)); + } } if (!cid) diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index 9ab42eacb9..d47e758cd7 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -769,9 +769,9 @@ static int attach_luks_or_plain_or_bitlk_by_fido2( if (r < 0) return r; - if (FLAGS_SET(required, FIDO2ENROLL_PIN) && arg_headless) + if (FLAGS_SET(required, FIDO2ENROLL_PIN | FIDO2ENROLL_UP) && arg_headless) return log_error_errno(SYNTHETIC_ERRNO(ENOPKG), - "A PIN is required to unlock this volume, but the 'headless' parameter was set."); + "Local verification is required to unlock this volume, but the 'headless' parameter was set."); rp_id = discovered_rp_id; key_data = discovered_salt; diff --git a/src/home/homectl-fido2.c b/src/home/homectl-fido2.c index 76775ee6bd..a2054fcf73 100644 --- a/src/home/homectl-fido2.c +++ b/src/home/homectl-fido2.c @@ -158,7 +158,7 @@ int identity_add_fido2_parameters( /* user_display_name= */ rn ? json_variant_string(rn) : NULL, /* user_icon_name= */ NULL, /* askpw_icon_name= */ "user-home", - FIDO2ENROLL_PIN, // FIXME: add a --lock-with-pin parameter like cryptenroll + FIDO2ENROLL_PIN | FIDO2ENROLL_UP, // FIXME: add a --lock-with-pin/up parameter like cryptenroll &cid, &cid_size, &salt, &salt_size, &secret, &secret_size, diff --git a/src/home/homework-fido2.c b/src/home/homework-fido2.c index 818f2a5d16..8811c00550 100644 --- a/src/home/homework-fido2.c +++ b/src/home/homework-fido2.c @@ -28,8 +28,7 @@ int fido2_use_token( salt->salt, salt->salt_size, salt->credential.id, salt->credential.size, secret->token_pin, - h->fido2_user_presence_permitted > 0, - FIDO2ENROLL_PIN, // FIXME: add a --lock-with-pin parameter like cryptenroll + FIDO2ENROLL_PIN | (h->fido2_user_presence_permitted > 0 ? FIDO2ENROLL_UP : 0), // FIXME: add a --lock-with-pin parameter like cryptenroll &hmac, &hmac_size); if (r < 0) diff --git a/src/shared/libfido2-util.c b/src/shared/libfido2-util.c index 66a312bfb6..50e1efb2dc 100644 --- a/src/shared/libfido2-util.c +++ b/src/shared/libfido2-util.c @@ -218,8 +218,7 @@ static int fido2_use_hmac_hash_specific_token( const void *cid, size_t cid_size, char **pins, - bool up, /* user presence permitted */ - Fido2EnrollFlags required, /* client pin required */ + Fido2EnrollFlags required, /* client pin/user presence required */ void **ret_hmac, size_t *ret_hmac_size) { @@ -256,6 +255,11 @@ static int fido2_use_hmac_hash_specific_token( "PIN required to unlock, but FIDO2 device %s does not support it.", path); + if (!has_up && FLAGS_SET(required, FIDO2ENROLL_UP)) + return log_error_errno(SYNTHETIC_ERRNO(EHWPOISON), + "User presence test required to unlock, but FIDO2 device %s does not support it.", + path); + a = sym_fido_assert_new(); if (!a) return log_oom(); @@ -285,30 +289,20 @@ static int fido2_use_hmac_hash_specific_token( return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to add FIDO2 assertion credential ID: %s", sym_fido_strerr(r)); - if (has_up) { - r = sym_fido_assert_set_up(a, FIDO_OPT_FALSE); - if (r != FIDO_OK) - return log_error_errno(SYNTHETIC_ERRNO(EIO), - "Failed to set FIDO2 assertion user presence: %s", sym_fido_strerr(r)); - } - log_info("Asking FIDO2 token for authentication."); - r = sym_fido_dev_get_assert(d, a, NULL); /* try without pin and without up first */ - if (r == FIDO_ERR_UP_REQUIRED && up) { - - if (!has_up) - log_warning("Weird, device asked for User Presence check, but does not advertise it as feature. Ignoring."); - - r = sym_fido_assert_set_up(a, FIDO_OPT_TRUE); + if (has_up) { + r = sym_fido_assert_set_up(a, FLAGS_SET(required, FIDO2ENROLL_UP) ? FIDO_OPT_TRUE : FIDO_OPT_FALSE); if (r != FIDO_OK) return log_error_errno(SYNTHETIC_ERRNO(EIO), - "Failed to set FIDO2 assertion user presence: %s", sym_fido_strerr(r)); - - log_info("Security token requires user presence."); + "Failed to %s FIDO2 user presence test: %s", + enable_disable(FLAGS_SET(required, FIDO2ENROLL_UP)), + sym_fido_strerr(r)); - r = sym_fido_dev_get_assert(d, a, NULL); /* try without pin but with up now */ + if (FLAGS_SET(required, FIDO2ENROLL_UP)) + log_info("User presence required to unlock."); } + if (FLAGS_SET(required, FIDO2ENROLL_PIN)) { char **i; @@ -321,7 +315,8 @@ static int fido2_use_hmac_hash_specific_token( if (r != FIDO_ERR_PIN_INVALID) break; } - } + } else + r = sym_fido_dev_get_assert(d, a, NULL); switch (r) { case FIDO_OK: @@ -372,8 +367,7 @@ int fido2_use_hmac_hash( const void *cid, size_t cid_size, char **pins, - bool up, /* user presence permitted */ - Fido2EnrollFlags required, /* client pin required */ + Fido2EnrollFlags required, /* client pin/user presence required */ void **ret_hmac, size_t *ret_hmac_size) { @@ -386,7 +380,7 @@ int fido2_use_hmac_hash( return log_error_errno(r, "FIDO2 support is not installed."); if (device) - return fido2_use_hmac_hash_specific_token(device, rp_id, salt, salt_size, cid, cid_size, pins, up, required, ret_hmac, ret_hmac_size); + return fido2_use_hmac_hash_specific_token(device, rp_id, salt, salt_size, cid, cid_size, pins, required, ret_hmac, ret_hmac_size); di = sym_fido_dev_info_new(allocated); if (!di) @@ -421,7 +415,7 @@ int fido2_use_hmac_hash( goto finish; } - r = fido2_use_hmac_hash_specific_token(path, rp_id, salt, salt_size, cid, cid_size, pins, up, required, ret_hmac, ret_hmac_size); + r = fido2_use_hmac_hash_specific_token(path, rp_id, salt, salt_size, cid, cid_size, pins, required, ret_hmac, ret_hmac_size); if (!IN_SET(r, -EBADSLT, /* device doesn't understand our credential hash */ -ENODEV /* device is not a FIDO2 device with HMAC-SECRET */)) @@ -516,6 +510,11 @@ int fido2_generate_hmac_hash( "Requested to lock with PIN, but FIDO2 device %s does not support it.", device); + if (!has_up && FLAGS_SET(lock_with, FIDO2ENROLL_UP)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Locking with user presence test requested, but FIDO2 device %s does not support it.", + device); + c = sym_fido_cred_new(); if (!c) return log_oom(); @@ -652,32 +651,27 @@ int fido2_generate_hmac_hash( return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to add FIDO2 assertion credential ID: %s", sym_fido_strerr(r)); + log_info("Generating secret key on FIDO2 security token."); + if (has_up) { - r = sym_fido_assert_set_up(a, FIDO_OPT_FALSE); + r = sym_fido_assert_set_up(a, FLAGS_SET(lock_with, FIDO2ENROLL_UP) ? FIDO_OPT_TRUE : FIDO_OPT_FALSE); if (r != FIDO_OK) return log_error_errno(SYNTHETIC_ERRNO(EIO), - "Failed to turn off FIDO2 assertion user presence: %s", sym_fido_strerr(r)); + "Failed to %s FIDO2 user presence test: %s", + enable_disable(FLAGS_SET(lock_with, FIDO2ENROLL_UP)), + sym_fido_strerr(r)); + + if (FLAGS_SET(lock_with, FIDO2ENROLL_UP)) + log_notice("%s%sIn order to allow secret key generation, please confirm presence on security token.", + emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "", + emoji_enabled() ? " " : ""); } - log_info("Generating secret key on FIDO2 security token."); - r = sym_fido_dev_get_assert(d, a, FLAGS_SET(lock_with, FIDO2ENROLL_PIN) ? used_pin : NULL); - if (r == FIDO_ERR_UP_REQUIRED) { - - if (!has_up) - log_warning("Weird, device asked for User Presence check, but does not advertise it as feature. Ignoring."); - - r = sym_fido_assert_set_up(a, FIDO_OPT_TRUE); - if (r != FIDO_OK) - return log_error_errno(SYNTHETIC_ERRNO(EIO), - "Failed to turn on FIDO2 assertion user presence: %s", sym_fido_strerr(r)); - - log_notice("%s%sIn order to allow secret key generation, please verify presence 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), + "Locking without user presence test requested, but FIDO2 device %s requires it.", + device); if (r == FIDO_ERR_ACTION_TIMEOUT) return log_error_errno(SYNTHETIC_ERRNO(ENOSTR), "Token action timeout. (User didn't interact with token quickly enough.)"); diff --git a/src/shared/libfido2-util.h b/src/shared/libfido2-util.h index c22deebfcc..9eddf5ca78 100644 --- a/src/shared/libfido2-util.h +++ b/src/shared/libfido2-util.h @@ -5,6 +5,7 @@ typedef enum Fido2EnrollFlags { FIDO2ENROLL_PIN = 1 << 0, + FIDO2ENROLL_UP = 1 << 1, /* User presence (ie: touching token) */ _FIDO2ENROLL_TYPE_MAX, _FIDO2ENROLL_TYPE_INVALID = -EINVAL, } Fido2EnrollFlags; @@ -86,7 +87,6 @@ int fido2_use_hmac_hash( const void *cid, size_t cid_size, char **pins, - bool up, /* user presence permitted */ Fido2EnrollFlags required, void **ret_hmac, size_t *ret_hmac_size); |