]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
ukify: merge .sbat sections from stub and kernel
authorLuca Boccassi <bluca@debian.org>
Thu, 29 Jun 2023 00:05:36 +0000 (01:05 +0100)
committerLuca Boccassi <bluca@debian.org>
Fri, 30 Jun 2023 16:17:56 +0000 (17:17 +0100)
If the kernel contains a .sbat section (they should start soon) then merge
it with the stub's so that revocations can apply to either component.

Fixes https://github.com/systemd/systemd/issues/27866

man/ukify.xml
src/ukify/ukify.py

index 44fb3a5237467af781f498cb0e3500e8fa604d57..31e54c473a657a8a44cf13497e76d4417b335163 100644 (file)
       discussion of automatic enrollment in
       <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
       </para>
+
+      <para>If the stub and/or the kernel contain <literal>.sbat</literal> sections they will be merged in
+      the UKI so that revocation updates affecting either are considered when the UKI is loaded by Shim. For
+      more information on SBAT see
+      <ulink url="https://github.com/rhboot/shim/blob/main/SBAT.md">Shim's documentation.</ulink>
+      </para>
     </refsect2>
 
     <refsect2>
index 9cdcd0f76aebc66e8d5c62318ac059b99a0676c9..de356d993cb0b858223c8509a2409399200908bf 100755 (executable)
@@ -572,12 +572,26 @@ def pe_add_sections(uki: UKI, output: str):
         else:
             new_section.IMAGE_SCN_CNT_INITIALIZED_DATA = True
 
-        pe.__data__ = pe.__data__[:] + bytes(new_section.PointerToRawData - len(pe.__data__)) + data + bytes(new_section.SizeOfRawData - len(data))
+        # Special case, mostly for .sbat: the stub will already have a .sbat section, but we want to append
+        # the one from the kernel to it. It should be small enough to fit in the existing section, so just
+        # swap the data.
+        for i, s in enumerate(pe.sections):
+            if s.Name.rstrip(b"\x00").decode() == section.name:
+                if new_section.Misc_VirtualSize > s.SizeOfRawData:
+                    raise PEError(f'Not enough space in existing section {section.name} to append new data.')
+
+                padding = bytes(new_section.SizeOfRawData - new_section.Misc_VirtualSize)
+                pe.__data__ = pe.__data__[:s.PointerToRawData] + data + padding + pe.__data__[pe.sections[i+1].PointerToRawData:]
+                s.SizeOfRawData = new_section.SizeOfRawData
+                s.Misc_VirtualSize = new_section.Misc_VirtualSize
+                break
+        else:
+            pe.__data__ = pe.__data__[:] + bytes(new_section.PointerToRawData - len(pe.__data__)) + data + bytes(new_section.SizeOfRawData - len(data))
 
-        pe.FILE_HEADER.NumberOfSections += 1
-        pe.OPTIONAL_HEADER.SizeOfInitializedData += new_section.Misc_VirtualSize
-        pe.__structures__.append(new_section)
-        pe.sections.append(new_section)
+            pe.FILE_HEADER.NumberOfSections += 1
+            pe.OPTIONAL_HEADER.SizeOfInitializedData += new_section.Misc_VirtualSize
+            pe.__structures__.append(new_section)
+            pe.sections.append(new_section)
 
     pe.OPTIONAL_HEADER.CheckSum = 0
     pe.OPTIONAL_HEADER.SizeOfImage = round_up(
@@ -587,6 +601,28 @@ def pe_add_sections(uki: UKI, output: str):
 
     pe.write(output)
 
+def merge_sbat(input: [pathlib.Path]) -> str:
+    sbat = []
+
+    for f in input:
+        try:
+            pe = pefile.PE(f, fast_load=True)
+        except pefile.PEFormatError:
+            print(f"{f} is not a valid PE file, not extracting SBAT section.")
+            continue
+
+        for section in pe.sections:
+            if section.Name.rstrip(b"\x00").decode() == ".sbat":
+                split = section.get_data().rstrip(b"\x00").decode().splitlines()
+                if not split[0].startswith('sbat,'):
+                    print(f"{f} does not contain a valid SBAT section, skipping.")
+                    continue
+                # Filter out the sbat line, we'll add it back later, there needs to be only one and it
+                # needs to be first.
+                sbat += split[1:]
+
+    return 'sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md\n' + '\n'.join(sbat) + "\n\x00"
+
 def signer_sign(cmd):
     print('+', shell_join(cmd))
     subprocess.check_call(cmd)
@@ -719,6 +755,8 @@ def make_uki(opts):
     # UKI or addon creation - addons don't use the stub so we add SBAT manually
 
     if linux is not None:
+        # Merge the .sbat sections from the stub and the kernel, so that revocation can be done on either.
+        uki.add_section(Section.create('.sbat', merge_sbat([opts.stub, linux]), measure=False))
         uki.add_section(Section.create('.linux', linux, measure=True))
     elif opts.sbat:
         uki.add_section(Section.create('.sbat', opts.sbat, measure=False))