1
0
mirror of https://github.com/systemd/systemd synced 2026-03-14 17:14:49 +01:00

Compare commits

..

10 Commits

Author SHA1 Message Date
Luca Boccassi
63225fe159
Merge pull request #19298 from bluca/cryptsetup_nopass
cryptsetup: add 'headless' parameter to skip password/pin query, allow pin-less enroll on FIDO2, support user presence/verification flags
2021-05-09 13:49:55 +01:00
Luca Boccassi
84b5f40821
Merge pull request #19552 from yuwata/fix-typo-and-coverty-issues
Fix typo and coverity issues
2021-05-09 13:36:17 +01:00
Yu Watanabe
19cc6d5e54 tree-wide: fix typo 2021-05-09 14:36:19 +09:00
Yu Watanabe
6ac6549251 userdb: shorten code a bit
Hopefully fixes CID#1452937.
2021-05-09 14:34:23 +09:00
Yu Watanabe
fedd793cea test: add one more assertion to make Coverty happy
Fixes CID#1452934.
2021-05-09 14:33:59 +09:00
Luca Boccassi
8f214355c6 FIDO2: if defined, check for FIDO_ERR_UV_BLOCKED
Newer libfido versions added this error, so check for it since it
can help the user with a more specific message
2021-05-07 21:36:27 +01:00
Luca Boccassi
896cc0da98 FIDO2: ask and record whether user verification was used to lock the volume
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.
2021-05-07 21:36:27 +01:00
Luca Boccassi
06f087192d FIDO2: ask and record whether user presence was used to lock the volume
In some cases user presence might not be required to get _a_
secret out of a FIDO2 device, but it might be required to
the get actual secret that was used to lock the volume.
Record whether we used it in the LUKS header JSON metadata.
Let the cryptenroll user ask for the feature, but bail out if it is
required by the token and the user disabled it.
Enabled by default.
2021-05-07 21:36:27 +01:00
Luca Boccassi
cde2f8605e FIDO2: support pin-less LUKS enroll/unlock
Closes: https://github.com/systemd/systemd/issues/19246

Some FIDO2 devices allow the user to choose whether to use a PIN or not
and will HMAC with a different secret depending on the choice.
Some other devices (or some device-specific configuration) can instead
make it mandatory.
Allow the cryptenroll user to choose whether to use a PIN or not, but
fail immediately if it is a hard requirement.
Record the choice in the JSON-encoded LUKS header metadata so that the
right set of options can be used on unlock.
2021-05-07 21:36:27 +01:00
Luca Boccassi
cd5f57bda7 cryptsetup: add 'headless' parameter to skip password/pin query
On headless setups, in case other methods fail, asking for a password/pin
is not useful as there are no users on the terminal, and generates
unwanted noise. Add a parameter to /etc/crypttab to skip it.
2021-05-07 21:36:27 +01:00
22 changed files with 327 additions and 91 deletions

View File

@ -514,6 +514,13 @@
user is queried for a password indefinitely.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>headless=</option></term>
<listitem><para>Takes a boolean argument, defaults to false. If true, never query interactively
for the password/PIN. Useful for headless systems.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>verify</option></term>

View File

@ -125,6 +125,30 @@
<filename>/etc/crypttab</filename> line.</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 volume. Defaults to <literal>yes</literal>.</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 volume.
Defaults to <literal>yes</literal>.
</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>

View File

@ -73,7 +73,8 @@
<listitem><para>The kernel keyring is then checked for a suitable cached password from previous
attempts.</para></listitem>
<listitem><para>Finally, the user is queried for a password, possibly multiple times.</para></listitem>
<listitem><para>Finally, the user is queried for a password, possibly multiple times, unless
the <varname>headless</varname> option is set.</para></listitem>
</orderedlist>
<para>If no suitable key may be acquired via any of the mechanisms describes above, volume activation fails.</para>

View File

@ -190,7 +190,7 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags
assert(line);
assert(pid >= 0);
/* Retrieve adn format a commandline. See above for discussion of retrieval options.
/* Retrieve and format a commandline. See above for discussion of retrieval options.
*
* There are two main formatting modes:
*

View File

@ -11,7 +11,8 @@ int enroll_fido2(
struct crypt_device *cd,
const void *volume_key,
size_t volume_key_size,
const char *device) {
const char *device,
Fido2EnrollFlags lock_with) {
_cleanup_(erase_and_freep) void *salt = NULL, *secret = NULL;
_cleanup_(erase_and_freep) char *base64_encoded = NULL;
@ -40,6 +41,7 @@ int enroll_fido2(
/* user_display_name= */ node,
/* user_icon_name= */ NULL,
/* askpw_icon_name= */ "drive-harddisk",
lock_with,
&cid, &cid_size,
&salt, &salt_size,
&secret, &secret_size,
@ -75,7 +77,10 @@ int enroll_fido2(
JSON_BUILD_PAIR("keyslots", JSON_BUILD_ARRAY(JSON_BUILD_STRING(keyslot_as_string))),
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-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-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");

View File

@ -4,12 +4,13 @@
#include <sys/types.h>
#include "cryptsetup-util.h"
#include "libfido2-util.h"
#include "log.h"
#if HAVE_LIBFIDO2
int enroll_fido2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device);
int enroll_fido2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, Fido2EnrollFlags lock_with);
#else
static inline int enroll_fido2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device) {
static inline int enroll_fido2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, Fido2EnrollFlags lock_with) {
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"FIDO2 key enrollment not supported.");
}

View File

@ -36,6 +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 | FIDO2ENROLL_UP;
assert_cc(sizeof(arg_wipe_slots_mask) * 8 >= _ENROLL_TYPE_MAX);
@ -88,6 +89,12 @@ static int help(void) {
" Specify PKCS#11 security token URI\n"
" --fido2-device=PATH\n"
" 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"
" --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"
@ -114,6 +121,9 @@ static int parse_argv(int argc, char *argv[]) {
ARG_TPM2_DEVICE,
ARG_TPM2_PCRS,
ARG_WIPE_SLOT,
ARG_FIDO2_WITH_PIN,
ARG_FIDO2_WITH_UP,
ARG_FIDO2_WITH_UV,
};
static const struct option options[] = {
@ -123,6 +133,9 @@ static int parse_argv(int argc, char *argv[]) {
{ "recovery-key", no_argument, NULL, ARG_RECOVERY_KEY },
{ "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 },
{ "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE },
{ "tpm2-pcrs", required_argument, NULL, ARG_TPM2_PCRS },
{ "wipe-slot", required_argument, NULL, ARG_WIPE_SLOT },
@ -144,6 +157,42 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_VERSION:
return version();
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_PASSWORD:
if (arg_enroll_type >= 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
@ -486,7 +535,7 @@ static int run(int argc, char *argv[]) {
break;
case ENROLL_FIDO2:
slot = enroll_fido2(cd, vk, vks, arg_fido2_device);
slot = enroll_fido2(cd, vk, vks, arg_fido2_device, arg_fido2_lock_with);
break;
case ENROLL_TPM2:

View File

@ -23,6 +23,8 @@ int acquire_fido2_key(
const void *key_data,
size_t key_data_size,
usec_t until,
bool headless,
Fido2EnrollFlags required,
void **ret_decrypted_key,
size_t *ret_decrypted_key_size) {
@ -72,22 +74,27 @@ int acquire_fido2_key(
}
for (;;) {
if (!FLAGS_SET(required, FIDO2ENROLL_PIN) || pins) {
r = fido2_use_hmac_hash(
device,
rp_id ?: "io.systemd.cryptsetup",
salt, salt_size,
cid, cid_size,
pins,
/* up= */ true,
required,
ret_decrypted_key,
ret_decrypted_key_size);
if (!IN_SET(r,
-ENOANO, /* needs pin */
-ENOLCK)) /* pin incorrect */
return r;
}
pins = strv_free_erase(pins);
if (headless)
return log_error_errno(SYNTHETIC_ERRNO(ENOPKG), "PIN querying disabled via 'headless' option. Use the '$PIN' environment variable.");
r = ask_password_auto("Please enter security token PIN:", "drive-harddisk", NULL, "fido2-pin", "cryptsetup.fido2-pin", until, flags, &pins);
if (r < 0)
return log_error_errno(r, "Failed to ask for user password: %m");
@ -103,12 +110,15 @@ int find_fido2_auto_data(
size_t *ret_salt_size,
void **ret_cid,
size_t *ret_cid_size,
int *ret_keyslot) {
int *ret_keyslot,
Fido2EnrollFlags *ret_required) {
_cleanup_free_ void *cid = NULL, *salt = NULL;
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;
assert(cd);
assert(ret_salt);
@ -116,6 +126,7 @@ int find_fido2_auto_data(
assert(ret_cid);
assert(ret_cid_size);
assert(ret_keyslot);
assert(ret_required);
/* Loads FIDO2 metadata from LUKS2 JSON token headers. */
@ -172,6 +183,39 @@ int find_fido2_auto_data(
if (!rp)
return log_oom();
}
w = json_variant_by_key(v, "fido2-clientPin-required");
if (w) {
/* The "fido2-clientPin-required" field is optional. */
if (!json_variant_is_boolean(w))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"FIDO2 token data's 'fido2-clientPin-required' field is not a boolean.");
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));
}
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)
@ -186,5 +230,6 @@ int find_fido2_auto_data(
*ret_salt = TAKE_PTR(salt);
*ret_salt_size = salt_size;
*ret_keyslot = keyslot;
*ret_required = required;
return 0;
}

View File

@ -4,6 +4,7 @@
#include <sys/types.h>
#include "cryptsetup-util.h"
#include "libfido2-util.h"
#include "log.h"
#include "time-util.h"
@ -22,6 +23,8 @@ int acquire_fido2_key(
const void *key_data,
size_t key_data_size,
usec_t until,
bool headless,
Fido2EnrollFlags required,
void **ret_decrypted_key,
size_t *ret_decrypted_key_size);
@ -32,7 +35,8 @@ int find_fido2_auto_data(
size_t *ret_salt_size,
void **ret_cid,
size_t *ret_cid_size,
int *ret_keyslot);
int *ret_keyslot,
Fido2EnrollFlags *ret_required);
#else
@ -49,6 +53,8 @@ static inline int acquire_fido2_key(
const void *key_data,
size_t key_data_size,
usec_t until,
bool headless,
Fido2EnrollFlags required,
void **ret_decrypted_key,
size_t *ret_decrypted_key_size) {
@ -63,7 +69,8 @@ static inline int find_fido2_auto_data(
size_t *ret_salt_size,
void **ret_cid,
size_t *ret_cid_size,
int *ret_keyslot) {
int *ret_keyslot,
Fido2EnrollFlags *ret_required) {
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"FIDO2 token support not available.");

View File

@ -32,6 +32,7 @@ struct pkcs11_callback_data {
void *decrypted_key;
size_t decrypted_key_size;
bool free_encrypted_key;
bool headless;
};
static void pkcs11_callback_data_release(struct pkcs11_callback_data *data) {
@ -72,6 +73,7 @@ static int pkcs11_callback(
"pkcs11-pin",
"cryptsetup.pkcs11-pin",
data->until,
data->headless,
NULL);
if (r < 0)
return r;
@ -109,12 +111,14 @@ int decrypt_pkcs11_key(
const void *key_data, /* … or key_data and key_data_size (for literal keys) */
size_t key_data_size,
usec_t until,
bool headless,
void **ret_decrypted_key,
size_t *ret_decrypted_key_size) {
_cleanup_(pkcs11_callback_data_release) struct pkcs11_callback_data data = {
.friendly_name = friendly_name,
.until = until,
.headless = headless,
};
int r;

View File

@ -19,6 +19,7 @@ int decrypt_pkcs11_key(
const void *key_data,
size_t key_data_size,
usec_t until,
bool headless,
void **ret_decrypted_key,
size_t *ret_decrypted_key_size);
@ -41,6 +42,7 @@ static inline int decrypt_pkcs11_key(
const void *key_data,
size_t key_data_size,
usec_t until,
bool headless,
void **ret_decrypted_key,
size_t *ret_decrypted_key_size) {

View File

@ -79,6 +79,7 @@ static char *arg_fido2_rp_id = NULL;
static char *arg_tpm2_device = NULL;
static bool arg_tpm2_device_auto = false;
static uint32_t arg_tpm2_pcr_mask = UINT32_MAX;
static bool arg_headless = false;
STATIC_DESTRUCTOR_REGISTER(arg_cipher, freep);
STATIC_DESTRUCTOR_REGISTER(arg_hash, freep);
@ -381,6 +382,17 @@ static int parse_one_option(const char *option) {
} else if (streq(option, "try-empty-password"))
arg_try_empty_password = true;
else if ((val = startswith(option, "headless="))) {
r = parse_boolean(val);
if (r < 0) {
log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
return 0;
}
arg_headless = r;
} else if (streq(option, "headless"))
arg_headless = true;
else if (!streq(option, "x-initrd.attach"))
log_warning("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
@ -532,6 +544,9 @@ static int get_password(
assert(src);
assert(ret);
if (arg_headless)
return log_error_errno(SYNTHETIC_ERRNO(ENOPKG), "Password querying disabled via 'headless' option.");
friendly = friendly_disk_name(src, vol);
if (!friendly)
return log_oom();
@ -724,6 +739,7 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
int keyslot = arg_key_slot, r;
const char *rp_id;
const void *cid;
Fido2EnrollFlags required;
assert(cd);
assert(name);
@ -744,7 +760,8 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
&discovered_salt_size,
&discovered_cid,
&discovered_cid_size,
&keyslot);
&keyslot,
&required);
if (IN_SET(r, -ENOTUNIQ, -ENXIO))
return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
@ -752,6 +769,10 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
if (r < 0)
return r;
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.");
rp_id = discovered_rp_id;
key_data = discovered_salt;
key_data_size = discovered_salt_size;
@ -775,6 +796,8 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
key_file, arg_keyfile_size, arg_keyfile_offset,
key_data, key_data_size,
until,
arg_headless,
required,
&decrypted_key, &decrypted_key_size);
if (r >= 0)
break;
@ -895,6 +918,7 @@ static int attach_luks_or_plain_or_bitlk_by_pkcs11(
key_file, arg_keyfile_size, arg_keyfile_offset,
key_data, key_data_size,
until,
arg_headless,
&decrypted_key, &decrypted_key_size);
if (r >= 0)
break;

View File

@ -158,6 +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 | FIDO2ENROLL_UP, // FIXME: add a --lock-with-pin/up parameter like cryptenroll
&cid, &cid_size,
&salt, &salt_size,
&secret, &secret_size,

View File

@ -28,7 +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 | (h->fido2_user_presence_permitted > 0 ? FIDO2ENROLL_UP : 0), // FIXME: add a --lock-with-pin parameter like cryptenroll
&hmac,
&hmac_size);
if (r < 0)

View File

@ -1082,7 +1082,7 @@ static int server_receive_message(sd_event_source *s, int fd,
size_t buflen = datagram_size;
if (sd_dhcp_server_is_in_relay_mode(server))
/* Preallocate the additional size for DHCP Relay Agent Information Option if neeeded */
/* Preallocate the additional size for DHCP Relay Agent Information Option if needed */
buflen += relay_agent_information_length(server->agent_circuit_id, server->agent_remote_id) + 2;
message = malloc(buflen);

View File

@ -112,7 +112,7 @@ typedef enum DissectImageFlags {
DISSECT_IMAGE_READ_ONLY = DISSECT_IMAGE_DEVICE_READ_ONLY |
DISSECT_IMAGE_MOUNT_READ_ONLY,
DISSECT_IMAGE_GROWFS = 1 << 18, /* Grow file systems in partitions marked for that to the size of the partitions after mount */
DISSECT_IMAGE_MOUNT_IDMAPPED = 1 << 19, /* Mount mounts with kernel 5.12-style userns ID mapping, if file sytem type doesn't support uid=/gid= */
DISSECT_IMAGE_MOUNT_IDMAPPED = 1 << 19, /* Mount mounts with kernel 5.12-style userns ID mapping, if file system type doesn't support uid=/gid= */
} DissectImageFlags;
struct DissectedImage {

View 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),
@ -218,14 +220,14 @@ 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/user presence required */
void **ret_hmac,
size_t *ret_hmac_size) {
_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,10 +248,25 @@ 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;
if (!has_client_pin && FLAGS_SET(required, FIDO2ENROLL_PIN))
return log_error_errno(SYNTHETIC_ERRNO(EHWPOISON),
"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);
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();
@ -279,31 +296,33 @@ 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));
"Failed to %s FIDO2 user presence test: %s",
enable_disable(FLAGS_SET(required, FIDO2ENROLL_UP)),
sym_fido_strerr(r));
log_info("Security token requires user presence.");
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 (r == FIDO_ERR_PIN_REQUIRED) {
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;
if (!has_client_pin)
@ -315,7 +334,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:
@ -329,6 +349,11 @@ static int fido2_use_hmac_hash_specific_token(
case FIDO_ERR_PIN_AUTH_BLOCKED:
return log_error_errno(SYNTHETIC_ERRNO(EOWNERDEAD),
"PIN of security token is blocked, please remove/reinsert token.");
#ifdef FIDO_ERR_UV_BLOCKED
case FIDO_ERR_UV_BLOCKED:
return log_error_errno(SYNTHETIC_ERRNO(EOWNERDEAD),
"Verification of security token is blocked, please remove/reinsert token.");
#endif
case FIDO_ERR_PIN_INVALID:
return log_error_errno(SYNTHETIC_ERRNO(ENOLCK),
"PIN of security token incorrect.");
@ -366,7 +391,7 @@ int fido2_use_hmac_hash(
const void *cid,
size_t cid_size,
char **pins,
bool up, /* user presence permitted */
Fido2EnrollFlags required, /* client pin/user presence required */
void **ret_hmac,
size_t *ret_hmac_size) {
@ -379,7 +404,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, 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)
@ -414,7 +439,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, 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 */))
@ -439,6 +464,7 @@ int fido2_generate_hmac_hash(
const char *user_display_name,
const char *user_icon,
const char *askpw_icon_name,
Fido2EnrollFlags lock_with,
void **ret_cid, size_t *ret_cid_size,
void **ret_salt, size_t *ret_salt_size,
void **ret_secret, size_t *ret_secret_size,
@ -503,6 +529,21 @@ int fido2_generate_hmac_hash(
if (r < 0)
return r;
if (!has_client_pin && FLAGS_SET(lock_with, FIDO2ENROLL_PIN))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"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);
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();
@ -597,6 +638,11 @@ int fido2_generate_hmac_hash(
if (r == FIDO_ERR_PIN_AUTH_BLOCKED)
return log_notice_errno(SYNTHETIC_ERRNO(EPERM),
"Token PIN is currently blocked, please remove and reinsert token.");
#ifdef FIDO_ERR_UV_BLOCKED
if (r == FIDO_ERR_UV_BLOCKED)
return log_notice_errno(SYNTHETIC_ERRNO(EPERM),
"Token verification is currently blocked, please remove and reinsert token.");
#endif
if (r == FIDO_ERR_ACTION_TIMEOUT)
return log_error_errno(SYNTHETIC_ERRNO(ENOSTR),
"Token action timeout. (User didn't interact with token quickly enough.)");
@ -639,32 +685,41 @@ int fido2_generate_hmac_hash(
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 turn off FIDO2 assertion user presence: %s", sym_fido_strerr(r));
}
log_info("Generating secret key on FIDO2 security token.");
r = sym_fido_dev_get_assert(d, a, used_pin);
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 (has_up) {
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 on 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));
log_notice("%s%sIn order to allow secret key generation, please verify presence on security token.",
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() ? " " : "");
r = sym_fido_dev_get_assert(d, a, used_pin);
}
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),
"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.)");

View File

@ -3,6 +3,14 @@
#include "macro.h"
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;
#if HAVE_LIBFIDO2
#include <fido.h>
@ -16,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 **);
@ -80,7 +89,7 @@ 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);
@ -93,6 +102,7 @@ int fido2_generate_hmac_hash(
const char *user_display_name,
const char *user_icon,
const char *askpw_icon_name,
Fido2EnrollFlags lock_with,
void **ret_cid, size_t *ret_cid_size,
void **ret_salt, size_t *ret_salt_size,
void **ret_secret, size_t *ret_secret_size,

View File

@ -184,6 +184,7 @@ int pkcs11_token_login(
const char *key_name,
const char *credential_name,
usec_t until,
bool headless,
char **ret_used_pin) {
_cleanup_free_ char *token_uri_string = NULL, *token_uri_escaped = NULL, *id = NULL, *token_label = NULL;
@ -247,7 +248,9 @@ int pkcs11_token_login(
string_erase(e);
if (unsetenv("PIN") < 0)
return log_error_errno(errno, "Failed to unset $PIN: %m");
} else {
} else if (headless)
return log_error_errno(SYNTHETIC_ERRNO(ENOPKG), "PIN querying disabled via 'headless' option. Use the 'PIN' environment variable.");
else {
_cleanup_free_ char *text = NULL;
if (FLAGS_SET(token_info->flags, CKF_USER_PIN_FINAL_TRY))
@ -960,7 +963,7 @@ static int pkcs11_acquire_certificate_callback(
/* Called for every token matching our URI */
r = pkcs11_token_login(m, session, slot_id, token_info, data->askpw_friendly_name, data->askpw_icon_name, "pkcs11-pin", "pkcs11-pin", UINT64_MAX, &pin_used);
r = pkcs11_token_login(m, session, slot_id, token_info, data->askpw_friendly_name, data->askpw_icon_name, "pkcs11-pin", "pkcs11-pin", UINT64_MAX, false, &pin_used);
if (r < 0)
return r;

View File

@ -30,7 +30,7 @@ char *pkcs11_token_label(const CK_TOKEN_INFO *token_info);
char *pkcs11_token_manufacturer_id(const CK_TOKEN_INFO *token_info);
char *pkcs11_token_model(const CK_TOKEN_INFO *token_info);
int pkcs11_token_login(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, CK_SLOT_ID slotid, const CK_TOKEN_INFO *token_info, const char *friendly_name, const char *icon_name, const char *key_name, const char *credential_name, usec_t until, char **ret_used_pin);
int pkcs11_token_login(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, CK_SLOT_ID slotid, const CK_TOKEN_INFO *token_info, const char *friendly_name, const char *icon_name, const char *key_name, const char *credential_name, usec_t until, bool headless, char **ret_used_pin);
int pkcs11_token_find_x509_certificate(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, P11KitUri *search_uri, CK_OBJECT_HANDLE *ret_object);
#if HAVE_OPENSSL

View File

@ -29,6 +29,7 @@ static void test_strbuf(void) {
/* check the content of the buffer directly */
l = strv_parse_nulstr(sb->buf, sb->len);
assert_se(l);
assert_se(streq(l[0], "")); /* root */
assert_se(streq(l[1], "waldo"));

View File

@ -393,13 +393,10 @@ static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, Var
JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name))));
if (r < 0)
return r;
free(last_user_name);
free(last_group_name);
}
last_user_name = TAKE_PTR(user_name);
last_group_name = TAKE_PTR(group_name);
free_and_replace(last_user_name, user_name);
free_and_replace(last_group_name, group_name);
}
if (!last_user_name) {