I’ve written a few posts in the past about some code I’ve written to parse / display the TPM2 UEFI / pre-boot event log. Back when I wrote this stuff up initially, getting access to the event log on Linux systems was a PITA. There hadn’t been any work done to expose the event log to the OS and so I wrote my code to run under the Grub shell and eventually the UEFI shell:
In the intervening years (yes it really has been that long) some important things have changed: The UEFI firmware is now exposing the event log through an ACPI table (this was previously not required by the spec). Also the the Linux kernel is now consuming this table and exposing the event log through the securityfs typically at
/sys/kernel/security/tpm0/binary_bios_measurements. This is enough for us to parse and display the event log in your favorite shell from userspace!
ASIDE: Configuring a Linux system to expose the event log through securityfs requires enabling secure boot. The easiest way to do this quickly is to use the latest Fedora release (31 when this was written). I did this by running Fedora 31 as a VM, but this requires using an OVMF image with secure boot enabled. How to do this is beyond the scope of this post but it may show up in a future post.
This code was ported from my past efforts in the UEFI shell and significantly cleaned up to separate the display logic from the parsing logic. It was merged into the tpm2-tools project in pull request #1898, commit 164737a7cd1bdfe5659cab0da58d2d93d925c27d as the
tpm2_eventlog tool. The parser is a simple routine that extracts data from the binary log while ensuring that the various size fields in the log don’t cause it to walk off the end of the data buffer.
The event log is a series of concatenated event structures (format is documented here: https://trustedcomputinggroup.org/resource/pc-client-specific-platform-firmware-profile-specification/) and so the most natural interface to the parsers is a simple
foreach function that invokes a user supplied callback for each event structure in the log. The parsers validates the event structure before invoking the callback both for safety and to prevent the need for duplicate validation code in each callback.
Within each event structure is a series of variable length digests, one for each digest supported and enabled for the TPM2 on the platform. A second
foreach function is supplied for iterating over these digests in a method similar the foreach that iterates over the event structures. This is useful since the digest structures are variable length and byte aligned within the larger event structure so walking through the binary log requires some pointer arithmetic.
As part of porting this over to the tpm2-tools project the output format was updated to use yaml since it’s the preferred format for the project when displaying non-standard text formats. Interestingly enough the ‘field: value’ ascii format I had been using only required a few ‘-‘s and some indentation changes to pass
All of this would be a bit over engineered if all we had to do was to convert the log from binary to text. The binary log produced by the firmware is in host byte order and so it’s not suitable for exchange between hosts (among other reasons). Other log formats are being standardized for this purpose, most notable is the TCG “canonical event log format” presented at the linux security summit 2018 as applied to IMA: https://events19.linuxfoundation.org/wp-content/uploads/2017/11/A-Canonical-Event-Log-Structure-for-IMA-David-Safford-Monty-Wiseman-GE.pdf. My expectation is that, when the TCG publishes this new event log format, developers handling these event logs will need to be able to dump them as text for debugging purposes but also transform between these formats as well.
For now, the
tpm2_eventlog only accepts the binary firmware log as input and displays yaml as output. Now that this tool has been merged I’m working on decoding the event blobs defined in the spec and will hopefully have that merged before the next release.