]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
FIDO2: ask and record whether user verification was used to lock the volume
authorLuca Boccassi <luca.boccassi@microsoft.com>
Tue, 13 Apr 2021 12:12:46 +0000 (13:12 +0100)
committerLuca Boccassi <bluca@debian.org>
Fri, 7 May 2021 20:36:27 +0000 (21:36 +0100)
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.

man/systemd-cryptenroll.xml
src/cryptenroll/cryptenroll-fido2.c
src/cryptenroll/cryptenroll.c
src/cryptsetup/cryptsetup-fido2.c
src/cryptsetup/cryptsetup.c
src/shared/libfido2-util.c
src/shared/libfido2-util.h

index 5b1b60db645f594c98150f0bdcdcb65ff2508464..c7f4e63f600eb33ab46a3a757db6cf8a44d701b8 100644 (file)
         </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>
 
index eab8f220e4deaeee6474076353714c9c8da96fa1..3ba7866738efcdcbe797d51afe10c1ca5242c5e4 100644 (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");
 
index 5eca69f8516a77d47ae9b9c3aafbb62a62d5994a..559a3468043117c46f0c40f1f4d014d0a10a9906 100644 (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),
index 9a3af2d8ff3987039c0f88a4b8ccb22e8a8cfe86..b21f970db7753f1bc5433cf4165e62aa1d5a6c20 100644 (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)
index d47e758cd7f86e7cda7795700c457174afdd2261..e8e5b6dbfc2163362ba1ee02c7ec6527eaef2149 100644 (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.");
 
index 50e1efb2dced209a426fc967ca351306fd6a219f..ec69793f7cfe889df4b3aa24cce19f8bb339cdd3 100644 (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),
index 9eddf5ca78e2086b38d9acdb524dc0c45db4ff92..1b31577e06c1ba268cc3eac2df3de7525c99e26c 100644 (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 **);