Re: [PATCH] efi: expose TPM event log to userspace via sysfs

Mikko Rapeli
2024-04-25T08:56:37Z

Hi,

On Wed, Apr 24, 2024 at 07:15:35PM +0200, Ard Biesheuvel wrote:
> On Mon, 22 Apr 2024 at 17:22, Ilias Apalodimas
> <ilias.apalodimas@linaro.org> wrote:
> >
> > On Mon, 22 Apr 2024 at 17:31, James Bottomley
> > <James.Bottomley@hansenpartnership.com> wrote:
> > >
> > > On Mon, 2024-04-22 at 16:54 +0300, Ilias Apalodimas wrote:
> > > > Hi James
> > > >
> > > > On Mon, 22 Apr 2024 at 16:38, James Bottomley
> > > > <James.Bottomley@hansenpartnership.com> wrote:
> > > > >
> > > > > On Mon, 2024-04-22 at 16:32 +0300, Ilias Apalodimas wrote:
> > > > > > Hi all,
> > > > > >
> > > > > > On Mon, 22 Apr 2024 at 16:08, Mikko Rapeli
> > > > > > <mikko.rapeli@linaro.org>
> > > > > > wrote:
> > > > > > >
> > > > > > > Hi,
> > > > > > >
> > > > > > > On Mon, Apr 22, 2024 at 08:42:41AM -0400, James Bottomley
> > > > > > > wrote:
> > > > > > > > On Mon, 2024-04-22 at 14:27 +0300, Mikko Rapeli wrote:
> > > > > > > > > Userspace needs to know if TPM kernel drivers need to be
> > > > > > > > > loaded and related services started early in the boot if
> > > > > > > > > TPM device is used and available.
> > > > > > > >
> > > > > > > > This says what but not why. We already have module
> > > > > > > > autoloading that works correctly for TPM devices, so why is
> > > > > > > > this needed?
> > > > > > > >
> > > > > > > > We do have a chicken and egg problem with IMA in that the TPM
> > > > > > > > driver needs to be present *before* any filesystem, including
> > > > > > > > the one the TPM modules would be on, is mounted so executions
> > > > > > > > can be measured into IMA (meaning that if you use IMA the TPM
> > > > > > > > drivers must be built in) but this sounds to be something
> > > > > > > > different. However, because of the IMA problem, most
> > > > > > > > distributions don't end up compiling TPM drivers as modules
> > > > > > > > anyway.
> > > > > > > >
> > > > > > > > Is what you want simply that tpm modules be loaded earlier?
> > > > > > >
> > > > > > > Yes, ealier is the problem. In my specific testing case the
> > > > > > > machine is qemu arm64 with swtpm with EFI firmware for secure
> > > > > > > boot and TPM support.
> > > > > > >
> > > > > > > Firmware uses TPM and does measurements and thus TPM event log
> > > > > > > is
> > > > > > > available on this machine and a bunch of other arm64 boards.
> > > > > > > Visible in early boot dmesg as TPMEventLog lines like:
> > > > > > >
> > > > > > > [ 0.000000] efi: ESRT=0xf0ea5040 TPMFinalLog=0xf0ea9040
> > > > > > > RTPROP=0xf0ea7040 SMBIOS=0xf0ea3000 TPMEventLog=0xeb3b3040
> > > > > > > INITRD=0xeb3b2040 RNG=0xe5c0f040 MEMRESERVE=0xe5c0e040
> > > > > > >
> > > > > > > Different boards use different TPM HW and drivers so compiling
> > > > > > > all these in is possible but a bit ugly. systemd recently
> > > > > > > gained support for a specific tpm2.target which makes TPM
> > > > > > > support modular and also works with kernel modules for some TPM
> > > > > > > use cases but not rootfs encryption.
> > > > > > >
> > > > > > > In my test case we have a kernel+initramfs uki binary which is
> > > > > > > loaded by EFI firmware as a secure boot binary. TPM support on
> > > > > > > various boards is visible in devicetree but not as ACPI table
> > > > > > > entries. systemd currently detect TPM2 support either via ACPI
> > > > > > > table /sys/firmware/acpi/tables/TPM2 or TPM entry or via
> > > > > > > firmware measurement via
> > > > > > > /sys/kernel/security/tpm0/binary_bios_measurements
> > > > > > > .
> > > > > >
> > > > > > One corner case worth noting here is that scanning the device
> > > > > > tree won't always work for non-ACPI systems... The reason is that
> > > > > > a firmware TPM (running in OP-TEE) might or might not have a DT
> > > > > > entry, since OP-TEE can discover the device dynamically and
> > > > > > doesn't always rely on a DT entry.
> > > > > >
> > > > > > I don't particularly love the idea that an EventLog existence
> > > > > > automatically means a TPM will be there, but it seems that
> > > > > > systemd already relies on that and it does solve the problem we
> > > > > > have.
> > > > >
> > > > > Well, quite. That's why the question I was interested in, perhaps
> > > > > not asked as clearly as it could be is: since all the TPM devices
> > > > > rely on discovery mechanisms like ACPI or DT or the like which are
> > > > > ready quite early, should we simply be auto loading the TPM drivers
> > > > > earlier?
> > > >
> > > > This would be an elegant way to solve this and on top of that, we
> > > > have a single discovery mechanism from userspace -- e.g ls /dev/tpmX.
> > > > But to answer that we need more feedback from systemd. What 'earlier'
> > > > means? Autload it from the kernel before we go into launching the
> > > > initrd?
> > >
> > > Right, so this is another timing problem: we can't autoload modules
> > > *before* they appear in the filesystem and presumably they're on the
> > > initrd, so auto loading must be post initrd mount (and init execution)
> > > but otherwise quite early?
> >
> > Exactly. But is that enough?
> >
> > >
> > > This might be quite a bit of work. Logically, just moving the matching
> > > and loading code earlier might work, but we used to have a
> > > load_default_modules() at the end of init/main.c and it got removed
> > > (because it only eventually loaded elevator modules) everything is now
> > > loaded in it's various init routines, so to get, say, TPM ACPI modules
> > > loaded earlier, we'd have to run the ACPI device matching code earlier
> > > and so on for every other subsystem ...
> >
> > Being the devil's advocate here and as I stated I don't love this but ...
> > The kernel isn't technically doing anything wrong. We just expose an
> > *existing* EFI config table. The kernel also exposes filesystems so
> > people are free to do rm -rf *.
> > The fact that applications might use it as a means of "oh there's
> > probably a TPM" shouldn't be the end of the world. On top of that,
> > since it's an EFI config table we can keep it around and never break
> > any ABIs we create to userspace. If in the future we find a better
> > way, userspace can use that?
> >
> > So perhaps this is ok as long as make sure we understand why systemd
> > needs it that early?
> >
>
> What I would like to know is which API systemd is attempting to use,
> and which -apparently- may never become available if no TPM is exposed
> by the kernel.

ACPI TPM table entry, available without TPM drivers.

TPM PCR bios measurement, available only with working TPM drivers.

> Ideally, we should be able to take inspiration from the probe deferral
> work, and return -EAGAIN to convey that it is too early to signal
> either success or permanent failure.
>
> Exposing random firmware assets directly to user space to make guesses
> about this doesn't seem like a very robust approach to this issue.

As I understand, there are two questions which systemd and userspace need aswers to:

1) is there a TPM device

2) was a TPM device used by firmware to measure boot

If answer to 1) is yes, then drivers need to be loaded, but not necessarily
in early boot before main rootfs mount. If answer to 2) is yes, then
drivers and userspace components should be setup for TPM in early boot.

The x86 and server grade arm standard for 1) is ACPI table TPM entry, basically
/sys/firmware/acpi/tables/TPM2. For other HW, like embedded arm boards/SoCs,
the TPM device can be on discoverable bus (optee firmware TPM etc)
or in devicetree. These work correctly with udev etc and drivers get loaded
in normal boot and thus answer 1).

For 2), once kernel side TPM device has been initialized, driver built into the
kernel or loaded before this check, userspace and systemd can check if
/sys/firmware/acpi/tables/TPM2 is there, or alternatively if TPM driver
is already working /sys/kernel/security/tpm0/binary_bios_measurements
which indicates that firmware was measured with TPM. The latter really requires
built in TPM drivers or modules which are loaded really early in boot, almost
as first thing in userspace, before systemd does any checks for the TPM
device existance. These steps happen in initramfs so that TPM can be used
to protect main rootfs. systemd implements this with efi_has_tpm2()
function in https://github.com/systemd/systemd/blob/main/src/shared/efi-api.c#L482

One problem is how to support TPM devices as kernel modules. Linux distros
should be able to build these drivers too as modules and systemd etc will
take care to load them once devices are discovered, and then queue in
other possibly complex userspace services/daemons so that TPM is useful and
rootfs can be decrypted. /sys/kernel/security/tpm0/binary_bios_measurements is
available only when the TPM driver is loaded and working. For example, if boot firmware has
optee and firmware TPM (fTPM) as an early trusted application (early TA), then this
only works when optee driver is initialized, tee-supplicant userspace daemon runs
and tpm_ftpm_tee kernel driver for fTPM has been loaded to the kernel. tee-supplicant
implements RPMB storage if board/HW does not support it directly. The optee and fTPM
kernel drivers can be built into kernel, but they still need tee-supplicant in
userspace.

To answer 2), ACPI TPM table entry works without kernel drivers, but what
about devices without ACPI support, or TPM on a discoverable bus like optee with fTPM?

Parsing devicetree in userspace is really painful, but could possible be done.
Or kernel could provide a sysfs indicator if there is a devicetree entry
for TPM. But there are some devices where devicetree does not show a TPM
device but one exists in the discoverable bus, optee fTPM.

In all, granted limited, arm64 cases which I have seen, when firmare was aware
of the TPM device and used it for measuring boot, then the TPM Event Log was
provided to the kernel. The early boot log messages like show this:

arm64 qemu with swtpm, u-boot based EFI firmware, TPM Event Log and TPM
in devicetree, no ACPI:

https://ledge.validation.linaro.org/scheduler/job/85933
[ 0.000000] efi: TPMFinalLog=0xbd649040 RTPROP=0xbd647040 SMBIOS 3.0=0xbe6a9000 TPMEventLog=0xbd5b0040 INITRD=0xbd5af040 MEMRESERVE=0xbd5ae040
...
tpm_tis@0 {
tpm_event_log_size = <0x18f>;
compatible = "tcg,tpm-tis-mmio";
reg = <0x00 0x5000>;
tpm_event_log_addr = <0x00 0x40100000>;
};

rockpi4b with firmware TPM, u-boot based EFI firmware, TPM Event Log but
no TPM in devicetree, no ACPI:

https://ledge.validation.linaro.org/scheduler/job/85936
[ 0.000000] efi: ESRT=0xf0ea5040 TPMFinalLog=0xf0ea9040 RTPROP=0xf0ea7040 SMBIOS=0xf0ea3000 TPMEventLog=0xeb381040 INITRD=0xeb380040 RNG=0xe5baf040 MEMRESERVE=0xe5bae040

synquacer with firmware TPM and real TPM, u-boot based EFI firmware,
TPM Event Log using fTPM but only real but disabled TPM in devicetree, no ACPI:

https://ledge.validation.linaro.org/scheduler/job/85934
[ 0.000000] efi: ESRT=0xf9c90040 TPMFinalLog=0xf9c94040 RTPROP=0xf9c92040 SMBIOS=0xf9c8e000 TPMEventLog=0xf9bf7040 INITRD=0xf9bf6040 RNG=0xf9bf5040 MEMRESERVE=0xf9bf4040
...
tpm_tis@10000000 {
compatible = "socionext,synquacer-tpm-mmio";
status = "disabled";
reg = <0x00 0x10000000 0x00 0x5000>;
};

kv260/zyncmp-kria-starter with real TPM, u-boot based EFI firmware,
TPM Event Log using TPM but no devicetree, no ACPI:

https://ledge.validation.linaro.org/scheduler/job/85937
[ 0.000000] efi: ESRT=0x777f8040 TPMFinalLog=0x777fc040 RTPROP=0x777fa040 SMBIOS=0x777f6000 TPMEventLog=0x7775a040 INITRD=0x77759040 MEMRESERVE=0x77758040

Ampere AVA with ACPI TPM entry but support disabled in firmare,
no TPM Event Log, no devicetree, but an ACPI TPM entry:

https://ledge.validation.linaro.org/scheduler/job/85935
[ 0.000000] ACPI: TPM2 0x00000000FFFEFD18 00004C (v04 Ampere Altra 00000002 AMP. 01000013)

Some of these TPM indicators could be considered firmware bugs, but in all
cases TPM firmware support can be detected via TPM Event Log
so that answers question 2).

TPM Event Log is not a random firmware interface. It is an optional interface
to make TPM usable and in practice most TPM using firmware provide it to the kernel.
The actual measurement and checks for it require the TPM HW and measurement PCRs,
but the TPM Event Log is a strong indicator from firmware to the kernel, and userspace,
that TPM is available and was used.

Kernel already knows about TPM Event Log. The proper interface to read it is from
securityfs file binary_bios_measurements implemented by loaded TPM driver.
Existence of this measurement is know before TPM driver is loaded and this change
only exports that to sysfs so that userspace can queue the TPM driver loading
and userspace services/daemons in the early initramfs before main rootfs is available.

Hope this helps,

-Mikko