Skip to content

sd-boot: added TPM measurements for initrd#6124

Closed
mxre wants to merge 1 commit intosystemd:masterfrom
mxre:tpm-initrd
Closed

sd-boot: added TPM measurements for initrd#6124
mxre wants to merge 1 commit intosystemd:masterfrom
mxre:tpm-initrd

Conversation

@mxre
Copy link
Contributor

@mxre mxre commented Jun 14, 2017

Currently only the efi executable (the linux kernel) is hashed in the TPM PCR[4] using the LoadImage() function of the EFI system. The initrd, which contains code to either unlock or verify the root partition is not hashed in a PCR unless a unified EFI executable is created (systemd efi stub + kernel + initrd in one efi exe)

This patch adds support for hashing all the initrds that are supplied in the sd-boot configfile and append the hash values to the sd-boot TPM PCR hash of the boot options (usually PCR[8])

I thoroughly tested this patch and i have noticed that it has a noticeable performance impact. on my system sd-boot now takes about 250ms to boot, thats about 70ms longer than without this patch.

I'm also not sure if this is the direction the sd-boot developers want to take.

@poettering
Copy link
Member

I have only a very limited understanding of TPMs, hence I am not going to comment whether this is conceptually OK, but I'll do a code review. For the conceptual part I figure @haraldh should say somehting, he knows TPMs, sd-boot and initrds best. @haraldh?

#ifdef SD_BOOT_LOG_TPM
UINTN i, j;
UINTN length;
CHAR16* begin, *end;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so far we tried to follow the logic that variables should be decalred at the beginning of code blocks only. Also see CODING_STYLE. Hence, please move these variable declarations up, or even better split this addition out into a new function maybe?

UINTN length;
CHAR16* begin, *end;

EFI_FILE* root_dir = LibOpenRoot(entry->device);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, please don't mix function invocations and variable declarations in the same line, also see CODING_STYLE.


EFI_FILE* root_dir = LibOpenRoot(entry->device);
if (!root_dir) {
Print(L"Unable to open root directory: %r ", err);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trailing whitespace at the end of the string? is that intended?

EFI_FILE* root_dir = LibOpenRoot(entry->device);
if (!root_dir) {
Print(L"Unable to open root directory: %r ", err);
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

310001000 appears quite arbitary? also, what#s the logic here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the same statement that is used throughout boot.c, it salls, i.e. waits for 3 seconds to allow the user to read the error message

begin = &options[i + 7];
for (j = i + 7; j < length && options[j] != L' '; j++);
end = &options[j];
UINTN initrd_path_len = (end - begin) * sizeof(CHAR16);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no variable declarations in the middle of code blocks. (here and evreywhere else, see above/CODING_STYLE)

for (i = 0; i < length; i++) {
if (StrnCmp(L"initrd=", &options[i], 7) == 0) {
begin = &options[i + 7];
for (j = i + 7; j < length && options[j] != L' '; j++);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you write for() loops without body, please place the trailing semicolon in an empty line, to make this clear. i.e.:

for (…)
        ;

@poettering poettering added the reviewed/needs-rework 🔨 PR has been reviewed and needs another round of reworks label Jun 16, 2017
Measure all initrds that are passed in the commandline and measure them
into the default PCR, before measuring the commandline itself and
starting the kernel image.
@mxre
Copy link
Contributor Author

mxre commented Jun 25, 2017

I moved my changes in a separate methods. I originally extended the code that hashed the kernel command line for the TPM. I moved that part too, because it makes sense that code that is related should be in the same method.

As before the compilation is conditional on the SD_BOOT_LOG_TPM define.

@poettering
Copy link
Member

@haraldh any chance you can have a look?

@haraldh
Copy link
Member

haraldh commented Jun 28, 2017

I don't know. This patch measures a file, which is read from disk and then the buffer is thrown away and not passed to the kernel.
IMHO, what really has to happen, is that the kernel measures the buffer it reads from disk afterwards.
The bootloader only passes the path to the kernel. The kernel loads the file.
In theory one could craft a disk presenting a false file on the first request and then present the malicious file on kernel read.
IMHO, it's the wrong approach.

@mxre
Copy link
Contributor Author

mxre commented Jun 28, 2017

The buffer is thrown away because sd-boot uses the kernel's efi stub to load the initrd and does not pass an allocated memory like in the multiboot protocol.

The reason I created this patch primarily for my self is to assure that the initrd was not modified in between boots there is (currently) no possibility for the kernel to actually do this.

I do not see how a potential attacker could pass an alternate disk to load a modified file, because an efi executable that was loaded before sd-boot would modify PCR[4] through the LoadImage() call. And modifying the file on the disk would alter the generated hash, (and this is actually what we want, a mechanism to detect modification).

A later userspace mechanism obviously has to check the integrity of the boot using PCR[4] for the boot loader and the kernel and PCR[8] for the initrds and the commandline options.

Edit:

IMHO, it's the wrong approach.

Should it better be part of sd-efistub?

@haraldh
Copy link
Member

haraldh commented Jun 28, 2017

    IMHO, it's the wrong approach.

Should it better be part of sd-efistub?

Not needed for the efistub, because the whole thing (combined kernel, initrd, cmdline) is measured by the EFI BIOS directly already.

@mxre
Copy link
Contributor Author

mxre commented Jun 28, 2017

Only if you use a combined efi, it needs support from the OS (using kernel-install). How would intel microcode updates work? these need a seperate initrd which need to be loaded first. (at least this is the case on arch-linux)

This patch only works for initrds in the commandline, for combined efi images it would not change a thing, thus only enhancing the experience for users who don't use the combined efi executables.

@haraldh
Copy link
Member

haraldh commented Jun 29, 2017

For fedora the microcode initrd and the real initrd are concatenated. The kernel splits that automatically. So it's only one initrd file.

@haraldh
Copy link
Member

haraldh commented Jun 29, 2017

As I said. It does not make sense to measure something, which you are not using immediately. The kernel efi stub should do the measurement, as it is providing the loaded bytes to the kernel directly.

@poettering
Copy link
Member

OK, closing as per @haraldh's comments. Sorry!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

reviewed/needs-rework 🔨 PR has been reviewed and needs another round of reworks sd-boot/sd-stub/bootctl

Development

Successfully merging this pull request may close these issues.

3 participants