Compare commits

...

6 Commits

Author SHA1 Message Date
Daan De Meyer 1f8da0f460
Merge 0f883f2134 into fed7857672 2024-11-07 01:11:23 +00:00
Yu Watanabe fed7857672 NEWS: fix typo
Follow-up for a6d7cc74d6.
2024-11-07 10:05:32 +09:00
Daan De Meyer 0f883f2134 ukify: Introduce --certificate-provider= option
This translates to --certificate-source=provider:<provider> for
signing tools invoked by ukify.
2024-11-06 18:09:37 +01:00
Daan De Meyer c59ed3b80d 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.
2024-11-06 18:08:26 +01:00
Daan De Meyer 44f354aa9b openssl-util: Set expected object type to private keys
Configures the store to only try to fetch private keys and nothing
else.
2024-11-06 18:07:11 +01:00
Daan De Meyer 583c305d15 bootctl: Validate private key path 2024-11-06 18:05:54 +01:00
15 changed files with 381 additions and 67 deletions

2
NEWS
View File

@ -188,7 +188,7 @@ CHANGES WITH 257 in spe:
* The systemd.machine_id= kernel command line parameter interpreted by
PID 1 now supports an additional special value: if set to "firmware"
the machine ID is initialized from the SMBIOS/DeviceTree system
UUID. (Previously this was already done autmatically in VM
UUID. (Previously this was already done automatically in VM
environments, this extends the concept to any system, but only on
explicit request via this option.)

View File

@ -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>

View File

@ -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>

View File

@ -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,13 +370,24 @@
<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>

View File

@ -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>

View File

@ -527,6 +527,17 @@
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>CertificateProvider=<replaceable>PROVIDER</replaceable></varname></term>
<term><option>--certificate-provider=<replaceable>PROVIDER</replaceable></option></term>
<listitem><para>An OpenSSL provider to be used for loading the certificate used to sign the
resulting binary and PCR measurements. This option can only be used when using
<command>systemd-sbsign</command> as the signing tool.</para>
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>SignKernel=<replaceable>BOOL</replaceable></varname></term>
<term><option>--sign-kernel</option></term>

View File

@ -956,10 +956,26 @@ 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);
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)
return log_error_errno(r, "Failed to parse private key path %s: %m", arg_private_key);
}
r = openssl_load_private_key(
arg_private_key_source_type,
arg_private_key_source,

View 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);

View 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;

View 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 CertificateSourceType arg_certificate_source_type = OPENSSL_CERTIFICATE_SOURCE_FILE;
static char *arg_certificate_source = NULL;
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);
}

View File

@ -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)

View 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);
}

View File

@ -1340,6 +1340,9 @@ static int load_key_from_provider(
if (!store)
return log_openssl_errors("Failed to open OpenSSL store via '%s'", private_key_uri);
if (OSSL_STORE_expect(store, OSSL_STORE_INFO_PKEY) == 0)
return log_openssl_errors("Failed to filter store by private keys");
_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'", private_key_uri);
@ -1479,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) {
@ -1514,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);
assert(certificate);
r = read_full_file_full(
AT_FDCWD, path, UINT64_MAX, SIZE_MAX,
READ_FULL_FILE_CONNECT_SOCKET,
NULL,
&rawcert, &rawcertsz);
switch (certificate_source_type) {
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 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 log_debug_errno(
r,
"Failed to load certificate '%s' from OpenSSL certificate source %s: %m",
certificate,
certificate_source);
return 0;
#else
@ -1603,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,

View 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,

View File

@ -264,6 +264,7 @@ class UkifyConfig:
sign_kernel: bool
signing_engine: Optional[str]
signing_provider: Optional[str]
certificate_provider: Optional[str]
signtool: Optional[type['SignTool']]
splash: Optional[Path]
stub: Path
@ -554,6 +555,11 @@ class SystemdSbSign(SignTool):
if opts.signing_provider is not None
else []
),
*(
['--certificate-source', f'provider:{opts.certificate_provider}']
if opts.certificate_provider is not None
else []
),
input_f,
'--output', output_f,
] # fmt: skip
@ -757,6 +763,10 @@ def call_systemd_measure(uki: UKI, opts: UkifyConfig, profile_start: int = 0) ->
extra += [f'--certificate={pub_key}']
elif pub_key:
extra += [f'--public-key={pub_key}']
if opts.certificate_provider is not None:
extra += [f'--certificate-source={opts.certificate_provider}']
extra += [f'--phase={phase_path}' for phase_path in group or ()]
print('+', shell_join(cmd + extra)) # type: ignore
@ -1674,6 +1684,12 @@ CONFIG_ITEMS = [
help='OpenSSL provider to use for signing',
config_key='UKI/SigningProvider',
),
ConfigItem(
'--certificate-provider',
metavar='PROVIDER',
help='OpenSSL provider to load certificate from',
config_key='UKI/CertificateProvider',
),
ConfigItem(
'--signtool',
choices=('sbsign', 'pesign', 'systemd-sbsign'),
@ -1982,11 +1998,11 @@ def finalize_options(opts: argparse.Namespace) -> None:
if opts.signing_engine and opts.signing_provider:
raise ValueError('Only one of --signing-engine= and --signing-provider= may be specified')
if opts.signing_engine is None and opts.signing_provider is None:
if opts.sb_key:
opts.sb_key = Path(opts.sb_key)
if opts.sb_cert:
opts.sb_cert = Path(opts.sb_cert)
if opts.signing_engine is None and opts.signing_provider is None and opts.sb_key:
opts.sb_key = Path(opts.sb_key)
if opts.certificate_provider is None and opts.sb_cert:
opts.sb_cert = Path(opts.sb_cert)
if bool(opts.sb_key) ^ bool(opts.sb_cert):
# one param only given, sbsign needs both
@ -2012,6 +2028,9 @@ def finalize_options(opts: argparse.Namespace) -> None:
if opts.signing_provider and opts.signtool != SystemdSbSign:
raise ValueError('--signing-provider= can only be used with--signtool=systemd-sbsign')
if opts.certificate_provider and opts.signtool != SystemdSbSign:
raise ValueError('--certificate-provider= can only be used with--signtool=systemd-sbsign')
if opts.sign_kernel and not opts.sb_key and not opts.sb_cert_name:
raise ValueError(
'--sign-kernel requires either --secureboot-private-key= and --secureboot-certificate= (for sbsign) or --secureboot-certificate-name= (for pesign) to be specified' # noqa: E501