From: Lennart Poettering Date: Mon, 25 Jul 2022 15:44:24 +0000 (+0200) Subject: efi: from the stub measure the ELF kernel + built-in initrd and so on into PCR 11 X-Git-Tag: v252-rc1~542^2~15 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=72c97c19c3865f72ec3e40d31d2a57f40880dbe5;p=thirdparty%2Fsystemd.git efi: from the stub measure the ELF kernel + built-in initrd and so on into PCR 11 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. --- diff --git a/man/systemd-cryptenroll.xml b/man/systemd-cryptenroll.xml index 8e1b9857a72..4a5127b02d1 100644 --- a/man/systemd-cryptenroll.xml +++ b/man/systemd-cryptenroll.xml @@ -296,6 +296,11 @@ The IMA project measures its runtime state into this PCR. + + 11 + systemd-stub7 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). + + 12 systemd-boot7 measures any specified kernel command line into this PCR. systemd-stub7 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 systemd-boot and systemd-stub are used in combination the command line might be measured twice!) diff --git a/man/systemd-stub.xml b/man/systemd-stub.xml index 955fa6f98f4..df7cabcf503 100644 --- a/man/systemd-stub.xml +++ b/man/systemd-stub.xml @@ -53,6 +53,10 @@ The ELF Linux kernel images will be looked for in the .linux PE section of the executed image. + OS release information, i.e. the + os-release5 file of + the OS the kernel belongs to, in the .osrel PE section. + The initial RAM disk (initrd) will be looked for in the .initrd PE section. @@ -76,6 +80,9 @@ If a DeviceTree is embedded in the .dtb section, it replaces an existing DeviceTree in the corresponding EFI configuration table. systemd-stub will ask the firmware via the EFI_DT_FIXUP_PROTOCOL for hardware specific fixups to the DeviceTree. + + 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. @@ -133,10 +140,10 @@ core kernel, the embedded initrd and kernel command line (see above for a full list). 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: + 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: OS Resource PCR Summary @@ -160,22 +167,22 @@ Boot splash (embedded in the unified PE binary) - 4 + 4 + 11 Core kernel code (embedded in unified PE binary) - 4 + 4 + 11 Main initrd (embedded in unified PE binary) - 4 + 9 + 4 + 9 + 11 Default kernel command line (embedded in unified PE binary) - 4 + 4 + 11 @@ -240,6 +247,15 @@ this data. + + StubPcrKernelImage + + 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. 11). This variable is set if a measurement was successfully completed, and + remains unset otherwise. + + StubPcrKernelParameters diff --git a/src/boot/efi/measure.h b/src/boot/efi/measure.h index 5da160f8c87..df7f04c9fbb 100644 --- a/src/boot/efi/measure.h +++ b/src/boot/efi/measure.h @@ -5,6 +5,12 @@ #include #include +/* 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 diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 3673d163450..9ec01d1baa6 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -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);