diff --git a/efi/preinstall/check_pcr2.go b/efi/preinstall/check_pcr2.go new file mode 100644 index 00000000..7ed1c734 --- /dev/null +++ b/efi/preinstall/check_pcr2.go @@ -0,0 +1,75 @@ +// -*- 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" + + "github.com/canonical/tcglog-parser" + internal_efi "github.com/snapcore/secboot/internal/efi" +) + +type checkDriversAndAppsMeasurementsResult int + +const ( + noDriversAndAppsPresent checkDriversAndAppsMeasurementsResult = iota + driversAndAppsPresent +) + +// checkDriversAndAppsMeasurements performs minimal checks on PCR 2, which is where +// addon code from value-added-retailer components such as option ROMs and UEFI +// drivers are measured. +// +// It returns whether the PCR indicates that there is code from value-added-retailer +// components executing. +// +// As efi.WithDriversAndAppsProfile just copies events from the log and does no +// prediction for this PCR, this function doesn't do any more extensive testing, such +// as ensuring that the PCR only contains events of the expected type, the event data +// for each event is of an expected type, and for events where the digest is a tagged +// hash of the event data, that the digest is consistent with the event data. +func checkDriversAndAppsMeasurements(log *tcglog.Log) (checkDriversAndAppsMeasurementsResult, error) { + // Iterate over the log until OS-present and check if there are any + // drivers or applications loaded + phaseTracker := newTcgLogPhaseTracker() + for _, ev := range log.Events { + phase, err := phaseTracker.processEvent(ev) + if err != nil { + return noDriversAndAppsPresent, err + } + + if phase >= tcglogPhaseTransitioningToOSPresent { + return noDriversAndAppsPresent, nil + } + + if ev.PCRIndex != internal_efi.DriversAndAppsPCR { + // Not PCR2 + continue + } + + switch ev.EventType { + case tcglog.EventTypeEFIBootServicesApplication, tcglog.EventTypeEFIBootServicesDriver, tcglog.EventTypeEFIRuntimeServicesDriver, + tcglog.EventTypeEFIPlatformFirmwareBlob, tcglog.EventTypeEFIPlatformFirmwareBlob2, tcglog.EventTypeEFISPDMFirmwareBlob: + return driversAndAppsPresent, nil + } + } + + return noDriversAndAppsPresent, errors.New("internal error: reached end of log before encountering transition to OS-present") +} diff --git a/efi/preinstall/check_pcr2_test.go b/efi/preinstall/check_pcr2_test.go new file mode 100644 index 00000000..3e054f68 --- /dev/null +++ b/efi/preinstall/check_pcr2_test.go @@ -0,0 +1,87 @@ +// -*- 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 ( + "errors" + + "github.com/canonical/go-tpm2" + "github.com/canonical/tcglog-parser" + . "github.com/snapcore/secboot/efi/preinstall" + "github.com/snapcore/secboot/internal/efitest" + . "gopkg.in/check.v1" +) + +type pcr2Suite struct{} + +var _ = Suite(&pcr2Suite{}) + +func (s *pcr2Suite) TestCheckDriversAndAppsMeasurementsGoodNonePresent(c *C) { + log := efitest.NewLog(c, &efitest.LogOptions{Algorithms: []tpm2.HashAlgorithmId{tpm2.HashAlgorithmSHA256}}) + result, err := CheckDriversAndAppsMeasurements(log) + c.Check(err, IsNil) + c.Check(result, Equals, NoDriversAndAppsPresent) +} + +func (s *pcr2Suite) TestCheckDriversAndAppsMeasurementsGoodDriversPresent(c *C) { + log := efitest.NewLog(c, &efitest.LogOptions{ + Algorithms: []tpm2.HashAlgorithmId{tpm2.HashAlgorithmSHA256}, + IncludeDriverLaunch: true, + }) + result, err := CheckDriversAndAppsMeasurements(log) + c.Check(err, IsNil) + c.Check(result, Equals, DriversAndAppsPresent) +} + +func (s *pcr2Suite) TestCheckDriversAndAppsMeasurementsLogError(c *C) { + log := efitest.NewLog(c, &efitest.LogOptions{Algorithms: []tpm2.HashAlgorithmId{tpm2.HashAlgorithmSHA256}}) + events := log.Events + for len(events) > 0 { + ev := events[0] + events = events[1:] + + if ev.EventType != tcglog.EventTypeSeparator { + continue + } + + ev.Data = &invalidEventData{errors.New("some error")} + break + } + + _, err := CheckDriversAndAppsMeasurements(log) + c.Check(err, ErrorMatches, `invalid event data for EV_SEPARATOR event in PCR 7: some error`) +} + +func (s *pcr2Suite) TestCheckDriversAndAppsMeasurementsLogNoTransitionToOSPresent(c *C) { + log := efitest.NewLog(c, &efitest.LogOptions{Algorithms: []tpm2.HashAlgorithmId{tpm2.HashAlgorithmSHA256}}) + events := log.Events + for len(events) > 0 { + ev := events[0] + if (ev.PCRIndex >= 0 && ev.PCRIndex < 7) && ev.EventType == tcglog.EventTypeSeparator { + break + } + events = events[1:] + } + // Truncate the log + log.Events = log.Events[:len(log.Events)-len(events)] + + _, err := CheckDriversAndAppsMeasurements(log) + c.Check(err, ErrorMatches, `internal error: reached end of log before encountering transition to OS-present`) +} diff --git a/efi/preinstall/export_test.go b/efi/preinstall/export_test.go index 53508d68..985ba7e8 100644 --- a/efi/preinstall/export_test.go +++ b/efi/preinstall/export_test.go @@ -20,10 +20,11 @@ package preinstall type ( - CheckTPM2DeviceFlags = checkTPM2DeviceFlags - CpuVendor = cpuVendor - DetectVirtResult = detectVirtResult - MeVersion = meVersion + CheckDriversAndAppsMeasurementsResult = checkDriversAndAppsMeasurementsResult + CheckTPM2DeviceFlags = checkTPM2DeviceFlags + CpuVendor = cpuVendor + DetectVirtResult = detectVirtResult + MeVersion = meVersion ) const ( @@ -33,16 +34,19 @@ const ( CpuVendorAMD = cpuVendorAMD DetectVirtNone = detectVirtNone DetectVirtVM = detectVirtVM + DriversAndAppsPresent = driversAndAppsPresent MeFamilyUnknown = meFamilyUnknown MeFamilySps = meFamilySps MeFamilyTxe = meFamilyTxe MeFamilyMe = meFamilyMe MeFamilyCsme = meFamilyCsme + NoDriversAndAppsPresent = noDriversAndAppsPresent ) var ( CalculateIntelMEFamily = calculateIntelMEFamily CheckCPUDebuggingLockedMSR = checkCPUDebuggingLockedMSR + CheckDriversAndAppsMeasurements = checkDriversAndAppsMeasurements CheckFirmwareLogAndChoosePCRBank = checkFirmwareLogAndChoosePCRBank CheckForKernelIOMMU = checkForKernelIOMMU CheckPlatformFirmwareProtections = checkPlatformFirmwareProtections