Skip to content

Commit

Permalink
preinstall: Add CheckResult and WithAutoTCGPCRProfile APIs
Browse files Browse the repository at this point in the history
CheckResult is returned from RunChecks on successful completion. It is
JSON serializable and intended to be supplied later on to
WithAutoTCGPCRProfile along with some user customization flags (defined
by PCRProfileOptionsFlags) in order to generate an option that can be
supplied to secboot_efi.AddPCRProfile.

Note that some user options don't work yet because of missing PCR
support, although these limitations will be addressed later. Even so,
some options may still fail depending on the CheckResult flags.
  • Loading branch information
chrisccoulson committed Oct 17, 2024
1 parent 5655f3f commit 878ee98
Show file tree
Hide file tree
Showing 14 changed files with 2,120 additions and 31 deletions.
6 changes: 3 additions & 3 deletions efi/preinstall/check_pcr7.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ const (
)

type secureBootPolicyResult struct {
UsedAuthorities []*x509.Certificate
UsedAuthorities []*X509CertificateID
Flags secureBootPolicyResultFlags
}

Expand Down Expand Up @@ -354,7 +354,7 @@ NextEvent:
var foundSig *efi.WinCertificateAuthenticode
for _, cert := range result.UsedAuthorities {
for _, sig := range sigs {
if sig.CertLikelyTrustAnchor(cert) {
if sig.CertWithIDLikelyTrustAnchor(cert) {
foundSig = sig
break
}
Expand Down Expand Up @@ -487,7 +487,7 @@ NextEvent:
if err != nil {
return nil, fmt.Errorf("cannot decode X.509 certificate from db EV_EFI_VARIABLE_AUTHORITY event: %w", err)
}
result.UsedAuthorities = append(result.UsedAuthorities, cert)
result.UsedAuthorities = append(result.UsedAuthorities, newX509CertificateID(cert))

switch cert.PublicKeyAlgorithm {
case x509.RSA:
Expand Down
37 changes: 18 additions & 19 deletions efi/preinstall/check_pcr7_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ package preinstall_test
import (
"context"
"crypto"
"crypto/x509"
"errors"
"io"

Expand Down Expand Up @@ -58,7 +57,7 @@ type testCheckSecureBootPolicyMeasurementsAndObtainAuthoritiesParams struct {
iblImage secboot_efi.Image

expectedFlags SecureBootPolicyResultFlags
expectedUsedAuthorities []*x509.Certificate
expectedUsedAuthorities []*X509CertificateID
}

func (s *pcr7Suite) testCheckSecureBootPolicyMeasurementsAndObtainAuthorities(c *C, params *testCheckSecureBootPolicyMeasurementsAndObtainAuthoritiesParams) error {
Expand Down Expand Up @@ -93,7 +92,7 @@ func (s *pcr7Suite) testCheckSecureBootPolicyMeasurementsAndObtainAuthorities(c
c.Check(result.Flags, Equals, params.expectedFlags)
c.Assert(result.UsedAuthorities, HasLen, len(params.expectedUsedAuthorities))
for i, authority := range result.UsedAuthorities {
c.Check(authority.Equal(params.expectedUsedAuthorities[i]), testutil.IsTrue)
c.Check(authority, DeepEquals, params.expectedUsedAuthorities[i])
}
return nil
}
Expand All @@ -118,8 +117,8 @@ func (s *pcr7Suite) TestCheckSecureBootPolicyMeasurementsAndObtainAuthoritiesGoo
},
},
expectedFlags: SecureBootPolicyResultFlags(0),
expectedUsedAuthorities: []*x509.Certificate{
testutil.ParseCertificate(c, msUefiCACert),
expectedUsedAuthorities: []*X509CertificateID{
NewX509CertificateID(testutil.ParseCertificate(c, msUefiCACert)),
},
})
c.Check(err, IsNil)
Expand All @@ -145,8 +144,8 @@ func (s *pcr7Suite) TestCheckSecureBootPolicyMeasurementsAndObtainAuthoritiesGoo
},
},
expectedFlags: SecureBootPolicyResultFlags(0),
expectedUsedAuthorities: []*x509.Certificate{
testutil.ParseCertificate(c, msUefiCACert),
expectedUsedAuthorities: []*X509CertificateID{
NewX509CertificateID(testutil.ParseCertificate(c, msUefiCACert)),
},
})
c.Check(err, IsNil)
Expand Down Expand Up @@ -175,8 +174,8 @@ func (s *pcr7Suite) TestCheckSecureBootPolicyMeasurementsAndObtainAuthoritiesGoo
},
},
expectedFlags: SecureBootPolicyResultFlags(0),
expectedUsedAuthorities: []*x509.Certificate{
testutil.ParseCertificate(c, msUefiCACert),
expectedUsedAuthorities: []*X509CertificateID{
NewX509CertificateID(testutil.ParseCertificate(c, msUefiCACert)),
},
})
c.Check(err, IsNil)
Expand Down Expand Up @@ -206,8 +205,8 @@ func (s *pcr7Suite) TestCheckSecureBootPolicyMeasurementsAndObtainAuthoritiesGoo
},
},
expectedFlags: SecureBootPreOSVerificationIncludesDigest,
expectedUsedAuthorities: []*x509.Certificate{
testutil.ParseCertificate(c, msUefiCACert),
expectedUsedAuthorities: []*X509CertificateID{
NewX509CertificateID(testutil.ParseCertificate(c, msUefiCACert)),
},
})
c.Check(err, IsNil)
Expand Down Expand Up @@ -237,8 +236,8 @@ func (s *pcr7Suite) TestCheckSecureBootPolicyMeasurementsAndObtainAuthoritiesGoo
},
},
expectedFlags: SecureBootIncludesWeakAlg | SecureBootPreOSVerificationIncludesDigest,
expectedUsedAuthorities: []*x509.Certificate{
testutil.ParseCertificate(c, msUefiCACert),
expectedUsedAuthorities: []*X509CertificateID{
NewX509CertificateID(testutil.ParseCertificate(c, msUefiCACert)),
},
})
c.Check(err, IsNil)
Expand Down Expand Up @@ -267,8 +266,8 @@ func (s *pcr7Suite) TestCheckSecureBootPolicyMeasurementsAndObtainAuthoritiesGoo
},
},
expectedFlags: SecureBootPolicyResultFlags(0),
expectedUsedAuthorities: []*x509.Certificate{
testutil.ParseCertificate(c, msUefiCACert),
expectedUsedAuthorities: []*X509CertificateID{
NewX509CertificateID(testutil.ParseCertificate(c, msUefiCACert)),
},
})
c.Check(err, IsNil)
Expand Down Expand Up @@ -297,8 +296,8 @@ func (s *pcr7Suite) TestCheckSecureBootPolicyMeasurementsAndObtainAuthoritiesGoo
},
},
expectedFlags: SecureBootPolicyResultFlags(0),
expectedUsedAuthorities: []*x509.Certificate{
testutil.ParseCertificate(c, msUefiCACert),
expectedUsedAuthorities: []*X509CertificateID{
NewX509CertificateID(testutil.ParseCertificate(c, msUefiCACert)),
},
})
c.Check(err, IsNil)
Expand All @@ -322,8 +321,8 @@ func (s *pcr7Suite) TestCheckSecureBootPolicyMeasurementsAndObtainAuthoritiesGoo
},
},
expectedFlags: SecureBootPolicyResultFlags(0),
expectedUsedAuthorities: []*x509.Certificate{
testutil.ParseCertificate(c, msUefiCACert),
expectedUsedAuthorities: []*X509CertificateID{
NewX509CertificateID(testutil.ParseCertificate(c, msUefiCACert)),
},
})
c.Check(err, IsNil)
Expand Down
102 changes: 100 additions & 2 deletions efi/preinstall/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,69 @@
package preinstall

import (
"bufio"
"bytes"
"errors"
"fmt"
"io"

"github.com/canonical/go-tpm2"
internal_efi "github.com/snapcore/secboot/internal/efi"
)

// indentLines is a helper for managing indenting in nested multi-line
// errors.
func indentLines(n int, str string) string {
r := bytes.NewReader([]byte(str))
w := new(bytes.Buffer)
br := bufio.NewReader(r)
for {
line, err := br.ReadString('\n')
fmt.Fprintf(w, "%*s%s", n, "", line)
if err == io.EOF {
break
}
if err != nil {
fmt.Fprintf(w, "%*serror occurred whilst indenting: %v", n, "", err)
break
}
}
return w.String()
}

// RunChecksErrors may be returned unwrapped from [RunChecks] containing a collection
// of errors found during the process of running various tests on the platform.
// It provides a mechanism to access each individual error.
type RunChecksErrors struct {
errs []error
}

func (e *RunChecksErrors) Error() string {
w := new(bytes.Buffer)
fmt.Fprintf(w, "one or more errors detected:\n")
for _, err := range e.errs {
fmt.Fprintf(w, "%s\n", indentLines(2, "- "+err.Error()))
}
return w.String()
}

// NumErrors returns the number of errors.
func (e *RunChecksErrors) NumErrors() int {
return len(e.errs)
}

// UnwrapError unwraps the specific error at the specified index (zero-indexed).
func (e *RunChecksErrors) UnwrapError(n int) error {
if n > len(e.errs)-1 {
return errors.New("error index out of range")
}
return e.errs[n]
}

func (e *RunChecksErrors) addErr(err error) {
e.errs = append(e.errs, err)
}

// Errors related to checking platform firmware protections.

// NoHardwareRootOfTrustError is returned wrapped from [RunChecks] if the platform
Expand Down Expand Up @@ -108,8 +163,6 @@ var (
// supported by snapd. Snapd needs the use of both of these hierarchies, so if we
// want to support something other than snapd taking ownership of these in the future,
// this will need coordination with snapd.
// Unless the authorization values are known, clearing this will most likely require
// the TPM to be cleared.
ErrUnsupportedTPMOwnership = errors.New("either the TPM's storage or endorsement hierarchy is owned and this isn't currently supported")

// ErrTPMInsufficientNVCounters is returned wrapped from RunChecks if there are
Expand Down Expand Up @@ -216,3 +269,48 @@ var (
// UEFI >= 2.5 that are in user mode, but this is not the case today.
ErrNoDeployedMode = errors.New("deployed mode should be enabled in order to generate secure boot profiles")
)

// RequiredUnsupportedPCRsError is returned from methods of [PCRProfileAutoEnablePCRsOption]
// when a valid PCR configuration cannot be created based on the supplied [PCRProfileOptionsFlags]
// and [CheckResult].
type RequiredUnsupportedPCRsError struct {
PCRs tpm2.HandleList
}

func newRequiredUnsupportedPCRsError(required tpm2.HandleList, flags CheckResultFlags) *RequiredUnsupportedPCRsError {
var pcrs tpm2.HandleList
for _, pcr := range required {
var flag CheckResultFlags
switch pcr {
case 0:
flag = NoPlatformFirmwareProfileSupport
case 1:
flag = NoPlatformConfigProfileSupport
case 2:
flag = NoDriversAndAppsProfileSupport
case 3:
flag = NoDriversAndAppsConfigProfileSupport
case 4:
flag = NoBootManagerCodeProfileSupport
case 5:
flag = NoBootManagerConfigProfileSupport
case 7:
flag = NoSecureBootPolicyProfileSupport
}

if flags&flag > 0 {
pcrs = append(pcrs, pcr)
}
}

return &RequiredUnsupportedPCRsError{pcrs}
}

func (e *RequiredUnsupportedPCRsError) Error() string {
switch len(e.PCRs) {
case 1:
return fmt.Sprintf("PCR %v is required, but is unsupported", e.PCRs[0])
default:
return fmt.Sprintf("PCRs %v are required, but are unsupported", e.PCRs)
}
}
14 changes: 14 additions & 0 deletions efi/preinstall/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ import (
)

type (
AuthorityTrust = authorityTrust
AuthorityTrustData = authorityTrustData
AuthorityTrustDataSet = authorityTrustDataSet
BootManagerCodeResultFlags = bootManagerCodeResultFlags
CheckDriversAndAppsMeasurementsResult = checkDriversAndAppsMeasurementsResult
CheckTPM2DeviceFlags = checkTPM2DeviceFlags
Expand All @@ -39,6 +42,8 @@ type (
)

const (
AuthorityTrustBootCode = authorityTrustBootCode
AuthorityTrustDrivers = authorityTrustDrivers
BootManagerCodeSysprepAppsPresent = bootManagerCodeSysprepAppsPresent
BootManagerCodeAbsoluteComputraceRunning = bootManagerCodeAbsoluteComputraceRunning
BootManagerCodeNotAllLaunchDigestsVerified = bootManagerCodeNotAllLaunchDigestsVerified
Expand Down Expand Up @@ -73,6 +78,7 @@ var (
DetectVirtualization = detectVirtualization
DetermineCPUVendor = determineCPUVendor
IsLaunchedFromLoadOption = isLaunchedFromLoadOption
NewX509CertificateID = newX509CertificateID
OpenAndCheckTPM2Device = openAndCheckTPM2Device
ReadCurrentBootLoadOptionFromLog = readCurrentBootLoadOptionFromLog
ReadIntelHFSTSRegistersFromMEISysfs = readIntelHFSTSRegistersFromMEISysfs
Expand All @@ -96,6 +102,14 @@ func MockInternalEfiSecureBootSignaturesFromPEFile(fn func(*pe.File, io.ReaderAt
}
}

func MockKnownCAs(set AuthorityTrustDataSet) (restore func()) {
orig := knownCAs
knownCAs = set
return func() {
knownCAs = orig
}
}

func MockPeNewFile(fn func(io.ReaderAt) (*pe.File, error)) (restore func()) {
orig := peNewFile
peNewFile = fn
Expand Down
Loading

0 comments on commit 878ee98

Please sign in to comment.