]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
ukify: introduce new --measure-base= switch 34295/head
authorLennart Poettering <lennart@poettering.net>
Tue, 2 Jul 2024 10:36:21 +0000 (12:36 +0200)
committerLennart Poettering <lennart@poettering.net>
Fri, 6 Sep 2024 14:12:03 +0000 (16:12 +0200)
man/ukify.xml
src/ukify/ukify.py

index 5e7681279b37ca94d779a6225b36f41e170e2a93..983e89c270e8e136d64d419682a3b434de3bc2d3 100644 (file)
           <xi:include href="version-info.xml" xpointer="v257"/></listitem>
         </varlistentry>
 
+        <varlistentry>
+          <term><option>--measure-base=<replaceable>PATH</replaceable></option></term>
+
+          <listitem><para>Takes a path to an existing PE file to use as base profile, for measuring
+          multi-profile UKIs. When calculating the PCR values, this has the effect that the sections
+          specified on the command line are combined with any sections from the PE file specified here (up to
+          the first <literal>.profile</literal> section, and only if not already specified on the command
+          line). Typically, this is used together with <option>--extend=</option> to both import and use as
+          measurement base an existing UKI.</para>
+
+          <xi:include href="version-info.xml" xpointer="v257"/></listitem>
+        </varlistentry>
+
         <varlistentry>
           <term><option>--tools=<replaceable>DIRS</replaceable></option></term>
 
index b3fab755b6e65cfee9cdc7147c83d6794574bffc..5057d72d2c97e9317213b8cc2a3b38dc8a407328 100755 (executable)
@@ -505,6 +505,14 @@ def pe_strip_section_name(name):
 
 
 def call_systemd_measure(uki, linux, opts):
+
+    if not opts.measure and not opts.pcr_private_keys:
+        return
+
+    measure_sections = ('.linux', '.osrel', '.cmdline', '.initrd',
+                       '.ucode', '.splash', '.dtb', '.uname',
+                       '.sbat', '.pcrpkey', '.profile')
+
     measure_tool = find_tool('systemd-measure',
                              '/usr/lib/systemd/systemd-measure',
                              opts=opts)
@@ -513,16 +521,67 @@ def call_systemd_measure(uki, linux, opts):
 
     # PCR measurement
 
+    to_measure = []
+    tflist = []
+
+    # First, pick up the sections we shall measure now */
+    for s in uki.sections:
+
+        print(s)
+
+        if not s.measure:
+            continue
+
+        if s.content is not None:
+            assert(s.name != ".linux" or linux is None)
+            to_measure.append(f"--{s.name.removeprefix('.')}={s.content}")
+        else:
+            raise ValueError(f"Don't know how to measure section {s.name}");
+
+    if linux is not None:
+        to_measure.append(f'--linux={linux}')
+
+    # And now iterate through the base profile and measure what we haven't measured above
+    if opts.measure_base is not None:
+        pe = pefile.PE(opts.measure_base, fast_load=True)
+
+        # Find matching PE section in base image
+        for base_section in pe.sections:
+            name = pe_strip_section_name(base_section.Name)
+
+            # If we reach the first .profile section the base is over
+            if name == ".profile":
+                break
+
+            # Only some sections are measured
+            if name not in measure_sections:
+                continue
+
+            # Check if this is a section we already covered above
+            already_covered = False
+            for s in uki.sections:
+                if s.measure and name == s.name:
+                    already_covered = True
+                    break;
+
+            if already_covered:
+                continue
+
+            # Split out section and use as base
+            tf = tempfile.NamedTemporaryFile()
+            tf.write(base_section.get_data(length=base_section.Misc_VirtualSize))
+            tf.flush()
+            tflist.append(tf)
+
+            to_measure.append(f"--{name.removeprefix('.')}={tf.name}")
+
     if opts.measure:
         pp_groups = opts.phase_path_groups or []
 
         cmd = [
             measure_tool,
             'calculate',
-            f'--linux={linux}',
-            *(f"--{s.name.removeprefix('.')}={s.content}"
-              for s in uki.sections
-              if s.measure),
+            *to_measure,
             *(f'--bank={bank}'
               for bank in banks),
             # For measurement, the keys are not relevant, so we can lump all the phase paths
@@ -542,10 +601,7 @@ def call_systemd_measure(uki, linux, opts):
         cmd = [
             measure_tool,
             'sign',
-            f'--linux={linux}',
-            *(f"--{s.name.removeprefix('.')}={s.content}"
-              for s in uki.sections
-              if s.measure),
+            *to_measure,
             *(f'--bank={bank}'
               for bank in banks),
         ]
@@ -1431,6 +1487,14 @@ CONFIG_ITEMS = [
         config_key = 'UKI/Extend',
     ),
 
+    ConfigItem(
+        '--measure-base',
+        metavar = 'UKI',
+        type = pathlib.Path,
+        help = 'path to existing UKI file whose relevant sections shall be used as base for PCR11 prediction',
+        config_key = 'UKI/MeasureBase',
+    ),
+
     ConfigItem(
         '--pcr-banks',
         metavar = 'BANK…',