]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
stub: Add support for .initrd addon files
authorTobias Fleig <tfleig@meta.com>
Tue, 8 Oct 2024 14:54:43 +0000 (07:54 -0700)
committerLuca Boccassi <luca.boccassi@gmail.com>
Wed, 9 Oct 2024 13:06:10 +0000 (14:06 +0100)
Teaches systemd-stub how to load additional initrds from addon files.
This is very similar to the support for .ucode sections in addon files,
but with different ordering. Initrds from addons have a chance to
overwrite files from the base initrd in the UKI.

docs/TPM2_PCR_MEASUREMENTS.md
man/systemd-stub.xml
src/boot/efi/stub.c
src/fundamental/tpm2-pcr.h

index 81c8aaa156e7026d8ca852007cfc75a469be005e..daee33980bc7fb8927f51ea697bf35aacf116ce5 100644 (file)
@@ -120,6 +120,16 @@ Devicetree addons are measured individually as a tagged event.
 
 → **Measured hash** covers the content of the Devicetree.
 
+### PCR 12, `EV_EVENT_TAG`, "Initrd addons"
+
+Initrd addons are measured individually as a tagged event.
+
+→ **Event Tag** `0x49dffe0f`
+
+→ **Description** the addon filename.
+
+→ **Measured hash** covers the contents of the initrd.
+
 ### PCR 12, `EV_EVENT_TAG`, "Ucode addons"
 
 Ucode addons are measured individually as a tagged event.
index 18f3023a65fc0ece32c4f21cbbb82bbf4042689f..439d999c64cec0381c5d5a88613bcf9e8096796d 100644 (file)
       <filename><replaceable>foo</replaceable>.efi.extra.d/*.addon.efi</filename> are loaded and verified as
       PE binaries and specific sections are loaded from them. Addons are used to pass additional kernel
       command line parameters (<literal>.cmdline</literal> section), or Devicetree blobs
-      (<literal>.dtb</literal> section), and microcode updates (<literal>.ucode</literal> section). Addons
-      allow those resources to be passed regardless of the kernel version being booted, for example allowing
-      platform vendors to ship platform-specific configuration.</para>
+      (<literal>.dtb</literal> section), additional initrds (<literal>.initrd</literal> section),
+      and microcode updates (<literal>.ucode</literal> section). Addons allow those resources to be passed
+      regardless of the kernel version being booted, for example allowing platform vendors to ship
+      platform-specific configuration.</para>
 
       <para>In case Secure Boot is enabled, these files will be validated using keys in UEFI DB, Shim's DB or
       Shim's MOK, and only loaded if the check passes. Additionally, if both the addon and the UKI contain a
       archive is measured into TPM PCR 12 (if a TPM is present).</para></listitem>
 
       <listitem><para>Additionally, files <filename>/loader/addons/*.addon.efi</filename> are loaded and
-      verified as PE binaries, and <literal>.cmdline</literal>, <literal>.dtb</literal>, and
-      <literal>.ucode</literal> sections are parsed from them. This is supposed to be used to pass additional
-      command line parameters, DeviceTree blobs, and microcode updates to the kernel, regardless of the
-      kernel version being booted.</para></listitem>
+      verified as PE binaries, and <literal>.cmdline</literal>, <literal>.dtb</literal>,
+      <literal>.initrd</literal>, and <literal>.ucode</literal> sections are parsed from them.
+      This is supposed to be used to pass additional command line parameters, DeviceTree blobs, initrds,
+      and microcode updates to the kernel, regardless of the kernel version being booted.</para></listitem>
     </itemizedlist>
 
     <para>These mechanisms may be used to parameterize and extend trusted (i.e. signed), immutable initrd
index 387250423279f2c5c6068ecef785d827a1f2c1e5..4fc2174669ad621b641921893f53c1ae4067f8d0 100644 (file)
@@ -449,6 +449,42 @@ static inline void iovec_array_extend(struct iovec **arr, size_t *n_arr, struct
         (*arr)[(*n_arr)++] = elem;
 }
 
+static void measure_and_append_initrd_addons(
+                struct iovec **all_initrds,
+                size_t *n_all_initrds,
+                const NamedAddon *initrd_addons,
+                size_t n_initrd_addons,
+                int *sections_measured) {
+
+        EFI_STATUS err;
+
+        assert(all_initrds);
+        assert(n_all_initrds);
+        assert(initrd_addons || n_initrd_addons == 0);
+        assert(sections_measured);
+
+        FOREACH_ARRAY(i, initrd_addons, n_initrd_addons) {
+                bool m = false;
+                err = tpm_log_tagged_event(
+                                TPM2_PCR_KERNEL_CONFIG,
+                                POINTER_TO_PHYSICAL_ADDRESS(i->blob.iov_base),
+                                i->blob.iov_len,
+                                INITRD_ADDON_EVENT_TAG_ID,
+                                i->filename,
+                                &m);
+                if (err != EFI_SUCCESS)
+                        return (void) log_error_status(
+                                        err,
+                                        "Unable to extend PCR %i with INITRD addon '%ls': %m",
+                                        TPM2_PCR_KERNEL_CONFIG,
+                                        i->filename);
+
+                combine_measured_flag(sections_measured, m);
+
+                iovec_array_extend(all_initrds, n_all_initrds, i->blob);
+        }
+}
+
 static void measure_and_append_ucode_addons(
                 struct iovec **all_initrds,
                 size_t *n_all_initrds,
@@ -508,6 +544,8 @@ static EFI_STATUS load_addons(
                 char16_t **cmdline,                         /* Both input+output, extended with new addons we find */
                 NamedAddon **devicetree_addons,             /* Ditto */
                 size_t *n_devicetree_addons,
+                NamedAddon **initrd_addons,                 /* Ditto */
+                size_t *n_initrd_addons,
                 NamedAddon **ucode_addons,                  /* Ditto */
                 size_t *n_ucode_addons) {
 
@@ -575,11 +613,12 @@ static EFI_STATUS load_addons(
                 if (err != EFI_SUCCESS ||
                     (!PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_CMDLINE) &&
                      !PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_DTB) &&
+                     !PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_INITRD) &&
                      !PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_UCODE))) {
                         if (err == EFI_SUCCESS)
                                 err = EFI_NOT_FOUND;
                         log_error_status(err,
-                                         "Unable to locate embedded .cmdline/.dtb/.ucode sections in %ls, ignoring: %m",
+                                         "Unable to locate embedded .cmdline/.dtb/.initrd/.ucode sections in %ls, ignoring: %m",
                                          items[i]);
                         continue;
                 }
@@ -621,6 +660,19 @@ static EFI_STATUS load_addons(
                         };
                 }
 
+                if (initrd_addons && PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_INITRD)) {
+                        *initrd_addons = xrealloc(*initrd_addons,
+                                        *n_initrd_addons * sizeof(NamedAddon),
+                                        (*n_initrd_addons + 1)  * sizeof(NamedAddon));
+                        (*initrd_addons)[(*n_initrd_addons)++] = (NamedAddon) {
+                                .blob = {
+                                        .iov_base = xmemdup((const uint8_t*) loaded_addon->ImageBase + sections[UNIFIED_SECTION_INITRD].memory_offset, sections[UNIFIED_SECTION_INITRD].size),
+                                        .iov_len = sections[UNIFIED_SECTION_INITRD].size,
+                                },
+                                .filename = xstrdup16(items[i]),
+                        };
+                }
+
                 if (ucode_addons && PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_UCODE)) {
                         *ucode_addons = xrealloc(*ucode_addons,
                                         *n_ucode_addons * sizeof(NamedAddon),
@@ -933,6 +985,8 @@ static void load_all_addons(
                 char16_t **cmdline_addons,
                 NamedAddon **dt_addons,
                 size_t *n_dt_addons,
+                NamedAddon **initrd_addons,
+                size_t *n_initrd_addons,
                 NamedAddon **ucode_addons,
                 size_t *n_ucode_addons) {
 
@@ -942,6 +996,8 @@ static void load_all_addons(
         assert(cmdline_addons);
         assert(dt_addons);
         assert(n_dt_addons);
+        assert(initrd_addons);
+        assert(n_initrd_addons);
         assert(ucode_addons);
         assert(n_ucode_addons);
 
@@ -953,6 +1009,8 @@ static void load_all_addons(
                         cmdline_addons,
                         dt_addons,
                         n_dt_addons,
+                        initrd_addons,
+                        n_initrd_addons,
                         ucode_addons,
                         n_ucode_addons);
         if (err != EFI_SUCCESS)
@@ -971,6 +1029,8 @@ static void load_all_addons(
                         cmdline_addons,
                         dt_addons,
                         n_dt_addons,
+                        initrd_addons,
+                        n_initrd_addons,
                         ucode_addons,
                         n_ucode_addons);
         if (err != EFI_SUCCESS)
@@ -1097,8 +1157,8 @@ static EFI_STATUS run(EFI_HANDLE image) {
         PeSectionVector sections[ELEMENTSOF(unified_sections)] = {};
         EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
         _cleanup_free_ char *uname = NULL;
-        NamedAddon *dt_addons = NULL, *ucode_addons = NULL;
-        size_t n_dt_addons = 0, n_ucode_addons = 0;
+        NamedAddon *dt_addons = NULL, *initrd_addons = NULL, *ucode_addons = NULL;
+        size_t n_dt_addons = 0, n_initrd_addons = 0, n_ucode_addons = 0;
         _cleanup_free_ struct iovec *all_initrds = NULL;
         size_t n_all_initrds = 0;
         unsigned profile = 0;
@@ -1134,8 +1194,9 @@ static EFI_STATUS run(EFI_HANDLE image) {
         /* Now that we have the UKI sections loaded, also load global first and then local (per-UKI)
          * addons. The data is loaded at once, and then used later. */
         CLEANUP_ARRAY(dt_addons, n_dt_addons, named_addon_free_many);
+        CLEANUP_ARRAY(initrd_addons, n_initrd_addons, named_addon_free_many);
         CLEANUP_ARRAY(ucode_addons, n_ucode_addons, named_addon_free_many);
-        load_all_addons(image, loaded_image, uname, &cmdline_addons, &dt_addons, &n_dt_addons, &ucode_addons, &n_ucode_addons);
+        load_all_addons(image, loaded_image, uname, &cmdline_addons, &dt_addons, &n_dt_addons, &initrd_addons, &n_initrd_addons, &ucode_addons, &n_ucode_addons);
 
         /* If we have any extra command line to add via PE addons, load them now and append, and measure the
          * additions together, after the embedded options, but before the smbios ones, so that the order is
@@ -1156,10 +1217,17 @@ static EFI_STATUS run(EFI_HANDLE image) {
         generate_embedded_initrds(loaded_image, sections, initrds);
         lookup_embedded_initrds(loaded_image, sections, initrds);
 
-        /* Measures ucode addons and puts them into all_initrds */
+        /* Add initrds in the right order. Generally, later initrds can overwrite files in earlier ones,
+         * except for ucode, where the kernel uses the first matching embedded filename.
+         * We want addons to take precedence over the base initrds, so the order is:
+         * 1. Ucode addons
+         * 2. UKI ucode
+         * 3. UKI initrd
+         * 4. Generated initrds
+         * 5. initrd addons */
         measure_and_append_ucode_addons(&all_initrds, &n_all_initrds, ucode_addons, n_ucode_addons, &parameters_measured);
-        /* Adds all other initrds to all_initrds */
         extend_initrds(initrds, &all_initrds, &n_all_initrds);
+        measure_and_append_initrd_addons(&all_initrds, &n_all_initrds, initrd_addons, n_initrd_addons, &parameters_measured);
 
         /* Export variables indicating what we measured */
         export_pcr_variables(sections_measured, parameters_measured, sysext_measured, confext_measured);
index 9f5529b8272ad6a9e1a4e9298ff4a4b1c477b4c8..f903ec3e3bcb10e5e0adcf0a5f18a1e59def2d40 100644 (file)
@@ -50,6 +50,9 @@ enum {
 /* The tag used for EV_EVENT_TAG event log records covering Devicetree blobs */
 #define DEVICETREE_ADDON_EVENT_TAG_ID UINT32_C(0x6c46f751)
 
+/* The tag used for EV_EVENT_TAG event log records covering initrd addons */
+#define INITRD_ADDON_EVENT_TAG_ID UINT32_C(0x49dffe0f)
+
 /* The tag used for EV_EVENT_TAG event log records covering ucode addons (effectively initrds) */
 #define UCODE_ADDON_EVENT_TAG_ID UINT32_C(0xdac08e1a)