]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
homectl: store FIDO2 up/uv/clientPin fields in user records too
authorLennart Poettering <lennart@poettering.net>
Fri, 28 May 2021 16:18:54 +0000 (18:18 +0200)
committerLennart Poettering <lennart@poettering.net>
Tue, 1 Jun 2021 11:31:53 +0000 (13:31 +0200)
This catches up homed's FIDO2 support with cryptsetup's: we'll now store
the uv/up/clientPin configuration at enrollment in the user record JSON
data, and use it when authenticating with it.

This also adds explicit "uv" support: we'll only allow it to happen when
the client explicity said it's OK. This is then used by clients to print
a nice message suggesting "uv" has to take place before retrying
allowing it this time. This is modelled after the existing handling for
"up".

15 files changed:
docs/USER_RECORD.md
man/homectl.xml
src/home/homectl-fido2.c
src/home/homectl-fido2.h
src/home/homectl.c
src/home/homed-home.c
src/home/homework-fido2.c
src/home/homework.c
src/home/pam_systemd_home.c
src/home/user-record-util.c
src/home/user-record-util.h
src/libsystemd/sd-bus/bus-common-errors.c
src/libsystemd/sd-bus/bus-common-errors.h
src/shared/user-record.c
src/shared/user-record.h

index 6435d2cf5f5f1a6b9a543f9e89293701b2b49370..11ab31b93319ea738b00f9d820ecbd58300cdcfe 100644 (file)
@@ -628,18 +628,21 @@ user records.
 `fido2HmacSalt` → An array of objects, implementing authentication support with
 FIDO2 devices that implement the `hmac-secret` extension. Each element of the
 array should be an object consisting of three string fields: `credential`,
-`salt`, `hashedPassword`. The first two shall contain Base64-encoded binary
+`salt`, `hashedPassword`, and three boolean fields: `up`, `uv` and
+`clientPin`. The first two string fields shall contain Base64-encoded binary
 data: the FIDO2 credential ID and the salt value to pass to the FIDO2
 device. During authentication this salt along with the credential ID is sent to
 the FIDO2 token, which will HMAC hash the salt with its internal secret key and
 return the result. This resulting binary key should then be Base64-encoded and
 used as string password for the further layers of the stack. The
 `hashedPassword` field of the `fido2HmacSalt` field shall be a UNIX password
-hash to test this derived secret key against for authentication. It is
-generally recommended that for each entry in `fido2HmacSalt` there's also a
-matching one in `fido2HmacCredential`, and vice versa, with the same credential
-ID, appearing in the same order, but this should not be required by
-applications processing user records.
+hash to test this derived secret key against for authentication. The `up`, `uv`
+and `clientPin` booleans map to the FIDO2 concepts of the same name and encode
+whether the `uv`/`up` options are enabled during the authentication, and
+whether a PIN shall be required. It is generally recommended that for each
+entry in `fido2HmacSalt` there's also a matching one in `fido2HmacCredential`,
+and vice versa, with the same credential ID, appearing in the same order, but
+this should not be required by applications processing user records.
 
 `recoveryKey`→ An array of objects, each defining a recovery key. The object
 has two mandatory fields: `type` indicates the type of recovery key. The only
@@ -927,8 +930,15 @@ user. If false or unset, authentication this way shall not be attempted.
 
 `fido2UserPresencePermitted` → a boolean. If set to true allows the receiver to
 use the FIDO2 "user presence" flag. This is similar to the concept of
-`pkcs11ProtectedAuthenticationPathPermitted`, but exposes the FIDO2 concept
-behind it. If false or unset authentication this way shall not be attempted.
+`pkcs11ProtectedAuthenticationPathPermitted`, but exposes the FIDO2 "up"
+concept behind it. If false or unset authentication this way shall not be
+attempted.
+
+`fido2UserVerificationPermitted` → a boolean. If set to true allows the
+receiver to use the FIDO2 "user verification" flag. This is similar to the
+concept of `pkcs11ProtectedAuthenticationPathPermitted`, but exposes the FIDO2
+"uv" concept behind it. If false or unset authentication this way shall not be
+attempted.
 
 ## Mapping to `struct passwd` and `struct spwd`
 
index f2858166f7babdde64d67265d4bb9ed83ab532fc..4b0b120ca8c23ba7abc0c62b2cd4fec6cba2766f 100644 (file)
         discussion see above.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--fido2-with-client-pin=</option><replaceable>BOOL</replaceable></term>
+
+        <listitem><para>When enrolling a FIDO2 security token, controls whether to require the user to enter
+        a PIN when unlocking the account (the FIDO2 <literal>clientPin</literal> feature). Defaults to
+        <literal>yes</literal>. (Note: this setting is without effect if the security token does not support
+        the <literal>clientPin</literal> feature at all, or does not allow enabling or disabling
+        it.)</para></listitem>
+      </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 account.
+        Defaults to <literal>yes</literal>. (Note: this setting is without effect if the security token does not support
+        the <literal>up</literal> feature at all, or does not allow enabling or disabling it.)
+        </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 account (the FIDO2 <literal>uv</literal> feature). Defaults to
+        <literal>no</literal>. (Note: this setting is without effect if the security token does not support
+        the <literal>uv</literal> feature at all, or does not allow enabling or disabling it.)</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>--recovery-key=</option><replaceable>BOOL</replaceable></term>
 
index e8e9826bb9177ed1c97097380a566222114384b1..d0457d8e29f4b41651543b6f6649a190f88edb4d 100644 (file)
@@ -68,7 +68,8 @@ static int add_fido2_salt(
                 const void *fido2_salt,
                 size_t fido2_salt_size,
                 const void *secret,
-                size_t secret_size) {
+                size_t secret_size,
+                Fido2EnrollFlags lock_with) {
 
         _cleanup_(json_variant_unrefp) JsonVariant *l = NULL, *w = NULL, *e = NULL;
         _cleanup_(erase_and_freep) char *base64_encoded = NULL, *hashed = NULL;
@@ -87,7 +88,11 @@ static int add_fido2_salt(
         r = json_build(&e, JSON_BUILD_OBJECT(
                                        JSON_BUILD_PAIR("credential", JSON_BUILD_BASE64(cid, cid_size)),
                                        JSON_BUILD_PAIR("salt", JSON_BUILD_BASE64(fido2_salt, fido2_salt_size)),
-                                       JSON_BUILD_PAIR("hashedPassword", JSON_BUILD_STRING(hashed))));
+                                       JSON_BUILD_PAIR("hashedPassword", JSON_BUILD_STRING(hashed)),
+                                       JSON_BUILD_PAIR("up", JSON_BUILD_BOOLEAN(FLAGS_SET(lock_with, FIDO2ENROLL_UP))),
+                                       JSON_BUILD_PAIR("uv", JSON_BUILD_BOOLEAN(FLAGS_SET(lock_with, FIDO2ENROLL_UV))),
+                                       JSON_BUILD_PAIR("clientPin", JSON_BUILD_BOOLEAN(FLAGS_SET(lock_with, FIDO2ENROLL_PIN)))));
+
         if (r < 0)
                 return log_error_errno(r, "Failed to build FIDO2 salt JSON key object: %m");
 
@@ -112,7 +117,8 @@ static int add_fido2_salt(
 
 int identity_add_fido2_parameters(
                 JsonVariant **v,
-                const char *device) {
+                const char *device,
+                Fido2EnrollFlags lock_with) {
 
 #if HAVE_LIBFIDO2
         JsonVariant *un, *realm, *rn;
@@ -158,12 +164,12 @@ 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 | FIDO2ENROLL_UP, // FIXME: add a --lock-with-pin/up parameter like cryptenroll
+                        lock_with,
                         &cid, &cid_size,
                         &salt, &salt_size,
                         &secret, &secret_size,
                         &used_pin,
-                        NULL);
+                        &lock_with);
         if (r < 0)
                 return r;
 
@@ -181,7 +187,8 @@ int identity_add_fido2_parameters(
                         salt,
                         salt_size,
                         secret,
-                        secret_size);
+                        secret_size,
+                        lock_with);
         if (r < 0)
                 return r;
 
index 7b8d9f60acd10dda6f8e22d0921943a6ab4eb909..5087069c3c153580abd590ec45e76754bf75f199 100644 (file)
@@ -2,5 +2,6 @@
 #pragma once
 
 #include "json.h"
+#include "libfido2-util.h"
 
-int identity_add_fido2_parameters(JsonVariant **v, const char *device);
+int identity_add_fido2_parameters(JsonVariant **v, const char *device, Fido2EnrollFlags lock_with);
index 09d424734783f1ede7237666843c228b7b1daf75..7128f6cea1f88f3b4ab6bcfe4eceba3b739414e2 100644 (file)
@@ -57,6 +57,7 @@ static uint64_t arg_disk_size = UINT64_MAX;
 static uint64_t arg_disk_size_relative = UINT64_MAX;
 static char **arg_pkcs11_token_uri = NULL;
 static char **arg_fido2_device = NULL;
+static Fido2EnrollFlags arg_fido2_lock_with = FIDO2ENROLL_PIN | FIDO2ENROLL_UP;
 static bool arg_recovery_key = false;
 static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
 static bool arg_and_resize = false;
@@ -380,7 +381,7 @@ static int handle_generic_user_record_error(
 
         } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED)) {
 
-                log_notice("%s%sAuthentication requires presence verification on security token.",
+                log_notice("%s%sPlease confirm presence on security token.",
                            emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
                            emoji_enabled() ? " " : "");
 
@@ -388,6 +389,16 @@ static int handle_generic_user_record_error(
                 if (r < 0)
                         return log_error_errno(r, "Failed to set FIDO2 user presence permitted flag: %m");
 
+        } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_USER_VERIFICATION_NEEDED)) {
+
+                log_notice("%s%sPlease verify user on security token.",
+                           emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
+                           emoji_enabled() ? " " : "");
+
+                r = user_record_set_fido2_user_verification_permitted(hr, true);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to set FIDO2 user verification permitted flag: %m");
+
         } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_LOCKED))
                 return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Security token PIN is locked, please unlock it first. (Hint: Removal and re-insertion might suffice.)");
 
@@ -1027,7 +1038,7 @@ static int acquire_new_home_record(UserRecord **ret) {
         }
 
         STRV_FOREACH(i, arg_fido2_device) {
-                r = identity_add_fido2_parameters(&v, *i);
+                r = identity_add_fido2_parameters(&v, *i, arg_fido2_lock_with);
                 if (r < 0)
                         return r;
         }
@@ -1397,7 +1408,7 @@ static int acquire_updated_home_record(
         }
 
         STRV_FOREACH(i, arg_fido2_device) {
-                r = identity_add_fido2_parameters(&json, *i);
+                r = identity_add_fido2_parameters(&json, *i, arg_fido2_lock_with);
                 if (r < 0)
                         return r;
         }
@@ -1440,6 +1451,10 @@ static int home_record_reset_human_interaction_permission(UserRecord *hr) {
         if (r < 0)
                 return log_error_errno(r, "Failed to reset FIDO2 user presence permission flag: %m");
 
+        r = user_record_set_fido2_user_verification_permitted(hr, -1);
+        if (r < 0)
+                return log_error_errno(r, "Failed to reset FIDO2 user verification permission flag: %m");
+
         return 0;
 }
 
@@ -2071,6 +2086,15 @@ static int help(int argc, char *argv[], void *userdata) {
                "                              private key and matching X.509 certificate\n"
                "     --fido2-device=PATH      Path to FIDO2 hidraw device with hmac-secret\n"
                "                              extension\n"
+               "     --fido2-with-client-pin=BOOL\n"
+               "                              Whether to require entering a PIN to unlock the\n"
+               "                              account\n"
+               "     --fido2-with-user-presence=BOOL\n"
+               "                              Whether to require user presence to unlock the\n"
+               "                              account\n"
+               "     --fido2-with-user-verification=BOOL\n"
+               "                              Whether to require user verification to unlock the\n"
+               "                              account\n"
                "     --recovery-key=BOOL      Add a recovery key\n"
                "\n%4$sAccount Management User Record Properties:%5$s\n"
                "     --locked=BOOL            Set locked account state\n"
@@ -2220,6 +2244,9 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_AUTO_LOGIN,
                 ARG_PKCS11_TOKEN_URI,
                 ARG_FIDO2_DEVICE,
+                ARG_FIDO2_WITH_PIN,
+                ARG_FIDO2_WITH_UP,
+                ARG_FIDO2_WITH_UV,
                 ARG_RECOVERY_KEY,
                 ARG_AND_RESIZE,
                 ARG_AND_CHANGE_PASSWORD,
@@ -2299,6 +2326,9 @@ static int parse_argv(int argc, char *argv[]) {
                 { "export-format",               required_argument, NULL, ARG_EXPORT_FORMAT               },
                 { "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               },
+                { "fido2-with-user-verification",required_argument, NULL, ARG_FIDO2_WITH_UV               },
                 { "recovery-key",                required_argument, NULL, ARG_RECOVERY_KEY                },
                 { "and-resize",                  required_argument, NULL, ARG_AND_RESIZE                  },
                 { "and-change-password",         required_argument, NULL, ARG_AND_CHANGE_PASSWORD         },
@@ -3323,7 +3353,6 @@ static int parse_argv(int argc, char *argv[]) {
                                 r = strv_consume(&arg_fido2_device, TAKE_PTR(found));
                         } else
                                 r = strv_extend(&arg_fido2_device, optarg);
-
                         if (r < 0)
                                 return r;
 
@@ -3331,6 +3360,39 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
                 }
 
+                case ARG_FIDO2_WITH_PIN: {
+                        bool lock_with_pin;
+
+                        r = parse_boolean_argument("--fido2-with-client-pin=", optarg, &lock_with_pin);
+                        if (r < 0)
+                                return r;
+
+                        SET_FLAG(arg_fido2_lock_with, FIDO2ENROLL_PIN, lock_with_pin);
+                        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_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_RECOVERY_KEY: {
                         const char *p;
 
index dc9a446c71474ffed9751607cb11e6daaa807fe1..54e36e3b712ae55e50896be89b15957b630a2e91 100644 (file)
@@ -461,7 +461,9 @@ static int convert_worker_errno(Home *h, int e, sd_bus_error *error) {
         case -ERFKILL:
                 return sd_bus_error_set(error, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED, "Security token requires protected authentication path.");
         case -EMEDIUMTYPE:
-                return sd_bus_error_set(error, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED, "Security token requires user presence.");
+                return sd_bus_error_set(error, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED, "Security token requires presence confirmation.");
+        case -ENOCSI:
+                return sd_bus_error_set(error, BUS_ERROR_TOKEN_USER_VERIFICATION_NEEDED, "Security token requires user verification.");
         case -ENOSTR:
                 return sd_bus_error_set(error, BUS_ERROR_TOKEN_ACTION_TIMEOUT, "Token action timeout. (User was supposed to verify presence or similar, by interacting with the token, and didn't do that in time.)");
         case -EOWNERDEAD:
index 8811c00550df80f26531bfbad5eb2b2be3bbc0b6..23fda4a355af227354933d7351b331a8f437936d 100644 (file)
@@ -6,6 +6,7 @@
 #include "homework-fido2.h"
 #include "libfido2-util.h"
 #include "memory-util.h"
+#include "strv.h"
 
 int fido2_use_token(
                 UserRecord *h,
@@ -15,6 +16,7 @@ int fido2_use_token(
 
         _cleanup_(erase_and_freep) void *hmac = NULL;
         size_t hmac_size;
+        Fido2EnrollFlags flags = 0;
         int r;
 
         assert(h);
@@ -22,13 +24,42 @@ int fido2_use_token(
         assert(salt);
         assert(ret);
 
+        /* If we know the up/uv/clientPin settings used during enrollment, let's pass this on for
+         * authentication, or generate errors immediately if interactivity of the specified kind is not
+         * allowed. */
+
+        if (salt->up > 0) {
+                if (h->fido2_user_presence_permitted <= 0)
+                        return -EMEDIUMTYPE;
+
+                flags |= FIDO2ENROLL_UP;
+        } else if (salt->up < 0) /* unset? */
+                flags |= FIDO2ENROLL_UP_IF_NEEDED; /* compat with pre-248 */
+
+        if (salt->uv > 0) {
+                if (h->fido2_user_verification_permitted <= 0)
+                        return -ENOCSI;
+
+                flags |= FIDO2ENROLL_UV;
+        } else if (salt->uv < 0)
+                flags |= FIDO2ENROLL_UV_OMIT; /* compat with pre-248 */
+
+        if (salt->client_pin > 0) {
+
+                if (strv_isempty(secret->token_pin))
+                        return -ENOANO;
+
+                flags |= FIDO2ENROLL_PIN;
+        } else if (salt->client_pin < 0)
+                flags |= FIDO2ENROLL_PIN_IF_NEEDED; /* compat with pre-248 */
+
         r = fido2_use_hmac_hash(
                         NULL,
                         "io.systemd.home",
                         salt->salt, salt->salt_size,
                         salt->credential.id, salt->credential.size,
                         secret->token_pin,
-                        FIDO2ENROLL_PIN | (h->fido2_user_presence_permitted > 0 ? FIDO2ENROLL_UP : 0), // FIXME: add a --lock-with-pin parameter like cryptenroll
+                        flags,
                         &hmac,
                         &hmac_size);
         if (r < 0)
index bb5a774f81c0992547965f1e2db189547054b97b..3b1f41309578997f62183c1c91848779056f831d 100644 (file)
@@ -48,8 +48,10 @@ int user_record_authenticate(
                 PasswordCache *cache,
                 bool strict_verify) {
 
-        bool need_password = false, need_recovery_key = false, need_token = false, need_pin = false, need_protected_authentication_path_permitted = false, need_user_presence_permitted = false,
-                pin_locked = false, pin_incorrect = false, pin_incorrect_few_tries_left = false, pin_incorrect_one_try_left = false, token_action_timeout = false;
+        bool need_password = false, need_recovery_key = false, need_token = false, need_pin = false,
+                need_protected_authentication_path_permitted = false, need_user_presence_permitted = false,
+                need_user_verification_permitted = false, pin_locked = false, pin_incorrect = false,
+                pin_incorrect_few_tries_left = false, pin_incorrect_one_try_left = false, token_action_timeout = false;
         int r;
 
         assert(h);
@@ -208,6 +210,9 @@ int user_record_authenticate(
                 case -EMEDIUMTYPE:
                         need_user_presence_permitted = true;
                         break;
+                case -ENOCSI:
+                        need_user_verification_permitted = true;
+                        break;
                 case -ENOSTR:
                         token_action_timeout = true;
                         break;
@@ -250,6 +255,8 @@ int user_record_authenticate(
                 return -ERFKILL;
         if (need_user_presence_permitted)
                 return -EMEDIUMTYPE;
+        if (need_user_verification_permitted)
+                return -ENOCSI;
         if (need_pin)
                 return -ENOANO;
         if (need_token)
@@ -1680,6 +1687,7 @@ static int run(int argc, char *argv[]) {
          * ENOANO          → suitable PKCS#11/FIDO2 device found, but PIN is missing to unlock it
          * ERFKILL         → suitable PKCS#11 device found, but OK to ask for on-device interactive authentication not given
          * EMEDIUMTYPE     → suitable FIDO2 device found, but OK to ask for user presence not given
+         * ENOCSI          → suitable FIDO2 device found, but OK to ask for user verification not given
          * ENOSTR          → suitable FIDO2 device found, but user didn't react to action request on token quickly enough
          * EOWNERDEAD      → suitable PKCS#11/FIDO2 device found, but its PIN is locked
          * ENOLCK          → suitable PKCS#11/FIDO2 device found, but PIN incorrect
index 64dc5325778e5e4c8e6d9fc3ba85abf3461123e4..6c2bcbd7d728168a696e74466dd8ffd8174570a8 100644 (file)
@@ -377,7 +377,7 @@ static int handle_generic_user_record_error(
 
         } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED)) {
 
-                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Please verify presence on security token of user %s.", user_name);
+                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Please confirm presence on security token of user %s.", user_name);
 
                 r = user_record_set_fido2_user_presence_permitted(secret, true);
                 if (r < 0) {
@@ -385,6 +385,16 @@ static int handle_generic_user_record_error(
                         return PAM_SERVICE_ERR;
                 }
 
+        } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_USER_VERIFICATION_NEEDED)) {
+
+                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Please verify user on security token of user %s.", user_name);
+
+                r = user_record_set_fido2_user_verification_permitted(secret, true);
+                if (r < 0) {
+                        pam_syslog(handle, LOG_ERR, "Failed to set FIDO2 user verification permitted flag: %s", strerror_safe(r));
+                        return PAM_SERVICE_ERR;
+                }
+
         } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_LOCKED)) {
 
                 (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Security token PIN is locked, please unlock it first. (Hint: Removal and re-insertion might suffice.)");
index e244ba5772a106baf574d6c853ab8d6d6b4c644b..4e4f5d2341bda42f5f48ba6b09625602c1118633 100644 (file)
@@ -1065,6 +1065,34 @@ int user_record_set_fido2_user_presence_permitted(UserRecord *h, int b) {
         return 0;
 }
 
+int user_record_set_fido2_user_verification_permitted(UserRecord *h, int b) {
+        _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+        int r;
+
+        assert(h);
+
+        w = json_variant_ref(json_variant_by_key(h->json, "secret"));
+
+        if (b < 0)
+                r = json_variant_filter(&w, STRV_MAKE("fido2UserVerificationPermitted"));
+        else
+                r = json_variant_set_field_boolean(&w, "fido2UserVerificationPermitted", b);
+        if (r < 0)
+                return r;
+
+        if (json_variant_is_blank_object(w))
+                r = json_variant_filter(&h->json, STRV_MAKE("secret"));
+        else
+                r = json_variant_set_field(&h->json, "secret", w);
+        if (r < 0)
+                return r;
+
+        h->fido2_user_verification_permitted = b;
+
+        SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
+        return 0;
+}
+
 static bool per_machine_entry_empty(JsonVariant *v) {
         const char *k;
         _unused_ JsonVariant *e;
@@ -1167,6 +1195,14 @@ int user_record_merge_secret(UserRecord *h, UserRecord *secret) {
                         return r;
         }
 
+        if (secret->fido2_user_verification_permitted >= 0) {
+                r = user_record_set_fido2_user_verification_permitted(
+                                h,
+                                secret->fido2_user_verification_permitted);
+                if (r < 0)
+                        return r;
+        }
+
         return 0;
 }
 
index f7cc4e04eb1f5508bcf5423d059e8d879bd7183f..74f4a0eaab28fe6c170a29aa7b9122173435a2be 100644 (file)
@@ -52,6 +52,7 @@ int user_record_set_hashed_password(UserRecord *h, char **hashed_password);
 int user_record_set_token_pin(UserRecord *h, char **pin, bool prepend);
 int user_record_set_pkcs11_protected_authentication_path_permitted(UserRecord *h, int b);
 int user_record_set_fido2_user_presence_permitted(UserRecord *h, int b);
+int user_record_set_fido2_user_verification_permitted(UserRecord *h, int b);
 int user_record_set_password_change_now(UserRecord *h, int b);
 int user_record_merge_secret(UserRecord *h, UserRecord *secret);
 int user_record_good_authentication(UserRecord *h);
index dc6211bc978f608f33b1d968781db71d46bcc784..43f9a7bdaadadd3c6aa489bf9f9aa1ae35024403 100644 (file)
@@ -125,6 +125,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
         SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_PIN_NEEDED,             ENOANO),
         SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED, ERFKILL),
         SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED,   EMEDIUMTYPE),
+        SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_USER_VERIFICATION_NEEDED, ENOCSI),
         SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_ACTION_TIMEOUT,         ENOSTR),
         SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_PIN_LOCKED,             EOWNERDEAD),
         SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_BAD_PIN,                ENOLCK),
index e56b5d139dea5a2396d1f2f5ef08e2fc002c62e6..fd8f0c240e96a5c8bfb410eb0bef6ef1f1960d18 100644 (file)
 #define BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED \
                                                "org.freedesktop.home1.TokenProtectedAuthenticationPathNeeded"
 #define BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED   "org.freedesktop.home1.TokenUserPresenceNeeded"
+#define BUS_ERROR_TOKEN_USER_VERIFICATION_NEEDED \
+                                               "org.freedesktop.home1.TokenUserVerificationNeeded"
 #define BUS_ERROR_TOKEN_ACTION_TIMEOUT         "org.freedesktop.home1.TokenActionTimeout"
 #define BUS_ERROR_TOKEN_PIN_LOCKED             "org.freedesktop.home1.TokenPinLocked"
 #define BUS_ERROR_TOKEN_BAD_PIN                "org.freedesktop.home1.BadPin"
index d82b4d36361efcee057a444a7473469eeb503ea1..d519ea089559a0d15a021462f3bd256ec745df2c 100644 (file)
@@ -200,6 +200,7 @@ UserRecord* user_record_new(void) {
                 .password_change_now = -1,
                 .pkcs11_protected_authentication_path_permitted = -1,
                 .fido2_user_presence_permitted = -1,
+                .fido2_user_verification_permitted = -1,
         };
 
         return h;
@@ -774,6 +775,7 @@ static int dispatch_secret(const char *name, JsonVariant *variant, JsonDispatchF
                 { "pkcs11Pin",   /* legacy alias */             _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv,     offsetof(UserRecord, token_pin),                                      0 },
                 { "pkcs11ProtectedAuthenticationPathPermitted", JSON_VARIANT_BOOLEAN,       json_dispatch_tristate, offsetof(UserRecord, pkcs11_protected_authentication_path_permitted), 0 },
                 { "fido2UserPresencePermitted",                 JSON_VARIANT_BOOLEAN,       json_dispatch_tristate, offsetof(UserRecord, fido2_user_presence_permitted),                  0 },
+                { "fido2UserVerificationPermitted",             JSON_VARIANT_BOOLEAN,       json_dispatch_tristate, offsetof(UserRecord, fido2_user_verification_permitted),              0 },
                 {},
         };
 
@@ -1016,9 +1018,12 @@ static int dispatch_fido2_hmac_salt(const char *name, JsonVariant *variant, Json
                 Fido2HmacSalt *array, *k;
 
                 static const JsonDispatch fido2_hmac_salt_dispatch_table[] = {
-                        { "credential",     JSON_VARIANT_STRING, dispatch_fido2_hmac_credential, offsetof(Fido2HmacSalt, credential),      JSON_MANDATORY },
-                        { "salt",           JSON_VARIANT_STRING, dispatch_fido2_hmac_salt_value, 0,                                        JSON_MANDATORY },
-                        { "hashedPassword", JSON_VARIANT_STRING, json_dispatch_string,           offsetof(Fido2HmacSalt, hashed_password), JSON_MANDATORY },
+                        { "credential",     JSON_VARIANT_STRING,  dispatch_fido2_hmac_credential, offsetof(Fido2HmacSalt, credential),      JSON_MANDATORY },
+                        { "salt",           JSON_VARIANT_STRING,  dispatch_fido2_hmac_salt_value, 0,                                        JSON_MANDATORY },
+                        { "hashedPassword", JSON_VARIANT_STRING,  json_dispatch_string,           offsetof(Fido2HmacSalt, hashed_password), JSON_MANDATORY },
+                        { "up",             JSON_VARIANT_BOOLEAN, json_dispatch_tristate,         offsetof(Fido2HmacSalt, up),              0              },
+                        { "uv",             JSON_VARIANT_BOOLEAN, json_dispatch_tristate,         offsetof(Fido2HmacSalt, uv),              0              },
+                        { "clientPin",      JSON_VARIANT_BOOLEAN, json_dispatch_tristate,         offsetof(Fido2HmacSalt, client_pin),      0              },
                         {},
                 };
 
@@ -1031,7 +1036,11 @@ static int dispatch_fido2_hmac_salt(const char *name, JsonVariant *variant, Json
 
                 h->fido2_hmac_salt = array;
                 k = h->fido2_hmac_salt + h->n_fido2_hmac_salt;
-                *k = (Fido2HmacSalt) {};
+                *k = (Fido2HmacSalt) {
+                        .uv = -1,
+                        .up = -1,
+                        .client_pin = -1,
+                };
 
                 r = json_dispatch(e, fido2_hmac_salt_dispatch_table, NULL, flags, k);
                 if (r < 0) {
index 66dceecfdddaf151c891f92904d0f2f2943e2d95..fa58dfdb6e6e33662e36403d3c47f76cdebaa9d5 100644 (file)
@@ -236,6 +236,9 @@ typedef struct Fido2HmacSalt {
 
         /* What to test the hashed salt value against, usually UNIX password hash here. */
         char *hashed_password;
+
+        /* Whether the 'up', 'uv', 'clientPin' features are enabled. */
+        int uv, up, client_pin;
 } Fido2HmacSalt;
 
 typedef struct RecoveryKey {
@@ -371,6 +374,7 @@ typedef struct UserRecord {
         Fido2HmacSalt *fido2_hmac_salt;
         size_t n_fido2_hmac_salt;
         int fido2_user_presence_permitted;
+        int fido2_user_verification_permitted;
 
         char **recovery_key_type;
         RecoveryKey *recovery_key;