repart: add --private-key-source and drop --private-key-uri

It turns out it's mostly PKCS11 that supports the URI format,
and other engines just take files. For example the tpm2-tss-openssl
engine just takes a sealed private key file path as the key input,
and the engine needs to be specified separately.

Add --private-key-source=file|engine:foo|provider:bar to
manually specify how to use the private key parameter.

Follow-up for 0a8264080a
This commit is contained in:
Luca Boccassi 2024-02-11 20:15:51 +00:00
parent 793ceda177
commit a73144bbdf
5 changed files with 100 additions and 61 deletions

View file

@ -129,14 +129,6 @@ All tools:
* `$SYSTEMD_VERITY_SHARING=0` — if set, sharing dm-verity devices by
using a stable `<ROOTHASH>-verity` device mapper name will be disabled.
* `$SYSTEMD_OPENSSL_KEY_LOADER`— when using OpenSSL to load a key via an engine
or a provider, can be used to force the usage of one or the other interface.
Set to 'engine' to force the usage of the old engine API, and to 'provider'
force the usage of the new provider API. If unset, the provider will be tried
first and the engine as a fallback if that fails. Providers are the new OpenSSL
3 API, but there are very few if any in a production-ready state, so engines
are still needed.
`systemctl`:
* `$SYSTEMCTL_FORCE_BUS=1` — if set, do not connect to PID 1's private D-Bus

View file

@ -355,11 +355,13 @@
</varlistentry>
<varlistentry>
<term><option>--private-key-uri=</option></term>
<term><option>--private-key-source=</option></term>
<listitem><para>Takes a URI-like string referring to a private key, that will be passed to OpenSSL's
"engine" or "provider" logic. Configures the signing key to use when creating verity signature
partitions with the <varname>Verity=signature</varname> setting in partition files.</para>
<listitem><para>Takes one of <literal>file</literal>, <literal>engine</literal> or
<literal>provider</literal>. In the latter two cases, it is followed by the name of a provider or
engine, separated by colon, that will be passed to OpenSSL's "engine" or "provider" logic.
Configures the signing mechanism to use when creating verity signature partitions with the
<varname>Verity=signature</varname> setting in partition files.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
@ -662,7 +664,7 @@ VERSION_ID=38
IMAGE_ID=my-foo
IMAGE_VERSION=7
EOF
systemd-repart --make-ddi=sysext --private-key-uri="pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=0123456789abcdef;token=Some%20Cert" --certificate=cert.crt -s tree/ /var/lib/extensions/my-foo.sysext.raw
systemd-repart --make-ddi=sysext --private-key-source=engine:pkcs11 --private-key="pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=0123456789abcdef;token=Some%20Cert" --certificate=cert.crt -s tree/ /var/lib/extensions/my-foo.sysext.raw
systemd-sysext refresh</programlisting>
<para>The DDI generated that way may be applied to the system with

View file

@ -146,6 +146,8 @@ static bool arg_legend = true;
static void *arg_key = NULL;
static size_t arg_key_size = 0;
static EVP_PKEY *arg_private_key = NULL;
static KeySourceType arg_private_key_source_type = OPENSSL_KEY_SOURCE_FILE;
static char *arg_private_key_source = NULL;
static X509 *arg_certificate = NULL;
static char *arg_tpm2_device = NULL;
static uint32_t arg_tpm2_seal_key_handle = 0;
@ -177,6 +179,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
STATIC_DESTRUCTOR_REGISTER(arg_definitions, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_key, erase_and_freep);
STATIC_DESTRUCTOR_REGISTER(arg_private_key, EVP_PKEY_freep);
STATIC_DESTRUCTOR_REGISTER(arg_private_key_source, freep);
STATIC_DESTRUCTOR_REGISTER(arg_certificate, X509_freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device_key, freep);
@ -6806,8 +6809,14 @@ static int help(void) {
" Specify disk image dissection policy\n"
" --definitions=DIR Find partition definitions in specified directory\n"
" --key-file=PATH Key to use when encrypting partitions\n"
" --private-key=PATH Private key to use when generating verity roothash\n"
" signatures\n"
" --private-key=PATH|URI\n"
" Private key to use when generating verity roothash\n"
" signatures, or an engine or provider specific\n"
" designation if --private-key-source= is used.\n"
" --private-key-source=file|provider:PROVIDER|engine:ENGINE\n"
" Specify how to use the --private-key=. Allows to use\n"
" an OpenSSL engine/provider when generating verity\n"
" roothash signatures\n"
" --certificate=PATH PEM certificate to use when generating verity\n"
" roothash signatures\n"
" --tpm2-device=PATH Path to TPM2 device node to use\n"
@ -6857,7 +6866,7 @@ static int help(void) {
}
static int parse_argv(int argc, char *argv[]) {
_cleanup_free_ char *private_key = NULL, *private_key_uri = NULL;
_cleanup_free_ char *private_key = NULL;
enum {
ARG_VERSION = 0x100,
@ -6878,7 +6887,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_JSON,
ARG_KEY_FILE,
ARG_PRIVATE_KEY,
ARG_PRIVATE_KEY_URI,
ARG_PRIVATE_KEY_SOURCE,
ARG_CERTIFICATE,
ARG_TPM2_DEVICE,
ARG_TPM2_DEVICE_KEY,
@ -6921,7 +6930,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "json", required_argument, NULL, ARG_JSON },
{ "key-file", required_argument, NULL, ARG_KEY_FILE },
{ "private-key", required_argument, NULL, ARG_PRIVATE_KEY },
{ "private-key-uri", required_argument, NULL, ARG_PRIVATE_KEY_URI },
{ "private-key-source", required_argument, NULL, ARG_PRIVATE_KEY_SOURCE },
{ "certificate", required_argument, NULL, ARG_CERTIFICATE },
{ "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE },
{ "tpm2-device-key", required_argument, NULL, ARG_TPM2_DEVICE_KEY },
@ -7115,12 +7124,14 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
case ARG_PRIVATE_KEY_URI: {
r = free_and_strdup_warn(&private_key_uri, optarg);
case ARG_PRIVATE_KEY_SOURCE:
r = parse_openssl_key_source_argument(
optarg,
&arg_private_key_source,
&arg_private_key_source_type);
if (r < 0)
return r;
break;
}
case ARG_CERTIFICATE: {
_cleanup_free_ char *cert = NULL;
@ -7465,12 +7476,7 @@ static int parse_argv(int argc, char *argv[]) {
*p = gpt_partition_type_override_architecture(*p, arg_architecture);
}
if (private_key && private_key_uri)
return log_error_errno(
SYNTHETIC_ERRNO(EINVAL),
"Cannot specify both --private-key= and --private-key-uri=.");
if (private_key) {
if (private_key && arg_private_key_source_type == OPENSSL_KEY_SOURCE_FILE) {
_cleanup_(erase_and_freep) char *k = NULL;
size_t n = 0;
@ -7485,16 +7491,22 @@ static int parse_argv(int argc, char *argv[]) {
r = parse_private_key(k, n, &arg_private_key);
if (r < 0)
return r;
} else if (private_key_uri) {
} else if (private_key &&
IN_SET(arg_private_key_source_type, OPENSSL_KEY_SOURCE_ENGINE, OPENSSL_KEY_SOURCE_PROVIDER)) {
/* This must happen after parse_x509_certificate() is called above, otherwise
* signing later will get stuck as the parsed private key won't have the
* certificate, so this block cannot be inline in ARG_PRIVATE_KEY. */
r = openssl_load_key_from_token(private_key_uri, &arg_private_key);
r = openssl_load_key_from_token(
arg_private_key_source_type,
arg_private_key_source,
private_key,
&arg_private_key);
if (r < 0)
return log_error_errno(
r,
"Failed to load key '%s' from OpenSSL provider: %m",
private_key);
"Failed to load key '%s' from OpenSSL private key source %s: %m",
private_key,
arg_private_key_source);
}
return 1;

View file

@ -1366,38 +1366,25 @@ static int load_key_from_engine(const char *engine, const char *private_key_uri,
return 0;
}
int openssl_load_key_from_token(const char *private_key_uri, EVP_PKEY **ret) {
_cleanup_free_ char *provider = NULL;
const char *colon, *e;
int r;
int openssl_load_key_from_token(
KeySourceType private_key_source_type,
const char *private_key_source,
const char *private_key,
EVP_PKEY **ret) {
assert(private_key_uri);
assert(IN_SET(private_key_source_type, OPENSSL_KEY_SOURCE_ENGINE, OPENSSL_KEY_SOURCE_PROVIDER));
assert(private_key_source);
assert(private_key);
colon = strchr(private_key_uri, ':');
if (!colon)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid URI '%s'", private_key_uri);
switch (private_key_source_type) {
provider = strndup(private_key_uri, colon - private_key_uri);
if (!provider)
return log_oom_debug();
e = secure_getenv("SYSTEMD_OPENSSL_KEY_LOADER");
if (e) {
if (streq(e, "provider"))
r = load_key_from_provider(provider, private_key_uri, ret);
else if (streq(e, "engine"))
r = load_key_from_engine(provider, private_key_uri, ret);
else
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid value for SYSTEMD_OPENSSL_KEY_LOADER: %s", e);
} else {
r = load_key_from_provider(provider, private_key_uri, ret);
if (r < 0) {
log_debug_errno(r, "Failed to load key from provider '%s', falling back to engine", provider);
r = load_key_from_engine(provider, private_key_uri, ret);
}
case OPENSSL_KEY_SOURCE_ENGINE:
return load_key_from_engine(private_key_source, private_key, ret);
case OPENSSL_KEY_SOURCE_PROVIDER:
return load_key_from_provider(private_key_source, private_key, ret);
default:
assert_not_reached();
}
return r;
}
#endif
@ -1418,3 +1405,34 @@ int x509_fingerprint(X509 *cert, uint8_t buffer[static SHA256_DIGEST_SIZE]) {
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL is not supported, cannot calculate X509 fingerprint: %m");
#endif
}
int parse_openssl_key_source_argument(
const char *argument,
char **private_key_source,
KeySourceType *private_key_source_type) {
KeySourceType type;
const char *e = NULL;
int r;
assert(argument);
assert(private_key_source);
assert(private_key_source_type);
if (streq(argument, "file"))
type = OPENSSL_KEY_SOURCE_FILE;
else if ((e = startswith(argument, "engine:")))
type = OPENSSL_KEY_SOURCE_ENGINE;
else if ((e = startswith(argument, "provider:")))
type = OPENSSL_KEY_SOURCE_PROVIDER;
else
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid private key source '%s'", argument);
r = free_and_strdup_warn(private_key_source, e);
if (r < 0)
return r;
*private_key_source_type = type;
return 0;
}

View file

@ -5,6 +5,16 @@
#include "macro.h"
#include "sha256.h"
typedef enum KeySourceType {
OPENSSL_KEY_SOURCE_FILE,
OPENSSL_KEY_SOURCE_ENGINE,
OPENSSL_KEY_SOURCE_PROVIDER,
_OPENSSL_KEY_SOURCE_MAX,
_OPENSSL_KEY_SOURCE_INVALID = -EINVAL,
} KeySourceType;
int parse_openssl_key_source_argument(const char *argument, char **private_key_source, KeySourceType *private_key_source_type);
#define X509_FINGERPRINT_SIZE SHA256_DIGEST_SIZE
#if HAVE_OPENSSL
@ -123,7 +133,7 @@ int pubkey_fingerprint(EVP_PKEY *pk, const EVP_MD *md, void **ret, size_t *ret_s
int digest_and_sign(const EVP_MD *md, EVP_PKEY *privkey, const void *data, size_t size, void **ret, size_t *ret_size);
int openssl_load_key_from_token(const char *private_key_uri, EVP_PKEY **ret);
int openssl_load_key_from_token(KeySourceType private_key_source_type, const char *private_key_source, const char *private_key, EVP_PKEY **ret);
#else
@ -140,7 +150,12 @@ static inline void *EVP_PKEY_free(EVP_PKEY *p) {
return NULL;
}
static inline int openssl_load_key_from_token(const char *private_key_uri, EVP_PKEY **ret) {
static inline int openssl_load_key_from_token(
KeySourceType private_key_source_type,
const char *private_key_source,
const char *private_key,
EVP_PKEY **ret) {
return -EOPNOTSUPP;
}