]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
efi: from the stub measure the ELF kernel + built-in initrd and so on into PCR 11
authorLennart Poettering <lennart@poettering.net>
Mon, 25 Jul 2022 15:44:24 +0000 (17:44 +0200)
committerLennart Poettering <lennart@poettering.net>
Tue, 2 Aug 2022 08:28:49 +0000 (10:28 +0200)
Here we grab a new – on Linux so far unused (by my Googling skills, that
is) – and measure all static components of the PE kernel image into.
This is useful since for the first time we'll have a PCR that contains
only a PCR of the booted kernel, nothing else. That allows putting
together TPM policies that bind to a specific kernel (+ builtin initrd),
without having to have booted that kernel first. PCRs can be
pre-calculated. Yay!

You might wonder, why we measure just the discovered PE sections we are
about to use, instead of the whole PE image. That's because of the next
step I have in mind: PE images should also be able to carry an
additional section that contains a signature for its own expected,
pre-calculated PCR values. This signature data should then be passed
into the booted kernel and can be used there in TPM policies. Benefit:
TPM policies can now be bound to *signatures* of PCRs, instead of the
raw hash values themselves. This makes update management a *lot* easier,
as policies don't need to be updated whenever a kernel is updated, as
long as the signature is available. Now, if the PCR signature is
embedded in the kernel PE image it cannot be of a PCR hash of the kernel
PE image itself, because that would be a chicken-and-egg problem. Hence,
by only measuring the relavent payload sections (and that means
excluding the future section that will contain the PCR hash signature)
we avoid this problem, naturally.

man/systemd-cryptenroll.xml
man/systemd-stub.xml
src/boot/efi/measure.h
src/boot/efi/stub.c

index 8e1b9857a72aaeb354ee03e89d587baf98d44524..4a5127b02d15bfd186ac07def8343036fb6ac6b3 100644 (file)
                 <entry>The IMA project measures its runtime state into this PCR.</entry>
               </row>
 
+              <row>
+                <entry>11</entry>
+                <entry><citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures the ELF kernel image, embedded initrd and other payload of the PE image it is placed in into this PCR. Unlike PCR 4 (where the same data should be measured into), this PCR value should be easy to pre-calculate, as this only contains static parts of the PE binary. Use this PCR to bind TPM policies to a specific kernel image, possibly with an embedded initial RAM disk (initrd).</entry>
+              </row>
+
               <row>
                 <entry>12</entry>
                 <entry><citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures any specified kernel command line into this PCR. <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures any manually specified kernel command line (i.e. a kernel command line that overrides the one embedded in the unified PE image) and loaded credentials into this PCR. (Note that if <command>systemd-boot</command> and <command>systemd-stub</command> are used in combination the command line might be measured twice!)</entry>
index 955fa6f98f49550eb88b4319aaba14cf23a8493f..df7cabcf503f2047c22cec70d24251d41985d592 100644 (file)
       <listitem><para>The ELF Linux kernel images will be looked for in the <literal>.linux</literal> PE
       section of the executed image.</para></listitem>
 
+      <listitem><para>OS release information, i.e. the
+      <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry> file of
+      the OS the kernel belongs to, in the <literal>.osrel</literal> PE section.</para></listitem>
+
       <listitem><para>The initial RAM disk (initrd) will be looked for in the <literal>.initrd</literal> PE
       section.</para></listitem>
 
@@ -76,6 +80,9 @@
     <para>If a DeviceTree is embedded in the <literal>.dtb</literal> section, it replaces an existing
     DeviceTree in the corresponding EFI configuration table. systemd-stub will ask the firmware via the
     <literal>EFI_DT_FIXUP_PROTOCOL</literal> for hardware specific fixups to the DeviceTree.</para>
+
+    <para>The contents of these six PE sections are measured into TPM PCR 11, that is otherwise not
+    used. Thus, it can be pre-calculated without too much effort.</para>
   </refsect1>
 
   <refsect1>
     core kernel, the embedded initrd and kernel command line (see above for a full list).</para>
 
     <para>Also note that the Linux kernel will measure all initrds it receives into TPM PCR 9. This means
-    every type of initrd will be measured twice: the initrd embedded in the kernel image will be measured to
-    both PCR 4 and PCR 9; the initrd synthesized from credentials will be measured to both PCR 12 and PCR 9;
-    the initrd synthesized from system extensions will be measured to both PCR 4 and PCR 9. Let's summarize
-    the OS resources and the PCRs they are measured to:</para>
+    every type of initrd will be measured two or three times: the initrd embedded in the kernel image will be
+    measured to PCR 4, PCR 9 and PCR 11; the initrd synthesized from credentials will be measured to both PCR
+    9 and PCR 12; the initrd synthesized from system extensions will be measured to both PCR 4 and PCR
+    9. Let's summarize the OS resources and the PCRs they are measured to:</para>
 
     <table>
       <title>OS Resource PCR Summary</title>
 
           <row>
             <entry>Boot splash (embedded in the unified PE binary)</entry>
-            <entry>4</entry>
+            <entry>4 + 11</entry>
           </row>
 
           <row>
             <entry>Core kernel code (embedded in unified PE binary)</entry>
-            <entry>4</entry>
+            <entry>4 + 11</entry>
           </row>
 
           <row>
             <entry>Main initrd (embedded in unified PE binary)</entry>
-            <entry>4 + 9</entry>
+            <entry>4 + 9 + 11</entry>
           </row>
 
           <row>
             <entry>Default kernel command line (embedded in unified PE binary)</entry>
-            <entry>4</entry>
+            <entry>4 + 11</entry>
           </row>
 
           <row>
         this data.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>StubPcrKernelImage</varname></term>
+
+        <listitem><para>The PCR register index the ELF kernel image/initial RAM disk image/boot
+        splash/devicetree database/embedded command line are measured into, formatted as decimal ASCII string
+        (i.e. <literal>11</literal>). This variable is set if a measurement was successfully completed, and
+        remains unset otherwise.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>StubPcrKernelParameters</varname></term>
 
index 5da160f8c87b3da13b3d7da2ebf8fed7a42426cc..df7f04c9fbbd4db8441430c695ab95449ba805ba 100644 (file)
@@ -5,6 +5,12 @@
 #include <stdbool.h>
 #include <uchar.h>
 
+/* This TPM PCR is where we extend the sd-stub "payloads" into, before using them. i.e. the kernel ELF image,
+ * embedded initrd, and so on. In contrast to PCR 4 (which also contains this data, given the whole
+ * surrounding PE image is measured into it) this should be reasonably pre-calculatable, because it *only*
+ * consists of static data from the kernel PE image. */
+#define TPM_PCR_INDEX_KERNEL_IMAGE 11U
+
 /* This TPM PCR is where sd-stub extends the kernel command line and any passed credentials into. */
 #define TPM_PCR_INDEX_KERNEL_PARAMETERS 12U
 
index 3673d163450550361999eddb0feadc4cb9d2d10c..9ec01d1baa6361eae94fb15e009670ec53f33cce 100644 (file)
@@ -149,7 +149,7 @@ static void export_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) {
 
 EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
 
-        enum {
+        enum Section {
                 SECTION_CMDLINE,
                 SECTION_LINUX,
                 SECTION_INITRD,
@@ -178,7 +178,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
         UINTN szs[_SECTION_MAX] = {};
         char *cmdline = NULL;
         _cleanup_free_ char *cmdline_owned = NULL;
-        int parameters_measured = -1;
+        int sections_measured = -1, parameters_measured = -1;
         EFI_STATUS err;
         bool m;
 
@@ -204,6 +204,41 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
                 return log_error_status_stall(err, L"Unable to locate embedded .linux section: %r", err);
         }
 
+        /* Measure all "payload" of this PE image into a separate PCR (i.e. where nothing else is written
+         * into so far), so that we have one PCR that we can nicely write policies against because it
+         * contains all static data of this image, and thus can be easily be pre-calculated. */
+        for (enum Section section = 0; section < _SECTION_MAX; section++) {
+                m = false;
+
+                if (szs[section] == 0) /* not found */
+                        continue;
+
+                /* First measure the name of the section */
+                (void) tpm_log_event_ascii(
+                                TPM_PCR_INDEX_KERNEL_IMAGE,
+                                POINTER_TO_PHYSICAL_ADDRESS(sections[section]),
+                                strsize8(sections[section]), /* including NUL byte */
+                                sections[section],
+                                &m);
+
+                sections_measured = sections_measured < 0 ? m : (sections_measured && m);
+
+                /* Then measure the data of the section */
+                (void) tpm_log_event_ascii(
+                                TPM_PCR_INDEX_KERNEL_IMAGE,
+                                POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[section],
+                                szs[section],
+                                sections[section],
+                                &m);
+
+                sections_measured = sections_measured < 0 ? m : (sections_measured && m);
+        }
+
+        /* After we are done, set an EFI variable that tells userspace this was done successfully, and encode
+         * in it which PCR was used. */
+        if (sections_measured > 0)
+                (void) efivar_set_uint_string(LOADER_GUID, L"StubPcrKernelImage", TPM_PCR_INDEX_KERNEL_IMAGE, 0);
+
         /* Show splash screen as early as possible */
         graphics_splash((const uint8_t*) loaded_image->ImageBase + addrs[SECTION_SPLASH], szs[SECTION_SPLASH], NULL);