]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
tree-wide: Introduce --certificate-source= option
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Wed, 6 Nov 2024 17:08:26 +0000 (18:08 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Thu, 7 Nov 2024 19:30:47 +0000 (20:30 +0100)
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.

12 files changed:
man/bootctl.xml
man/systemd-measure.xml
man/systemd-repart.xml
man/systemd-sbsign.xml
src/bootctl/bootctl-install.c
src/bootctl/bootctl.c
src/bootctl/bootctl.h
src/measure/measure.c
src/partition/repart.c
src/sbsign/sbsign.c
src/shared/openssl-util.c
src/shared/openssl-util.h

index eab18f7575a513ddafb283815c3d29517c1c0bfa..3159f42347db3b55dc312e9a8d4e9641df31277a 100644 (file)
       <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
 
         <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>
 
index b82aabac0494173239c0f8b1881ef972df5f0ddc..c7e5a5e9e2171822f55b5c11c3e8d5e877f9d923 100644 (file)
 
       <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
         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>
 
index 1e6ffaa70fbdac7e2705e797fdf96fc33394075f..575be14912defaeeb402bd2034e6d2e16e808c05 100644 (file)
       <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>
         <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>
       <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>
index 1e42d601d6750f5bdfadbcfb6f582f630719abb5..1248377845f5d334e4040fa17652fbec92f35a6f 100644 (file)
         <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>
 
index ebbdab0ce8a882eab43f6ff73664db94660a3b68..26ee2865b2f22083495107cc404d1c008187b2ee 100644 (file)
@@ -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);
 
index 23a3d2f9228519d6a7dc442a015954ecaa66f9f0..98721347f4eac7238aa99d4aa1207488fe50fab9 100644 (file)
@@ -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);
index 8a67f5d8f88ee6e87981983f4751cc4ac70930b4..6d0dfec47f0dcebe1d2add3922753c053059cf72 100644 (file)
@@ -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;
index eacf90f08c098f320ff94e304663194218bcd811..979426c18fd2d913cdfc92fbc404acb0b441563b 100644 (file)
@@ -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);
         }
index be5171d5e18e58c7cc3abab4b3fc42e26adab8f0..7e6fd2a29a4d9486c503ab85840db2b647af701b 100644 (file)
@@ -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);
         }
index 961a514dac60e2b443814caac6096ab2a620efff..d65f28b4c4e00c6f52c05fd9b99edbd64c40ce29 100644 (file)
 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)
index 914f30989bb127be8fa21006897833a28d9b8f05..417f84254680f9642c732f77b47f66a36f90887a 100644 (file)
@@ -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,
index 853aded2c740a948d4cf40d0fc8e1891576f49e7..7eb1ea15c0fb83b13f3f1a9176eb54cefd0e30ab 100644 (file)
@@ -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,