Parsing the TPM2 Event Log from Userspace Part 2

The first part of this work put the general structure for the code to parse the core event structures in place. With that merged the next step was to handle special cases and refactor the core logic to accommodate. The special cases make this work far more interesting since in it gives context specific meaning to the generic fields in the core event structures. For those interested this work was merged in https://github.com/tpm2-software/tpm2-tools/pull/1950. The rest of this post will walk through the interesting parts of the PR.

Refactoring the parsing loop

The first commit in this series restructures the initial `foreach_*` interface functions to simplify the output module. This became necessary as I was writing the code to parse the event data field. The previous approach relied on the callback functions from the caller to do farm more work than was required and adding the logic required to parse the event data in this model only made things worse. The details of this decision are in the commit message (https://github.com/tpm2-software/tpm2-tools/pull/1950/commits/f0179c1de7f56f2514bcea4c6832a28c406f88af) but for our purposes here I’ll just say it was necessary to keep from requiring too much coordination between the parsing module and the output module.

SpecID Event

The TCG PC Client Platform Firmware Profile Specification (section 9.4.5.1 in the current version) requires that the first even in the log follow a format that includes data about the version of the format used in the log. This data is encapsulated in the generic event structure seen throughout the log. This structure is parsed and displayed by the tool though the tool doesn’t use it to check compatible versions. This may be necessary / useful in the future but for the initial implementation being permissive seemed like the right thing to do. If an incompatible log is passed it it will be treated the same as a malformed one and an error will be returned.

The test harness now has several examples of SpecID events since each eventlog instance must have one as the first event. A good example is here: https://github.com/tpm2-software/tpm2-tools/blob/92307af75df9fbe3418efb6c6e0509d291a52d7a/test/integration/fixtures/specid-vendordata.bin. Running this through the tool at the time of writing produces output like so:

- Event[0]:
  pcrIndex: 0
  eventType: EV_NO_ACTION
  digest: 0000000000000000000000000000000000000000
  eventDataSize: 45
  SpecID:
    - Signature: Spec ID Event03
      platformClass: 0
      specVersionMinor: 0
      specVersionMajor: 2
      specErrata: 0
      uintnSize: 2
      numberOfAlgorithms: 2
      Algorithms:
        - Algorithm[0]:
          algorithmId: sha1
          digestSize: 20
        - Algorithm[1]:
          algorithmId: sha256
          digestSize: 32
      vendorInfoSize: 0

UEFI Events

Finally this PR adds code to parse UEFI specific events. These events are generated as part of the platform firmware configuring the system and launching the OS. The reason for measuring these various components is interesting but out of scope here (see https://trustedcomputinggroup.org/resource/pc-client-specific-platform-firmware-profile-specification/). What we will cover is a bit about the implementation along with some example output.

UEFI Variables

Variables in UEFI are not like variables in a programming language. They’re bits of storage with scope and some backing that may even be persistent across reboots. This includes the various databases that back UEFI secure boot, as well as the boot configuration variables. The output of the variable holding the first boot variable on a recent Fedora VM looks like so:

- Event[X]:
  PCRIndex: 1
  EventType: EV_EFI_VARIABLE_BOOT
  DigestCount: 2
  Digests:
    - AlgorithmId: sha1
      Digest: 22a4f6ee9af6dba01d3528deb64b74b582fc182b
    - AlgorithmId: sha256
      Digest: 3197be1e300fa1600d1884c3a4bd4a90a15405bfb546cf2e6cf6095f8c362a93
  EventSize: 110
  Event:
    - VariableName: 61dfe48b-ca93-d211-aa0d-00e098032b8c
      UnicodeNameLength: 8
      VariableDataLength: 62
      UnicodeName: Boot0000
      VariableData: 090100002c0055006900410070007000000004071400c9bdb87cebf8344faaea3ee4af6516a10406140021aa2c4614760345836e8ab6f46623317fff0400

Entries for the other boot variables as well as the secure boot KEK, db and dbx should be present as well.

UEFI Blobs

Firmware blobs are opaque and so the event type only includes the base address where the blob is located in memory along with it’s size / length. The blob is not itself included in the event.

- Event[x]:
  PCRIndex: 0
  EventType: EV_EFI_PLATFORM_FIRMWARE_BLOB
  DigestCount: 2
  Digests:
    - AlgorithmId: sha1
      Digest: 47247be85d64522117a9589f8df169c4dc6d1750
    - AlgorithmId: sha256
      Digest: 17d8b73c395cde3cf206083e2ebe774348b811850d957cb65c3712096814d8b1
  EventSize: 16
  Event:
    - BlobBase: 0x820000
      BlobLength: 0xe0000

UEFI Actions

The event for an `EV_EFI_ACTION` type contains a string describing the action. From the event log generated by OVMF it appears that these actions correlate to transitions between firmware phases like when ExitBootServices is invoked:

- Event[X]:
  PCRIndex: 5
  EventType: EV_EFI_ACTION
  DigestCount: 2
  Digests:
    - AlgorithmId: sha1
      Digest: 443a6b7b82b7af564f2e393cd9d5a388b7fa4a98
    - AlgorithmId: sha256
      Digest: d8043d6b7b85ad358eb3b6ae6a873ab7ef23a26352c5dc4faa5aeedacf5eb41b
  EventSize: 29
  Event: Exit Boot Services Invocation

UEFI Services

Finally when UEFI loads a driver from an ad-in card or a non-bootable application the `EV_EFI_BOOT_SERVICES_DRIVER` EventType is used. The structure includes information like the modules location in memory and its length. Additionally it includes the UEFI device path. Currently we dump this as a hex string though someone willing to dig around in the right UEFI spec could decode the value.

- Event[X]:
  PCRIndex: 2
  EventType: EV_EFI_BOOT_SERVICES_DRIVER
  DigestCount: 2
  Digests:
    - AlgorithmId: sha1
      Digest: 855685b4dbd4b67d50e0594571055054cfe2b1e9
    - AlgorithmId: sha256
      Digest: dd8576b4ff346c19c56c3e4f97ce55c5afa646f9c669be0a7cdd05057a0ecdf3
  EventSize: 84
  Event:
    - ImageLocationInMemory: 0x7dcf6018
      ImageLengthInMemory: 171464
      ImageLinkTimeAddress: 0x0
      LengthOfDevicePath: 52
      DevicePath: 02010c00d041030a0000000001010600000201010600000004081800000000000026010000000000ffc30300000000007fff0400

Conclusion

With the above merged into the tpm2-tools project the majority of the events generated by the current OVMF release can be parsed and transformed into a textual (yaml) form. From the testing I’ve done I’d say the implementation is likely ~80% complete. The remaining work requires knowledge of UEFI structures like the

UEFI_DEVICE_PATH

and UEFI_GPT_DATA which will take some doing. Eventually I’m hoping this tool will be a good starting point for supporting the new canonical event log format.

Leave a comment