]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
cryptsetup: revert to systemd 248 up/pin/uv FIDO2 settings when we don't have LUKS2...
authorLennart Poettering <lennart@poettering.net>
Thu, 27 May 2021 20:55:39 +0000 (22:55 +0200)
committerLennart Poettering <lennart@poettering.net>
Fri, 28 May 2021 14:36:52 +0000 (16:36 +0200)
Let's improve compatibility with systemd 248 enrollments of FIDO2 keys:
if we have no information about the up/uv/pin settings, let's try to
determine them automatically, i.e. use up and pin if needed.

This only has an effect on LUKS2 volumes where a FIDO2 key was enrolled
with systemd 248 and thus the JSON data lacks the up/uv/pin fields. It
also matters if the user configured FIDO2 parameters explicitly via
crypttab options, so that the JSON data is not used.

For newer enrollments we'll stick to the explicit settings, as that's
generally much safer and robust.

src/cryptsetup/cryptsetup-fido2.c
src/cryptsetup/cryptsetup.c
src/shared/libfido2-util.c
src/shared/libfido2-util.h

index b21f970db7753f1bc5433cf4165e62aa1d5a6c20..6e400e44e371660e6adf5153da11b337a31c9183 100644 (file)
@@ -117,8 +117,7 @@ int find_fido2_auto_data(
         size_t cid_size = 0, salt_size = 0;
         _cleanup_free_ char *rp = NULL;
         int r, keyslot = -1;
-        /* For backward compatibility, require pin and presence by default */
-        Fido2EnrollFlags required = FIDO2ENROLL_PIN | FIDO2ENROLL_UP;
+        Fido2EnrollFlags required = 0;
 
         assert(cd);
         assert(ret_salt);
@@ -193,7 +192,8 @@ int find_fido2_auto_data(
                                                        "FIDO2 token data's 'fido2-clientPin-required' field is not a boolean.");
 
                         SET_FLAG(required, FIDO2ENROLL_PIN, json_variant_boolean(w));
-                }
+                } else
+                        required |= FIDO2ENROLL_PIN_IF_NEEDED; /* compat with 248, where the field was unset */
 
                 w = json_variant_by_key(v, "fido2-up-required");
                 if (w) {
@@ -204,7 +204,8 @@ int find_fido2_auto_data(
                                                        "FIDO2 token data's 'fido2-up-required' field is not a boolean.");
 
                         SET_FLAG(required, FIDO2ENROLL_UP, json_variant_boolean(w));
-                }
+                } else
+                        required |= FIDO2ENROLL_UP_IF_NEEDED; /* compat with 248 */
 
                 w = json_variant_by_key(v, "fido2-uv-required");
                 if (w) {
@@ -215,7 +216,8 @@ int find_fido2_auto_data(
                                                        "FIDO2 token data's 'fido2-uv-required' field is not a boolean.");
 
                         SET_FLAG(required, FIDO2ENROLL_UV, json_variant_boolean(w));
-                }
+                } else
+                        required |= FIDO2ENROLL_UV_OMIT; /* compat with 248 */
         }
 
         if (!cid)
index 74bd071c10eaf8f5f7ee2b7f96e8fc2eb894160e..f0826c47e56ab6159f1a4287cce3754a883a973c 100644 (file)
@@ -758,7 +758,11 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
                 cid = arg_fido2_cid;
                 cid_size = arg_fido2_cid_size;
 
-                required = FIDO2ENROLL_PIN | FIDO2ENROLL_UP; /* For backwards compatibility, PIN+presence is required by default. */
+                /* For now and for compatibility, if the user explicitly configured FIDO2 support and we do
+                 * not read FIDO2 metadata off the LUKS2 header, default to the systemd 248 logic, where we
+                 * use PIN + UP when needed, and do not configure UV at all. Eventually, we should make this
+                 * explicitly configurable. */
+                required = FIDO2ENROLL_PIN_IF_NEEDED | FIDO2ENROLL_UP_IF_NEEDED | FIDO2ENROLL_UV_OMIT;
         } else {
                 r = find_fido2_auto_data(
                                 cd,
index 3da64b549a5b0a022b58b3f2c4115bc0b95e20e4..0272d0bde7e56feb0dae831939ce784f4ab8e6ad 100644 (file)
@@ -310,7 +310,7 @@ static int fido2_use_hmac_hash_specific_token(
                         log_info("User presence required to unlock.");
         }
 
-        if (has_uv) {
+        if (has_uv && !FLAGS_SET(required, FIDO2ENROLL_UV_OMIT)) {
                 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),
@@ -322,20 +322,98 @@ static int fido2_use_hmac_hash_specific_token(
                         log_info("User verification required to unlock.");
         }
 
-        if (FLAGS_SET(required, FIDO2ENROLL_PIN)) {
-                char **i;
+        for (;;) {
+                bool retry_with_up = false, retry_with_pin = false;
 
-                /* OK, we need a pin, try with all pins in turn */
-                if (strv_isempty(pins))
-                        r = FIDO_ERR_PIN_REQUIRED;
-                else
-                        STRV_FOREACH(i, pins) {
-                                r = sym_fido_dev_get_assert(d, a, *i);
-                                if (r != FIDO_ERR_PIN_INVALID)
-                                        break;
+                if (FLAGS_SET(required, FIDO2ENROLL_PIN)) {
+                        char **i;
+
+                        /* OK, we need a pin, try with all pins in turn */
+                        if (strv_isempty(pins))
+                                r = FIDO_ERR_PIN_REQUIRED;
+                        else
+                                STRV_FOREACH(i, pins) {
+                                        r = sym_fido_dev_get_assert(d, a, *i);
+                                        if (r != FIDO_ERR_PIN_INVALID)
+                                                break;
+                                }
+
+                } else
+                        r = sym_fido_dev_get_assert(d, a, NULL);
+
+                /* In some conditions, where a PIN or UP is required we might accept that. Let's check the
+                 * conditions and if so try immediately again. */
+
+                switch (r) {
+
+                case FIDO_ERR_UP_REQUIRED:
+                        /* So the token asked for "up". Try to turn it on, for compat with systemd 248 and try again. */
+
+                        if (!has_up)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                       "Token asks for user presence check but doesn't advertise 'up' feature.");
+
+                        if (FLAGS_SET(required, FIDO2ENROLL_UP))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                       "Token asks for user presence check but was already enabled.");
+
+                        if (FLAGS_SET(required, FIDO2ENROLL_UP_IF_NEEDED)) {
+                                log_info("User presence required to unlock.");
+                                retry_with_up = true;
+                        }
+
+                        break;
+
+                case FIDO_ERR_UNSUPPORTED_OPTION:
+                        /* AuthenTrend ATKey.Pro returns this instead of FIDO_ERR_UP_REQUIRED, let's handle
+                         * it gracefully (also see below.) */
+
+                        if (has_up && (required & (FIDO2ENROLL_UP|FIDO2ENROLL_UP_IF_NEEDED)) == FIDO2ENROLL_UP_IF_NEEDED) {
+                                log_notice("Got unsupported option error when when user presence test is turned off. Trying with user presence test turned on.");
+                                retry_with_up = true;
                         }
-        } else
-                r = sym_fido_dev_get_assert(d, a, NULL);
+
+                        break;
+
+                case FIDO_ERR_PIN_REQUIRED:
+                        /* A pin was requested. Maybe supply one, if we are configured to do so on request */
+
+                        if (!has_client_pin)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                       "Token asks for PIN but doesn't advertise 'clientPin' feature.");
+
+                        if (FLAGS_SET(required, FIDO2ENROLL_PIN) && !strv_isempty(pins))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                       "Token asks for PIN but one was already supplied.");
+
+                        if ((required & (FIDO2ENROLL_PIN|FIDO2ENROLL_PIN_IF_NEEDED)) == FIDO2ENROLL_PIN_IF_NEEDED) {
+                                /* If a PIN so far wasn't specified but is requested by the device, and
+                                 * FIDO2ENROLL_PIN_IF_NEEDED is set, then provide it */
+                                log_debug("Retrying to create credential with PIN.");
+                                retry_with_pin = true;
+                        }
+
+                        break;
+
+                default:
+                        break;
+                }
+
+                if (!retry_with_up && !retry_with_pin)
+                        break;
+
+                if (retry_with_up) {
+                        r = sym_fido_assert_set_up(a, FIDO_OPT_TRUE);
+                        if (r != FIDO_OK)
+                                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                                       "Failed to enable FIDO2 user presence test: %s", sym_fido_strerr(r));
+
+                        required |= FIDO2ENROLL_UP;
+                }
+
+                if (retry_with_pin)
+                        required |= FIDO2ENROLL_PIN;
+        }
 
         switch (r) {
         case FIDO_OK:
index bcf3c73706d756e85d4496c67f9624f47c902657..5640cca5e39b2b6cda86a10192d8897469cda8c1 100644 (file)
@@ -7,6 +7,9 @@ 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_PIN_IF_NEEDED = 1 << 3, /* If auth doesn't work without PIN ask for one, as in systemd 248 */
+        FIDO2ENROLL_UP_IF_NEEDED  = 1 << 4, /* If auth doesn't work without UP, enable it, as in systemd 248 */
+        FIDO2ENROLL_UV_OMIT       = 1 << 5, /* Leave "uv" untouched, as in systemd 248 */
         _FIDO2ENROLL_TYPE_MAX,
         _FIDO2ENROLL_TYPE_INVALID = -EINVAL,
 } Fido2EnrollFlags;