mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-01-08 21:17:47 +03:00
homectl: store FIDO2 up/uv/clientPin fields in user records too
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".
This commit is contained in:
parent
7dba77a67e
commit
17e7561a97
@ -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`
|
||||
|
||||
|
@ -378,6 +378,35 @@
|
||||
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>
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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.)");
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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),
|
||||
|
@ -107,6 +107,8 @@
|
||||
#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"
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user