From c3f7501c4d014482b17988d5aed1d88127a50b6e Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Thu, 29 Jun 2023 01:05:36 +0100 Subject: [PATCH 1/2] ukify: merge .sbat sections from stub and kernel 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 | 6 ++++++ src/ukify/ukify.py | 48 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/man/ukify.xml b/man/ukify.xml index 44fb3a52374..31e54c473a6 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/ukify/ukify.py b/src/ukify/ukify.py index 9cdcd0f76ae..de356d993cb 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)) From d5f91cf79361cab58e32bf7b76c41ba244add75f Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Thu, 29 Jun 2023 23:41:48 +0100 Subject: [PATCH 2/2] boot: measure .sbat section We are now merging .sbat sections from sd-stub and kernel image, so measure it in PCR11. --- man/systemd-measure.xml | 8 +++++--- src/boot/measure.c | 3 +++ src/fundamental/tpm-pcr.c | 1 + src/fundamental/tpm-pcr.h | 1 + 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/man/systemd-measure.xml b/man/systemd-measure.xml index dddc2bf16b7..7279df8d08f 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/src/boot/measure.c b/src/boot/measure.c index bd7cc783996..e388fb666ed 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 0685d37b057..2f7e9b428d4 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 4989d93f0c1..f8ed816894f 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,