diff options
author | Daan De Meyer <daan.j.demeyer@gmail.com> | 2024-11-06 18:08:26 +0100 |
---|---|---|
committer | Daan De Meyer <daan.j.demeyer@gmail.com> | 2024-11-07 20:30:47 +0100 |
commit | a1d46e3078a67b128a2eb93da7ae51d253b326f7 (patch) | |
tree | 7b1a4994fae1b5367e84e3766f27cd4b034c0a3d | |
parent | openssl-util: Set expected object type to private keys (diff) | |
download | systemd-a1d46e3078a67b128a2eb93da7ae51d253b326f7.tar.xz systemd-a1d46e3078a67b128a2eb93da7ae51d253b326f7.zip |
tree-wide: Introduce --certificate-source= option
This allows loading the X.509 certificate from an OpenSSL provider
instead of a file system path. This allows loading certficates directly
from hardware tokens instead of having to export them to a file on
disk first.
-rw-r--r-- | man/bootctl.xml | 12 | ||||
-rw-r--r-- | man/systemd-measure.xml | 10 | ||||
-rw-r--r-- | man/systemd-repart.xml | 25 | ||||
-rw-r--r-- | man/systemd-sbsign.xml | 9 | ||||
-rw-r--r-- | src/bootctl/bootctl-install.c | 12 | ||||
-rw-r--r-- | src/bootctl/bootctl.c | 30 | ||||
-rw-r--r-- | src/bootctl/bootctl.h | 2 | ||||
-rw-r--r-- | src/measure/measure.c | 35 | ||||
-rw-r--r-- | src/partition/repart.c | 41 | ||||
-rw-r--r-- | src/sbsign/sbsign.c | 56 | ||||
-rw-r--r-- | src/shared/openssl-util.c | 150 | ||||
-rw-r--r-- | src/shared/openssl-util.h | 15 |
12 files changed, 336 insertions, 61 deletions
diff --git a/man/bootctl.xml b/man/bootctl.xml index eab18f7575..3159f42347 100644 --- a/man/bootctl.xml +++ b/man/bootctl.xml @@ -529,8 +529,9 @@ <varlistentry> <term><option>--secure-boot-auto-enroll=yes|no</option></term> <term><option>--private-key=<replaceable>PATH/URI</replaceable></option></term> - <term><option>--private-key-source=<replaceable>TYPE[:NAME]</replaceable></option></term> + <term><option>--private-key-source=<replaceable>TYPE</replaceable>[:<replaceable>NAME</replaceable>]</option></term> <term><option>--certificate=<replaceable>PATH</replaceable></option></term> + <term><option>--certificate-source=<replaceable>TYPE</replaceable>[:<replaceable>NAME</replaceable>]</option></term> <listitem><para>Configure the ESP for secure boot auto-enrollment when invoking the <command>install</command> command. Takes a boolean argument. Disabled by default. Enabling this @@ -542,9 +543,12 @@ <para>When specifying this option, a certificate and private key have to be provided as well using the <option>--certificate=</option> and <option>--private-key=</option> options. The - <option>--certificate=</option> option takes a path to a PEM encoded X.509 certificate. The - <option>--private-key=</option> option can take a path or a URI that will be passed to the OpenSSL - engine or provider, as specified by <option>--private-key-source=</option> as a + <option>--certificate=</option> option takes a path to a PEM encoded X.509 certificate or a URI + that's passed to the OpenSSL provider configured with <option>--certificate-source</option> which + takes one of <literal>file</literal> or <literal>provider</literal>, with the latter being followed + by a specific provider identifier, separated with a colon, e.g. <literal>provider:pkcs11</literal>. + The <option>--private-key=</option> option can take a path or a URI that will be passed to the + OpenSSL engine or provider, as specified by <option>--private-key-source=</option> as a <literal>type:name</literal> tuple, such as <literal>engine:pkcs11</literal>. The specified OpenSSL signing engine or provider will be used to sign the EFI signature lists.</para> diff --git a/man/systemd-measure.xml b/man/systemd-measure.xml index b82aabac04..c7e5a5e9e2 100644 --- a/man/systemd-measure.xml +++ b/man/systemd-measure.xml @@ -188,8 +188,9 @@ <varlistentry> <term><option>--private-key=<replaceable>PATH/URI</replaceable></option></term> - <term><option>--private-key-source=<replaceable>TYPE[:NAME]</replaceable></option></term> - <term><option>--certificate=<replaceable>PATH</replaceable></option></term> + <term><option>--private-key-source=<replaceable>TYPE</replaceable>[:<replaceable>NAME</replaceable>]</option></term> + <term><option>--certificate=<replaceable>PATH/URI</replaceable></option></term> + <term><option>--certificate-source=<replaceable>TYPE</replaceable>[:<replaceable>NAME</replaceable>]</option></term> <listitem><para>As an alternative to <option>--public-key=</option> for the <command>sign</command> command, these switches can be used to sign with an hardware token. The @@ -197,6 +198,11 @@ provider, as specified by <option>--private-key-source=</option> as a type:name tuple, such as engine:pkcs11. The specified OpenSSL signing engine or provider will be used to sign.</para> + <para>The <option>--certificate=</option> option also takes a path or a URI that will be passed to + the OpenSSL provider, as specified by <option>--certificate-source=</option> as a + <literal>type:name</literal> tuple, such as <literal>provider:pkcs11</literal>. Note that unlike + <option>--private-key-source=</option> this option only supports providers and not engines.</para> + <xi:include href="version-info.xml" xpointer="v256"/></listitem> </varlistentry> diff --git a/man/systemd-repart.xml b/man/systemd-repart.xml index 1e6ffaa70f..575be14912 100644 --- a/man/systemd-repart.xml +++ b/man/systemd-repart.xml @@ -348,9 +348,9 @@ <varlistentry> <term><option>--private-key=</option></term> - <listitem><para>Takes a file system path. 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 a file system path or an engine or provider specific designation. Configures + the signing key 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="v252"/></listitem> </varlistentry> @@ -361,7 +361,7 @@ <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 + Configures how to load the private key 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> @@ -370,14 +370,25 @@ <varlistentry> <term><option>--certificate=</option></term> - <listitem><para>Takes a file system path. Configures the PEM encoded X.509 certificate to use when - creating verity signature partitions with the <varname>Verity=signature</varname> setting in - partition files.</para> + <listitem><para>Takes a file system path or a provider specific designation. Configures the PEM + encoded X.509 certificate 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="v252"/></listitem> </varlistentry> <varlistentry> + <term><option>--certificate-source=</option></term> + + <listitem><para>Takes one of <literal>file</literal>, or <literal>provider</literal>. In the latter + case, it is followed by the name of a provider, separated by colon, that will be passed to OpenSSL's + "provider" logic. Configures how to load the X.509 certificate 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="v257"/></listitem> + </varlistentry> + + <varlistentry> <term><option>--tpm2-device=</option></term> <term><option>--tpm2-pcrs=</option></term> diff --git a/man/systemd-sbsign.xml b/man/systemd-sbsign.xml index 1e42d601d6..1248377845 100644 --- a/man/systemd-sbsign.xml +++ b/man/systemd-sbsign.xml @@ -85,11 +85,16 @@ <term><option>--private-key=<replaceable>PATH/URI</replaceable></option></term> <term><option>--private-key-source=<replaceable>TYPE</replaceable>[:<replaceable>NAME</replaceable>]</option></term> <term><option>--certificate=<replaceable>PATH</replaceable></option></term> + <term><option>--certificate-source=<replaceable>TYPE</replaceable>[:<replaceable>NAME</replaceable>]</option></term> <listitem><para>Set the Secure Boot private key and certificate for use with the <command>sign</command>. The <option>--certificate=</option> option takes a path to a PEM encoded - X.509 certificate. The <option>--private-key=</option> option can take a path or a URI that will be - passed to the OpenSSL engine or provider, as specified by <option>--private-key-source=</option> as a + X.509 certificate or a URI that's passed to the OpenSSL provider configured with + <option>--certificate-source</option>. The <option>--certificate-source</option> takes one of + <literal>file</literal> or <literal>provider</literal>, with the latter being followed by a specific + provider identifier, separated with a colon, e.g. <literal>provider:pkcs11</literal>. The + <option>--private-key=</option> option can take a path or a URI that will be passed to the OpenSSL + engine or provider, as specified by <option>--private-key-source=</option> as a <literal>type:name</literal> tuple, such as <literal>engine:pkcs11</literal>. The specified OpenSSL signing engine or provider will be used to sign the PE binary.</para> diff --git a/src/bootctl/bootctl-install.c b/src/bootctl/bootctl-install.c index ebbdab0ce8..26ee2865b2 100644 --- a/src/bootctl/bootctl-install.c +++ b/src/bootctl/bootctl-install.c @@ -956,7 +956,17 @@ int verb_install(int argc, char *argv[], void *userdata) { graceful = !install && arg_graceful; /* support graceful mode for updates */ if (arg_secure_boot_auto_enroll) { - r = openssl_load_x509_certificate(arg_certificate, &certificate); + if (arg_certificate_source_type == OPENSSL_CERTIFICATE_SOURCE_FILE) { + r = parse_path_argument(arg_certificate, /*suppress_root=*/ false, &arg_certificate); + if (r < 0) + return r; + } + + r = openssl_load_x509_certificate( + arg_certificate_source_type, + arg_certificate_source, + arg_certificate, + &certificate); if (r < 0) return log_error_errno(r, "Failed to load X.509 certificate from %s: %m", arg_certificate); diff --git a/src/bootctl/bootctl.c b/src/bootctl/bootctl.c index 23a3d2f922..98721347f4 100644 --- a/src/bootctl/bootctl.c +++ b/src/bootctl/bootctl.c @@ -64,6 +64,8 @@ ImagePolicy *arg_image_policy = NULL; bool arg_varlink = false; bool arg_secure_boot_auto_enroll = false; char *arg_certificate = NULL; +CertificateSourceType arg_certificate_source_type = OPENSSL_CERTIFICATE_SOURCE_FILE; +char *arg_certificate_source = NULL; char *arg_private_key = NULL; KeySourceType arg_private_key_source_type = OPENSSL_KEY_SOURCE_FILE; char *arg_private_key_source = NULL; @@ -77,6 +79,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_image, freep); STATIC_DESTRUCTOR_REGISTER(arg_efi_boot_option_description, freep); STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep); STATIC_DESTRUCTOR_REGISTER(arg_certificate, freep); +STATIC_DESTRUCTOR_REGISTER(arg_certificate_source, freep); STATIC_DESTRUCTOR_REGISTER(arg_private_key, freep); STATIC_DESTRUCTOR_REGISTER(arg_private_key_source, freep); @@ -295,9 +298,14 @@ static int help(int argc, char *argv[], void *userdata) { " Specify how to use KEY for --private-key=. Allows\n" " an OpenSSL engine/provider to be used when setting\n" " up secure boot auto-enrollment\n" - " --certificate=PATH\n" - " PEM certificate to use when setting up secure boot\n" - " auto-enrollment\n" + " --certificate=PATH|URI\n" + " PEM certificate to use when setting up Secure Boot\n" + " auto-enrollment, or a provider specific designation\n" + " if --certificate-source= is used\n" + " --certificate-source=file|provider:PROVIDER\n" + " Specify how to interpret the certificate from\n" + " --certificate=. Allows the certificate to be loaded\n" + " from an OpenSSL provider\n" "\nSee the %2$s for details.\n", program_invocation_short_name, link, @@ -332,6 +340,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_PRINT_STUB_PATH, ARG_SECURE_BOOT_AUTO_ENROLL, ARG_CERTIFICATE, + ARG_CERTIFICATE_SOURCE, ARG_PRIVATE_KEY, ARG_PRIVATE_KEY_SOURCE, }; @@ -366,6 +375,7 @@ static int parse_argv(int argc, char *argv[]) { { "dry-run", no_argument, NULL, ARG_DRY_RUN }, { "secure-boot-auto-enroll", required_argument, NULL, ARG_SECURE_BOOT_AUTO_ENROLL }, { "certificate", required_argument, NULL, ARG_CERTIFICATE }, + { "certificate-source", required_argument, NULL, ARG_CERTIFICATE_SOURCE }, { "private-key", required_argument, NULL, ARG_PRIVATE_KEY }, { "private-key-source", required_argument, NULL, ARG_PRIVATE_KEY_SOURCE }, {} @@ -526,12 +536,20 @@ static int parse_argv(int argc, char *argv[]) { return r; break; - case ARG_CERTIFICATE: { - r = parse_path_argument(optarg, /*suppress_root=*/ false, &arg_certificate); + case ARG_CERTIFICATE: + r = free_and_strdup_warn(&arg_certificate, optarg); + if (r < 0) + return r; + break; + + case ARG_CERTIFICATE_SOURCE: + r = parse_openssl_certificate_source_argument( + optarg, + &arg_certificate_source, + &arg_certificate_source_type); if (r < 0) return r; break; - } case ARG_PRIVATE_KEY: { r = free_and_strdup_warn(&arg_private_key, optarg); diff --git a/src/bootctl/bootctl.h b/src/bootctl/bootctl.h index 8a67f5d8f8..6d0dfec47f 100644 --- a/src/bootctl/bootctl.h +++ b/src/bootctl/bootctl.h @@ -41,6 +41,8 @@ extern ImagePolicy *arg_image_policy; extern bool arg_varlink; extern bool arg_secure_boot_auto_enroll; extern char *arg_certificate; +extern CertificateSourceType arg_certificate_source_type; +extern char *arg_certificate_source; extern char *arg_private_key; extern KeySourceType arg_private_key_source_type; extern char *arg_private_key_source; diff --git a/src/measure/measure.c b/src/measure/measure.c index eacf90f08c..979426c18f 100644 --- a/src/measure/measure.c +++ b/src/measure/measure.c @@ -38,6 +38,8 @@ static KeySourceType arg_private_key_source_type = OPENSSL_KEY_SOURCE_FILE; static char *arg_private_key_source = NULL; static char *arg_public_key = NULL; static char *arg_certificate = NULL; +static char *arg_certificate_source = NULL; +static CertificateSourceType arg_certificate_source_type = OPENSSL_CERTIFICATE_SOURCE_FILE; static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_PRETTY_AUTO|SD_JSON_FORMAT_COLOR_AUTO|SD_JSON_FORMAT_OFF; static PagerFlags arg_pager_flags = 0; static bool arg_current = false; @@ -50,6 +52,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_private_key, freep); STATIC_DESTRUCTOR_REGISTER(arg_private_key_source, freep); STATIC_DESTRUCTOR_REGISTER(arg_public_key, freep); STATIC_DESTRUCTOR_REGISTER(arg_certificate, freep); +STATIC_DESTRUCTOR_REGISTER(arg_certificate_source, freep); STATIC_DESTRUCTOR_REGISTER(arg_phase, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_append, freep); @@ -87,7 +90,13 @@ static int help(int argc, char *argv[], void *userdata) { " Specify how to use KEY for --private-key=. Allows\n" " an OpenSSL engine/provider to be used for signing\n" " --public-key=KEY Public key (PEM) to validate against\n" - " --certificate=PATH PEM certificate to use when signing with a URI\n" + " --certificate=PATH|URI\n" + " PEM certificate to use for signing, or a provider\n" + " specific designation if --certificate-source= is used\n" + " --certificate-source=file|provider:PROVIDER\n" + " Specify how to interpret the certificate from\n" + " --certificate=. Allows the certificate to be loaded\n" + " from an OpenSSL provider\n" " --json=MODE Output as JSON\n" " -j Same as --json=pretty on tty, --json=short otherwise\n" " --append=PATH Load specified JSON signature, and append new signature to it\n" @@ -156,6 +165,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_PRIVATE_KEY_SOURCE, ARG_PUBLIC_KEY, ARG_CERTIFICATE, + ARG_CERTIFICATE_SOURCE, ARG_TPM2_DEVICE, ARG_JSON, ARG_PHASE, @@ -186,6 +196,7 @@ static int parse_argv(int argc, char *argv[]) { { "private-key-source", required_argument, NULL, ARG_PRIVATE_KEY_SOURCE }, { "public-key", required_argument, NULL, ARG_PUBLIC_KEY }, { "certificate", required_argument, NULL, ARG_CERTIFICATE }, + { "certificate-source", required_argument, NULL, ARG_CERTIFICATE_SOURCE }, { "json", required_argument, NULL, ARG_JSON }, { "phase", required_argument, NULL, ARG_PHASE }, { "append", required_argument, NULL, ARG_APPEND }, @@ -265,10 +276,18 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_CERTIFICATE: - r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_certificate); + r = free_and_strdup_warn(&arg_certificate, optarg); if (r < 0) return r; + break; + case ARG_CERTIFICATE_SOURCE: + r = parse_openssl_certificate_source_argument( + optarg, + &arg_certificate_source, + &arg_certificate_source_type); + if (r < 0) + return r; break; case ARG_TPM2_DEVICE: { @@ -841,7 +860,17 @@ static int verb_sign(int argc, char *argv[], void *userdata) { /* This must be done before openssl_load_private_key() otherwise it will get stuck */ if (arg_certificate) { - r = openssl_load_x509_certificate(arg_certificate, &certificate); + if (arg_certificate_source_type == OPENSSL_CERTIFICATE_SOURCE_FILE) { + r = parse_path_argument(arg_certificate, /*suppress_root=*/ false, &arg_certificate); + if (r < 0) + return r; + } + + r = openssl_load_x509_certificate( + arg_certificate_source_type, + arg_certificate_source, + arg_certificate, + &certificate); if (r < 0) return log_error_errno(r, "Failed to load X.509 certificate from %s: %m", arg_certificate); } diff --git a/src/partition/repart.c b/src/partition/repart.c index be5171d5e1..7e6fd2a29a 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -154,6 +154,8 @@ static char *arg_private_key = NULL; static KeySourceType arg_private_key_source_type = OPENSSL_KEY_SOURCE_FILE; static char *arg_private_key_source = NULL; static char *arg_certificate = NULL; +static CertificateSourceType arg_certificate_source_type = OPENSSL_CERTIFICATE_SOURCE_FILE; +static char *arg_certificate_source = NULL; static char *arg_tpm2_device = NULL; static uint32_t arg_tpm2_seal_key_handle = 0; static char *arg_tpm2_device_key = NULL; @@ -186,6 +188,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_key, erase_and_freep); STATIC_DESTRUCTOR_REGISTER(arg_private_key, freep); STATIC_DESTRUCTOR_REGISTER(arg_private_key_source, freep); STATIC_DESTRUCTOR_REGISTER(arg_certificate, freep); +STATIC_DESTRUCTOR_REGISTER(arg_certificate_source, freep); STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep); STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device_key, freep); STATIC_DESTRUCTOR_REGISTER(arg_tpm2_hash_pcr_values, freep); @@ -7808,8 +7811,14 @@ static int help(void) { " Specify how to use KEY for --private-key=. Allows\n" " an OpenSSL engine/provider to be used when generating\n" " verity roothash signatures\n" - " --certificate=PATH PEM certificate to use when generating verity\n" - " roothash signatures\n" + " --certificate=PATH|URI\n" + " PEM certificate to use when generating verity roothash\n" + " signatures, or a provider specific designation if\n" + " --certificate-source= is used\n" + " --certificate-source=file|provider:PROVIDER\n" + " Specify how to interpret the certificate from\n" + " --certificate=. Allows the certificate to be loaded\n" + " from an OpenSSL provider\n" "\n%3$sEncryption:%4$s\n" " --key-file=PATH Key to use when encrypting partitions\n" " --tpm2-device=PATH Path to TPM2 device node to use\n" @@ -7878,6 +7887,7 @@ static int parse_argv(int argc, char *argv[], X509 **ret_certificate, EVP_PKEY * ARG_PRIVATE_KEY, ARG_PRIVATE_KEY_SOURCE, ARG_CERTIFICATE, + ARG_CERTIFICATE_SOURCE, ARG_TPM2_DEVICE, ARG_TPM2_DEVICE_KEY, ARG_TPM2_SEAL_KEY_HANDLE, @@ -7922,6 +7932,7 @@ static int parse_argv(int argc, char *argv[], X509 **ret_certificate, EVP_PKEY * { "private-key", required_argument, NULL, ARG_PRIVATE_KEY }, { "private-key-source", required_argument, NULL, ARG_PRIVATE_KEY_SOURCE }, { "certificate", required_argument, NULL, ARG_CERTIFICATE }, + { "certificate-source", required_argument, NULL, ARG_CERTIFICATE_SOURCE }, { "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE }, { "tpm2-device-key", required_argument, NULL, ARG_TPM2_DEVICE_KEY }, { "tpm2-seal-key-handle", required_argument, NULL, ARG_TPM2_SEAL_KEY_HANDLE }, @@ -8130,12 +8141,20 @@ static int parse_argv(int argc, char *argv[], X509 **ret_certificate, EVP_PKEY * return r; break; - case ARG_CERTIFICATE: { - r = parse_path_argument(optarg, /*suppress_root=*/ false, &arg_certificate); + case ARG_CERTIFICATE: + r = free_and_strdup_warn(&arg_certificate, optarg); + if (r < 0) + return r; + break; + + case ARG_CERTIFICATE_SOURCE: + r = parse_openssl_certificate_source_argument( + optarg, + &arg_certificate_source, + &arg_certificate_source_type); if (r < 0) return r; break; - } case ARG_TPM2_DEVICE: { _cleanup_free_ char *device = NULL; @@ -8468,7 +8487,17 @@ static int parse_argv(int argc, char *argv[], X509 **ret_certificate, EVP_PKEY * } if (arg_certificate) { - r = openssl_load_x509_certificate(arg_certificate, &certificate); + if (arg_certificate_source_type == OPENSSL_CERTIFICATE_SOURCE_FILE) { + r = parse_path_argument(arg_certificate, /*suppress_root=*/ false, &arg_certificate); + if (r < 0) + return r; + } + + r = openssl_load_x509_certificate( + arg_certificate_source_type, + arg_certificate_source, + arg_certificate, + &certificate); if (r < 0) return log_error_errno(r, "Failed to load X.509 certificate from %s: %m", arg_certificate); } diff --git a/src/sbsign/sbsign.c b/src/sbsign/sbsign.c index 961a514dac..d65f28b4c4 100644 --- a/src/sbsign/sbsign.c +++ b/src/sbsign/sbsign.c @@ -21,12 +21,15 @@ static PagerFlags arg_pager_flags = 0; static char *arg_output = NULL; static char *arg_certificate = NULL; +static CertificateSourceType arg_certificate_source_type = OPENSSL_CERTIFICATE_SOURCE_FILE; +static char *arg_certificate_source = NULL; static char *arg_private_key = NULL; static KeySourceType arg_private_key_source_type = OPENSSL_KEY_SOURCE_FILE; static char *arg_private_key_source = NULL; STATIC_DESTRUCTOR_REGISTER(arg_output, freep); STATIC_DESTRUCTOR_REGISTER(arg_certificate, freep); +STATIC_DESTRUCTOR_REGISTER(arg_certificate_source, freep); STATIC_DESTRUCTOR_REGISTER(arg_private_key, freep); STATIC_DESTRUCTOR_REGISTER(arg_private_key_source, freep); @@ -42,13 +45,19 @@ static int help(int argc, char *argv[], void *userdata) { "\n%5$sSign binaries for EFI Secure Boot%6$s\n" "\n%3$sCommands:%4$s\n" " sign EXEFILE Sign the given binary for EFI Secure Boot\n" - " validate-key Load and validate the given private key\n" + " validate-key Load and validate the given certificate and private key\n" "\n%3$sOptions:%4$s\n" " -h --help Show this help\n" " --version Print version\n" " --no-pager Do not pipe output into a pager\n" " --output Where to write the signed PE binary\n" - " --certificate=PATH PEM certificate to use when signing with a URI\n" + " --certificate=PATH|URI\n" + " PEM certificate to use for signing, or a provider\n" + " specific designation if --certificate-source= is used\n" + " --certificate-source=file|provider:PROVIDER\n" + " Specify how to interpret the certificate from\n" + " --certificate=. Allows the certificate to be loaded\n" + " from an OpenSSL provider\n" " --private-key=KEY Private key (PEM) to sign with\n" " --private-key-source=file|provider:PROVIDER|engine:ENGINE\n" " Specify how to use KEY for --private-key=. Allows\n" @@ -70,6 +79,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_NO_PAGER, ARG_OUTPUT, ARG_CERTIFICATE, + ARG_CERTIFICATE_SOURCE, ARG_PRIVATE_KEY, ARG_PRIVATE_KEY_SOURCE, }; @@ -80,6 +90,7 @@ static int parse_argv(int argc, char *argv[]) { { "version", no_argument, NULL, ARG_VERSION }, { "output", required_argument, NULL, ARG_OUTPUT }, { "certificate", required_argument, NULL, ARG_CERTIFICATE }, + { "certificate-source", required_argument, NULL, ARG_CERTIFICATE_SOURCE }, { "private-key", required_argument, NULL, ARG_PRIVATE_KEY }, { "private-key-source", required_argument, NULL, ARG_PRIVATE_KEY_SOURCE }, {} @@ -112,10 +123,18 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_CERTIFICATE: - r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_certificate); + r = free_and_strdup_warn(&arg_certificate, optarg); if (r < 0) return r; + break; + case ARG_CERTIFICATE_SOURCE: + r = parse_openssl_certificate_source_argument( + optarg, + &arg_certificate_source, + &arg_certificate_source_type); + if (r < 0) + return r; break; case ARG_PRIVATE_KEY: @@ -168,7 +187,17 @@ static int verb_sign(int argc, char *argv[], void *userdata) { if (!arg_output) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No output specified, use --output="); - r = openssl_load_x509_certificate(arg_certificate, &certificate); + if (arg_certificate_source_type == OPENSSL_CERTIFICATE_SOURCE_FILE) { + r = parse_path_argument(arg_certificate, /*suppress_root=*/ false, &arg_certificate); + if (r < 0) + return r; + } + + r = openssl_load_x509_certificate( + arg_certificate_source_type, + arg_certificate_source, + arg_certificate, + &certificate); if (r < 0) return log_error_errno(r, "Failed to load X.509 certificate from %s: %m", arg_certificate); @@ -470,14 +499,33 @@ static int verb_sign(int argc, char *argv[], void *userdata) { } static int verb_validate_key(int argc, char *argv[], void *userdata) { + _cleanup_(X509_freep) X509 *certificate = NULL; _cleanup_(openssl_ask_password_ui_freep) OpenSSLAskPasswordUI *ui = NULL; _cleanup_(EVP_PKEY_freep) EVP_PKEY *private_key = NULL; int r; + if (!arg_certificate) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "No certificate specified, use --certificate="); + if (!arg_private_key) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No private key specified, use --private-key=."); + if (arg_certificate_source_type == OPENSSL_CERTIFICATE_SOURCE_FILE) { + r = parse_path_argument(arg_certificate, /*suppress_root=*/ false, &arg_certificate); + if (r < 0) + return r; + } + + r = openssl_load_x509_certificate( + arg_certificate_source_type, + arg_certificate_source, + arg_certificate, + &certificate); + if (r < 0) + return log_error_errno(r, "Failed to load X.509 certificate from %s: %m", arg_certificate); + if (arg_private_key_source_type == OPENSSL_KEY_SOURCE_FILE) { r = parse_path_argument(arg_private_key, /* suppress_root= */ false, &arg_private_key); if (r < 0) diff --git a/src/shared/openssl-util.c b/src/shared/openssl-util.c index 914f30989b..417f842546 100644 --- a/src/shared/openssl-util.c +++ b/src/shared/openssl-util.c @@ -1482,6 +1482,80 @@ static int openssl_ask_password_ui_new(const AskPasswordRequest *request, OpenSS *ret = TAKE_PTR(ui); return 0; } + +static int load_x509_certificate_from_file(const char *path, X509 **ret) { + _cleanup_free_ char *rawcert = NULL; + _cleanup_(X509_freep) X509 *cert = NULL; + _cleanup_(BIO_freep) BIO *cb = NULL; + size_t rawcertsz; + int r; + + assert(path); + assert(ret); + + r = read_full_file_full( + AT_FDCWD, path, UINT64_MAX, SIZE_MAX, + READ_FULL_FILE_CONNECT_SOCKET, + NULL, + &rawcert, &rawcertsz); + if (r < 0) + return log_debug_errno(r, "Failed to read certificate file '%s': %m", path); + + cb = BIO_new_mem_buf(rawcert, rawcertsz); + if (!cb) + return log_oom_debug(); + + cert = PEM_read_bio_X509(cb, NULL, NULL, NULL); + if (!cert) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed to parse X.509 certificate: %s", + ERR_error_string(ERR_get_error(), NULL)); + + if (ret) + *ret = TAKE_PTR(cert); + + return 0; +} + +static int load_x509_certificate_from_provider(const char *provider, const char *certificate_uri, X509 **ret) { + assert(provider); + assert(certificate_uri); + assert(ret); + +#if OPENSSL_VERSION_MAJOR >= 3 + /* Load the provider so that this can work without any custom written configuration in /etc/. + * Also load the 'default' as that seems to be the recommendation. */ + if (!OSSL_PROVIDER_try_load(/* ctx= */ NULL, provider, /* retain_fallbacks= */ true)) + return log_openssl_errors("Failed to load OpenSSL provider '%s'", provider); + if (!OSSL_PROVIDER_try_load(/* ctx= */ NULL, "default", /* retain_fallbacks= */ true)) + return log_openssl_errors("Failed to load OpenSSL provider 'default'"); + + _cleanup_(OSSL_STORE_closep) OSSL_STORE_CTX *store = OSSL_STORE_open( + certificate_uri, + /*ui_method=*/ NULL, + /*ui_method=*/ NULL, + /* post_process= */ NULL, + /* post_process_data= */ NULL); + if (!store) + return log_openssl_errors("Failed to open OpenSSL store via '%s'", certificate_uri); + + if (OSSL_STORE_expect(store, OSSL_STORE_INFO_CERT) == 0) + return log_openssl_errors("Failed to filter store by X.509 certificates"); + + _cleanup_(OSSL_STORE_INFO_freep) OSSL_STORE_INFO *info = OSSL_STORE_load(store); + if (!info) + return log_openssl_errors("Failed to load OpenSSL store via '%s'", certificate_uri); + + _cleanup_(X509_freep) X509 *cert = OSSL_STORE_INFO_get1_CERT(info); + if (!cert) + return log_openssl_errors("Failed to load certificate via '%s'", certificate_uri); + + *ret = TAKE_PTR(cert); + + return 0; +#else + return -EOPNOTSUPP; +#endif +} #endif OpenSSLAskPasswordUI* openssl_ask_password_ui_free(OpenSSLAskPasswordUI *ui) { @@ -1517,36 +1591,33 @@ int x509_fingerprint(X509 *cert, uint8_t buffer[static SHA256_DIGEST_SIZE]) { #endif } -int openssl_load_x509_certificate(const char *path, X509 **ret) { +int openssl_load_x509_certificate( + CertificateSourceType certificate_source_type, + const char *certificate_source, + const char *certificate, + X509 **ret) { #if HAVE_OPENSSL - _cleanup_free_ char *rawcert = NULL; - _cleanup_(X509_freep) X509 *cert = NULL; - _cleanup_(BIO_freep) BIO *cb = NULL; - size_t rawcertsz; int r; - assert(path); - assert(ret); - - r = read_full_file_full( - AT_FDCWD, path, UINT64_MAX, SIZE_MAX, - READ_FULL_FILE_CONNECT_SOCKET, - NULL, - &rawcert, &rawcertsz); - if (r < 0) - return log_debug_errno(r, "Failed to read certificate file '%s': %m", path); - - cb = BIO_new_mem_buf(rawcert, rawcertsz); - if (!cb) - return log_oom_debug(); + assert(certificate); - cert = PEM_read_bio_X509(cb, NULL, NULL, NULL); - if (!cert) - return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed to parse X.509 certificate: %s", - ERR_error_string(ERR_get_error(), NULL)); + switch (certificate_source_type) { - if (ret) - *ret = TAKE_PTR(cert); + case OPENSSL_CERTIFICATE_SOURCE_FILE: + r = load_x509_certificate_from_file(certificate, ret); + break; + case OPENSSL_CERTIFICATE_SOURCE_PROVIDER: + r = load_x509_certificate_from_provider(certificate_source, certificate, ret); + break; + default: + assert_not_reached(); + } + if (r < 0) + return log_debug_errno( + r, + "Failed to load certificate '%s' from OpenSSL certificate source %s: %m", + certificate, + certificate_source); return 0; #else @@ -1606,6 +1677,35 @@ int openssl_load_private_key( #endif } +int parse_openssl_certificate_source_argument( + const char *argument, + char **certificate_source, + CertificateSourceType *certificate_source_type) { + + CertificateSourceType type; + const char *e = NULL; + int r; + + assert(argument); + assert(certificate_source); + assert(certificate_source_type); + + if (streq(argument, "file")) + type = OPENSSL_CERTIFICATE_SOURCE_FILE; + else if ((e = startswith(argument, "provider:"))) + type = OPENSSL_CERTIFICATE_SOURCE_PROVIDER; + else + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid certificate source '%s'", argument); + + r = free_and_strdup_warn(certificate_source, e); + if (r < 0) + return r; + + *certificate_source_type = type; + + return 0; +} + int parse_openssl_key_source_argument( const char *argument, char **private_key_source, diff --git a/src/shared/openssl-util.h b/src/shared/openssl-util.h index 853aded2c7..7eb1ea15c0 100644 --- a/src/shared/openssl-util.h +++ b/src/shared/openssl-util.h @@ -6,6 +6,13 @@ #include "macro.h" #include "sha256.h" +typedef enum CertificateSourceType { + OPENSSL_CERTIFICATE_SOURCE_FILE, + OPENSSL_CERTIFICATE_SOURCE_PROVIDER, + _OPENSSL_CERTIFICATE_SOURCE_MAX, + _OPENSSL_CERTIFICATE_SOURCE_INVALID = -EINVAL, +} CertificateSourceType; + typedef enum KeySourceType { OPENSSL_KEY_SOURCE_FILE, OPENSSL_KEY_SOURCE_ENGINE, @@ -16,6 +23,8 @@ typedef enum KeySourceType { typedef struct OpenSSLAskPasswordUI OpenSSLAskPasswordUI; +int parse_openssl_certificate_source_argument(const char *argument, char **certificate_source, CertificateSourceType *certificate_source_type); + 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 @@ -182,7 +191,11 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(OpenSSLAskPasswordUI*, openssl_ask_password_ui_ int x509_fingerprint(X509 *cert, uint8_t buffer[static X509_FINGERPRINT_SIZE]); -int openssl_load_x509_certificate(const char *path, X509 **ret); +int openssl_load_x509_certificate( + CertificateSourceType certificate_source_type, + const char *certificate_source, + const char *certificate, + X509 **ret); int openssl_load_private_key( KeySourceType private_key_source_type, |