diff options
| author | Maciej Borzecki <maciej.zenon.borzecki@canonical.com> | 2022-06-02 14:36:24 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-06-02 14:36:24 +0200 |
| commit | 0a9b0934e6de5d91470e2ffd30276ac4d78895b0 (patch) | |
| tree | 68af962da0fec7063da5b4587ffc380fa7d4e09c | |
| parent | 13e8d038fc5ad44aacdf73d733c4c6f68f6b3809 (diff) | |
| parent | f0dcd4c839281ed783eb6168e9ca6ca1440639c0 (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.go | 7 | ||||
| -rw-r--r-- | secboot/secboot_dummy.go | 8 | ||||
| -rw-r--r-- | secboot/secboot_sb_test.go | 70 | ||||
| -rw-r--r-- | secboot/secboot_tpm.go | 55 |
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 +} |
