From 0a8264080a5d4b5e13e65eed80ac98a476f7fe43 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Wed, 11 Oct 2023 19:23:40 +0100 Subject: [PATCH] repart: support OpenSSL engines/providers for signing The provider API which is new requires providers, which are not widely available and don't work very well yet, so also use a fallback with the legacy engine API. --- man/systemd-repart.xml | 31 ++++++++++++++++++++++++ src/partition/repart.c | 53 ++++++++++++++++++++++++++++++++---------- 2 files changed, 72 insertions(+), 12 deletions(-) diff --git a/man/systemd-repart.xml b/man/systemd-repart.xml index b645027ee36..da5d5858459 100644 --- a/man/systemd-repart.xml +++ b/man/systemd-repart.xml @@ -354,6 +354,16 @@ + + + + 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 Verity=signature setting in partition files. + + + + @@ -616,6 +626,27 @@ systemd-confext refresh systemd-confext1. + + Generate a system extension image and sign it via PKCS11 + + The following creates a system extension DDI (sysext) for an + /usr/foo update and signs it with a hardware token via PKCS11. + + mkdir tree tree/usr tree/usr/lib/extension-release.d +echo "Hello World" > tree/usr/foo +cat > tree/usr/lib/extension-release.d/extension-release.my-foo <<EOF +ID=fedora +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-sysext refresh + + The DDI generated that way may be applied to the system with + systemd-sysext1. + + diff --git a/src/partition/repart.c b/src/partition/repart.c index 05332f9680f..6f347a6e1fa 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -6506,6 +6506,7 @@ static int help(void) { } static int parse_argv(int argc, char *argv[]) { + _cleanup_free_ char *private_key = NULL, *private_key_uri = NULL; enum { ARG_VERSION = 0x100, @@ -6526,6 +6527,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_JSON, ARG_KEY_FILE, ARG_PRIVATE_KEY, + ARG_PRIVATE_KEY_URI, ARG_CERTIFICATE, ARG_TPM2_DEVICE, ARG_TPM2_DEVICE_KEY, @@ -6566,6 +6568,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 }, { "certificate", required_argument, NULL, ARG_CERTIFICATE }, { "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE }, { "tpm2-device-key", required_argument, NULL, ARG_TPM2_DEVICE_KEY }, @@ -6751,20 +6754,14 @@ static int parse_argv(int argc, char *argv[]) { } case ARG_PRIVATE_KEY: { - _cleanup_(erase_and_freep) char *k = NULL; - size_t n = 0; - - r = read_full_file_full( - AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX, - READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET, - NULL, - &k, &n); + r = free_and_strdup_warn(&private_key, optarg); if (r < 0) - return log_error_errno(r, "Failed to read key file '%s': %m", optarg); + return r; + break; + } - EVP_PKEY_free(arg_private_key); - arg_private_key = NULL; - r = parse_private_key(k, n, &arg_private_key); + case ARG_PRIVATE_KEY_URI: { + r = free_and_strdup_warn(&private_key_uri, optarg); if (r < 0) return r; break; @@ -7101,6 +7098,38 @@ 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) { + _cleanup_(erase_and_freep) char *k = NULL; + size_t n = 0; + + r = read_full_file_full( + AT_FDCWD, private_key, UINT64_MAX, SIZE_MAX, + READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET, + NULL, + &k, &n); + if (r < 0) + return log_error_errno(r, "Failed to read key file '%s': %m", private_key); + + r = parse_private_key(k, n, &arg_private_key); + if (r < 0) + return r; + } else if (private_key_uri) { + /* 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); + if (r < 0) + return log_error_errno( + r, + "Failed to load key '%s' from OpenSSL provider: %m", + private_key); + } + return 1; } -- 2.47.3