1
0
mirror of https://github.com/systemd/systemd synced 2026-04-25 16:34:50 +02:00

Compare commits

...

22 Commits

Author SHA1 Message Date
MkfsSion
70e723c000 cryptenroll,homectl: Introduce --fido2-credential-algorithm option
* Some authenticators(like Yubikey) support credential algorithm other than ES256
* Introduce a new option so users can make use of it
2022-04-22 20:22:40 +02:00
Lance
6dc18ca5dd hwdb: Fix rotation for HP Pro Tablet 408 G1 2022-04-23 01:48:33 +09:00
Benjamin Berg
386885c4e6 hwdb: Remap micmute to f20 for ASUS WMI hotkeys
For micmute userspace handles both micmute and f20, as Xorg cannot
handle the high keycode that the micmute key has. As such, adding the
remapping means that the key will work on Xorg clients and not just when
using wayland.
2022-04-22 18:24:00 +02:00
Lennart Poettering
005b126745 docs: suggest to erase /var/lib/systemd/credential.secret when preparing golden images 2022-04-22 16:51:50 +02:00
Lennart Poettering
0b3a64fed9 update TODO 2022-04-22 15:56:33 +02:00
Lennart Poettering
0f2ac643d8
Merge pull request #23157 from poettering/execute-refactor-fix
execute: refactor credential passing code, and fix two bugs
2022-04-22 15:51:41 +02:00
Lennart Poettering
42a3f23cc1 test: also test nspawn system→service inheritance of creds 2022-04-22 11:32:47 +02:00
Lennart Poettering
6d085447fa test: make sure that SetCredential=/LoadCredential fallback won#t regress 2022-04-22 11:32:47 +02:00
Lennart Poettering
94602bff1f execute: add more debug logging 2022-04-22 11:32:47 +02:00
Lennart Poettering
1d68a2e168 execute: restore ability that SetCredential= can act as fallback for LoadCredential=
If SetCredential= and LoadCredentials= are combined for the same
credential name, then the former shall act as fallback for the latter in
case the source file does not exist. That's documented, but didn't work.
Let's fix that.
2022-04-22 11:32:47 +02:00
Lennart Poettering
f344f7fdca execute: restore ability to propagate creds from further up (i.e. container manager and such)
This was broken in 3989bdc1ad7cca4d75c06cdf601fea2cb37ba337 let's
restore the functionality.

Basically, we want that if a relative name is specified as source to
load from we take it relative to the credentials dir the service manager
itself got passed.
2022-04-22 11:32:47 +02:00
Lennart Poettering
61c5a49eb2 execute: share error path between reg file/dir credential loading 2022-04-22 11:32:47 +02:00
Lennart Poettering
9e6e9d61bd execute: correct comments
This is not done first, but second.

Also, while we are at it, explain why faccessat() is OK here.
2022-04-22 11:32:47 +02:00
Lennart Poettering
9883cbb203 execute: sort directory entries when loading credentials recursively
Given that the recusive credential loading allows two ways to load the
same credentials, it's important to define a clear order so that it is
always the same one that wins.

i.e. if you use LoadCredential=foobar:/tmp/xyz and there are two files
/tmp/xyz/abc/cde and /tmp/xyz/abc_cde these would both result in a
credential foobar_abc_cde being set, hence it is important to make clear
which one shall win, and that it is always the same one.
2022-04-22 11:32:47 +02:00
Lennart Poettering
5bec447afb execute: drop 'seen_creds' set
When checking whether we already loaded a credential before, let's just
use faccessat() in the credential dir we are populating. First of all,
we already do it exactly that way when appliying SetCredential= settings
later. Secondly, this is not performance relevant, and by using
faccessat() things simply become a lot simpler.
2022-04-22 11:32:46 +02:00
Lennart Poettering
461345a164 execute: simplify 'load_creds_args' struct a bit
Given we only need a single field off the ExecLoadCredential structure
we don't have to link it as a whole, but just copy that one bit over
directly, simplifying the struct a bit.
2022-04-22 11:32:46 +02:00
Lennart Poettering
1134838615 execute: let recurse_dir() concate the cred name for us
recurse_dir() allows specifiying a freely choosable initial path to
which to append the subdirs as it descends into the tree. If we pass the
configured id there, recurse_dir() will suffix the subdir to that for
us, so that we don't have to do that manually anymore in the callback,
simplifying things a bit.
2022-04-22 11:32:46 +02:00
Lennart Poettering
3691083ce5 execute: passing NULL as second argument for recurse_dir() is equivalent to "" 2022-04-22 11:32:46 +02:00
Lennart Poettering
1451435ca5 execute: debug log if a generated recursive cred name is too long 2022-04-22 11:32:46 +02:00
Lennart Poettering
10b44e1dc1 execute: rework load_credential() not to take an ExecLoadCredential object we must synthesize
Let's just simplify the logic and pass the fields we need as regular
arguments, even if that means the function now has a lot. It's otherwise
really weird that we have to fake a local ExecLoadCredential from the
real one.
2022-04-22 11:32:46 +02:00
Lennart Poettering
9a6994e971 execute: drop double empty line 2022-04-22 10:58:41 +02:00
Lennart Poettering
6394e5cd34 execute: use ASSERT_PTR where appropriate 2022-04-22 10:58:41 +02:00
17 changed files with 238 additions and 85 deletions

5
TODO
View File

@ -78,6 +78,11 @@ Janitorial Clean-ups:
Features:
* Add ConditionCredentialExists= or so, that allows conditionalizing services
depending on whether a specific system credential is set. Usecase: a service
similar to the ssh keygen service that installs any SSH host key supplied via
system credentials into /etc/ssh.
* drop support for kernels that lack ambient capabilities support (i.e. make
4.3 new baseline). Then drop support for "!!" modifier for ExecStart= which
is only supported for such old kernels

View File

@ -53,6 +53,15 @@ boot. For that it's essential to:
[`/etc/machine-info`](https://www.freedesktop.org/software/systemd/man/machine-info.html)
which carry additional identifying information about the OS image.
5. Remove `/var/lib/systemd/credential.secret` which is used for protecting
service credentials, see
[`systemd.exec(5)`](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Credentials)
and
[`systemd-creds(1)`](https://www.freedesktop.org/software/systemd/man/systemd-creds.html)
for details. Note that by removing this file access to previously encrypted
credentials from this image is lost. The file is automatically generated if
a new credential is encrypted and the file does not exist yet.
## Boot Menu Entry Identifiers
The

View File

@ -233,6 +233,7 @@ evdev:name:Asus WMI hotkeys:dmi:bvn*:bvr*:bd*:svnASUS*:pn*:*
evdev:name:Eee PC WMI hotkeys:dmi:bvn*:bvr*:bd*:svnASUS*:pn*:*
evdev:name:Asus Laptop extra buttons:dmi:bvn*:bvr*:bd*:svnASUS*:pn*:*
KEYBOARD_KEY_6b=f21 # Touchpad Toggle
KEYBOARD_KEY_7c=f20 # Remap micmute to f20
# USB keyboard in Asus FX503VD
evdev:input:b0003v0B05p1869*

View File

@ -453,6 +453,10 @@ sensor:modalias:acpi:SMO8500*:dmi:*:svnHewlett-Packard:pnHPStream8Tablet:*
sensor:modalias:i2c:bmc150_accel:dmi:*:svnHewlett-Packard:pnHPPavilionx2Detachable:*:rn815D:*
ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
# HP Pro Tablet 408 G1
sensor:modalias:i2c:bmc150_accel:dmi:*:svnHewlett-Packard:pnHPProTablet408:*:rn8048:*
ACCEL_MOUNT_MATRIX=0, 1, 0; -1, 0, 0; 0, 0, 1
#########################################
# I.T.Works
#########################################

View File

@ -356,6 +356,19 @@
generally do not required that, and work out of the box.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--fido2-credential-algorithm=</option><replaceable>STRING</replaceable></term>
<listitem><para>Specify COSE algorithm used in credential generation. The default value is
<literal>es256</literal>. Supported values are <literal>es256</literal>, <literal>rs256</literal>
and <literal>eddsa</literal>.</para>
<para><literal>es256</literal> denotes ECDSA over NIST P-256 with SHA-256. <literal>rs256</literal>
denotes 2048-bit RSA with PKCS#1.5 padding and SHA-256. <literal>eddsa</literal> denotes
EDDSA over Curve25519 with SHA-512.</para>
<para>Note that your authenticator may not support some algorithms.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--fido2-device=</option><replaceable>PATH</replaceable></term>

View File

@ -122,6 +122,19 @@
<filename>/etc/crypttab</filename> line.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--fido2-credential-algorithm=</option><replaceable>STRING</replaceable></term>
<listitem><para>Specify COSE algorithm used in credential generation. The default value is
<literal>es256</literal>. Supported values are <literal>es256</literal>, <literal>rs256</literal>
and <literal>eddsa</literal>.</para>
<para><literal>es256</literal> denotes ECDSA over NIST P-256 with SHA-256. <literal>rs256</literal>
denotes 2048-bit RSA with PKCS#1.5 padding and SHA-256. <literal>eddsa</literal> denotes
EDDSA over Curve25519 with SHA-512.</para>
<para>Note that your authenticator may not support some algorithms.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--fido2-device=</option><replaceable>PATH</replaceable></term>

View File

@ -2598,7 +2598,9 @@ static int write_credential(
static int load_credential(
const ExecContext *context,
const ExecParameters *params,
ExecLoadCredential *lc,
const char *id,
const char *path,
bool encrypted,
const char *unit,
int read_dfd,
int write_dfd,
@ -2606,12 +2608,6 @@ static int load_credential(
bool ownership_ok,
uint64_t *left) {
assert(context);
assert(lc);
assert(unit);
assert(write_dfd >= 0);
assert(left);
ReadFullFileFlags flags = READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER;
_cleanup_(erase_and_freep) char *data = NULL;
_cleanup_free_ char *j = NULL, *bindname = NULL;
@ -2620,14 +2616,23 @@ static int load_credential(
size_t size, add;
int r;
if (path_is_absolute(lc->path) || read_dfd >= 0) {
/* If this is an absolute path, read the data directly from it, and support AF_UNIX sockets */
source = lc->path;
assert(context);
assert(params);
assert(id);
assert(path);
assert(unit);
assert(write_dfd >= 0);
assert(left);
if (path_is_absolute(path) || read_dfd >= 0) {
/* If this is an absolute path (or a directory fd is specifier relative which to read), read
* the data directly from it, and support AF_UNIX sockets */
source = path;
flags |= READ_FULL_FILE_CONNECT_SOCKET;
/* Pass some minimal info about the unit and the credential name we are looking to acquire
* via the source socket address in case we read off an AF_UNIX socket. */
if (asprintf(&bindname, "@%" PRIx64"/unit/%s/%s", random_u64(), unit, lc->id) < 0)
if (asprintf(&bindname, "@%" PRIx64"/unit/%s/%s", random_u64(), unit, id) < 0)
return -ENOMEM;
missing_ok = false;
@ -2636,7 +2641,7 @@ static int load_credential(
/* If this is a relative path, take it relative to the credentials we received
* ourselves. We don't support the AF_UNIX stuff in this mode, since we are operating
* on a credential store, i.e. this is guaranteed to be regular files. */
j = path_join(params->received_credentials, lc->path);
j = path_join(params->received_credentials, path);
if (!j)
return -ENOMEM;
@ -2648,14 +2653,14 @@ static int load_credential(
r = read_full_file_full(
read_dfd, source,
UINT64_MAX,
lc->encrypted ? CREDENTIAL_ENCRYPTED_SIZE_MAX : CREDENTIAL_SIZE_MAX,
flags | (lc->encrypted ? READ_FULL_FILE_UNBASE64 : 0),
encrypted ? CREDENTIAL_ENCRYPTED_SIZE_MAX : CREDENTIAL_SIZE_MAX,
flags | (encrypted ? READ_FULL_FILE_UNBASE64 : 0),
bindname,
&data, &size);
else
r = -ENOENT;
if (r == -ENOENT && (missing_ok || hashmap_contains(context->set_credentials, lc->id))) {
if (r == -ENOENT && (missing_ok || hashmap_contains(context->set_credentials, id))) {
/* Make a missing inherited credential non-fatal, let's just continue. After all apps
* will get clear errors if we don't pass such a missing credential on as they
* themselves will get ENOENT when trying to read them, which should not be much
@ -2663,17 +2668,17 @@ static int load_credential(
*
* Also, if the source file doesn't exist, but a fallback is set via SetCredentials=
* we are fine, too. */
log_debug_errno(r, "Couldn't read inherited credential '%s', skipping: %m", lc->path);
log_debug_errno(r, "Couldn't read inherited credential '%s', skipping: %m", path);
return 0;
}
if (r < 0)
return log_debug_errno(r, "Failed to read credential '%s': %m", lc->path);
return log_debug_errno(r, "Failed to read credential '%s': %m", path);
if (lc->encrypted) {
if (encrypted) {
_cleanup_free_ void *plaintext = NULL;
size_t plaintext_size = 0;
r = decrypt_credential_and_warn(lc->id, now(CLOCK_REALTIME), NULL, data, size, &plaintext, &plaintext_size);
r = decrypt_credential_and_warn(id, now(CLOCK_REALTIME), NULL, data, size, &plaintext, &plaintext_size);
if (r < 0)
return r;
@ -2681,24 +2686,22 @@ static int load_credential(
size = plaintext_size;
}
add = strlen(lc->id) + size;
add = strlen(id) + size;
if (add > *left)
return -E2BIG;
r = write_credential(write_dfd, lc->id, data, size, uid, ownership_ok);
r = write_credential(write_dfd, id, data, size, uid, ownership_ok);
if (r < 0)
return r;
return log_debug_errno(r, "Failed to write credential '%s': %m", id);
*left -= add;
return 0;
}
struct load_cred_args {
Set *seen_creds;
const ExecContext *context;
const ExecParameters *params;
ExecLoadCredential *parent_local_credential;
bool encrypted;
const char *unit;
int dfd;
uid_t uid;
@ -2715,8 +2718,8 @@ static int load_cred_recurse_dir_cb(
const struct statx *sx,
void *userdata) {
_cleanup_free_ char *credname = NULL, *sub_id = NULL;
struct load_cred_args *args = userdata;
struct load_cred_args *args = ASSERT_PTR(userdata);
_cleanup_free_ char *sub_id = NULL;
int r;
if (event != RECURSE_DIR_ENTRY)
@ -2725,32 +2728,32 @@ static int load_cred_recurse_dir_cb(
if (!IN_SET(de->d_type, DT_REG, DT_SOCK))
return RECURSE_DIR_CONTINUE;
credname = strreplace(path, "/", "_");
if (!credname)
return -ENOMEM;
sub_id = strjoin(args->parent_local_credential->id, "_", credname);
sub_id = strreplace(path, "/", "_");
if (!sub_id)
return -ENOMEM;
if (!credential_name_valid(sub_id))
return -EINVAL;
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Credential would get ID %s, which is not valid, refusing", sub_id);
if (set_contains(args->seen_creds, sub_id)) {
if (faccessat(args->dfd, sub_id, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) {
log_debug("Skipping credential with duplicated ID %s at %s", sub_id, path);
return RECURSE_DIR_CONTINUE;
}
if (errno != ENOENT)
return log_debug_errno(errno, "Failed to test if credential %s exists: %m", sub_id);
r = set_put_strdup(&args->seen_creds, sub_id);
if (r < 0)
return r;
r = load_credential(args->context, args->params,
&(ExecLoadCredential) {
.id = sub_id,
.path = (char *) de->d_name,
.encrypted = args->parent_local_credential->encrypted,
}, args->unit, dir_fd, args->dfd, args->uid, args->ownership_ok, args->left);
r = load_credential(
args->context,
args->params,
sub_id,
de->d_name,
args->encrypted,
args->unit,
dir_fd,
args->dfd,
args->uid,
args->ownership_ok,
args->left);
if (r < 0)
return r;
@ -2767,7 +2770,6 @@ static int acquire_credentials(
uint64_t left = CREDENTIALS_TOTAL_SIZE_MAX;
_cleanup_close_ int dfd = -1;
_cleanup_set_free_ Set *seen_creds = NULL;
ExecLoadCredential *lc;
ExecSetCredential *sc;
int r;
@ -2779,62 +2781,71 @@ static int acquire_credentials(
if (dfd < 0)
return -errno;
seen_creds = set_new(&string_hash_ops_free);
if (!seen_creds)
return -ENOMEM;
/* First, load credentials off disk (or acquire via AF_UNIX socket) */
HASHMAP_FOREACH(lc, context->load_credentials) {
_cleanup_close_ int sub_fd = -1;
/* Skip over credentials with unspecified paths. These are received by the
* service manager via the $CREDENTIALS_DIRECTORY environment variable. */
if (!is_path(lc->path) && streq(lc->id, lc->path))
continue;
/* If this is an absolute path, then try to open it as a directory. If that works, then we'll
* recurse into it. If it is an absolute path but it isn't a directory, then we'll open it as
* a regular file. Finally, if it's a relative path we will use it as a credential name to
* propagate a credential passed to us from further up. */
sub_fd = open(lc->path, O_DIRECTORY|O_CLOEXEC|O_RDONLY);
if (sub_fd < 0 && errno != ENOTDIR)
return -errno;
if (path_is_absolute(lc->path)) {
sub_fd = open(lc->path, O_DIRECTORY|O_CLOEXEC|O_RDONLY);
if (sub_fd < 0 && !IN_SET(errno,
ENOTDIR, /* Not a directory */
ENOENT)) /* Doesn't exist? */
return log_debug_errno(errno, "Failed to open '%s': %m", lc->path);
}
if (sub_fd < 0) {
r = set_put_strdup(&seen_creds, lc->id);
if (r < 0)
return r;
r = load_credential(context, params, lc, unit, -1, dfd, uid, ownership_ok, &left);
if (r < 0)
return r;
} else {
if (sub_fd < 0)
/* Regular file (incl. a credential passed in from higher up) */
r = load_credential(
context,
params,
lc->id,
lc->path,
lc->encrypted,
unit,
-1,
dfd,
uid,
ownership_ok,
&left);
else
/* Directory */
r = recurse_dir(
sub_fd,
/* path= */ "",
/* path= */ lc->id, /* recurse_dir() will suffix the subdir paths from here to the top-level id */
/* statx_mask= */ 0,
/* n_depth_max= */ UINT_MAX,
RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE,
RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE,
load_cred_recurse_dir_cb,
&(struct load_cred_args) {
.seen_creds = seen_creds,
.context = context,
.params = params,
.parent_local_credential = lc,
.encrypted = lc->encrypted,
.unit = unit,
.dfd = dfd,
.uid = uid,
.ownership_ok = ownership_ok,
.left = &left,
});
if (r < 0)
return r;
}
if (r < 0)
return r;
}
/* First we use the literally specified credentials. Note that they might be overridden again below,
* and thus act as a "default" if the same credential is specified multiple times */
/* Second, we add in literally specified credentials. If the credentials already exist, we'll not add
* them, so that they can act as a "default" if the same credential is specified multiple times. */
HASHMAP_FOREACH(sc, context->set_credentials) {
_cleanup_(erase_and_freep) void *plaintext = NULL;
const char *data;
size_t size, add;
/* Note that we check ahead of time here instead of relying on O_EXCL|O_CREAT later to return
* EEXIST if the credential already exists. That's because the TPM2-based decryption is kinda
* slow and involved, hence it's nice to be able to skip that if the credential already
* exists anyway. */
if (faccessat(dfd, sc->id, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
continue;
if (errno != ENOENT)
@ -2859,7 +2870,6 @@ static int acquire_credentials(
if (r < 0)
return r;
left -= add;
}

View File

@ -12,7 +12,8 @@ int enroll_fido2(
const void *volume_key,
size_t volume_key_size,
const char *device,
Fido2EnrollFlags lock_with) {
Fido2EnrollFlags lock_with,
int cred_alg) {
_cleanup_(erase_and_freep) void *salt = NULL, *secret = NULL;
_cleanup_(erase_and_freep) char *base64_encoded = NULL;
@ -42,6 +43,7 @@ int enroll_fido2(
/* user_icon_name= */ NULL,
/* askpw_icon_name= */ "drive-harddisk",
lock_with,
cred_alg,
&cid, &cid_size,
&salt, &salt_size,
&secret, &secret_size,

View File

@ -8,9 +8,9 @@
#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, Fido2EnrollFlags lock_with);
int enroll_fido2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, Fido2EnrollFlags lock_with, int cred_alg);
#else
static inline int enroll_fido2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, Fido2EnrollFlags lock_with) {
static inline int enroll_fido2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, Fido2EnrollFlags lock_with, int cred_alg) {
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"FIDO2 key enrollment not supported.");
}

View File

@ -39,6 +39,11 @@ 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;
#if HAVE_LIBFIDO2
static int arg_fido2_cred_alg = COSE_ES256;
#else
static int arg_fido2_cred_alg = 0;
#endif
assert_cc(sizeof(arg_wipe_slots_mask) * 8 >= _ENROLL_TYPE_MAX);
@ -89,6 +94,8 @@ static int help(void) {
" --recovery-key Enroll a recovery key\n"
" --pkcs11-token-uri=URI\n"
" Specify PKCS#11 security token URI\n"
" --fido2-credential-algorithm=STRING\n"
" Specify COSE algorithm for FIDO2 credential\n"
" --fido2-device=PATH\n"
" Enroll a FIDO2-HMAC security token\n"
" --fido2-with-client-pin=BOOL\n"
@ -129,6 +136,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_FIDO2_WITH_PIN,
ARG_FIDO2_WITH_UP,
ARG_FIDO2_WITH_UV,
ARG_FIDO2_CRED_ALG,
};
static const struct option options[] = {
@ -137,6 +145,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "password", no_argument, NULL, ARG_PASSWORD },
{ "recovery-key", no_argument, NULL, ARG_RECOVERY_KEY },
{ "pkcs11-token-uri", required_argument, NULL, ARG_PKCS11_TOKEN_URI },
{ "fido2-credential-algorithm", required_argument, NULL, ARG_FIDO2_CRED_ALG },
{ "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 },
@ -240,6 +249,12 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
case ARG_FIDO2_CRED_ALG:
r = parse_fido2_algorithm(optarg, &arg_fido2_cred_alg);
if (r < 0)
return log_error_errno(r, "Failed to parse COSE algorithm: %s", optarg);
break;
case ARG_FIDO2_DEVICE: {
_cleanup_free_ char *device = NULL;
@ -566,7 +581,7 @@ static int run(int argc, char *argv[]) {
break;
case ENROLL_FIDO2:
slot = enroll_fido2(cd, vk, vks, arg_fido2_device, arg_fido2_lock_with);
slot = enroll_fido2(cd, vk, vks, arg_fido2_device, arg_fido2_lock_with, arg_fido2_cred_alg);
break;
case ENROLL_TPM2:

View File

@ -118,7 +118,8 @@ static int add_fido2_salt(
int identity_add_fido2_parameters(
JsonVariant **v,
const char *device,
Fido2EnrollFlags lock_with) {
Fido2EnrollFlags lock_with,
int cred_alg) {
#if HAVE_LIBFIDO2
JsonVariant *un, *realm, *rn;
@ -165,6 +166,7 @@ int identity_add_fido2_parameters(
/* user_icon_name= */ NULL,
/* askpw_icon_name= */ "user-home",
lock_with,
cred_alg,
&cid, &cid_size,
&salt, &salt_size,
&secret, &secret_size,

View File

@ -4,4 +4,4 @@
#include "json.h"
#include "libfido2-util.h"
int identity_add_fido2_parameters(JsonVariant **v, const char *device, Fido2EnrollFlags lock_with);
int identity_add_fido2_parameters(JsonVariant **v, const char *device, Fido2EnrollFlags lock_with, int cred_alg);

View File

@ -61,6 +61,11 @@ 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;
#if HAVE_LIBFIDO2
static int arg_fido2_cred_alg = COSE_ES256;
#else
static int arg_fido2_cred_alg = 0;
#endif
static bool arg_recovery_key = false;
static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
static bool arg_and_resize = false;
@ -1114,7 +1119,7 @@ static int acquire_new_home_record(UserRecord **ret) {
}
STRV_FOREACH(i, arg_fido2_device) {
r = identity_add_fido2_parameters(&v, *i, arg_fido2_lock_with);
r = identity_add_fido2_parameters(&v, *i, arg_fido2_lock_with, arg_fido2_cred_alg);
if (r < 0)
return r;
}
@ -1473,7 +1478,7 @@ static int acquire_updated_home_record(
}
STRV_FOREACH(i, arg_fido2_device) {
r = identity_add_fido2_parameters(&json, *i, arg_fido2_lock_with);
r = identity_add_fido2_parameters(&json, *i, arg_fido2_lock_with, arg_fido2_cred_alg);
if (r < 0)
return r;
}
@ -2387,6 +2392,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_LUKS_EXTRA_MOUNT_OPTIONS,
ARG_AUTO_RESIZE_MODE,
ARG_REBALANCE_WEIGHT,
ARG_FIDO2_CRED_ALG,
};
static const struct option options[] = {
@ -2463,6 +2469,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "json", required_argument, NULL, ARG_JSON },
{ "export-format", required_argument, NULL, ARG_EXPORT_FORMAT },
{ "pkcs11-token-uri", required_argument, NULL, ARG_PKCS11_TOKEN_URI },
{ "fido2-credential-algorithm", required_argument, NULL, ARG_FIDO2_CRED_ALG },
{ "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 },
@ -3485,6 +3492,12 @@ static int parse_argv(int argc, char *argv[]) {
strv_uniq(arg_pkcs11_token_uri);
break;
case ARG_FIDO2_CRED_ALG:
r = parse_fido2_algorithm(optarg, &arg_fido2_cred_alg);
if (r < 0)
return log_error_errno(r, "Failed to parse COSE algorithm: %s", optarg);
break;
case ARG_FIDO2_DEVICE:
if (streq(optarg, "list"))
return fido2_list_devices();

View File

@ -450,6 +450,20 @@ static int fido2_use_hmac_hash_specific_token(
return 0;
}
/* COSE_ECDH_ES256 is not usable with fido_cred_set_type() thus it's not listed here. */
static const char *fido2_algorithm_to_string(int alg) {
switch(alg) {
case COSE_ES256:
return "es256";
case COSE_RS256:
return "rs256";
case COSE_EDDSA:
return "eddsa";
default:
return NULL;
}
}
int fido2_use_hmac_hash(
const char *device,
const char *rp_id,
@ -532,6 +546,7 @@ int fido2_generate_hmac_hash(
const char *user_icon,
const char *askpw_icon_name,
Fido2EnrollFlags lock_with,
int cred_alg,
void **ret_cid, size_t *ret_cid_size,
void **ret_salt, size_t *ret_salt_size,
void **ret_secret, size_t *ret_secret_size,
@ -628,10 +643,10 @@ int fido2_generate_hmac_hash(
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Failed to set FIDO2 credential relying party ID/name: %s", sym_fido_strerr(r));
r = sym_fido_cred_set_type(c, COSE_ES256);
r = sym_fido_cred_set_type(c, cred_alg);
if (r != FIDO_OK)
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Failed to set FIDO2 credential type to ES256: %s", sym_fido_strerr(r));
"Failed to set FIDO2 credential type to %s: %s", fido2_algorithm_to_string(cred_alg), sym_fido_strerr(r));
r = sym_fido_cred_set_user(
c,
@ -721,6 +736,9 @@ int fido2_generate_hmac_hash(
if (r == FIDO_ERR_ACTION_TIMEOUT)
return log_error_errno(SYNTHETIC_ERRNO(ENOSTR),
"Token action timeout. (User didn't interact with token quickly enough.)");
if (r == FIDO_ERR_UNSUPPORTED_ALGORITHM)
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Token doesn't support credential algorithm %s.", fido2_algorithm_to_string(cred_alg));
if (r != FIDO_OK)
return log_error_errno(SYNTHETIC_ERRNO(EIO),
"Failed to generate FIDO2 credential: %s", sym_fido_strerr(r));
@ -1124,3 +1142,24 @@ finish:
"FIDO2 tokens not supported on this build.");
#endif
}
#if HAVE_LIBFIDO2
int parse_fido2_algorithm(const char *s, int *ret) {
int a;
assert(s);
if (streq(s, "es256"))
a = COSE_ES256;
else if (streq(s, "rs256"))
a = COSE_RS256;
else if (streq(s, "eddsa"))
a = COSE_EDDSA;
else
return -EINVAL;
if (ret)
*ret = a;
return 0;
}
#endif

View File

@ -109,12 +109,18 @@ int fido2_generate_hmac_hash(
const char *user_icon,
const char *askpw_icon_name,
Fido2EnrollFlags lock_with,
int cred_alg,
void **ret_cid, size_t *ret_cid_size,
void **ret_salt, size_t *ret_salt_size,
void **ret_secret, size_t *ret_secret_size,
char **ret_usedpin,
Fido2EnrollFlags *ret_locked_with);
int parse_fido2_algorithm(const char *s, int *ret);
#else
static inline int parse_fido2_algorithm(const char *s, int *ret) {
return -EOPNOTSUPP;
}
#endif
int fido2_list_devices(void);

View File

@ -3,6 +3,7 @@
set -e
TEST_DESCRIPTION="test credentials"
NSPAWN_ARGUMENTS="--set-credential=mynspawncredential:strangevalue"
# shellcheck source=test/test-functions
. "${TEST_BASE_DIR:?}/test-functions"

View File

@ -16,6 +16,26 @@ systemd-run -p LoadCredential=passwd:/etc/passwd \
( cat /etc/passwd /etc/shadow && echo -n wuff ) | cmp /tmp/ts54-concat
rm /tmp/ts54-concat
# Test that SetCredential= acts as fallback for LoadCredential=
echo piff > /tmp/ts54-fallback
[ "$(systemd-run -p LoadCredential=paff:/tmp/ts54-fallback -p SetCredential=paff:poff --pipe --wait systemd-creds cat paff)" = "piff" ]
rm /tmp/ts54-fallback
[ "$(systemd-run -p LoadCredential=paff:/tmp/ts54-fallback -p SetCredential=paff:poff --pipe --wait systemd-creds cat paff)" = "poff" ]
if systemd-detect-virt -q -c ; then
# If this test is run in nspawn a credential should have been passed to us. See test/TEST-54-CREDS/test.sh
[ "$(systemd-creds --system cat mynspawncredential)" = "strangevalue" ]
# Test that propagation from system credential to service credential works
[ "$(systemd-run -p LoadCredential=mynspawncredential --pipe --wait systemd-creds cat mynspawncredential)" = "strangevalue" ]
# Check it also works, if we rename it while propagating it
[ "$(systemd-run -p LoadCredential=miau:mynspawncredential --pipe --wait systemd-creds cat miau)" = "strangevalue" ]
# Combine it with a fallback (which should have no effect, given the cred should be passed down)
[ "$(systemd-run -p LoadCredential=mynspawncredential -p SetCredential=mynspawncredential:zzz --pipe --wait systemd-creds cat mynspawncredential)" = "strangevalue" ]
fi
# Verify that the creds are immutable
systemd-run -p LoadCredential=passwd:/etc/passwd \
-p DynamicUser=1 \