Skip to content

Commit

Permalink
preinstall: Add simple checks for PCRs 0 and 2
Browse files Browse the repository at this point in the history
This adds some trivial to checks to both PCRs 0 and 2:
- that they contain events of the expected type.
- that there are no measurements to them from the OS-present phase.

As we don't do any sort of prediction for these PCRs (yet), it doesn't
perform additional checking such as ensuring that the event data is
consistent with the event digests when the event digests are a tagged
hash of the event data (ie, not purely informational).

Note that this modifies the internal/efitest package to emit
EV_S_CRTM_VERSION and EV_EFI_PLATFORM_FIRMWARE_BLOB events with the
correct event data types.
  • Loading branch information
chrisccoulson committed Aug 10, 2024
1 parent 3a9d00b commit 0e4a3e4
Show file tree
Hide file tree
Showing 7 changed files with 350 additions and 7 deletions.
3 changes: 0 additions & 3 deletions efi/fw_load_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,6 @@ func (s *fwLoadHandlerSuite) testMeasureImageStart(c *C, data *testFwMeasureImag
handler := NewFwLoadHandler(efitest.NewLog(c, data.logOptions))
c.Check(handler.MeasureImageStart(ctx), IsNil)
c.Check(ctx.events, DeepEquals, data.expectedEvents)
for _, event := range ctx.events {
c.Logf("pcr:%d, type:%v, digest:%#x", event.pcr, event.eventType, event.digest)
}
c.Check(collector.More(), testutil.IsFalse)
return ctx.FwContext()
}
Expand Down
110 changes: 110 additions & 0 deletions efi/preinstall/check_pcr0.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// -*- Mode: Go; indent-tabs-mode: t -*-

/*
* Copyright (C) 2024 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

package preinstall

import (
"errors"
"fmt"

"github.com/canonical/tcglog-parser"
internal_efi "github.com/snapcore/secboot/internal/efi"
)

// checkPlatformFirmwareMeasurements checks measurements related to platfor firmware in PCR0,
// including that measurements are of expected types, and that no measurements are made during
// the OS-present phase.
func checkPlatformFirmwareMeasurements(log *tcglog.Log) error {
// Iterate over the log until OS-present and make sure that we have expected
// event types
events := log.Events
for len(events) > 0 {
ev := events[0]
events = events[1:]

if ev.PCRIndex != internal_efi.PlatformFirmwarePCR {
// Not PCR0
continue
}
if ev.EventType == tcglog.EventTypeSeparator {
break
}

switch ev.EventType {
case tcglog.EventTypePostCode, tcglog.EventTypeEFIPlatformFirmwareBlob:
// Platform firmware blobs - deprecated.
// EV_POST_CODE should contain a non-NULL terminated string or a
// UEFI_PLATFORM_FIRMWARE_BLOB structure. EV_EFI_PLATFORM_FIRMWARE_BLOB
// should contain a UEFI_PLATFORM_FIRMWARE_BLOB structure.
case tcglog.EventTypeNoAction:
// Information, not measured
case tcglog.EventTypeSCRTMContents, tcglog.EventTypeSCRTMVersion:
// SCRTM measurements. EV_S_SCRTM_CONTENTS should either contain a
// UEFI_PLATFORM_FIRMWARE_BLOB or UEFI_PLATFORM_FIRMWARE_BLOB2 structure,
// or it can contain a NULL terminated ASCII string if measured by a H-CRTM
// event. EV_S_CRTM_VERSION should contain a NULL terminated UCS2 string or
// a EFI_GUID.
//
// We don't make any special accommodations for these when measured as part
// of a H-CRTM sequence, so it's possible we already mis-predicted PCR0 and
// marked it invalid before getting to this point.
//
// EV_S_CRTM_VERSION is not informational but we don't check that the data matches
// the event digests because we don't do any prediction for this value.
case tcglog.EventTypeNonhostCode, tcglog.EventTypeNonhostInfo:
// Non-host platform code running on an embedded controller. The second one is used
// if the host platform cannot reliably measure the non-host code. The event data is
// determined by the platform manufacturer.
case tcglog.EventTypePostCode2, tcglog.EventTypeEFIPlatformFirmwareBlob2:
// Platform firmware blobs.
// EV_POST_CODE2 should contain a non-NULL terminated string or a
// UEFI_PLATFORM_FIRMWARE_BLOB2 structure. EV_EFI_PLATFORM_FIRMWARE_BLOB
// should contain a EF_EFI_PLATFORM_FIRMWARE_BLOB2 structure.
case tcglog.EventTypeEFIBootServicesDriver, tcglog.EventTypeEFIRuntimeServicesDriver:
// Platform firmware blobs as PE images and loaded via the LoadImage API.
// We don't check the digests here because it's likely that the device path
// takes us to something we can't read, and we don't do any prediction here
// yet either.
case tcglog.EventTypeEFIHCRTMEvent:
// a H-CRTM sequence that occurred before TPM2_Startup. There may be more than
// one of these.
// There should be a corresponding EV_NO_ACTION event indicating that the startup
// locality is 4, and there may be other EV_NO_ACTION events containing
// TCG_HCRTMComponentEvent structures.
case tcglog.EventTypeEFISPDMFirmwareBlob:
// Firmware of a component that supports SPDM "GET_MEASUREMENTS".
// Note that this is very new (only in the TCG PFP spec v1.06)
default:
return fmt.Errorf("unexpected pre-OS log event type %v", ev.EventType)
}
}

// Nothing should measure to PCR0 outside of pre-OS - we'll generate an invalid profile
// if it does.
for len(events) > 0 {
ev := events[0]
events = events[1:]

if ev.PCRIndex == internal_efi.PlatformFirmwarePCR {
return errors.New("firmware measures events as part of the OS-present environment")
}
}

return nil
}
67 changes: 67 additions & 0 deletions efi/preinstall/check_pcr0_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// -*- Mode: Go; indent-tabs-mode: t -*-

/*
* Copyright (C) 2024 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

package preinstall_test

import (
"github.com/canonical/tcglog-parser"
. "github.com/snapcore/secboot/efi/preinstall"
internal_efi "github.com/snapcore/secboot/internal/efi"
"github.com/snapcore/secboot/internal/efitest"
. "gopkg.in/check.v1"
)

type pcr0Suite struct{}

var _ = Suite(&pcr0Suite{})

func (s *pcr0Suite) TestCheckPlatformFirmwareMeasurementsGood(c *C) {
log := efitest.NewLog(c, &efitest.LogOptions{})
c.Check(CheckPlatformFirmwareMeasurements(log), IsNil)
}

func (s *pcr0Suite) TestCheckPlatformFirmwareMeasurementsGoodSL3(c *C) {
log := efitest.NewLog(c, &efitest.LogOptions{StartupLocality: 3})
c.Check(CheckPlatformFirmwareMeasurements(log), IsNil)
}

func (s *pcr0Suite) TestCheckPlatformFirmwareMeasurementsGoodHCRTM(c *C) {
log := efitest.NewLog(c, &efitest.LogOptions{StartupLocality: 4})
c.Check(CheckPlatformFirmwareMeasurements(log), IsNil)
}

func (s *pcr0Suite) TestCheckPlatformFirmwareMeasurementsUnexpectedEventType(c *C) {
log := efitest.NewLog(c, &efitest.LogOptions{})
for i, ev := range log.Events {
if ev.PCRIndex == internal_efi.PlatformFirmwarePCR && ev.EventType == tcglog.EventTypeEFIPlatformFirmwareBlob {
log.Events[i].EventType = tcglog.EventTypeNonhostConfig
}
}
c.Check(CheckPlatformFirmwareMeasurements(log), ErrorMatches, `unexpected pre-OS log event type EV_NONHOST_CONFIG`)
}

func (s *pcr0Suite) TestCheckPlatformFirmwareMeasurementsUnexpectedOSPresentEvent(c *C) {
log := efitest.NewLog(c, &efitest.LogOptions{})
log.Events = append(log.Events, &tcglog.Event{
PCRIndex: internal_efi.PlatformFirmwarePCR,
EventType: tcglog.EventTypeEFIAction,
Data: tcglog.StringEventData("foo"),
})
c.Check(CheckPlatformFirmwareMeasurements(log), ErrorMatches, `firmware measures events as part of the OS-present environment`)
}
97 changes: 97 additions & 0 deletions efi/preinstall/check_pcr2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// -*- Mode: Go; indent-tabs-mode: t -*-

/*
* Copyright (C) 2024 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

package preinstall

import (
"errors"
"fmt"

"github.com/canonical/tcglog-parser"
internal_efi "github.com/snapcore/secboot/internal/efi"
)

type driversAndAppsResultFlags int

const (
driversAndAppsDriversPresent driversAndAppsResultFlags = 1 << iota
)

func checkDriversAndAppsMeasurements(log *tcglog.Log) (result driversAndAppsResultFlags, err error) {
// Iterate over the log until OS-present and make sure we have expected
// event types.
events := log.Events
for len(events) > 0 {
ev := events[0]
events = events[1:]

if ev.PCRIndex != internal_efi.DriversAndAppsPCR {
// Not PCR2
continue
}
if ev.EventType == tcglog.EventTypeSeparator {
break
}

switch ev.EventType {
case tcglog.EventTypeAction, tcglog.EventTypeEFIAction:
// Some sort of action. The event data is a non-NULL terminated ASCII string.
// The data in these events is not informational (the event digests are the tagged
// hashes of the event data), but we don't verify that the event data is consistent
// with the digests yet because we don't do any prediction here.
case tcglog.EventTypeNonhostCode, tcglog.EventTypeNonhostInfo:
// Non-host platform code running on an embedded controller. The second one is used
// if the host platform cannot reliably measure the non-host code. The event data is
// determined by the platform manufacturer and is purely informational.
case tcglog.EventTypeEFIBootServicesApplication, tcglog.EventTypeEFIBootServicesDriver, tcglog.EventTypeEFIRuntimeServicesDriver:
// Code from value-added-retailer component loaded via the LoadImage API.
// We don't check the digests here because it's likely that the device path
// takes us to something we can't read, and we don't do any prediction here
// yet either.
result |= driversAndAppsDriversPresent
case tcglog.EventTypeEFIPlatformFirmwareBlob:
// Code blob from value-added-retailer component - deprecated. Event data should
// contain a UEFI_PLATFORM_FIRMWARE_BLOB structure.
result |= driversAndAppsDriversPresent
case tcglog.EventTypeEFIPlatformFirmwareBlob2:
// Code blob from value-added-retailer component. Event data should contain a
// UEFI_PLATFORM_FIRMWARE_BLOB2 structure.
result |= driversAndAppsDriversPresent
case tcglog.EventTypeEFISPDMFirmwareBlob:
// Firmware of a component that supports SPDM "GET_MEASUREMENTS".
// Note that this is very new (only in the TCG PFP spec v1.06)
result |= driversAndAppsDriversPresent
default:
return 0, fmt.Errorf("unexpected pre-OS log event type %v", ev.EventType)
}
}

// Nothing should measure to PCR2 outside of pre-OS - we'll generate an invalid profile
// if it does.
for len(events) > 0 {
ev := events[0]
events = events[1:]

if ev.PCRIndex == internal_efi.DriversAndAppsPCR {
return 0, errors.New("firmware measures events as part of the OS-present environment")
}
}

return result, nil
}
68 changes: 68 additions & 0 deletions efi/preinstall/check_pcr2_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// -*- Mode: Go; indent-tabs-mode: t -*-

/*
* Copyright (C) 2024 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

package preinstall_test

import (
"github.com/canonical/tcglog-parser"
. "github.com/snapcore/secboot/efi/preinstall"
internal_efi "github.com/snapcore/secboot/internal/efi"
"github.com/snapcore/secboot/internal/efitest"
. "gopkg.in/check.v1"
)

type pcr2Suite struct{}

var _ = Suite(&pcr2Suite{})

func (s *pcr2Suite) TestCheckDriversAndAppsMeasurementsGood(c *C) {
log := efitest.NewLog(c, &efitest.LogOptions{})
result, err := CheckDriversAndAppsMeasurements(log)
c.Check(err, IsNil)
c.Check(result, Equals, DriversAndAppsResultFlags(0))
}

func (s *pcr2Suite) TestCheckDriversAndAppsMeasurementsWithDrivers(c *C) {
log := efitest.NewLog(c, &efitest.LogOptions{IncludeDriverLaunch: true})
result, err := CheckDriversAndAppsMeasurements(log)
c.Check(err, IsNil)
c.Check(result, Equals, DriversAndAppsDriversPresent)
}

func (s *pcr2Suite) TestCheckDriversAndAppsMeasurementsUnexpectedEventType(c *C) {
log := efitest.NewLog(c, &efitest.LogOptions{IncludeDriverLaunch: true})
for i, ev := range log.Events {
if ev.PCRIndex == internal_efi.DriversAndAppsPCR && ev.EventType == tcglog.EventTypeEFIBootServicesDriver {
log.Events[i].EventType = tcglog.EventTypeNonhostConfig
}
}
_, err := CheckDriversAndAppsMeasurements(log)
c.Check(err, ErrorMatches, `unexpected pre-OS log event type EV_NONHOST_CONFIG`)
}

func (s *pcr2Suite) TestCheckDriversAndAppsMeasurementsUnexpectedOSPresentEvent(c *C) {
log := efitest.NewLog(c, &efitest.LogOptions{})
log.Events = append(log.Events, &tcglog.Event{
PCRIndex: internal_efi.DriversAndAppsPCR,
EventType: tcglog.EventTypeEFIAction,
Data: tcglog.StringEventData("foo"),
})
_, err := CheckDriversAndAppsMeasurements(log)
c.Check(err, ErrorMatches, `firmware measures events as part of the OS-present environment`)
}
8 changes: 4 additions & 4 deletions efi/preinstall/check_tcglog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ func (s *tcglogSuite) TestCheckFirmwareLogAndChoosePCRBankUnexpectedStartupLocal
c.Check(err, ErrorMatches, `no suitable PCR algorithm available:
- TPM_ALG_SHA512: digest algorithm not present in log.
- TPM_ALG_SHA384: digest algorithm not present in log.
- TPM_ALG_SHA256\(PCR0\): PCR value mismatch \(actual from TPM 0x25a58800ba22dff433a8bb1b5084a53ddf02dc71f204053b38036fe1c0f146e2, reconstructed from log 0x3d2b11b4c5cb623acbde6d14205217e47ebd368eab861e4fed782bb99be4598a\).
- TPM_ALG_SHA256\(PCR0\): PCR value mismatch \(actual from TPM 0xd41580fada9d73a2b118afbe38b4e47e4f6917c954e7266c20f324296f48729d, reconstructed from log 0x89f30470312a025085f66bc4a2c70288d753ed56496d7808926a36a7af57f8b1\).
- TPM_ALG_SHA256\(PCR1\): unexpected StartupLocality event \(should be in PCR0\).
`)
var e *NoSuitablePCRAlgorithmError
Expand All @@ -337,7 +337,7 @@ func (s *tcglogSuite) TestCheckFirmwareLogAndChoosePCRBankUnexpectedStartupLocal
c.Check(e.UnwrapBankError(tpm2.HashAlgorithmSHA384), ErrorMatches, `digest algorithm not present in log`)
c.Check(e.UnwrapPCRError(tpm2.HashAlgorithmSHA384, internal_efi.PlatformFirmwarePCR), IsNil)
c.Check(e.UnwrapBankError(tpm2.HashAlgorithmSHA256), IsNil)
c.Check(e.UnwrapPCRError(tpm2.HashAlgorithmSHA256, internal_efi.PlatformFirmwarePCR), ErrorMatches, `PCR value mismatch \(actual from TPM 0x25a58800ba22dff433a8bb1b5084a53ddf02dc71f204053b38036fe1c0f146e2, reconstructed from log 0x3d2b11b4c5cb623acbde6d14205217e47ebd368eab861e4fed782bb99be4598a\)`)
c.Check(e.UnwrapPCRError(tpm2.HashAlgorithmSHA256, internal_efi.PlatformFirmwarePCR), ErrorMatches, `PCR value mismatch (actual from TPM 0xd41580fada9d73a2b118afbe38b4e47e4f6917c954e7266c20f324296f48729d, reconstructed from log 0x89f30470312a025085f66bc4a2c70288d753ed56496d7808926a36a7af57f8b1)`)
c.Check(e.UnwrapPCRError(tpm2.HashAlgorithmSHA256, internal_efi.PlatformFirmwareConfigPCR), ErrorMatches, `unexpected StartupLocality event \(should be in PCR0\)`)
c.Check(e.UnwrapPCRError(tpm2.HashAlgorithmSHA256, internal_efi.DriversAndAppsPCR), IsNil)
}
Expand Down Expand Up @@ -468,7 +468,7 @@ func (s *tcglogSuite) TestCheckFirmwareLogAndChoosePCRBankPCRMismatchMandatory(c
c.Check(err, ErrorMatches, `no suitable PCR algorithm available:
- TPM_ALG_SHA512: digest algorithm not present in log.
- TPM_ALG_SHA384: digest algorithm not present in log.
- TPM_ALG_SHA256\(PCR0\): PCR value mismatch \(actual from TPM 0x25a58800ba22dff433a8bb1b5084a53ddf02dc71f204053b38036fe1c0f146e2, reconstructed from log 0x3d2b11b4c5cb623acbde6d14205217e47ebd368eab861e4fed782bb99be4598a\).
- TPM_ALG_SHA256\(PCR0\): PCR value mismatch \(actual from TPM 0xd41580fada9d73a2b118afbe38b4e47e4f6917c954e7266c20f324296f48729d, reconstructed from log 0x89f30470312a025085f66bc4a2c70288d753ed56496d7808926a36a7af57f8b1\).
`)
var e *NoSuitablePCRAlgorithmError
c.Check(errors.As(err, &e), testutil.IsTrue)
Expand Down Expand Up @@ -502,7 +502,7 @@ func (s *tcglogSuite) TestCheckFirmwareLogAndChoosePCRBankPCRMismatchNonMandator
c.Check(results.Alg, Equals, tpm2.HashAlgorithmSHA256)
c.Check(results.Ok(), Equals, true)
c.Check(results.Lookup(internal_efi.PlatformFirmwarePCR).Ok(), Equals, false)
c.Check(results.Lookup(internal_efi.PlatformFirmwarePCR).Err(), ErrorMatches, `PCR value mismatch \(actual from TPM 0x25a58800ba22dff433a8bb1b5084a53ddf02dc71f204053b38036fe1c0f146e2, reconstructed from log 0x3d2b11b4c5cb623acbde6d14205217e47ebd368eab861e4fed782bb99be4598a\)`)
c.Check(results.Lookup(internal_efi.PlatformFirmwarePCR).Err(), ErrorMatches, `PCR value mismatch \(actual from TPM 0xd41580fada9d73a2b118afbe38b4e47e4f6917c954e7266c20f324296f48729d, reconstructed from log 0x89f30470312a025085f66bc4a2c70288d753ed56496d7808926a36a7af57f8b1\)`)
}

func (s *tcglogSuite) TestCheckFirmwareLogAndChoosePCRBankPCRMismatchMandatoryInOneBank(c *C) {
Expand Down
Loading

0 comments on commit 0e4a3e4

Please sign in to comment.