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.

Reply via email to