mirror of
https://github.com/systemd/systemd.git
synced 2026-11-06 05:17:24 +00:00
firstboot: add --key= to provision kernel keyring certificates
Add --key=KEYRING:[NAME=]PATH to include certificates for the kernel keyrings systemd-keyring-setup manages, e.g. for verifying extension images, in the addon written by --addon=, as credentials keyring-setup.KEYRING.NAME, which that service picks up at boot.
This commit is contained in:
1 parent
96d6d09360
commit
4fafdc5f27
2 files changed
+114
No files matched your search
@@ -158,6 +158,22 @@
|
||||
<xi:include href="version-info.xml" xpointer="v263"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--key=<replaceable>KEYRING</replaceable>:<optional><replaceable>NAME</replaceable>=</optional><replaceable>PATH</replaceable></option></term>
|
||||
<listitem><para>Provisions a PEM-encoded X.509 certificate for one of the kernel keyrings
|
||||
<citerefentry><refentrytitle>systemd-keyring-setup.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
manages, for example to verify extension images with, as part of the addon written with
|
||||
<option>--addon=</option>. <replaceable>KEYRING</replaceable> is one of <literal>dm-verity</literal>,
|
||||
<literal>fs-verity</literal> and <literal>bpf</literal>. The file is included as credential
|
||||
<varname>keyring-setup.<replaceable>KEYRING</replaceable>.<replaceable>NAME</replaceable></varname>,
|
||||
with <replaceable>NAME</replaceable> defaulting to the file name without its suffix. It must consist
|
||||
of lowercase letters, digits, <literal>.</literal>, <literal>_</literal> and <literal>-</literal>. A
|
||||
<literal>=</literal> is only taken as the separator if it precedes any <literal>/</literal> in the
|
||||
argument. May be used multiple times.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v263"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--locale=<replaceable>LOCALE</replaceable></option></term>
|
||||
<term><option>--locale-messages=<replaceable>LOCALE</replaceable></option></term>
|
||||
|
||||
@@ -67,6 +67,7 @@
|
||||
#include "vconsole-util.h"
|
||||
#include "verbs.h"
|
||||
#include "vfat-util.h"
|
||||
#include "voa-util.h"
|
||||
|
||||
static char *arg_root = NULL;
|
||||
static char *arg_image = NULL;
|
||||
@@ -103,6 +104,7 @@ static bool arg_chrome = true;
|
||||
static bool arg_mute_console = false;
|
||||
static LabelContext *arg_label_context = NULL;
|
||||
static char *arg_addon = NULL;
|
||||
static MachineCredentialContext arg_keys = {};
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
|
||||
@@ -118,6 +120,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_kernel_cmdline, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_label_context, mac_label_context_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_addon, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_keys, machine_credential_context_done);
|
||||
|
||||
COMMAND(
|
||||
"systemd-firstboot\0",
|
||||
@@ -1420,6 +1423,86 @@ static int process_reset(int rfd) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_key_argument(const char *s) {
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
|
||||
/* Takes "KEYRING:[NAME=]PATH". The certificate in the file becomes the credential
|
||||
* "keyring-setup.KEYRING.NAME", which systemd-keyring-setup enrolls into the kernel keyring at boot,
|
||||
* with NAME defaulting to the file name without its suffix. */
|
||||
|
||||
const char *e = strchr(s, ':');
|
||||
if (!e)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing keyring in '%s', expected KEYRING:[NAME=]PATH.", s);
|
||||
|
||||
_cleanup_free_ char *keyring = strndup(s, e - s);
|
||||
if (!keyring)
|
||||
return log_oom();
|
||||
if (!STR_IN_SET(keyring, "dm-verity", "fs-verity", "bpf"))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown keyring '%s', expected dm-verity, fs-verity or bpf.", keyring);
|
||||
|
||||
/* A '=' only separates the name if it comes before any '/', so that a path containing '=' can be
|
||||
* passed without a name */
|
||||
_cleanup_free_ char *name = NULL, *path = NULL;
|
||||
const char *k = e + 1, *eq = strchr(k, '='), *slash = strchr(k, '/');
|
||||
if (eq && (!slash || eq < slash)) {
|
||||
name = strndup(k, eq - k);
|
||||
path = strdup(eq + 1);
|
||||
} else {
|
||||
_cleanup_free_ char *fn = NULL;
|
||||
r = path_extract_filename(k, &fn);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to extract file name from '%s': %m", k);
|
||||
|
||||
char *dot = strrchr(fn, '.');
|
||||
if (dot && dot != fn)
|
||||
*dot = 0;
|
||||
|
||||
name = TAKE_PTR(fn);
|
||||
path = strdup(k);
|
||||
}
|
||||
if (!name || !path)
|
||||
return log_oom();
|
||||
|
||||
if (isempty(path))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing key file path in '%s'.", s);
|
||||
if (isempty(name) || name[0] == '.')
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing key name in '%s'.", s);
|
||||
if (!voa_identifier_is_valid(name, /* allow_colon= */ false))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid key name '%s', expected lowercase letters, digits, '.', '_' and '-'.", name);
|
||||
|
||||
_cleanup_free_ char *id = strjoin("keyring-setup.", keyring, ".", name);
|
||||
if (!id)
|
||||
return log_oom();
|
||||
if (!credential_name_valid(id))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid key name '%s'.", name);
|
||||
|
||||
/* The file is read securely as it might not be a public key after all, and with the limit the
|
||||
* credential has at boot, rather than silently cut off there */
|
||||
_cleanup_(erase_and_freep) char *data = NULL;
|
||||
size_t size;
|
||||
r = read_full_file_full(
|
||||
AT_FDCWD, path,
|
||||
/* offset= */ UINT64_MAX,
|
||||
CREDENTIAL_SIZE_MAX,
|
||||
READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER,
|
||||
/* bind_name= */ NULL,
|
||||
&data, &size);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read key file '%s': %m", path);
|
||||
if (size == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENODATA), "Key file '%s' is empty.", path);
|
||||
|
||||
r = machine_credential_add(&arg_keys, id, data, size);
|
||||
if (r == -EEXIST)
|
||||
return log_error_errno(r, "Key '%s' specified more than once.", name);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add key '%s': %m", name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int addon_add_credential(MachineCredentialContext *creds, const char *id, const char *value) {
|
||||
int r;
|
||||
|
||||
@@ -1502,6 +1585,12 @@ static int write_addon(void) {
|
||||
return r;
|
||||
}
|
||||
|
||||
FOREACH_ARRAY(k, arg_keys.credentials, arg_keys.n_credentials) {
|
||||
r = machine_credential_add(&creds, k->id, k->data, k->size);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add key '%s': %m", k->id);
|
||||
}
|
||||
|
||||
if (creds.n_credentials == 0 && isempty(arg_kernel_cmdline))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No settings specified, refusing to write an empty addon.");
|
||||
|
||||
@@ -1631,6 +1720,12 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return r;
|
||||
break;
|
||||
|
||||
OPTION_LONG("key", "KEYRING:[NAME=]PATH", "Provision a certificate for a kernel keyring via the addon"):
|
||||
r = parse_key_argument(opts.arg);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
|
||||
OPTION_LONG("force", NULL, "Overwrite existing files"):
|
||||
arg_force = true;
|
||||
break;
|
||||
@@ -1859,6 +1954,9 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"--machine-id=/--setup-machine-id only works with --root= or --image=.");
|
||||
|
||||
if (arg_keys.n_credentials > 0 && !arg_addon)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--key= requires --addon=.");
|
||||
|
||||
if (arg_addon) {
|
||||
if (!arg_image)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--addon= requires --image=.");
|
||||
|
||||
Reference in new issue
Block a user