mirror of
https://github.com/systemd/systemd.git
synced 2025-03-19 22:50:17 +03:00
Merge 244efe9cef3f26d2b371a9147bb190b86d392a17 into fdab24bf6acc62d3011f9b5abdf834b4886642b2
This commit is contained in:
commit
39dfc6cf50
@ -49,6 +49,17 @@
|
||||
<xi:include href="version-info.xml" xpointer="v257"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>sign-secure-boot-database</option></term>
|
||||
|
||||
<listitem><para>Generates and signs a secure boot signature database. The signed secure boot
|
||||
signature database will be written to the path specified with <option>--output=</option>. The secure
|
||||
boot database identifier should be specified with the <option>--secure-boot-database=</option>
|
||||
option.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
@ -60,8 +71,9 @@
|
||||
<varlistentry>
|
||||
<term><option>--output=<replaceable>PATH</replaceable></option></term>
|
||||
|
||||
<listitem><para>Specifies the path where to write the signed PE binary or the data to be signed
|
||||
offline when using the <option>--prepare-offline-signing</option> option.</para>
|
||||
<listitem><para>Specifies the path where to write the signed PE binary, signed secure boot signature
|
||||
database or the data to be signed offline when using the <option>--prepare-offline-signing</option>
|
||||
option.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
</varlistentry>
|
||||
@ -73,15 +85,16 @@
|
||||
<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 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>
|
||||
<command>sign</command> and <command>sign-secure-boot-database</command> commands. The
|
||||
<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>. The
|
||||
<option>--certificate-source=</option> 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 or secure boot signature database.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
</varlistentry>
|
||||
@ -91,9 +104,9 @@
|
||||
|
||||
<listitem><para>When this option is specified, the <command>sign</command> command writes the data
|
||||
that should be signed to the path specified with <option>--output=</option> instead of writing the
|
||||
signed PE binary. This data can then be signed out of band after which the signature can be attached
|
||||
to the PE binary using the <option>--signed-data=</option> and
|
||||
<option>--signed-data-signature=</option> options.</para>
|
||||
signed PE binary or signed secure boot signature database. This data can then be signed out of band
|
||||
after which the signature can be attached to the PE binary or secure boot signature database using
|
||||
the <option>--signed-data=</option> and <option>--signed-data-signature=</option> options.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
</varlistentry>
|
||||
@ -104,7 +117,19 @@
|
||||
|
||||
<listitem><para>Configure the signed data (as written to the path specified with
|
||||
<option>--output=</option> when using the <option>--prepare-offline-signing</option> option) and
|
||||
corresponding signature for the <command>sign</command> command.</para>
|
||||
corresponding signature for the <command>sign</command> and
|
||||
<command>sign-secure-boot-database</command> commands.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--secure-boot-database=PK|KEK|db|dbx</option></term>
|
||||
|
||||
<listitem><para>Specify the secure boot signature database that should be generated and signed with
|
||||
the <command>sign-secure-boot-database</command> command. Takes one of the strings
|
||||
<varname>PK</varname>, <varname>KEK</varname>, <varname>db</varname> or <varname>dbx</varname>.
|
||||
</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
</varlistentry>
|
||||
|
@ -582,7 +582,7 @@ static int efi_timestamp(EFI_TIME *ret) {
|
||||
assert(ret);
|
||||
|
||||
r = secure_getenv_uint64("SOURCE_DATE_EPOCH", &epoch);
|
||||
if (r != -ENXIO)
|
||||
if (r < 0 && r != -ENXIO)
|
||||
log_debug_errno(r, "Failed to parse $SOURCE_DATE_EPOCH, ignoring: %m");
|
||||
|
||||
r = localtime_or_gmtime_usec(epoch != UINT64_MAX ? epoch : now(CLOCK_REALTIME), /*utc=*/ true, &tm);
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "build.h"
|
||||
#include "copy.h"
|
||||
#include "efi-fundamental.h"
|
||||
#include "efivars.h"
|
||||
#include "env-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
@ -18,6 +19,7 @@
|
||||
#include "pe-binary.h"
|
||||
#include "pretty-print.h"
|
||||
#include "stat-util.h"
|
||||
#include "strv.h"
|
||||
#include "tmpfile-util.h"
|
||||
#include "verbs.h"
|
||||
|
||||
@ -31,6 +33,7 @@ static char *arg_private_key_source = NULL;
|
||||
static bool arg_prepare_offline_signing = false;
|
||||
static char *arg_signed_data = NULL;
|
||||
static char *arg_signed_data_signature = NULL;
|
||||
static char *arg_secure_boot_database = NULL;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_output, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_certificate, freep);
|
||||
@ -40,6 +43,129 @@ STATIC_DESTRUCTOR_REGISTER(arg_private_key_source, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_signed_data, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_signed_data_signature, freep);
|
||||
|
||||
typedef struct Context {
|
||||
OpenSSLAskPasswordUI *ui;
|
||||
EVP_PKEY *private_key;
|
||||
X509 *certificate;
|
||||
int srcfd;
|
||||
struct stat srcfd_stat;
|
||||
int dstfd;
|
||||
char *tmp;
|
||||
} Context;
|
||||
|
||||
#define CONTEXT_NULL (Context) { .srcfd = -EBADF, .dstfd = -EBADF }
|
||||
|
||||
static int context_populate(int argc, char *argv[], Context *ret) {
|
||||
_cleanup_(openssl_ask_password_ui_freep) OpenSSLAskPasswordUI *ui = NULL;
|
||||
_cleanup_(EVP_PKEY_freep) EVP_PKEY *private_key = NULL;
|
||||
_cleanup_(X509_freep) X509 *certificate = NULL;
|
||||
_cleanup_close_ int srcfd = -EBADF;
|
||||
struct stat srcfd_stat = {};
|
||||
_cleanup_(unlink_and_freep) char *tmp = NULL;
|
||||
_cleanup_close_ int dstfd = -EBADF;
|
||||
int r;
|
||||
|
||||
assert(argv);
|
||||
assert(ret);
|
||||
|
||||
if (!arg_certificate)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No certificate specified, use --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) {
|
||||
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,
|
||||
arg_private_key,
|
||||
&(AskPasswordRequest) {
|
||||
.tty_fd = -EBADF,
|
||||
.id = "sbsign-private-key-pin",
|
||||
.keyring = arg_private_key,
|
||||
.credential = "sbsign.private-key-pin",
|
||||
.until = USEC_INFINITY,
|
||||
.hup_fd = -EBADF,
|
||||
},
|
||||
&private_key,
|
||||
&ui);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to load private key from %s: %m", arg_private_key);
|
||||
}
|
||||
|
||||
if (argc >= 2) {
|
||||
srcfd = open(argv[1], O_RDONLY|O_CLOEXEC);
|
||||
if (srcfd < 0)
|
||||
return log_error_errno(errno, "Failed to open %s: %m", argv[1]);
|
||||
|
||||
if (fstat(srcfd, &srcfd_stat) < 0)
|
||||
return log_error_errno(errno, "Failed to stat %s: %m", argv[1]);
|
||||
|
||||
r = stat_verify_regular(&srcfd_stat);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "%s is not a regular file: %m", argv[1]);
|
||||
}
|
||||
|
||||
if (arg_output) {
|
||||
dstfd = open_tmpfile_linkable(arg_output, O_RDWR|O_CLOEXEC, &tmp);
|
||||
if (dstfd < 0)
|
||||
return log_error_errno(dstfd, "Failed to open temporary file: %m");
|
||||
|
||||
r = fchmod_umask(dstfd, 0666);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to change temporary file mode: %m");
|
||||
}
|
||||
|
||||
*ret = (Context) {
|
||||
.ui = TAKE_PTR(ui),
|
||||
.private_key = TAKE_PTR(private_key),
|
||||
.certificate = TAKE_PTR(certificate),
|
||||
.srcfd = TAKE_FD(srcfd),
|
||||
.srcfd_stat = srcfd_stat,
|
||||
.tmp = TAKE_PTR(tmp),
|
||||
.dstfd = TAKE_FD(dstfd),
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void context_done(Context *context) {
|
||||
assert(context);
|
||||
|
||||
context->ui = openssl_ask_password_ui_free(context->ui);
|
||||
|
||||
if (context->private_key) {
|
||||
EVP_PKEY_free(context->private_key);
|
||||
context->private_key = NULL;
|
||||
}
|
||||
|
||||
if (context->certificate) {
|
||||
X509_free(context->certificate);
|
||||
context->certificate = NULL;
|
||||
}
|
||||
|
||||
context->srcfd = safe_close(context->srcfd);
|
||||
context->tmp = unlink_and_free(context->tmp);
|
||||
context->dstfd = safe_close(context->dstfd);
|
||||
}
|
||||
|
||||
static int help(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
int r;
|
||||
@ -52,10 +178,13 @@ 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"
|
||||
" sign-secure-boot-database\n"
|
||||
" Generate and sign a UEFI Secure Boot database\n"
|
||||
" for Secure Boot auto-enrollment\n"
|
||||
"\n%3$sOptions:%4$s\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Print version\n"
|
||||
" --output Where to write the signed PE binary\n"
|
||||
" --output=PATH Where to write the output\n"
|
||||
" --certificate=PATH|URI\n"
|
||||
" PEM certificate to use for signing, or a provider\n"
|
||||
" specific designation if --certificate-source= is used\n"
|
||||
@ -67,6 +196,13 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" --private-key-source=file|provider:PROVIDER|engine:ENGINE\n"
|
||||
" Specify how to use KEY for --private-key=. Allows\n"
|
||||
" an OpenSSL engine/provider to be used for signing\n"
|
||||
" --prepare-offline-signing\n"
|
||||
" Write the data that should be signed instead of the signed data"
|
||||
" --signed-data=PATH Path to the data that was signed offline\n"
|
||||
" --signed-data-signature=PATH\n"
|
||||
" Path to the raw signature of the data that was signed offline\n"
|
||||
" --secure-boot-database=PK|KEK|db|dbx\n"
|
||||
" Which UEFI Secure Boot database to generate and sign\n"
|
||||
"\nSee the %2$s for details.\n",
|
||||
program_invocation_short_name,
|
||||
link,
|
||||
@ -89,6 +225,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_PREPARE_OFFLINE_SIGNING,
|
||||
ARG_SIGNED_DATA,
|
||||
ARG_SIGNED_DATA_SIGNATURE,
|
||||
ARG_SECURE_BOOT_DATABASE,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@ -102,6 +239,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "prepare-offline-signing", no_argument, NULL, ARG_PREPARE_OFFLINE_SIGNING },
|
||||
{ "signed-data", required_argument, NULL, ARG_SIGNED_DATA },
|
||||
{ "signed-data-signature", required_argument, NULL, ARG_SIGNED_DATA_SIGNATURE },
|
||||
{ "secure-boot-database", required_argument, NULL, ARG_SECURE_BOOT_DATABASE },
|
||||
{}
|
||||
};
|
||||
|
||||
@ -177,6 +315,12 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
|
||||
break;
|
||||
|
||||
case ARG_SECURE_BOOT_DATABASE:
|
||||
r = free_and_strdup(&arg_secure_boot_database, optarg);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
break;
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@ -294,7 +438,7 @@ static int asn1_timestamp(ASN1_TIME **ret) {
|
||||
assert(ret);
|
||||
|
||||
r = secure_getenv_uint64("SOURCE_DATE_EPOCH", &epoch);
|
||||
if (r != -ENXIO)
|
||||
if (r < 0 && r != -ENXIO)
|
||||
log_debug_errno(r, "Failed to parse $SOURCE_DATE_EPOCH, ignoring: %m");
|
||||
|
||||
if (epoch == UINT64_MAX) {
|
||||
@ -333,7 +477,7 @@ static int pkcs7_new_with_attributes(
|
||||
PKCS7_SIGNER_INFO *si = NULL; /* avoid false maybe-uninitialized warning */
|
||||
r = pkcs7_new(certificate, private_key, &p7, &si);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to allocate PKCS# context: %m");
|
||||
return log_error_errno(r, "Failed to allocate PKCS#7 context: %m");
|
||||
|
||||
if (signed_attributes) {
|
||||
si->auth_attr = signed_attributes;
|
||||
@ -373,7 +517,7 @@ static int pkcs7_new_with_attributes(
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
if (PKCS7_add_signed_attribute(si, NID_pkcs9_contentType, V_ASN1_OBJECT, idc) == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to add signed attribute to pkcs7 signer info: %s",
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to add signed attribute to PKCS#7 signer info: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
*ret_p7 = TAKE_PTR(p7);
|
||||
@ -386,7 +530,7 @@ static int pkcs7_populate_data_bio(PKCS7* p7, const void *data, size_t size, BIO
|
||||
|
||||
_cleanup_(BIO_free_allp) BIO *bio = PKCS7_dataInit(p7, NULL);
|
||||
if (!bio)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to create PKCS7 data bio: %s",
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to create PKCS#7 data bio: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
int tag, class;
|
||||
@ -399,7 +543,7 @@ static int pkcs7_populate_data_bio(PKCS7* p7, const void *data, size_t size, BIO
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
if (BIO_write(bio, p, psz) < 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write to PKCS7 data bio: %s",
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write to PKCS#7 data bio: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
*ret = TAKE_PTR(bio);
|
||||
@ -437,9 +581,7 @@ static int pkcs7_add_digest_attribute(PKCS7 *p7, BIO *data, PKCS7_SIGNER_INFO *s
|
||||
}
|
||||
|
||||
static int verb_sign(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_(openssl_ask_password_ui_freep) OpenSSLAskPasswordUI *ui = NULL;
|
||||
_cleanup_(EVP_PKEY_freep) EVP_PKEY *private_key = NULL;
|
||||
_cleanup_(X509_freep) X509 *certificate = NULL;
|
||||
_cleanup_(context_done) Context ctx = CONTEXT_NULL;
|
||||
_cleanup_(x509_attribute_free_manyp) STACK_OF(X509_ATTRIBUTE) *signed_attributes = NULL;
|
||||
_cleanup_(iovec_done) struct iovec signed_attributes_signature = {};
|
||||
int r;
|
||||
@ -447,10 +589,6 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
|
||||
if (argc < 2)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No input file specified");
|
||||
|
||||
if (!arg_certificate)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"No certificate specified, use --certificate=");
|
||||
|
||||
if (!arg_private_key && !arg_signed_data_signature && !arg_prepare_offline_signing)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"No private key or signed data signature specified, use --private-key= or --signed-data-signature=.");
|
||||
@ -458,44 +596,9 @@ 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=");
|
||||
|
||||
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);
|
||||
r = context_populate(argc, argv, &ctx);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to load X.509 certificate from %s: %m", arg_certificate);
|
||||
|
||||
if (arg_private_key) {
|
||||
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,
|
||||
arg_private_key,
|
||||
&(AskPasswordRequest) {
|
||||
.tty_fd = -EBADF,
|
||||
.id = "sbsign-private-key-pin",
|
||||
.keyring = arg_private_key,
|
||||
.credential = "sbsign.private-key-pin",
|
||||
.until = USEC_INFINITY,
|
||||
.hup_fd = -EBADF,
|
||||
},
|
||||
&private_key,
|
||||
&ui);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to load private key from %s: %m", arg_private_key);
|
||||
}
|
||||
return r;
|
||||
|
||||
if (arg_signed_data) {
|
||||
_cleanup_free_ void *content = NULL;
|
||||
@ -508,7 +611,7 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
|
||||
const uint8_t *p = content;
|
||||
if (!ASN1_item_d2i((ASN1_VALUE **) &signed_attributes, &p, contentsz, ASN1_ITEM_rptr(PKCS7_ATTR_SIGN)))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to parse signed attributes: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
|
||||
if (arg_signed_data_signature) {
|
||||
@ -522,30 +625,9 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
|
||||
signed_attributes_signature = IOVEC_MAKE(TAKE_PTR(content), contentsz);
|
||||
}
|
||||
|
||||
_cleanup_close_ int srcfd = open(argv[1], O_RDONLY|O_CLOEXEC);
|
||||
if (srcfd < 0)
|
||||
return log_error_errno(errno, "Failed to open %s: %m", argv[1]);
|
||||
|
||||
struct stat st;
|
||||
if (fstat(srcfd, &st) < 0)
|
||||
return log_error_errno(errno, "Failed to stat %s: %m", argv[1]);
|
||||
|
||||
r = stat_verify_regular(&st);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "%s is not a regular file: %m", argv[1]);
|
||||
|
||||
_cleanup_(unlink_and_freep) char *tmp = NULL;
|
||||
_cleanup_close_ int dstfd = open_tmpfile_linkable(arg_output, O_RDWR|O_CLOEXEC, &tmp);
|
||||
if (dstfd < 0)
|
||||
return log_error_errno(dstfd, "Failed to open temporary file: %m");
|
||||
|
||||
r = fchmod_umask(dstfd, 0666);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to change temporary file mode: %m");
|
||||
|
||||
_cleanup_free_ void *pehash = NULL;
|
||||
size_t pehashsz;
|
||||
r = pe_hash(srcfd, EVP_sha256(), &pehash, &pehashsz);
|
||||
r = pe_hash(ctx.srcfd, EVP_sha256(), &pehash, &pehashsz);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to hash PE binary %s: %m", argv[0]);
|
||||
|
||||
@ -557,18 +639,18 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
|
||||
|
||||
_cleanup_(PKCS7_freep) PKCS7 *p7 = NULL;
|
||||
PKCS7_SIGNER_INFO *si = NULL; /* avoid false maybe-uninitialized warning */
|
||||
r = pkcs7_new_with_attributes(certificate, private_key, signed_attributes, &p7, &si);
|
||||
r = pkcs7_new_with_attributes(ctx.certificate, ctx.private_key, signed_attributes, &p7, &si);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
TAKE_PTR(signed_attributes);
|
||||
|
||||
if (arg_prepare_offline_signing) {
|
||||
_cleanup_(BIO_free_allp) BIO *bio = NULL;
|
||||
r = pkcs7_populate_data_bio(p7, idcraw, idcrawsz, &bio);
|
||||
if (r < 0)
|
||||
return r;
|
||||
_cleanup_(BIO_free_allp) BIO *bio = NULL;
|
||||
r = pkcs7_populate_data_bio(p7, idcraw, idcrawsz, &bio);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (arg_prepare_offline_signing) {
|
||||
r = pkcs7_add_digest_attribute(p7, bio, si);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -579,11 +661,11 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to convert signed attributes ASN.1 to DER: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
r = loop_write(dstfd, abuf, alen);
|
||||
r = loop_write(ctx.dstfd, abuf, alen);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to write PKCS#7 DER-encoded signed attributes blob to temporary file: %m");
|
||||
|
||||
r = link_tmpfile(dstfd, tmp, arg_output, LINK_TMPFILE_REPLACE|LINK_TMPFILE_SYNC);
|
||||
r = link_tmpfile(ctx.dstfd, ctx.tmp, arg_output, LINK_TMPFILE_REPLACE|LINK_TMPFILE_SYNC);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to link temporary file to %s: %m", arg_output);
|
||||
|
||||
@ -591,18 +673,18 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (iovec_is_set(&signed_attributes_signature))
|
||||
ASN1_STRING_set0(si->enc_digest, TAKE_PTR(signed_attributes_signature.iov_base), signed_attributes_signature.iov_len);
|
||||
else {
|
||||
_cleanup_(BIO_free_allp) BIO *bio = NULL;
|
||||
r = pkcs7_populate_data_bio(p7, idcraw, idcrawsz, &bio);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (iovec_is_set(&signed_attributes_signature)) {
|
||||
ASN1_STRING_set0(si->enc_digest,
|
||||
TAKE_PTR(signed_attributes_signature.iov_base),
|
||||
signed_attributes_signature.iov_len);
|
||||
|
||||
if (PKCS7_dataFinal(p7, bio) == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to sign data: %s",
|
||||
if (PKCS7_signatureVerify(bio, p7, si, ctx.certificate) == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "PKCS#7 signature validation failed: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
}
|
||||
|
||||
} else if (PKCS7_dataFinal(p7, bio) == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to sign data: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
_cleanup_(PKCS7_freep) PKCS7 *p7c = PKCS7_new();
|
||||
if (!p7c)
|
||||
@ -627,7 +709,7 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
if (PKCS7_set_content(p7, p7c) == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to set PKCS7 data: %s",
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to set PKCS#7 data: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
TAKE_PTR(p7c);
|
||||
@ -635,12 +717,12 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_free_ uint8_t *sig = NULL;
|
||||
int sigsz = i2d_PKCS7(p7, &sig);
|
||||
if (sigsz < 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to convert PKCS7 signature to DER: %s",
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to convert PKCS#7 signature to DER: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
_cleanup_free_ IMAGE_DOS_HEADER *dos_header = NULL;
|
||||
_cleanup_free_ PeHeader *pe_header = NULL;
|
||||
r = pe_load_headers(srcfd, &dos_header, &pe_header);
|
||||
r = pe_load_headers(ctx.srcfd, &dos_header, &pe_header);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to load headers from PE file: %m");
|
||||
|
||||
@ -649,31 +731,31 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
|
||||
if (!certificate_table)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "File lacks certificate table.");
|
||||
|
||||
r = copy_bytes(srcfd, dstfd, UINT64_MAX, COPY_REFLINK);
|
||||
r = copy_bytes(ctx.srcfd, ctx.dstfd, UINT64_MAX, COPY_REFLINK);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to copy %s to %s: %m", argv[1], tmp);
|
||||
return log_error_errno(r, "Failed to copy %s to %s: %m", argv[1], ctx.tmp);
|
||||
|
||||
off_t end = st.st_size;
|
||||
off_t end = ctx.srcfd_stat.st_size;
|
||||
ssize_t n;
|
||||
|
||||
if (st.st_size % 8 != 0) {
|
||||
if (ctx.srcfd_stat.st_size % 8 != 0) {
|
||||
if (certificate_table->VirtualAddress != 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Certificate table is not aligned to 8 bytes");
|
||||
|
||||
n = pwrite(dstfd, (const uint8_t[8]) {}, 8 - (st.st_size % 8), st.st_size);
|
||||
n = pwrite(ctx.dstfd, (const uint8_t[8]) {}, 8 - (ctx.srcfd_stat.st_size % 8), ctx.srcfd_stat.st_size);
|
||||
if (n < 0)
|
||||
return log_error_errno(errno, "Failed to write zero padding: %m");
|
||||
if (n != 8 - (st.st_size % 8))
|
||||
if (n != 8 - (ctx.srcfd_stat.st_size % 8))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short write while writing zero padding.");
|
||||
|
||||
end += n;
|
||||
}
|
||||
|
||||
uint32_t certsz = offsetof(WIN_CERTIFICATE, bCertificate) + sigsz;
|
||||
n = pwrite(dstfd,
|
||||
n = pwrite(ctx.dstfd,
|
||||
&(WIN_CERTIFICATE) {
|
||||
.wRevision = htole16(0x200),
|
||||
.wCertificateType = htole16(0x0002), /* PKCS7 signedData */
|
||||
.wCertificateType = htole16(0x0002), /* PKCS#7 signedData */
|
||||
.dwLength = htole32(ROUND_UP(certsz, 8)),
|
||||
},
|
||||
sizeof(WIN_CERTIFICATE),
|
||||
@ -685,7 +767,7 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
|
||||
|
||||
end += n;
|
||||
|
||||
n = pwrite(dstfd, sig, sigsz, end);
|
||||
n = pwrite(ctx.dstfd, sig, sigsz, end);
|
||||
if (n < 0)
|
||||
return log_error_errno(errno, "Failed to write signature: %m");
|
||||
if (n != sigsz)
|
||||
@ -694,16 +776,16 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
|
||||
end += n;
|
||||
|
||||
if (certsz % 8 != 0) {
|
||||
n = pwrite(dstfd, (const uint8_t[8]) {}, 8 - (certsz % 8), end);
|
||||
n = pwrite(ctx.dstfd, (const uint8_t[8]) {}, 8 - (certsz % 8), end);
|
||||
if (n < 0)
|
||||
return log_error_errno(errno, "Failed to write zero padding: %m");
|
||||
if ((size_t) n != 8 - (certsz % 8))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short write while writing zero padding.");
|
||||
}
|
||||
|
||||
n = pwrite(dstfd,
|
||||
n = pwrite(ctx.dstfd,
|
||||
&(IMAGE_DATA_DIRECTORY) {
|
||||
.VirtualAddress = certificate_table->VirtualAddress ?: htole32(ROUND_UP(st.st_size, 8)),
|
||||
.VirtualAddress = certificate_table->VirtualAddress ?: htole32(ROUND_UP(ctx.srcfd_stat.st_size, 8)),
|
||||
.Size = htole32(le32toh(certificate_table->Size) + ROUND_UP(certsz, 8)),
|
||||
},
|
||||
sizeof(IMAGE_DATA_DIRECTORY),
|
||||
@ -714,11 +796,11 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short write while updating PE certificate table.");
|
||||
|
||||
uint32_t checksum;
|
||||
r = pe_checksum(dstfd, &checksum);
|
||||
r = pe_checksum(ctx.dstfd, &checksum);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to calculate PE file checksum: %m");
|
||||
|
||||
n = pwrite(dstfd,
|
||||
n = pwrite(ctx.dstfd,
|
||||
&(le32_t) { htole32(checksum) },
|
||||
sizeof(le32_t),
|
||||
le32toh(dos_header->e_lfanew) + offsetof(PeHeader, optional.CheckSum));
|
||||
@ -727,7 +809,7 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
|
||||
if ((size_t) n != sizeof(le32_t))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short write while updating PE checksum.");
|
||||
|
||||
r = link_tmpfile(dstfd, tmp, arg_output, LINK_TMPFILE_REPLACE|LINK_TMPFILE_SYNC);
|
||||
r = link_tmpfile(ctx.dstfd, ctx.tmp, arg_output, LINK_TMPFILE_REPLACE|LINK_TMPFILE_SYNC);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to link temporary file to %s: %m", arg_output);
|
||||
|
||||
@ -735,10 +817,267 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int efi_timestamp(EFI_TIME *ret) {
|
||||
uint64_t epoch = UINT64_MAX;
|
||||
struct tm tm = {};
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
r = secure_getenv_uint64("SOURCE_DATE_EPOCH", &epoch);
|
||||
if (r < 0 && r != -ENXIO)
|
||||
log_debug_errno(r, "Failed to parse $SOURCE_DATE_EPOCH, ignoring: %m");
|
||||
|
||||
r = localtime_or_gmtime_usec(epoch != UINT64_MAX ? epoch : now(CLOCK_REALTIME), /*utc=*/ true, &tm);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to convert timestamp to calendar time: %m");
|
||||
|
||||
*ret = (EFI_TIME) {
|
||||
.Year = 1900 + tm.tm_year,
|
||||
/* tm_mon starts at 0, EFI_TIME months start at 1. */
|
||||
.Month = tm.tm_mon + 1,
|
||||
.Day = tm.tm_mday,
|
||||
.Hour = tm.tm_hour,
|
||||
.Minute = tm.tm_min,
|
||||
.Second = tm.tm_sec,
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int populate_secure_boot_database_bio(
|
||||
const char16_t *db,
|
||||
const EFI_GUID *guid,
|
||||
uint32_t attrs,
|
||||
const EFI_TIME *timestamp,
|
||||
const EFI_SIGNATURE_LIST *siglist,
|
||||
size_t siglistsz,
|
||||
BIO **ret) {
|
||||
|
||||
assert(db);
|
||||
assert(guid);
|
||||
assert(timestamp);
|
||||
assert(siglist);
|
||||
assert(ret);
|
||||
|
||||
_cleanup_(BIO_freep) BIO *bio = NULL;
|
||||
bio = BIO_new(BIO_s_mem());
|
||||
if (!bio)
|
||||
return log_oom();
|
||||
|
||||
/* Don't count the trailing NUL terminator. */
|
||||
if (BIO_write(bio, db, char16_strsize(db) - sizeof(char16_t)) < 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write variable name to bio");
|
||||
|
||||
if (BIO_write(bio, guid, sizeof(*guid)) < 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write variable GUID to bio");
|
||||
|
||||
if (BIO_write(bio, &attrs, sizeof(attrs)) < 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write variable attributes to bio");
|
||||
|
||||
if (BIO_write(bio, timestamp, sizeof(*timestamp)) < 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write timestamp to bio");
|
||||
|
||||
if (BIO_write(bio, siglist, siglistsz) < 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write signature list to bio");
|
||||
|
||||
*ret = TAKE_PTR(bio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verb_sign_secure_boot_database(int argc, char *argv[], void *userdata) {
|
||||
static const uint32_t attrs =
|
||||
EFI_VARIABLE_NON_VOLATILE|
|
||||
EFI_VARIABLE_BOOTSERVICE_ACCESS|
|
||||
EFI_VARIABLE_RUNTIME_ACCESS|
|
||||
EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
|
||||
_cleanup_(context_done) Context ctx = CONTEXT_NULL;
|
||||
_cleanup_(iovec_done) struct iovec signed_data = {};
|
||||
_cleanup_(iovec_done) struct iovec signed_data_signature = {};
|
||||
int r;
|
||||
|
||||
if (!arg_private_key && !arg_signed_data_signature && !arg_prepare_offline_signing)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"No private key or signed data signature specified, use --private-key= or --signed-data-signature=.");
|
||||
|
||||
if (!arg_output)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No output specified, use --output=");
|
||||
|
||||
|
||||
if (!arg_secure_boot_database)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"No secure boot database identifier specified, use --secure-boot-database=");
|
||||
|
||||
if (!STR_IN_SET(arg_secure_boot_database, "PK", "KEK", "db", "dbx"))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Secure Boot database identifier '%s' is not valid", arg_secure_boot_database);
|
||||
|
||||
r = context_populate(argc, argv, &ctx);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (arg_signed_data) {
|
||||
r = read_full_file(arg_signed_data, (char**) &signed_data.iov_base, &signed_data.iov_len);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read secure boot database signed data file '%s': %m", arg_signed_data);
|
||||
}
|
||||
|
||||
if (arg_signed_data_signature) {
|
||||
r = read_full_file(arg_signed_data_signature, (char**) &signed_data_signature.iov_base, &signed_data_signature.iov_len);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read secure boot database signature file '%s': %m", arg_signed_data_signature);
|
||||
}
|
||||
|
||||
_cleanup_free_ uint8_t *dercert = NULL;
|
||||
int dercertsz = i2d_X509(ctx.certificate, &dercert);
|
||||
if (dercertsz < 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to convert X.509 certificate to DER: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
uint32_t siglistsz = offsetof(EFI_SIGNATURE_LIST, Signatures) + offsetof(EFI_SIGNATURE_DATA, SignatureData) + dercertsz;
|
||||
/* We use malloc0() to zero-initialize the SignatureOwner field of Signatures[0]. */
|
||||
_cleanup_free_ EFI_SIGNATURE_LIST *siglist = malloc0(siglistsz);
|
||||
if (!siglist)
|
||||
return log_oom();
|
||||
|
||||
*siglist = (EFI_SIGNATURE_LIST) {
|
||||
.SignatureType = EFI_CERT_X509_GUID,
|
||||
.SignatureListSize = siglistsz,
|
||||
.SignatureSize = offsetof(EFI_SIGNATURE_DATA, SignatureData) + dercertsz,
|
||||
};
|
||||
|
||||
memcpy(siglist->Signatures[0].SignatureData, dercert, dercertsz);
|
||||
|
||||
_cleanup_free_ char16_t *db16 = utf8_to_utf16(arg_secure_boot_database, SIZE_MAX);
|
||||
if (!db16)
|
||||
return log_oom();
|
||||
|
||||
EFI_TIME timestamp;
|
||||
if (iovec_is_set(&signed_data)) {
|
||||
/* Don't count the trailing NUL terminator. */
|
||||
size_t db16sz = char16_strsize(db16) - sizeof(char16_t);
|
||||
size_t expectedsz = db16sz + sizeof(EFI_GUID) + sizeof(attrs) + sizeof(timestamp) + siglistsz;
|
||||
|
||||
if (signed_data.iov_len != expectedsz)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ERANGE),
|
||||
"The secure boot database signed data file size does not match the expected size (%s != %s)",
|
||||
FORMAT_BYTES(signed_data.iov_len),
|
||||
FORMAT_BYTES(expectedsz));
|
||||
|
||||
/* The signed data includes a timestamp which also has to go in the EFI variable descriptor
|
||||
* which includes the signature and they have to match, so we extract the timestamp from the
|
||||
* signed data so we can store it in the EFI variable descriptor later. */
|
||||
size_t tsoffset = db16sz + sizeof(EFI_GUID) + sizeof(attrs);
|
||||
memcpy_safe(×tamp, (uint8_t*) signed_data.iov_base + tsoffset, sizeof(timestamp));
|
||||
} else {
|
||||
r = efi_timestamp(×tamp);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
EFI_GUID *guid = STR_IN_SET(arg_secure_boot_database, "PK", "KEK") ? &(EFI_GUID) EFI_GLOBAL_VARIABLE
|
||||
: &(EFI_GUID) EFI_IMAGE_SECURITY_DATABASE_GUID;
|
||||
|
||||
_cleanup_(BIO_freep) BIO *bio = NULL;
|
||||
r = populate_secure_boot_database_bio(db16, guid, attrs, ×tamp, siglist, siglistsz, &bio);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (arg_prepare_offline_signing) {
|
||||
char *buf;
|
||||
long bufsz = BIO_get_mem_data(bio, &buf);
|
||||
|
||||
r = loop_write(ctx.dstfd, buf, bufsz);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to write secure boot database unsigned data blob to temporary file: %m");
|
||||
|
||||
r = link_tmpfile(ctx.dstfd, ctx.tmp, arg_output, LINK_TMPFILE_REPLACE|LINK_TMPFILE_SYNC);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to link temporary file to %s: %m", arg_output);
|
||||
|
||||
log_info("Wrote secure boot database unsigned data blob to %s", arg_output);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_cleanup_(PKCS7_freep) PKCS7 *p7 = NULL;
|
||||
PKCS7_SIGNER_INFO *si = NULL; /* avoid false maybe-uninitialized warning */
|
||||
r = pkcs7_new(ctx.certificate, ctx.private_key, &p7, &si);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to allocate PKCS#7 context: %m");
|
||||
|
||||
if (PKCS7_set_detached(p7, true) == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to set PKCS#7 detached attribute: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
_cleanup_(BIO_free_allp) BIO *p7bio = PKCS7_dataInit(p7, NULL);
|
||||
if (!p7bio)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to create PKCS#7 data bio: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
if (SMIME_crlf_copy(bio, p7bio, PKCS7_BINARY) == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to copy unsigned data to PKCS#7 data bio: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
if (iovec_is_set(&signed_data_signature)) {
|
||||
ASN1_STRING_set0(si->enc_digest,
|
||||
TAKE_PTR(signed_data_signature.iov_base),
|
||||
signed_data_signature.iov_len);
|
||||
|
||||
if (PKCS7_signatureVerify(p7bio, p7, si, ctx.certificate) == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "PKCS#7 signature validation failed: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
} else if (PKCS7_dataFinal(p7, p7bio) == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to sign PKCS#7 data: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
_cleanup_free_ uint8_t *sig = NULL;
|
||||
int sigsz = i2d_PKCS7(p7, &sig);
|
||||
if (sigsz < 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to convert PKCS#7 signature to DER: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
size_t authsz = offsetof(EFI_VARIABLE_AUTHENTICATION_2, AuthInfo.CertData) + sigsz;
|
||||
_cleanup_free_ EFI_VARIABLE_AUTHENTICATION_2 *auth = malloc(authsz);
|
||||
if (!auth)
|
||||
return log_oom();
|
||||
|
||||
*auth = (EFI_VARIABLE_AUTHENTICATION_2) {
|
||||
.TimeStamp = timestamp,
|
||||
.AuthInfo = {
|
||||
.Hdr = {
|
||||
.dwLength = offsetof(WIN_CERTIFICATE_UEFI_GUID, CertData) + sigsz,
|
||||
.wRevision = 0x0200,
|
||||
.wCertificateType = 0x0EF1, /* WIN_CERT_TYPE_EFI_GUID */
|
||||
},
|
||||
.CertType = EFI_CERT_TYPE_PKCS7_GUID,
|
||||
}
|
||||
};
|
||||
|
||||
memcpy(auth->AuthInfo.CertData, sig, sigsz);
|
||||
|
||||
r = loop_write(ctx.dstfd, auth, authsz);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to write authentication descriptor to secure boot database file: %m");
|
||||
|
||||
r = loop_write(ctx.dstfd, siglist, siglistsz);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to write signature list to secure boot database file: %m");
|
||||
|
||||
r = link_tmpfile(ctx.dstfd, ctx.tmp, arg_output, LINK_TMPFILE_REPLACE|LINK_TMPFILE_SYNC);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to link temporary file to %s: %m", arg_output);
|
||||
|
||||
log_info("Wrote signed secure boot database to %s", arg_output);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int run(int argc, char *argv[]) {
|
||||
static const Verb verbs[] = {
|
||||
{ "help", VERB_ANY, VERB_ANY, 0, help },
|
||||
{ "sign", 2, 2, 0, verb_sign },
|
||||
{ "help", VERB_ANY, VERB_ANY, 0, help },
|
||||
{ "sign", 2, 2, 0, verb_sign },
|
||||
{ "sign-secure-boot-database", 1, 1, 0, verb_sign_secure_boot_database },
|
||||
{}
|
||||
};
|
||||
int r;
|
||||
@ -749,7 +1088,7 @@ static int run(int argc, char *argv[]) {
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
return dispatch_verb(argc, argv, verbs, NULL);
|
||||
return dispatch_verb(argc, argv, verbs, /* userdata= */ NULL);
|
||||
}
|
||||
|
||||
DEFINE_MAIN_FUNCTION(run);
|
||||
|
@ -43,8 +43,8 @@ testcase_sign_systemd_boot() {
|
||||
fi
|
||||
|
||||
SD_BOOT="$(find /usr/lib/systemd/boot/efi/ -name "systemd-boot*.efi" | head -n1)"
|
||||
|
||||
(! sbverify --cert /tmp/sb.crt "$SD_BOOT")
|
||||
|
||||
/usr/lib/systemd/systemd-sbsign sign --certificate /tmp/sb.crt --private-key /tmp/sb.key --output /tmp/sdboot "$SD_BOOT"
|
||||
sbverify --cert /tmp/sb.crt /tmp/sdboot
|
||||
|
||||
@ -60,18 +60,84 @@ testcase_sign_systemd_boot_offline() {
|
||||
fi
|
||||
|
||||
SD_BOOT="$(find /usr/lib/systemd/boot/efi/ -name "systemd-boot*.efi" | head -n1)"
|
||||
(! sbverify --cert /tmp/sb.crt "$SD_BOOT")
|
||||
|
||||
export SOURCE_DATE_EPOCH="123"
|
||||
|
||||
/usr/lib/systemd/systemd-sbsign sign --certificate /tmp/sb.crt --output /tmp/signed-data.bin --prepare-offline-signing "$SD_BOOT"
|
||||
openssl dgst -sha256 -sign /tmp/sb.key -out /tmp/signed-data.sig /tmp/signed-data.bin
|
||||
/usr/lib/systemd/systemd-sbsign \
|
||||
sign \
|
||||
--certificate /tmp/sb.crt \
|
||||
--output /tmp/sdboot \
|
||||
--private-key /tmp/sb.key \
|
||||
--output /tmp/sdboot-signed-online \
|
||||
"$SD_BOOT"
|
||||
|
||||
sbverify --cert /tmp/sb.crt /tmp/sdboot-signed-online
|
||||
|
||||
/usr/lib/systemd/systemd-sbsign \
|
||||
sign \
|
||||
--certificate /tmp/sb.crt \
|
||||
--output /tmp/signed-data.bin \
|
||||
--prepare-offline-signing \
|
||||
"$SD_BOOT"
|
||||
openssl dgst -sha256 -sign /tmp/sb.key -out /tmp/signed-data.sig /tmp/signed-data.bin
|
||||
|
||||
# Make sure systemd-sbsign can't pick up the timestamp from the environment when
|
||||
# attaching the signature.
|
||||
unset SOURCE_DATE_EPOCH
|
||||
|
||||
/usr/lib/systemd/systemd-sbsign \
|
||||
sign \
|
||||
--certificate /tmp/sb.crt \
|
||||
--output /tmp/sdboot-signed-offline \
|
||||
--signed-data /tmp/signed-data.bin \
|
||||
--signed-data-signature /tmp/signed-data.sig \
|
||||
"$SD_BOOT"
|
||||
|
||||
sbverify --cert /tmp/sb.crt /tmp/sdboot
|
||||
sbverify --cert /tmp/sb.crt /tmp/sdboot-signed-offline
|
||||
|
||||
cmp /tmp/sdboot-signed-online /tmp/sdboot-signed-offline
|
||||
}
|
||||
|
||||
testcase_sign_secure_boot_database() {
|
||||
/usr/lib/systemd/systemd-sbsign \
|
||||
sign-secure-boot-database \
|
||||
--certificate /tmp/sb.crt \
|
||||
--private-key /tmp/sb.key \
|
||||
--output /tmp/PK.signed \
|
||||
--secure-boot-database PK
|
||||
}
|
||||
|
||||
testcase_sign_secure_boot_database_offline() {
|
||||
export SOURCE_DATE_EPOCH="123"
|
||||
|
||||
/usr/lib/systemd/systemd-sbsign \
|
||||
sign-secure-boot-database \
|
||||
--certificate /tmp/sb.crt \
|
||||
--private-key /tmp/sb.key \
|
||||
--output /tmp/PK.signed-online \
|
||||
--secure-boot-database PK
|
||||
|
||||
/usr/lib/systemd/systemd-sbsign \
|
||||
sign-secure-boot-database \
|
||||
--certificate /tmp/sb.crt \
|
||||
--output /tmp/signed-data.bin \
|
||||
--prepare-offline-signing \
|
||||
--secure-boot-database PK
|
||||
openssl dgst -sha256 -sign /tmp/sb.key -out /tmp/signed-data.sig /tmp/signed-data.bin
|
||||
|
||||
# Make sure systemd-sbsign can't pick up the timestamp from the environment when
|
||||
# attaching the signature.
|
||||
unset SOURCE_DATE_EPOCH
|
||||
|
||||
/usr/lib/systemd/systemd-sbsign \
|
||||
sign-secure-boot-database \
|
||||
--certificate /tmp/sb.crt \
|
||||
--output /tmp/PK.signed-offline \
|
||||
--signed-data /tmp/signed-data.bin \
|
||||
--signed-data-signature /tmp/signed-data.sig \
|
||||
--secure-boot-database PK
|
||||
|
||||
cmp /tmp/PK.signed-offline /tmp/PK.signed-online
|
||||
}
|
||||
|
||||
run_testcases
|
||||
|
Loading…
x
Reference in New Issue
Block a user