Hi Christian,
On 7/2/2021 3:50 PM, Christian Storm wrote:
Hi Cedric,
Make use of systemd code to measure the kernel command line for the
selected configuration into PCR#8. This also causes the firmware
to add the measurement for the loaded EFI image into PCR#4 (as per
UEFI specs). With this change both the loaded kernel and command
line options are measured.
Measurement to PCR #4 is done by the EFI firmware for each LoadImage()'d
EFI binary. According to the spec, "UEFI Boot Manager Code (usually the
MBR) and Boot Attempts" is supposed to be measured to PCR #4.
Correct. More specifically, the target PCR is selected as follows:
EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION => 4
This is what EFI Boot Guard is supposed to be.
EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER => 2
EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER => 2
The type of the image is encoded into the PE/COFF header
Now, an OS kernel doesn't exactly qualify for this semantically,
right?
Correct but LoadImage() does not seem to provide a mean to select a PCR
True. It's always PCR #4 but that doesn't hinder you from measuring the
binary to some other and more fitting PCR(s) as well ― matching your use
case.
I can send a v3 that does that. Here are the measurements I collected
from the TPM event log
/* efibootguard measured in PCR[4] */
PCR 4 -- Event 80000003 <TpmEventType.EFI_BOOT_SERVICES_APPLICATION>
Extend (SHA1): 3877b5f89f59bc5d934ba4b26672fea0ffb0806c
Path vector:
* ACPIDevice ACPI b'\xd0A\x03\n\x00\x00\x00\x00'
* HardwareDevice PCI b'\x00\x10'
* MessagingDevice SCSI b'\x00\x00\x00\x00'
* MediaDevice HardDrive
b'\x01\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\\&\x0b\xa9\xa7\xee\xf1C\xb4".\xd3M\xf6\xe6Y\x02\x02'
* MediaDevice FilePath \EFI\BOOT\BOOTX64.EFI
* End 255 b''
/* our measurement of the specified kernel image in PCR[8]
* note: this is a raw measurement (i.e. SHA1 of the entire file
without interpreting e.g PE/COFF sections)
*/
PCR 8 -- Event 0000000D <TpmEventType.IPL>
Extend (SHA1): e42ee38fd3cf1f7d310f00e4331628b0a188ef3a
0x00000000: 4D 5A 90 00 03 00 00 |MZ..... |
-- not touching non-systemd IPL event --
--> after this event, PCR 8 contains value
58add887d84dd4be97a32a6ba6cb5535f84a7663
--> after reboot, PCR 8 will contain value
58add887d84dd4be97a32a6ba6cb5535f84a7663
/* LoadImage() still measures our kernel image in PCR[4]
* note: EFI knows it is a PE/COFF binary and only relevant sections
are hashed
*/
PCR 4 -- Event 80000003 <TpmEventType.EFI_BOOT_SERVICES_APPLICATION>
Extend (SHA1): a9823f0033e9cb2c30019b4bab7a9ee31071debf
Path vector:
* HardwareDevice MemoryMapped
b'\x02\x00\x00\x00\x18\xc0\x10\x0b\x00\x00\x00\x00\x10\xcd\xbd\r\x00\x00\x00\x00'
* End 255 b''
/* our measurement of the kernel command line into PCR[9] */
PCR 9 -- Event 0000000D <TpmEventType.IPL>
Extend (SHA1): 1985ccaadef02e21d2f161311bd35ea4ce89676e
0x00000000: 72 00 6F 00 6F 00 74 00 77 00 61 00 69 00 74 00
|r.o.o.t.w.a.i.t.|
0x00000010: 20 00 71 00 75 00 69 00 65 00 74 00 20 00 6E 00
|..q.u.i.e.t...n.|
0x00000020: 6F 00 68 00 7A 00 3D 00 6F 00 66 00 66 00 20 00
|o.h.z.=.o.f.f...|
0x00000030: 73 00 65 00 63 00 75 00 72 00 69 00 74 00 79 00
|s.e.c.u.r.i.t.y.|
(125 more bytes)
So as noted before, the kernel image gets measured twice...
Probably, PCRs #8 - #15 "Defined for use by the Static OS" (according to
the spec) are a better match for measuring the OS kernel, additionally.
You're measuring the kernel command line to this range already.
This would have been my preference as well but again LoadImage() isn't
giving an option to select a different PCR (unless I missed it).
No, that's not foreseen, but see above.
What we could do is side-load the UEFI image and HashLogExtend it
ourselves to PCR[8] but LoadImage would still be needed and would also
measure it and extend PCR[4].
Yes, you can measure the binary to PCR #8 as well, then however mashing
up the kernel binary and the kernel's cmdline into one PCR. Whether
that's sensible or not is the question I asked for referring to the
overarching concept you'll want to follow here, i.e., what information /
deduction does it give you and what is it you're after (as just
measuring is not an end in itself)?
Our concept is twofold: (1) use SecureBoot to make sure we are booted
from a trusted boot-loader + trusted Linux kernel (both are signed with
keys loaded into the trust store) (2) extend the root of trust by
mounting a LUKS encrypted volume with the key in the TPM and sealed with
selected PCRs. In an A/B scheme, we may have the same encryption key
placed both in slot[A] and slot[B] but sealed as follows:
* for A: PCR[4] == HashOf(efibootguard.efi) + PCR[8] ==
HashOf(kernelA) + PCR[9] == HashOf(optionsA) + other relevant PCRs (e.g.
BIOS, EFI variables, etc.)
* for B: PCR[4] == HashOf(efibootguard.efi) + PCR[8] ==
HashOf(kernelB) + PCR[9] == HashOf(optionsB) + other relevant PCRs (e.g.
BIOS, EFI variables, etc.)
Note that for our purpose (and I understand we may now want restrict
ourselves to that specific use-case), the following sealing would have
worked:
* for A: PCR[4] == Extend(HashOf(efibootguard.efi), HashOf(kernelA)) +
PCR[9] == HashOf(optionsA) + other relevant PCRs (e.g. BIOS, EFI
variables, etc.)
* for B: PCR[4] == Extend(HashOf(efibootguard.efi), HashOf(kernelB)) +
PCR[9] == HashOf(optionsB) + other relevant PCRs (e.g. BIOS, EFI
variables, etc.)
since we don't really need/want to know which binary was tampered but
simply to maintain the root of trust ("yes the booted kernel is the one
I expected, I may mount my root file-system and run my user-land stack)
I'll check again what other boot-loaders do (we may not be the only ones load
a Linux kernel image packaged as an EFI binary)
Certainly not :) No need, here it is:
- systemd-boot measures kernel command line options to PCR #8:
https://github.com/systemd/systemd/blob/main/src/boot/efi/boot.c#L2201
https://github.com/systemd/systemd/blob/main/src/boot/efi/stub.c#L70
- Trusted Grub measures its core to PCR #9 and chain-loadees to PCR #10
https://github.com/Rohde-Schwarz/TrustedGRUB2/blob/master/README.md#13-measurements-in-short
- rhboot/grub2 measures Strings to PCR #8 and Binaries to PCR #9
https://github.com/rhboot/grub2/blob/master/include/grub/tpm.h#L23
You surely can find some more but this will give you an idea of how
others do it...
That said, what's the overarching concept behind this? Just measuring
"something" is not an end in itself. How is that supposed to be sensibly
used (e.g., by the OS) to actually leverage the measurements? According
to this answer, the measurement(s) should be performed by EFI Boot Guard
to support the intended idea/concept.
We are wanting to get boot artifacts measured so we can locally attest that we
have booted into a well-known system and may safely unseal a secret sealed
into the TPM. Our secret would be sealed with at least PCR[4] and PCR[8]. In
our case, we don't really need to say which element was tampered, we just want
to prevent the bad guy from reading our secret.
OK, but that ties you to this particular kernel binary (PCR#4) and this
particular cmdline (PCR#8). If you now change something, think firmware
update, you have to re-seal the secret which is a delicate operation to
perform in the context of a firmware update transaction...
or seal the same encryption key using a different slot in your TPM so
you don't change anything in your current configuration. If something in
your new system wasn't done right, all pieces to revert to the old
system should still be in place
That said, if you have Secure Boot enabled and use unified kernel
images, what does measuring the cmdline to PCR#8 give you in addition to
just sealing the secret with ― probably among others ― PCR#4 and thereby
bind it to (at least) the unified kernel image ― which includes the
cmdline as PE/COFF section?
in my experiments, I was able to override the kernel command line with
bg_setenv -a. this is probably a "feature" of systemd's stub (maybe this
would not happen when directly building the kernel as an EFI binary but
I am guessing that efibootguard should not assume a specific unified
kernel image implementation?)
Use of the TPM may be disabled by passing --disable-tpm to the
configure script.
Why this distinction? It doesn't harm to have it enabled unconditionally
with measurement simply failing in case no TPM is available, does it?
What is the reasoning behind making in compile-time conditional?
I would be happy to remove this and all the #ifdef ENABLE_TPM. I was just
worried that some people may not want this code enabled/built even though I
see no harm in always having it.
I'm in favor of enabling it unconditionally so that it measures if a TPM
is available and fails (gracefully) in case it is not.
ok - thanks for the guidance
Kind regards,
Christian
--
You received this message because you are subscribed to the Google Groups "EFI Boot
Guard" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/efibootguard-dev/ecf5f68a-940e-70d9-34e3-d13a9896987a%40mentor.com.