-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
sd-boot: Measure initrd into TPM (attempt #2) #12716
Comments
it appears strange to me to not verify kernel and initrd in one go, i.e. use only https://systemd.io/BOOT_LOADER_SPECIFICATION.html#type-2-efi-unified-kernel-images But it all boils down to: if your patch is minimal, and pretty, and convincing we can merge it, but in general I think the current simplicity (i.e. treat everything as an EFI binary, let the firmware figure out everything, consider kernel and initrd a single unit as boot loader spec type #2 images do it) is very very sexy, and complicating stuff by splitting up and adding yet another validator to all steps of the boot is really ugly in comparison. What I don't understand is your usecase though: you want to be able measure (and thus sign I assume?) the initrd independently of the kernel because you want to update them independently. But how can that even work? I mean, the initrd generally contains kernel modules, hence you need to update the initrd at least as often as the kernel. And the initrd cannot be really be dynamically adjusted to the host, because that would mean everybody has to have their own signing keys (or is that what you are working towards?). Which hence means the only thing you gain is that you can update the same, immutable initrd slightly more often than the kernel attached to it. But is that worth it? At least in my experience, the userspace included in the initrd changes roughly at the same time as the kernel, so why do that work? Hence, please enlighten me, what precisely are you trying to do? |
Measurement doesn't imply signing. The initrd can be dynamically generated locally and TPM values updated such that loading a modified initrd will block release of disk decryption keys. Further, if the dynamic aspects of the initrd are well controlled, a remote server can synthesise the measurement and verify the initrd via remote attestation even if the initrd was generated locally. |
@josephlr : PR for (1) or (2) would be cool :) |
not sure if this should be the default though... |
BTW, i wonder if it wouldn't be smarter to move initrd loading/validation into the EFI stub shipped along with systemd? i.e. why sd-boot instead of the stub? |
So I think we should do this, and do it like this:
These two things together would give us complete coverage: arbitrary systemd-EFI-stub-equipped kernels invoked via any EFI loader (i.e. even non-sd-boot), and arbitrary EFI stub kernels (that do not use our systemd stub) when invoked via sd-boot. Would be delighted to merge/review a patch for either of these approaches, and of course ideally both. |
This sounds like a solid plan - it avoids any TOCTOU issues. |
Hi. We're trying to switch to systemd-boot as well, but we're hitting the same problem with the missing measurements due to having separate initrds. The requirements that we have are:
At this point this comment is just about reaching consensus on the implementation details. Further on, we can provide a patch where all of this is happening. Thanks for your input |
So looking at #20918, it seems like the Stub (but not the normal systemd-boot) uses the This means that when using the Stub the following events will be measured (into the following PCRs):
Note that the actual Linux kernel isn't measured into PCR 4, as it is not loaded from disk, it's just part of the stub binary (so is measured as part of measuring the stub). See the stub implementation here. However, when using the systemd-boot normally, we get the following measurements:
Given this, I think doing the following would be enough to meet @mimir-d's requirements:
Note that we don't actually need systemd-boot to do any more measuring itself. That will be done by the kernel. After these changes, we would get the following measurements with systemd-boot (normal not stub):
|
@mimir-d I'm going to go though your requirements, and see if my above solution meets them:
It's required that the kernel be initially loaded by the firmware (via a call to
This would be handled by the kernel, provided the bootloader implements
This would be handled when the bootloader loads the initrds from disk. They would just be concatenated before being passed via LINUX_EFI_INITRD_MEDIA_GUID. This would then result in a single measurement into PCR 9.
With Kernel binary, Kernel cmdline, and initrd all measured, I think we are good here. This would mostly be a matter of documenting security properties (and making sure that future changes respect those security properties). |
@josephlr is this something you would work on soon? I am also interested in working on a PR for this issue. |
@anitazha I would want to get consensus around the checklist in #12716 (comment) before we start implementing. I'm not sure how much time I'll have in Q1/Q2 to work on implementation, but I would be happy to review any changes in this space. |
Check list makes sense to me. |
Mind you, I am already adding LINUX_EFI_INITRD_MEDIA_GUID support to boot.c in #22550. So you may wanna wait for that to land or use it as a base. |
Appologies for my (very) late reply. We eventually turned to using the systemd-stub (with the embedded boot kernel, initramfs, cmdline) and measure the whole blob into PCR4 (by virtue of firmware). This would have a very infrequent lifecycle as it is reasonably stable, so it was acceptable to skip on the individual parts measurements. |
I tried out #22550 some months ago and on a new enough kernel the LINUX_EFI_INITRD_MEDIA_GUID feature to measure initrd works. Our teams don't have a current need for it now that our partners switched to sd-stub, but it at least works if we decide to change to sd-boot. |
OK, let's close this. I think the automatic measurement of initrds into PCR 9 in current kernels should be enough. We can close this one here, sd-boot doesn't need to do this anymore. |
Big fat warning: Unfortunately a simple evil maid attack is possible when the kernel EFI stub is loaded by sd-boot. The attack is rooted in the fact that the kernel is doing conditional TPM measurements. It only measures the initrd when LINUX_EFI_INITRD_MEDIA_GUID / LoadFile2 is used and that can be abused. To enable use of LoadFile2, and therefore having the kernel measure the initrd, the following kind of sd-boot entry can be used:
The hash of initrd will be measured, it will be measured exactly once. Looks good so far. TPM PCR 9 and 12 measurements (this list is complete and in order, no other measurements are done on this two PCR's): PCR 12 (sd-boot): hash of kernel command line, prepended with "initrd=<path of initrd, using backslashes as separator> " What would happen now, when the EFI loader is used and the kernel command line is prepended with "initrd=<path of initrd, using backslashes as separator> "?
TPM PCR 9 and 12 measurements (this list is complete and in order, no other measurements are done on this two PCR's): PCR 12 (sd-boot): hash of kernel command line, including "initrd=<path of initrd, using backslashes as separator> " on the left hand side The hash of the initrd is now not measured anymore. This is the only difference of all PCR registers compared to the intended boot. In fact, the evil boot measures exactly one event less, that of the hash of initrd, which is also the final measuerent in PCR 9. To complete the evil maid, the attacker will calculate the hash of the original initrd and provide a custom initrd which extends PCR 9 with the hash of the original initrd. The measured boot is now successfully broken, as all PCR registers cointain exactly the same values as on the intended boot, but with an evil initrd. Even eventual Secure Boot protections are circumvented. Possible mitigations (one of them is sufficient):
|
Will not it happen after exit from EFI so it will not match due to different order of events? |
The order will be different, but not the PCR values. When you seal a secret into the TPM using e.g. tpm2_policypcr, only the PCR values are taken into account, not the order. It would indeed be possible to detect this attack by using remote attestation, analyzing the order of the event log as well. But that is fully out of scope of tpm2_policypcr and out of scope for most FDE solutions as well. To be a bit more specific, as far as I know, simply grabbing the output of tpm2_eventlog and tpm2_pcrread and analyzing it for the correct order inside the initrd is not a good idea, as tpm2_pcrread is unencrypted and can be faked by an TPM interposer / genie. Therefore a remote or local attestation has to be done which is using a cryptographical quote to attest the correctness of the event log. Then the order check can be applied. A lot of work for something that should not happen in first place. And even then, what prevents the attacker to repackage the initrd, simply removing the code which is doing the remote or local attestation... Broken again? Yes Only runtime remote attestation will detect this correctly, FDE can not as far as I know. |
Did you test it? |
Yes, here are the two eventlogs (intended.yaml and evilmaid.yaml, using /v as /vmlinuz and /i as /initrd.img) |
Well, I actually meant "did you test unlocking of encrypted volume in this case". But yes, it sounds plausible. Personally I am not sure why kernel does not measure initrd from command like:
Nothing in commit message explains it. It should really be reported to the kernel. |
When I remember correctly from the kernel mailing list, measurements are done conditionally to not break existing things too much. The kernel arrived a bit too late to the party. |
Anyhow, with GRUB2 this attack will be detected as it requires changes to the boot config. As changes to the grub.cfg will be reflected in PCR 8 the attack is then not possible (even without measurement of the initrd itself). Wouldn't it be nice to measure all boot configs in sd-boot as well (incl. EFI blob sections and entry conf files) before parsing? That would allow sd-boot to be easily one step further as a trusted sd-boot. I would also definitely feel better if the bootloader completely measures the next stage (vmlinuz, initrd and the cmd line) before execution, instead of having the vmlinuz measuring the initrd and cmd line while vmlinuz (better said, the vmlinuz EFI stub) is already be executed. That alone violates what the TPM is intended for, to form a chain of trust. Of course it does not break the chain necessarily, but it weakens unnecessarily it's strength a bit as it could fake itself. The boot phase is not a place where it's good to save some code or work. BTW LOAD_FILE2 is also not available when the kernel is directly booted by an UEFI boot entry. So that is not an option as well. |
but i am not sure i follow here, i mean, sure if you only look at the kernel's own initrd measurements, and the two measurements of the kernel command line, then yeah, it's not safe. But measuring the kernel image itself is also done, so also bind against that? |
The kernel will not change so measurements will be the same. |
Quoting
Well, three years is a long run, so may be this should be revisited. |
I'll approach the kernel mailing list to ask for revisiting. Thanks for your input. |
The kernel EFI stub is not an unified kernel image (UKI) unlike the sd-stub, it's the kernel only and requires explicit measurement of the initrd. That is done with LoadFile2. Therefore a blind spot will arise in it's current state, which the evil maid can abuse when sd-boot using the EFI/chain loader or direct UEFI boot is used (without LoadFile2). It's for sure at least a kernel problem. On the other hand, it's also a responsability of the bootloader, that can't be denied. GRUB2 fixed it long time ago already and measures the initrd and configs always. Of course someone would bind to more PCR registers then 9 and 12. Most probably it's best to bind to PCR 7 as well and sign the kernel EFI stub, the vmlinuz, with custom Secure Boot keys. |
Describe the solution you'd like
#2587 added support for measuring the kernel command line into the TPM. This is to allow for "Measured Boot" where all components of the boot chain (firmware, bootloader, kernel, etc..) are measured into the TPM for later cryptographic attestation. However, that patch didn't include measuring a key component of the boot chain: the initrd. #6124 attempted to add this support, but @haraldh correctly pointed out that it doesn't make sense to measure the disk and then have the kernel reload the initrd from disk, as this is quite insecure.
It's worth looking at how @mjg59's Grub patches handle this. For context, the Linux kernel has two entry points:
initrd
command line parameterboot.c
codechainloader
bootparams
struct (née Zero Page) and jump to an address.shim.c
codelinuxefi
.In Matthew's patches to GRUB, the command line params can be measured into PCR 8 (like systemd-boot) and the initrds can be measured into PCR 9 (unlike systemd-boot) when the kernel is loaded with
linuxefi
.My proposal is two-fold:
linux
andinitrd
parameters, we load the kernel using the EFI handover protocol, similar to what we already do in the stub code.tpm
option is enabled, measure any initrds into the TPM. Which PCR would be determined by ainitrd-pcrindex
option, defaulting to PCR 9.Advantages to this approach
Questions for @haraldh
Describe alternatives you've considered
linuxefi
support) already measures the initrd(s) into PCR 9, if we also added this functionality to the kernel, we would measure the initrd(s) twice unless we were very careful with our versions and configurations.The text was updated successfully, but these errors were encountered: