mirror of
https://github.com/systemd/systemd
synced 2026-04-26 00:45:09 +02:00
Compare commits
22 Commits
127927b2c2
...
70e723c000
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
70e723c000 | ||
|
|
6dc18ca5dd | ||
|
|
386885c4e6 | ||
|
|
005b126745 | ||
|
|
0b3a64fed9 | ||
|
|
0f2ac643d8 | ||
|
|
42a3f23cc1 | ||
|
|
6d085447fa | ||
|
|
94602bff1f | ||
|
|
1d68a2e168 | ||
|
|
f344f7fdca | ||
|
|
61c5a49eb2 | ||
|
|
9e6e9d61bd | ||
|
|
9883cbb203 | ||
|
|
5bec447afb | ||
|
|
461345a164 | ||
|
|
1134838615 | ||
|
|
3691083ce5 | ||
|
|
1451435ca5 | ||
|
|
10b44e1dc1 | ||
|
|
9a6994e971 | ||
|
|
6394e5cd34 |
5
TODO
5
TODO
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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*
|
||||
|
||||
@ -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
|
||||
#########################################
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
@ -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,44 +2781,50 @@ 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. */
|
||||
|
||||
if (path_is_absolute(lc->path)) {
|
||||
sub_fd = open(lc->path, O_DIRECTORY|O_CLOEXEC|O_RDONLY);
|
||||
if (sub_fd < 0 && errno != ENOTDIR)
|
||||
return -errno;
|
||||
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,
|
||||
@ -2826,15 +2834,18 @@ static int acquire_credentials(
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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.");
|
||||
}
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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 \
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user