diff --git a/efi/fw_load_handler_test.go b/efi/fw_load_handler_test.go
index df961071..92e72584 100644
--- a/efi/fw_load_handler_test.go
+++ b/efi/fw_load_handler_test.go
@@ -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()
}
diff --git a/efi/preinstall/check_pcr0.go b/efi/preinstall/check_pcr0.go
new file mode 100644
index 00000000..34986368
--- /dev/null
+++ b/efi/preinstall/check_pcr0.go
@@ -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 .
+ *
+ */
+
+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
+}
diff --git a/efi/preinstall/check_pcr0_test.go b/efi/preinstall/check_pcr0_test.go
new file mode 100644
index 00000000..c9ad430b
--- /dev/null
+++ b/efi/preinstall/check_pcr0_test.go
@@ -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 .
+ *
+ */
+
+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`)
+}
diff --git a/efi/preinstall/check_pcr2.go b/efi/preinstall/check_pcr2.go
new file mode 100644
index 00000000..894ac231
--- /dev/null
+++ b/efi/preinstall/check_pcr2.go
@@ -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 .
+ *
+ */
+
+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
+}
diff --git a/efi/preinstall/check_pcr2_test.go b/efi/preinstall/check_pcr2_test.go
new file mode 100644
index 00000000..ad5da9bd
--- /dev/null
+++ b/efi/preinstall/check_pcr2_test.go
@@ -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 .
+ *
+ */
+
+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`)
+}
diff --git a/efi/preinstall/export_test.go b/efi/preinstall/export_test.go
index f0607077..01b4d509 100644
--- a/efi/preinstall/export_test.go
+++ b/efi/preinstall/export_test.go
@@ -23,6 +23,7 @@ type (
CheckTPM2DeviceFlags = checkTPM2DeviceFlags
CpuVendor = cpuVendor
DetectVirtResult = detectVirtResult
+ DriversAndAppsResultFlags = driversAndAppsResultFlags
MeVersion = meVersion
PlatformFirmwareProtectionsResultFlags = platformFirmwareProtectionsResultFlags
)
@@ -34,6 +35,7 @@ const (
CpuVendorAMD = cpuVendorAMD
DetectVirtNone = detectVirtNone
DetectVirtVM = detectVirtVM
+ DriversAndAppsDriversPresent = driversAndAppsDriversPresent
MeFamilyUnknown = meFamilyUnknown
MeFamilySps = meFamilySps
MeFamilyTxe = meFamilyTxe
@@ -45,8 +47,10 @@ const (
var (
CalculateIntelMEFamily = calculateIntelMEFamily
CheckCPUDebuggingLockedMSR = checkCPUDebuggingLockedMSR
+ CheckDriversAndAppsMeasurements = checkDriversAndAppsMeasurements
CheckFirmwareLogAndChoosePCRBank = checkFirmwareLogAndChoosePCRBank
CheckForKernelIOMMU = checkForKernelIOMMU
+ CheckPlatformFirmwareMeasurements = checkPlatformFirmwareMeasurements
CheckPlatformFirmwareProtections = checkPlatformFirmwareProtections
CheckPlatformFirmwareProtectionsIntelMEI = checkPlatformFirmwareProtectionsIntelMEI
CheckSecureBootPolicyPCRForDegradedFirmwareSettings = checkSecureBootPolicyPCRForDegradedFirmwareSettings