Skip to content

Commit

Permalink
Merge pull request canonical#334 from chrisccoulson/preinstall-add-mi…
Browse files Browse the repository at this point in the history
…nimal-check-pcr2

preinstall: Add minimal checks for PCR 2.

This adds a minimal check for PCR 2 which determines whether any
code from value-added-retailer components is executing or not. This
will be used by RunChecks because there will be an option to reject
systems that include code from value-added-retailer components.

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.
  • Loading branch information
chrisccoulson authored Oct 3, 2024
2 parents 9db1d6a + fb25543 commit eaa3655
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 4 deletions.
75 changes: 75 additions & 0 deletions efi/preinstall/check_pcr2.go
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*
*/

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")
}
87 changes: 87 additions & 0 deletions efi/preinstall/check_pcr2_test.go
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*
*/

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`)
}
12 changes: 8 additions & 4 deletions efi/preinstall/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -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
Expand Down

0 comments on commit eaa3655

Please sign in to comment.