diff --git a/man/systemd-measure.xml b/man/systemd-measure.xml
index dddc2bf16b..7279df8d08 100644
--- a/man/systemd-measure.xml
+++ b/man/systemd-measure.xml
@@ -73,9 +73,10 @@
Pre-calculate the expected values seen in PCR register 11 after boot-up of a unified
kernel image consisting of the components specified with ,
, , ,
- , , see below. Only
- is mandatory. (Alternatively, specify to use the
- current values of PCR register 11 instead.)
+ , , ,
+ see below. Only is mandatory. (Alternatively,
+ specify to use the current values of PCR register 11 instead.)
+
@@ -112,6 +113,7 @@
+ When used with the calculate or sign verb,
diff --git a/man/ukify.xml b/man/ukify.xml
index 44fb3a5237..31e54c473a 100644
--- a/man/ukify.xml
+++ b/man/ukify.xml
@@ -98,6 +98,12 @@
discussion of automatic enrollment in
systemd-boot7.
+
+ If the stub and/or the kernel contain .sbat 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
+ Shim's documentation.
+
diff --git a/src/boot/measure.c b/src/boot/measure.c
index bd7cc78399..e388fb666e 100644
--- a/src/boot/measure.c
+++ b/src/boot/measure.c
@@ -85,6 +85,7 @@ static int help(int argc, char *argv[], void *userdata) {
" --splash=PATH Path to splash bitmap file %7$s .splash\n"
" --dtb=PATH Path to Devicetree file %7$s .dtb\n"
" --uname=PATH Path to 'uname -r' file %7$s .uname\n"
+ " --sbat=PATH Path to SBAT file %7$s .sbat\n"
" --pcrpkey=PATH Path to public key for PCR signatures %7$s .pcrpkey\n"
"\nSee the %2$s for details.\n",
program_invocation_short_name,
@@ -125,6 +126,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_SPLASH,
ARG_DTB,
ARG_UNAME,
+ ARG_SBAT,
_ARG_PCRSIG, /* the .pcrsig section is not input for signing, hence not actually an argument here */
_ARG_SECTION_LAST,
ARG_PCRPKEY = _ARG_SECTION_LAST,
@@ -148,6 +150,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "splash", required_argument, NULL, ARG_SPLASH },
{ "dtb", required_argument, NULL, ARG_DTB },
{ "uname", required_argument, NULL, ARG_UNAME },
+ { "sbat", required_argument, NULL, ARG_SBAT },
{ "pcrpkey", required_argument, NULL, ARG_PCRPKEY },
{ "current", no_argument, NULL, 'c' },
{ "bank", required_argument, NULL, ARG_BANK },
diff --git a/src/fundamental/tpm-pcr.c b/src/fundamental/tpm-pcr.c
index 0685d37b05..2f7e9b428d 100644
--- a/src/fundamental/tpm-pcr.c
+++ b/src/fundamental/tpm-pcr.c
@@ -12,6 +12,7 @@ const char* const unified_sections[_UNIFIED_SECTION_MAX + 1] = {
[UNIFIED_SECTION_SPLASH] = ".splash",
[UNIFIED_SECTION_DTB] = ".dtb",
[UNIFIED_SECTION_UNAME] = ".uname",
+ [UNIFIED_SECTION_SBAT] = ".sbat",
[UNIFIED_SECTION_PCRSIG] = ".pcrsig",
[UNIFIED_SECTION_PCRPKEY] = ".pcrpkey",
NULL,
diff --git a/src/fundamental/tpm-pcr.h b/src/fundamental/tpm-pcr.h
index 4989d93f0c..f8ed816894 100644
--- a/src/fundamental/tpm-pcr.h
+++ b/src/fundamental/tpm-pcr.h
@@ -30,6 +30,7 @@ typedef enum UnifiedSection {
UNIFIED_SECTION_SPLASH,
UNIFIED_SECTION_DTB,
UNIFIED_SECTION_UNAME,
+ UNIFIED_SECTION_SBAT,
UNIFIED_SECTION_PCRSIG,
UNIFIED_SECTION_PCRPKEY,
_UNIFIED_SECTION_MAX,
diff --git a/src/ukify/ukify.py b/src/ukify/ukify.py
index 9cdcd0f76a..de356d993c 100755
--- a/src/ukify/ukify.py
+++ b/src/ukify/ukify.py
@@ -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.')
- pe.FILE_HEADER.NumberOfSections += 1
- pe.OPTIONAL_HEADER.SizeOfInitializedData += new_section.Misc_VirtualSize
- pe.__structures__.append(new_section)
- pe.sections.append(new_section)
+ 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.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))