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.
This commit is contained in:
Lennart Poettering
2022-08-02 10:28:49 +02:00
parent 599fe002a1
commit 72c97c19c3
4 changed files with 72 additions and 10 deletions
+6
View File
@@ -5,6 +5,12 @@
#include <stdbool.h>
#include <uchar.h>
/* 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
+37 -2
View File
@@ -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);