summaryrefslogtreecommitdiff
diff options
authorMaciej Borzecki <maciej.zenon.borzecki@canonical.com>2022-06-02 14:36:24 +0200
committerGitHub <noreply@github.com>2022-06-02 14:36:24 +0200
commit0a9b0934e6de5d91470e2ffd30276ac4d78895b0 (patch)
tree68af962da0fec7063da5b4587ffc380fa7d4e09c
parent13e8d038fc5ad44aacdf73d733c4c6f68f6b3809 (diff)
parentf0dcd4c839281ed783eb6168e9ca6ca1440639c0 (diff)
Merge pull request #11837 from bboozzoo/bboozzoo/secboot-helpers
secboot: helpers for dealing with PCR handles and TPM resources
-rw-r--r--secboot/export_sb_test.go7
-rw-r--r--secboot/secboot_dummy.go8
-rw-r--r--secboot/secboot_sb_test.go70
-rw-r--r--secboot/secboot_tpm.go55
4 files changed, 140 insertions, 0 deletions
diff --git a/secboot/export_sb_test.go b/secboot/export_sb_test.go
index 9c920f4b63..69b6b8252e 100644
--- a/secboot/export_sb_test.go
+++ b/secboot/export_sb_test.go
@@ -24,6 +24,7 @@ package secboot
import (
"io"
+ "github.com/canonical/go-tpm2"
sb "github.com/snapcore/secboot"
sb_efi "github.com/snapcore/secboot/efi"
sb_tpm2 "github.com/snapcore/secboot/tpm2"
@@ -50,6 +51,12 @@ func MockSbTPMEnsureProvisioned(f func(tpm *sb_tpm2.Connection, mode sb_tpm2.Pro
return restore
}
+func MockTPMReleaseResources(f func(tpm *sb_tpm2.Connection, handle tpm2.Handle) error) (restore func()) {
+ restore = testutil.Backup(&tpmReleaseResources)
+ tpmReleaseResources = f
+ return restore
+}
+
func MockSbEfiAddSecureBootPolicyProfile(f func(profile *sb_tpm2.PCRProtectionProfile, params *sb_efi.SecureBootPolicyProfileParams) error) (restore func()) {
old := sbefiAddSecureBootPolicyProfile
sbefiAddSecureBootPolicyProfile = f
diff --git a/secboot/secboot_dummy.go b/secboot/secboot_dummy.go
index c8ed2b4cd5..0b7b70df0f 100644
--- a/secboot/secboot_dummy.go
+++ b/secboot/secboot_dummy.go
@@ -48,3 +48,11 @@ func ResealKeys(params *ResealKeysParams) error {
func ProvisionTPM(lockoutAuthFile string) error {
return errBuildWithoutSecboot
}
+
+func PCRHandleOfSealedKey(p string) (uint32, error) {
+ return 0, errBuildWithoutSecboot
+}
+
+func ReleasePCRResourceHandles(handles ...uint32) error {
+ return errBuildWithoutSecboot
+}
diff --git a/secboot/secboot_sb_test.go b/secboot/secboot_sb_test.go
index a7601ef5a8..04620737ff 100644
--- a/secboot/secboot_sb_test.go
+++ b/secboot/secboot_sb_test.go
@@ -1919,3 +1919,73 @@ func (s *secbootSuite) TestUnlockVolumeUsingSealedKeyIfEncryptedFdeRevealKeyBadJ
c.Check(err, ErrorMatches, `cannot unlock encrypted partition: invalid key data:.*`)
}
+
+func (s *secbootSuite) TestPCRHandleOfSealedKey(c *C) {
+ d := c.MkDir()
+ h, err := secboot.PCRHandleOfSealedKey(filepath.Join(d, "not-found"))
+ c.Assert(err, ErrorMatches, "cannot open key file: .*/not-found: no such file or directory")
+ c.Assert(h, Equals, uint32(0))
+
+ skf := filepath.Join(d, "sealed-key")
+ // partially valid sealed key with correct header magic
+ c.Assert(ioutil.WriteFile(skf, []byte{0x55, 0x53, 0x4b, 0x24, 1, 1, 1, 'k', 'e', 'y', 1, 1, 1}, 0644), IsNil)
+ h, err = secboot.PCRHandleOfSealedKey(skf)
+ c.Assert(err, ErrorMatches, "(?s)cannot open key file: invalid key data: cannot unmarshal AFIS header: .*")
+ c.Check(h, Equals, uint32(0))
+
+ // TODO simulate the happy case, which needs a real (or at least
+ // partially mocked) sealed key object, which could be obtained using
+ // go-tpm2/testutil, but that has a dependency on an older version of
+ // snapd API and cannot be imported or procure a valid sealed key binary
+ // which unfortunately there are no examples of the secboot/tpm2 test
+ // code
+}
+
+func (s *secbootSuite) TestReleasePCRResourceHandles(c *C) {
+ _, restore := mockSbTPMConnection(c, fmt.Errorf("mock err"))
+ defer restore()
+
+ err := secboot.ReleasePCRResourceHandles(0x1234, 0x2345)
+ c.Assert(err, ErrorMatches, "cannot connect to TPM device: mock err")
+
+ conn, restore := mockSbTPMConnection(c, nil)
+ defer restore()
+
+ var handles []tpm2.Handle
+ restore = secboot.MockTPMReleaseResources(func(tpm *sb_tpm2.Connection, handle tpm2.Handle) error {
+ c.Check(tpm, Equals, conn)
+ handles = append(handles, handle)
+ switch handle {
+ case tpm2.Handle(0xeeeeee):
+ return fmt.Errorf("mock release error 1")
+ case tpm2.Handle(0xeeeeef):
+ return fmt.Errorf("mock release error 2")
+ }
+ return nil
+ })
+ defer restore()
+
+ // many handles
+ err = secboot.ReleasePCRResourceHandles(0x1234, 0x2345)
+ c.Assert(err, IsNil)
+ c.Check(handles, DeepEquals, []tpm2.Handle{
+ tpm2.Handle(0x1234), tpm2.Handle(0x2345),
+ })
+
+ // single handle
+ handles = nil
+ err = secboot.ReleasePCRResourceHandles(0x1234)
+ c.Assert(err, IsNil)
+ c.Check(handles, DeepEquals, []tpm2.Handle{tpm2.Handle(0x1234)})
+
+ // an error case
+ handles = nil
+ err = secboot.ReleasePCRResourceHandles(0x1234, 0xeeeeee, 0x2345, 0xeeeeef)
+ c.Assert(err, ErrorMatches, `
+cannot release TPM resources for 2 handles:
+handle 0xeeeeee: mock release error 1
+handle 0xeeeeef: mock release error 2`[1:])
+ c.Check(handles, DeepEquals, []tpm2.Handle{
+ tpm2.Handle(0x1234), tpm2.Handle(0xeeeeee), tpm2.Handle(0x2345), tpm2.Handle(0xeeeeef),
+ })
+}
diff --git a/secboot/secboot_tpm.go b/secboot/secboot_tpm.go
index e4e5ffd0df..218c652a66 100644
--- a/secboot/secboot_tpm.go
+++ b/secboot/secboot_tpm.go
@@ -26,6 +26,7 @@ import (
"errors"
"fmt"
"io/ioutil"
+ "strings"
"github.com/canonical/go-tpm2"
sb "github.com/snapcore/secboot"
@@ -65,6 +66,7 @@ var (
isTPMEnabled = (*sb_tpm2.Connection).IsEnabled
sbTPMEnsureProvisioned = (*sb_tpm2.Connection).EnsureProvisioned
+ tpmReleaseResources = tpmReleaseResourcesImpl
// check whether the interfaces match
_ (sb.SnapModel) = ModelForSealing(nil)
@@ -562,3 +564,56 @@ func efiImageFromBootFile(b *bootloader.BootFile) (sb_efi.Image, error) {
FileName: b.Path,
}, nil
}
+
+// PCRHandleOfSealedKey retunrs the PCR handle which was used when sealing a
+// given key object.
+func PCRHandleOfSealedKey(p string) (uint32, error) {
+ r, err := sb_tpm2.NewFileSealedKeyObjectReader(p)
+ if err != nil {
+ return 0, fmt.Errorf("cannot open key file: %v", err)
+ }
+ sko, err := sb_tpm2.ReadSealedKeyObject(r)
+ if err != nil {
+ return 0, fmt.Errorf("cannot read sealed key file: %v", err)
+ }
+ handle := uint32(sko.PCRPolicyCounterHandle())
+ return handle, nil
+}
+
+func tpmReleaseResourcesImpl(tpm *sb_tpm2.Connection, handle tpm2.Handle) error {
+ rc, err := tpm.CreateResourceContextFromTPM(handle)
+ if err != nil {
+ if _, ok := err.(tpm2.ResourceUnavailableError); ok {
+ // there's nothing to release, the handle isn't used
+ return nil
+ }
+ return fmt.Errorf("cannot create resource context: %v", err)
+ }
+ if err := tpm.NVUndefineSpace(tpm.OwnerHandleContext(), rc, tpm.HmacSession()); err != nil {
+ return fmt.Errorf("cannot undefine space: %v", err)
+ }
+ return nil
+}
+
+// ReleasePCRResourceHandles releases any TPM resources associated with given
+// PCR handles.
+func ReleasePCRResourceHandles(handles ...uint32) error {
+ tpm, err := sbConnectToDefaultTPM()
+ if err != nil {
+ err = fmt.Errorf("cannot connect to TPM device: %v", err)
+ return err
+ }
+ defer tpm.Close()
+
+ var errs []string
+ for _, handle := range handles {
+ logger.Debugf("releasing PCR handle %#x", handle)
+ if err := tpmReleaseResources(tpm, tpm2.Handle(handle)); err != nil {
+ errs = append(errs, fmt.Sprintf("handle %#x: %v", handle, err))
+ }
+ }
+ if errCnt := len(errs); errCnt != 0 {
+ return fmt.Errorf("cannot release TPM resources for %v handles:\n%v", errCnt, strings.Join(errs, "\n"))
+ }
+ return nil
+}