]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
bootctl: Add --secure-boot-auto-enroll 34948/head
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Thu, 31 Oct 2024 22:33:36 +0000 (23:33 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Sun, 3 Nov 2024 09:46:17 +0000 (10:46 +0100)
When specified, bootctl install will also set up secure boot
auto-enrollment. For now, We sign all variables using the same
certificate and key pair.

man/bootctl.xml
src/basic/efivars.h
src/basic/utf8.c
src/basic/utf8.h
src/boot/bootctl-install.c
src/boot/bootctl.c
src/boot/bootctl.h
src/boot/meson.build
src/fundamental/efi-fundamental.h
test/TEST-74-AUX-UTILS/test.sh
test/units/TEST-74-AUX-UTILS.bootctl.sh

index 6fa279aa35b87c262d7aac55f24bb8344abb1fd8..eab18f7575a513ddafb283815c3d29517c1c0bfa 100644 (file)
         <xi:include href="version-info.xml" xpointer="v253"/></listitem>
       </varlistentry>
 
+      <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>--certificate=<replaceable>PATH</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
+        option will make <command>bootctl</command> populate the ESP with signed <literal>PK</literal>,
+        <literal>KEK</literal> and <literal>db</literal> signature databases, each containing the given
+        certificate in <literal>DER</literal> format as their only entry. These secure boot signature
+        databases will be picked up and enrolled by <command>systemd-boot</command> if secure boot is in
+        setup mode and secure boot auto-enrollment is enabled.</para>
+
+        <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
+        <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>
+
+        <xi:include href="version-info.xml" xpointer="v257"/></listitem>
+      </varlistentry>
+
       <xi:include href="standard-options.xml" xpointer="no-pager"/>
       <xi:include href="standard-options.xml" xpointer="json" />
       <xi:include href="standard-options.xml" xpointer="help"/>
index b4f0da5ed8090ec815825d94a3d35e93eaada678..f9167a2ee68741f2572c292e6b3d196e1ba26798 100644 (file)
 #define EFI_VENDOR_SYSTEMD      SD_ID128_MAKE(8c,f2,64,4b,4b,0b,42,8f,93,87,6d,87,60,50,dc,67)
 #define EFI_VENDOR_SYSTEMD_STR  SD_ID128_MAKE_UUID_STR(8c,f2,64,4b,4b,0b,42,8f,93,87,6d,87,60,50,dc,67)
 
-#define EFI_VARIABLE_NON_VOLATILE       UINT32_C(0x00000001)
-#define EFI_VARIABLE_BOOTSERVICE_ACCESS UINT32_C(0x00000002)
-#define EFI_VARIABLE_RUNTIME_ACCESS     UINT32_C(0x00000004)
+#define EFI_VARIABLE_NON_VOLATILE                          UINT32_C(0x00000001)
+#define EFI_VARIABLE_BOOTSERVICE_ACCESS                    UINT32_C(0x00000002)
+#define EFI_VARIABLE_RUNTIME_ACCESS                        UINT32_C(0x00000004)
+#define EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS UINT32_C(0x00000020)
 
 /* Note that the <lowercaseuuid>-<varname> naming scheme is an efivarfs convention, i.e. part of the Linux
  * API file system implementation for EFI. EFI itself processes UIDS in binary form.
index 0d904e53af0bdff8e03c7f30a74ab67401cf7c40..2a9da5988128fdf579e91d5d9cbbb1a672dd303e 100644 (file)
@@ -504,6 +504,10 @@ size_t char16_strlen(const char16_t *s) {
         return n;
 }
 
+size_t char16_strsize(const char16_t *s) {
+        return s ? (char16_strlen(s) + 1) * sizeof(*s) : 0;
+}
+
 /* expected size used to encode one unicode char */
 static int utf8_unichar_to_encoded_len(char32_t unichar) {
 
index 8a5e884a0a21b061b6fb5ddb98bb98f5061cc08a..221bc46a2df018467d90a838d0f8311239f4acbc 100644 (file)
@@ -42,6 +42,7 @@ char* utf16_to_utf8(const char16_t *s, size_t length /* bytes! */);
 char16_t *utf8_to_utf16(const char *s, size_t length);
 
 size_t char16_strlen(const char16_t *s); /* returns the number of 16-bit words in the string (not bytes!) */
+size_t char16_strsize(const char16_t *s);
 
 int utf8_encoded_valid_unichar(const char *str, size_t length);
 int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar);
index c3d32a1c6b06d123bf686394672ec67b2c3aa852..a9e2eea486948bde02c8f3ff1956b0a578c729cf 100644 (file)
@@ -8,14 +8,17 @@
 #include "copy.h"
 #include "dirent-util.h"
 #include "efi-api.h"
+#include "efi-fundamental.h"
 #include "env-file.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
 #include "glyph-util.h"
 #include "id128-util.h"
+#include "io-util.h"
 #include "kernel-config.h"
 #include "os-util.h"
+#include "parse-argument.h"
 #include "path-util.h"
 #include "rm-rf.h"
 #include "stat-util.h"
@@ -295,6 +298,8 @@ static const char *const esp_subdirs[] = {
         "EFI/systemd",
         "EFI/BOOT",
         "loader",
+        "loader/keys",
+        "loader/keys/auto",
         NULL
 };
 
@@ -569,6 +574,164 @@ static int install_entry_token(void) {
         return 0;
 }
 
+#if HAVE_OPENSSL
+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 != -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;
+}
+#endif
+
+static int install_secure_boot_auto_enroll(const char *esp, X509 *certificate, EVP_PKEY *private_key) {
+#if HAVE_OPENSSL
+        int r;
+
+        _cleanup_free_ uint8_t *dercert = NULL;
+        int dercertsz;
+        dercertsz = i2d_X509(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));
+
+        _cleanup_close_ int keys_fd = chase_and_open("loader/keys/auto", esp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, O_DIRECTORY, NULL);
+        if (keys_fd < 0)
+                return log_error_errno(keys_fd, "Failed to chase loader/keys/auto in the ESP: %m");
+
+        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);
+
+        EFI_TIME timestamp;
+        r = efi_timestamp(&timestamp);
+        if (r < 0)
+                return r;
+
+        uint32_t attrs =
+                EFI_VARIABLE_NON_VOLATILE|
+                EFI_VARIABLE_BOOTSERVICE_ACCESS|
+                EFI_VARIABLE_RUNTIME_ACCESS|
+                EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
+
+        FOREACH_STRING(db, "PK", "KEK", "db") {
+                _cleanup_(BIO_freep) BIO *bio = NULL;
+
+                bio = BIO_new(BIO_s_mem());
+                if (!bio)
+                        return log_oom();
+
+                _cleanup_free_ char16_t *db16 = utf8_to_utf16(db, SIZE_MAX);
+                if (!db16)
+                        return log_oom();
+
+                /* Don't count the trailing NUL terminator. */
+                if (BIO_write(bio, db16, char16_strsize(db16) - sizeof(char16_t)) < 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write variable name to bio");
+
+                EFI_GUID *guid = STR_IN_SET(db, "PK", "KEK") ? &(EFI_GUID) EFI_GLOBAL_VARIABLE : &(EFI_GUID) EFI_IMAGE_SECURITY_DATABASE_GUID;
+
+                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");
+
+                _cleanup_(PKCS7_freep) PKCS7 *p7 = NULL;
+                p7 = PKCS7_sign(certificate, private_key, /*certs=*/ NULL, bio, PKCS7_DETACHED|PKCS7_NOATTR|PKCS7_BINARY|PKCS7_NOSMIMECAP);
+                if (!p7)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to calculate PKCS7 signature: %s",
+                                               ERR_error_string(ERR_get_error(), NULL));
+
+                _cleanup_free_ uint8_t *sig = NULL;
+                int sigsz = i2d_PKCS7_SIGNED(p7->d.sign, &sig);
+                if (sigsz < 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to convert PKCS7 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);
+
+                _cleanup_free_ char *filename = strjoin(db, ".auth");
+                if (!filename)
+                        return log_oom();
+
+                _cleanup_close_ int fd = openat(keys_fd, filename, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|O_WRONLY|O_CLOEXEC, 0600);
+                if (fd < 0)
+                        return log_error_errno(fd, "Failed to open secure boot auto-enrollment file for writing: %m");
+
+                r = loop_write(fd, auth, authsz);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to write authentication descriptor to secure boot auto-enrollment file: %m");
+
+                r = loop_write(fd, siglist, siglistsz);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to write signature list to secure boot auto-enrollment file: %m");
+
+                if (fsync(fd) < 0 || fsync(keys_fd) < 0)
+                        return log_error_errno(errno, "Failed to sync secure boot auto-enrollment file: %m");
+
+                log_info("Secure boot auto-enrollment file %s/loader/keys/auto/%s successfully written.", esp, filename);
+        }
+
+        return 0;
+#else
+        return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL is not supported, cannot set up secure boot auto-enrollment.");
+#endif
+}
+
 static bool same_entry(uint16_t id, sd_id128_t uuid, const char *path) {
         _cleanup_free_ char *opath = NULL;
         sd_id128_t ouuid;
@@ -778,6 +941,9 @@ static int are_we_installed(const char *esp_path) {
 }
 
 int verb_install(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;
         sd_id128_t uuid = SD_ID128_NULL;
         uint64_t pstart = 0, psize = 0;
         uint32_t part = 0;
@@ -789,6 +955,26 @@ int verb_install(int argc, char *argv[], void *userdata) {
         install = streq(argv[0], "install");
         graceful = !install && arg_graceful; /* support graceful mode for updates */
 
+        if (arg_secure_boot_auto_enroll) {
+                r = openssl_load_x509_certificate(arg_certificate, &certificate);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to load X.509 certificate from %s: %m", arg_certificate);
+
+                r = openssl_load_private_key(
+                                arg_private_key_source_type,
+                                arg_private_key_source,
+                                arg_private_key,
+                                &(AskPasswordRequest) {
+                                        .id = "bootctl-private-key-pin",
+                                        .keyring = arg_private_key,
+                                        .credential = "bootctl.private-key-pin",
+                                },
+                                &private_key,
+                                &ui);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to load private key from %s: %m", arg_private_key);
+        }
+
         r = acquire_esp(/* unprivileged_mode= */ false, graceful, &part, &pstart, &psize, &uuid, NULL);
         if (graceful && r == -ENOKEY)
                 return 0; /* If --graceful is specified and we can't find an ESP, handle this cleanly */
@@ -852,6 +1038,12 @@ int verb_install(int argc, char *argv[], void *userdata) {
                                 if (r < 0)
                                         return r;
                         }
+
+                        if (arg_secure_boot_auto_enroll) {
+                                r = install_secure_boot_auto_enroll(arg_esp_path, certificate, private_key);
+                                if (r < 0)
+                                        return r;
+                        }
                 }
 
                 r = install_loader_specification(arg_dollar_boot_path());
@@ -1069,6 +1261,16 @@ int verb_remove(int argc, char *argv[], void *userdata) {
         if (q < 0 && r >= 0)
                 r = q;
 
+        FOREACH_STRING(db, "PK.auth", "KEK.auth", "db.auth") {
+                _cleanup_free_ char *p = path_join("/loader/keys/auto", db);
+                if (!p)
+                        return log_oom();
+
+                q = remove_file(arg_esp_path, p);
+                if (q < 0 && r >= 0)
+                        r = q;
+        }
+
         q = remove_subdirs(arg_esp_path, esp_subdirs);
         if (q < 0 && r >= 0)
                 r = q;
index 90232b309643b8b506edf4e6269450eb963f223b..23a3d2f9228519d6a7dc442a015954ecaa66f9f0 100644 (file)
@@ -62,6 +62,11 @@ char *arg_efi_boot_option_description = NULL;
 bool arg_dry_run = false;
 ImagePolicy *arg_image_policy = NULL;
 bool arg_varlink = false;
+bool arg_secure_boot_auto_enroll = false;
+char *arg_certificate = NULL;
+char *arg_private_key = NULL;
+KeySourceType arg_private_key_source_type = OPENSSL_KEY_SOURCE_FILE;
+char *arg_private_key_source = NULL;
 
 STATIC_DESTRUCTOR_REGISTER(arg_esp_path, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path, freep);
@@ -71,6 +76,9 @@ STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
 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_private_key, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_private_key_source, freep);
 
 int acquire_esp(
                 int unprivileged_mode,
@@ -277,6 +285,19 @@ static int help(int argc, char *argv[], void *userdata) {
                "     --efi-boot-option-description=DESCRIPTION\n"
                "                       Description of the entry in the boot option list\n"
                "     --dry-run         Dry run (unlink and cleanup)\n"
+               "     --secure-boot-auto-enroll\n"
+               "                       Set up secure boot auto-enrollment\n"
+               "     --private-key=PATH|URI\n"
+               "                       Private key to use when setting up secure boot\n"
+               "                       auto-enrollment or an engine or provider specific\n"
+               "                       designation if --private-key-source= is used\n"
+               "     --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 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"
                "\nSee the %2$s for details.\n",
                program_invocation_short_name,
                link,
@@ -309,6 +330,10 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_DRY_RUN,
                 ARG_PRINT_LOADER_PATH,
                 ARG_PRINT_STUB_PATH,
+                ARG_SECURE_BOOT_AUTO_ENROLL,
+                ARG_CERTIFICATE,
+                ARG_PRIVATE_KEY,
+                ARG_PRIVATE_KEY_SOURCE,
         };
 
         static const struct option options[] = {
@@ -339,6 +364,10 @@ static int parse_argv(int argc, char *argv[]) {
                 { "all-architectures",           no_argument,       NULL, ARG_ARCH_ALL                    },
                 { "efi-boot-option-description", required_argument, NULL, ARG_EFI_BOOT_OPTION_DESCRIPTION },
                 { "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                 },
+                { "private-key",                 required_argument, NULL, ARG_PRIVATE_KEY                 },
+                { "private-key-source",          required_argument, NULL, ARG_PRIVATE_KEY_SOURCE          },
                 {}
         };
 
@@ -491,6 +520,35 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_dry_run = true;
                         break;
 
+                case ARG_SECURE_BOOT_AUTO_ENROLL:
+                        r = parse_boolean_argument("--secure-boot-auto-enroll=", optarg, &arg_secure_boot_auto_enroll);
+                        if (r < 0)
+                                return r;
+                        break;
+
+                case ARG_CERTIFICATE: {
+                        r = parse_path_argument(optarg, /*suppress_root=*/ false, &arg_certificate);
+                        if (r < 0)
+                                return r;
+                        break;
+                }
+
+                case ARG_PRIVATE_KEY: {
+                        r = free_and_strdup_warn(&arg_private_key, optarg);
+                        if (r < 0)
+                                return r;
+                        break;
+                }
+
+                case ARG_PRIVATE_KEY_SOURCE:
+                        r = parse_openssl_key_source_argument(
+                                        optarg,
+                                        &arg_private_key_source,
+                                        &arg_private_key_source_type);
+                        if (r < 0)
+                                return r;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -517,6 +575,12 @@ static int parse_argv(int argc, char *argv[]) {
         if (arg_dry_run && argv[optind] && !STR_IN_SET(argv[optind], "unlink", "cleanup"))
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--dry is only supported with --unlink or --cleanup");
 
+        if (arg_secure_boot_auto_enroll && !arg_certificate)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Secure boot auto-enrollment requested but no certificate provided");
+
+        if (arg_secure_boot_auto_enroll && !arg_private_key)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Secure boot auto-enrollment requested but no private key provided");
+
         r = sd_varlink_invocation(SD_VARLINK_ALLOW_ACCEPT);
         if (r < 0)
                 return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m");
index 19eb93c2b1d3228eaeb8c864eb0b9f7b62d0fe82..8a67f5d8f88ee6e87981983f4751cc4ac70930b4 100644 (file)
@@ -6,6 +6,7 @@
 
 #include "boot-entry.h"
 #include "image-policy.h"
+#include "openssl-util.h"
 #include "pager.h"
 
 typedef enum InstallSource {
@@ -38,6 +39,11 @@ extern char *arg_efi_boot_option_description;
 extern bool arg_dry_run;
 extern ImagePolicy *arg_image_policy;
 extern bool arg_varlink;
+extern bool arg_secure_boot_auto_enroll;
+extern char *arg_certificate;
+extern char *arg_private_key;
+extern KeySourceType arg_private_key_source_type;
+extern char *arg_private_key_source;
 
 static inline const char* arg_dollar_boot_path(void) {
         /* $BOOT shall be the XBOOTLDR partition if it exists, and otherwise the ESP */
index 55b9bd6294b0267150346a34f070f20eeae854af..9c28c623d4d798f8398989ed6453e05112948dad 100644 (file)
@@ -30,7 +30,7 @@ executables += [
                 ],
                 'sources' : bootctl_sources,
                 'link_with' : boot_link_with,
-                'dependencies' : libblkid,
+                'dependencies' : [libblkid, libopenssl],
         },
         libexec_template + {
                 'name' : 'systemd-bless-boot',
index 2862932ec9fa0b8a75e7558b5cb550106aab1e3b..75cfd23b8c7d75e517364bcfcefd5357eab8afa4 100644 (file)
@@ -10,4 +10,65 @@ typedef struct {
         uint16_t Data3;
         uint8_t Data4[8];
 } EFI_GUID;
+
+typedef struct {
+        EFI_GUID SignatureOwner;
+        uint8_t        SignatureData[];
+} EFI_SIGNATURE_DATA;
+
+typedef struct {
+        EFI_GUID SignatureType;
+        uint32_t SignatureListSize;
+        uint32_t SignatureHeaderSize;
+        uint32_t SignatureSize;
+        EFI_SIGNATURE_DATA Signatures[];
+} EFI_SIGNATURE_LIST;
+
+typedef struct {
+        uint32_t dwLength;
+        uint16_t wRevision;
+        uint16_t wCertificateType;
+        uint8_t bCertificate[];
+} WIN_CERTIFICATE;
+
+typedef struct {
+        WIN_CERTIFICATE Hdr;
+        EFI_GUID CertType;
+        uint8_t CertData[];
+} WIN_CERTIFICATE_UEFI_GUID;
+
+typedef struct {
+        uint16_t Year;
+        uint8_t Month;
+        uint8_t Day;
+        uint8_t Hour;
+        uint8_t Minute;
+        uint8_t Second;
+        uint8_t Pad1;
+        uint32_t Nanosecond;
+        int16_t TimeZone;
+        uint8_t Daylight;
+        uint8_t Pad2;
+} EFI_TIME;
+
+typedef struct {
+        EFI_TIME TimeStamp;
+        WIN_CERTIFICATE_UEFI_GUID AuthInfo;
+} EFI_VARIABLE_AUTHENTICATION_2;
+
+#define GUID_DEF(d1, d2, d3, d4_1, d4_2, d4_3, d4_4, d4_5, d4_6, d4_7, d4_8) \
+    { d1, d2, d3, { d4_1, d4_2, d4_3, d4_4, d4_5, d4_6, d4_7, d4_8 } }
+
+#define MAKE_GUID_PTR(name) ((EFI_GUID *) &(const EFI_GUID) name##_GUID)
+
+#define EFI_GLOBAL_VARIABLE \
+        GUID_DEF(0x8be4df61, 0x93ca, 0x11d2, 0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c)
+#define EFI_IMAGE_SECURITY_DATABASE_GUID \
+        GUID_DEF(0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f)
+
+#define EFI_CERT_X509_GUID \
+        GUID_DEF(0xa5c059a1, 0x94e4, 0x4aa7, 0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72)
+#define EFI_CERT_TYPE_PKCS7_GUID \
+        GUID_DEF(0x4aafd29d, 0x68df, 0x49ee, 0x8a, 0xa9, 0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7)
+
 #endif
index b10965040a0fcc91930fe55ed4f2d34f73df6b71..e90c55c28defbe15d598105cc7052cc5597ac803 100755 (executable)
@@ -38,6 +38,7 @@ test_append_files() {
     instmods vmw_vsock_vmci_transport
     inst_binary gcc
     generate_module_dependencies
+    inst_binary openssl
 }
 
 do_test "$@"
index 1767f1fc897a46d9944da7c2f60784a797892b31..46fd5d1f2d58d1ff3c3ce664aa96b94a8e10b2e7 100755 (executable)
@@ -276,4 +276,31 @@ testcase_bootctl_varlink() {
     SYSTEMD_LOG_TARGET=console varlinkctl call --json=short /run/systemd/io.systemd.BootControl io.systemd.BootControl.SetRebootToFirmware '{"state":false}' --graceful=io.systemd.BootControl.RebootToFirmwareNotSupported
 }
 
+testcase_bootctl_secure_boot_auto_enroll() {
+    cat >/tmp/openssl.conf <<EOF
+[ req ]
+prompt = no
+distinguished_name = req_distinguished_name
+
+[ req_distinguished_name ]
+C = DE
+ST = Test State
+L = Test Locality
+O = Org Name
+OU = Org Unit Name
+CN = Common Name
+emailAddress = test@email.com
+EOF
+
+    openssl req -config /tmp/openssl.conf -subj="/CN=waldo" \
+            -x509 -sha256 -nodes -days 365 -newkey rsa:4096 \
+            -keyout /tmp/sb.key -out /tmp/sb.crt
+
+    bootctl install --make-entry-directory=yes --secure-boot-auto-enroll=yes --certificate /tmp/sb.crt --private-key /tmp/sb.key
+    for var in PK KEK db; do
+        test -f "$(bootctl --print-esp-path)/loader/keys/auto/$var.auth"
+    done
+    bootctl remove
+}
+
 run_testcases