From 343affe420cb220c303ffffef20278a410ec789f Mon Sep 17 00:00:00 2001 From: Valentin David Date: Tue, 17 Sep 2024 14:14:02 +0200 Subject: [PATCH 1/9] overlord/fdestate: keep FDE state up to date Ensure() initializes the empty profiles, and reseal updates them. --- boot/assets_test.go | 16 +- boot/boot_test.go | 48 ++-- boot/export_test.go | 2 +- boot/model_test.go | 22 +- boot/seal.go | 10 +- boot/seal_test.go | 12 +- boot/systems_test.go | 26 +- osutil/disks/disks.go | 16 ++ osutil/disks/disks_linux.go | 5 +- overlord/fdestate/backend/reseal.go | 67 ++++- overlord/fdestate/backend/reseal_test.go | 328 +++++++++++++++-------- overlord/fdestate/export_test.go | 2 + overlord/fdestate/fdemgr.go | 16 +- overlord/fdestate/fdemgr_test.go | 121 +++++++++ overlord/fdestate/fdestate.go | 305 +++++++++++++++++++++ overlord/managers_test.go | 26 ++ secboot/secboot.go | 4 +- secboot/secboot_dummy.go | 14 + secboot/secboot_sb.go | 33 +++ secboot/secboot_sb_test.go | 111 ++++---- secboot/secboot_tpm.go | 23 +- 21 files changed, 960 insertions(+), 247 deletions(-) create mode 100644 overlord/fdestate/fdestate.go diff --git a/boot/assets_test.go b/boot/assets_test.go index cbe56350d97..fe42c46ab8f 100644 --- a/boot/assets_test.go +++ b/boot/assets_test.go @@ -53,7 +53,7 @@ var _ = Suite(&assetsSuite{}) func (s *assetsSuite) SetUpTest(c *C) { s.baseBootenvSuite.SetUpTest(c) - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { return nil }) s.AddCleanup(restore) @@ -788,7 +788,7 @@ func (s *assetsSuite) testUpdateObserverUpdateMockedWithReseal(c *C, seedRole st // everything is set up, trigger a reseal resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return nil }) @@ -893,7 +893,7 @@ func (s *assetsSuite) TestUpdateObserverUpdateExistingAssetMocked(c *C) { // everything is set up, trigger reseal resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return nil }) @@ -1649,7 +1649,7 @@ func (s *assetsSuite) TestUpdateObserverCanceledSimpleAfterBackupMocked(c *C) { "shim": []string{shimHash}, }) resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return nil }) @@ -1809,7 +1809,7 @@ func (s *assetsSuite) TestUpdateObserverCanceledNoActionsMocked(c *C) { obs, _ := s.uc20UpdateObserverEncryptedSystemMockedBootloader(c) resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return nil }) @@ -2561,7 +2561,7 @@ func (s *assetsSuite) TestUpdateObserverReseal(c *C) { // everything is set up, trigger a reseal resealCalls := 0 - restore = boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ c.Assert(params.RunModeBootChains, HasLen, 1) @@ -2713,7 +2713,7 @@ func (s *assetsSuite) TestUpdateObserverCanceledReseal(c *C) { resealCalls := 0 - restore = boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ c.Assert(params.RunModeBootChains, HasLen, 1) @@ -2846,7 +2846,7 @@ func (s *assetsSuite) TestUpdateObserverUpdateMockedNonEncryption(c *C) { // make sure that no reseal is triggered resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return nil }) diff --git a/boot/boot_test.go b/boot/boot_test.go index 3a0e8809cb0..cf64c23c9c5 100644 --- a/boot/boot_test.go +++ b/boot/boot_test.go @@ -137,7 +137,7 @@ type baseBootenv20Suite struct { func (s *baseBootenv20Suite) SetUpTest(c *C) { s.baseBootenvSuite.SetUpTest(c) - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { return nil }) s.AddCleanup(restore) @@ -1121,7 +1121,7 @@ func (s *bootenv20Suite) TestCoreParticipant20SetNextNewKernelSnapWithReseal(c * defer r() resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ c.Assert(params.RunModeBootChains, HasLen, 2) @@ -1241,7 +1241,7 @@ func (s *bootenv20Suite) TestCoreParticipant20SetNextNewUnassertedKernelSnapWith defer r() resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ c.Assert(params.RunModeBootChains, HasLen, 2) @@ -1362,7 +1362,7 @@ func (s *bootenv20Suite) TestCoreParticipant20SetNextSameKernelSnapNoReseal(c *C defer r() resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return nil }) @@ -1459,7 +1459,7 @@ func (s *bootenv20Suite) TestCoreParticipant20SetNextSameUnassertedKernelSnapNoR defer r() resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return nil }) @@ -2058,7 +2058,7 @@ func (s *bootenv20Suite) TestMarkBootSuccessful20KernelUpdateWithReseal(c *C) { defer r() resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ c.Assert(params.RunModeBootChains, HasLen, 1) @@ -2292,7 +2292,7 @@ func (s *bootenv20Suite) TestMarkBootSuccessful20BootAssetsUpdateHappy(c *C) { c.Assert(coreDev.HasModeenv(), Equals, true) resealCalls := 0 - restore = boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ c.Assert(params.RunModeBootChains, HasLen, 1) @@ -2453,7 +2453,7 @@ func (s *bootenv20Suite) TestMarkBootSuccessful20BootAssetsStableStateHappy(c *C c.Assert(coreDev.HasModeenv(), Equals, true) resealCalls := 0 - restore = boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return nil }) @@ -2567,7 +2567,7 @@ func (s *bootenv20Suite) TestMarkBootSuccessful20BootUnassertedKernelAssetsStabl c.Assert(coreDev.HasModeenv(), Equals, true) resealCalls := 0 - restore = boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return nil }) @@ -3156,7 +3156,7 @@ var _ = Suite(&bootConfigSuite{}) func (s *bootConfigSuite) SetUpTest(c *C) { s.baseBootenvSuite.SetUpTest(c) - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { return nil }) s.AddCleanup(restore) @@ -3193,7 +3193,7 @@ func (s *bootConfigSuite) TestBootConfigUpdateHappyNoKeysNoReseal(c *C) { c.Assert(m.WriteTo(""), IsNil) resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return nil }) @@ -3245,7 +3245,7 @@ func (s *bootConfigSuite) testBootConfigUpdateHappyWithReseal(c *C, cmdlineAppen newCmdline := strutil.JoinNonEmpty([]string{ "snapd_recovery_mode=run mocked candidate panic=-1", cmdlineAppend}, " ") resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ c.Assert(params, NotNil) @@ -3305,7 +3305,7 @@ func (s *bootConfigSuite) testBootConfigUpdateHappyNoChange(c *C, cmdlineAppend c.Assert(m.WriteTo(""), IsNil) resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return nil }) @@ -3470,7 +3470,7 @@ volumes: c.Assert(m.WriteTo(""), IsNil) resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ c.Assert(params, NotNil) @@ -3536,7 +3536,7 @@ volumes: // reseal does not happen, because the gadget overrides the static // command line which is part of boot config, thus there's no resulting // change in the command lines tracked in modeenv and no need to reseal - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return fmt.Errorf("unexpected call") }) @@ -3573,7 +3573,7 @@ var _ = Suite(&bootKernelCommandLineSuite{}) func (s *bootKernelCommandLineSuite) SetUpTest(c *C) { s.baseBootenvSuite.SetUpTest(c) - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { return nil }) s.AddCleanup(restore) @@ -3633,7 +3633,7 @@ func (s *bootKernelCommandLineSuite) SetUpTest(c *C) { s.resealCommandLines = nil s.resealCalls = 0 - restore = boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { s.resealCalls++ c.Assert(params, NotNil) c.Assert(params.RunModeBootChains, HasLen, 0) @@ -3909,7 +3909,7 @@ volumes: c.Assert(s.modeenvWithEncryption.WriteTo(""), IsNil) resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return fmt.Errorf("reseal fails") }) @@ -4053,7 +4053,7 @@ func (s *bootKernelCommandLineSuite) TestCommandLineUpdateUC20OverSpuriousReboot s.stampSealedKeys(c, dirs.GlobalRootDir) resealPanic := false - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { s.resealCalls++ c.Logf("reseal call %v", s.resealCalls) c.Assert(params, NotNil) @@ -4636,7 +4636,7 @@ func (s *bootenv20Suite) TestCoreParticipant20UndoKernelSnapInstallNewWithReseal defer r() resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ c.Assert(params.RunModeBootChains, HasLen, 1) @@ -4747,7 +4747,7 @@ func (s *bootenv20Suite) TestCoreParticipant20UndoUnassertedKernelSnapInstallNew defer r() resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ c.Assert(params.RunModeBootChains, HasLen, 1) @@ -4859,7 +4859,7 @@ func (s *bootenv20Suite) TestCoreParticipant20UndoKernelSnapInstallSameNoReseal( defer r() resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return nil }) @@ -4956,7 +4956,7 @@ func (s *bootenv20Suite) TestCoreParticipant20UndoUnassertedKernelSnapInstallSam defer r() resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return nil }) @@ -5092,7 +5092,7 @@ func (s *bootenv20Suite) TestCoreParticipant20UndoBaseSnapInstallNewNoReseal(c * model := coreDev.Model() resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return nil }) diff --git a/boot/export_test.go b/boot/export_test.go index 0b77742562c..dd18c8ed92f 100644 --- a/boot/export_test.go +++ b/boot/export_test.go @@ -226,7 +226,7 @@ func EnableTestingRebootFunction() (restore func()) { return func() { testingRebootItself = false } } -func MockResealKeyForBootChains(f func(method device.SealingMethod, rootdir string, params *ResealKeyForBootChainsParams, expectReseal bool) error) (restore func()) { +func MockResealKeyForBootChains(f func(locked bool, method device.SealingMethod, rootdir string, params *ResealKeyForBootChainsParams, expectReseal bool) error) (restore func()) { old := ResealKeyForBootChains ResealKeyForBootChains = f return func() { diff --git a/boot/model_test.go b/boot/model_test.go index 61643e2fe1e..398d5638c3f 100644 --- a/boot/model_test.go +++ b/boot/model_test.go @@ -89,7 +89,7 @@ func makeEncodableModel(signingAccounts *assertstest.SigningAccounts, overrides func (s *modelSuite) SetUpTest(c *C) { s.baseBootenvSuite.SetUpTest(c) - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { return nil }) s.AddCleanup(restore) @@ -204,7 +204,7 @@ func (s *modelSuite) TestDeviceChangeHappy(c *C) { "model: my-model-uc20\n") resealKeysCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealKeysCalls++ m, err := boot.ReadModeenv("") c.Assert(err, IsNil) @@ -267,7 +267,7 @@ func (s *modelSuite) TestDeviceChangeUnhappyFirstReseal(c *C) { "model: my-model-uc20\n") resealKeysCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealKeysCalls++ m, err := boot.ReadModeenv("") c.Assert(err, IsNil) @@ -318,7 +318,7 @@ func (s *modelSuite) TestDeviceChangeUnhappyFirstSwapModelFile(c *C) { "model: my-model-uc20\n") resealKeysCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealKeysCalls++ m, err := boot.ReadModeenv("") c.Assert(err, IsNil) @@ -371,7 +371,7 @@ func (s *modelSuite) TestDeviceChangeUnhappySecondReseal(c *C) { "model: my-model-uc20\n") resealKeysCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealKeysCalls++ m, err := boot.ReadModeenv("") c.Assert(err, IsNil) @@ -467,7 +467,7 @@ func (s *modelSuite) TestDeviceChangeRebootBeforeNewModel(c *C) { "model: my-model-uc20\n") resealKeysCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealKeysCalls++ c.Logf("reseal key call: %v", resealKeysCalls) m, err := boot.ReadModeenv("") @@ -586,7 +586,7 @@ func (s *modelSuite) TestDeviceChangeRebootAfterNewModelFileWrite(c *C) { "model: my-model-uc20\n") resealKeysCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealKeysCalls++ // timeline & calls: // 1 - pre reboot, run & recovery keys, try model set @@ -706,7 +706,7 @@ func (s *modelSuite) TestDeviceChangeRebootPostSameModel(c *C) { "model: my-model-uc20\n") resealKeysCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealKeysCalls++ c.Logf("reseal key call: %v", resealKeysCalls) m, err := boot.ReadModeenv("") @@ -849,7 +849,7 @@ func (s *modelSuite) testDeviceChangeUnhappyMockedWriteModelToBoot(c *C, tc unha writeModelToBootCalls := 0 resealKeysCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealKeysCalls++ m, err := boot.ReadModeenv("") c.Assert(err, IsNil) @@ -972,7 +972,7 @@ func (s *modelSuite) TestDeviceChangeUnhappyFailReseaWithSwappedModelMockedWrite writeModelToBootCalls := 0 resealKeysCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealKeysCalls++ if resealKeysCalls == 2 { m, err := boot.ReadModeenv("") @@ -1059,7 +1059,7 @@ func (s *modelSuite) TestDeviceChangeRebootRestoreModelKeyChangeMockedWriteModel })) resealKeysCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealKeysCalls++ c.Logf("reseal key call: %v", resealKeysCalls) m, err := boot.ReadModeenv("") diff --git a/boot/seal.go b/boot/seal.go index 05e89c8f017..2e253374929 100644 --- a/boot/seal.go +++ b/boot/seal.go @@ -251,11 +251,13 @@ func resealKeyToModeenvImpl(rootdir string, modeenv *Modeenv, expectReseal bool, return err } + locked := true if unlocker != nil { // unlock/relock global state defer unlocker()() + locked = false } - return resealKeyToModeenvForMethod(method, rootdir, modeenv, expectReseal) + return resealKeyToModeenvForMethod(locked, method, rootdir, modeenv, expectReseal) } type ResealKeyForBootChainsParams struct { @@ -270,7 +272,7 @@ type ResealKeyForBootChainsParams struct { RoleToBlName map[bootloader.Role]string } -func resealKeyToModeenvForMethod(method device.SealingMethod, rootdir string, modeenv *Modeenv, expectReseal bool) error { +func resealKeyToModeenvForMethod(locked bool, method device.SealingMethod, rootdir string, modeenv *Modeenv, expectReseal bool) error { // this is just optimization. If the backend does not need it, we should not calculate it. requiresBootChains := true switch method { @@ -348,10 +350,10 @@ func resealKeyToModeenvForMethod(method device.SealingMethod, rootdir string, mo } } - return ResealKeyForBootChains(method, rootdir, params, expectReseal) + return ResealKeyForBootChains(locked, method, rootdir, params, expectReseal) } -func resealKeyForBootChainsImpl(method device.SealingMethod, rootdir string, params *ResealKeyForBootChainsParams, expectReseal bool) error { +func resealKeyForBootChainsImpl(locked bool, method device.SealingMethod, rootdir string, params *ResealKeyForBootChainsParams, expectReseal bool) error { return fmt.Errorf("FDE manager was not started") } diff --git a/boot/seal_test.go b/boot/seal_test.go index d43a643cfe4..67fe0e84f79 100644 --- a/boot/seal_test.go +++ b/boot/seal_test.go @@ -560,7 +560,7 @@ func (s *sealSuite) TestResealKeyToModeenvWithSystemFallback(c *C) { // set mock key resealing resealKeysCalls := 0 - restore = boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdirArg string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdirArg string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealKeysCalls++ c.Check(method, Equals, device.SealingMethodTPM) @@ -654,7 +654,7 @@ func (s *sealSuite) TestResealKeyToModeenvRecoveryKeysForGoodSystemsOnly(c *C) { // set mock key resealing resealKeysCalls := 0 - restore = boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdirArg string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdirArg string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealKeysCalls++ c.Check(method, Equals, device.SealingMethodTPM) @@ -869,7 +869,7 @@ func (s *sealSuite) TestResealKeyToModeenvFallbackCmdline(c *C) { // set mock key resealing resealKeysCalls := 0 - restore = boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdirArg string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdirArg string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { c.Check(rootdirArg, Equals, rootdir) c.Check(method, Equals, device.SealingMethodTPM) c.Check(expectReseal, Equals, false) @@ -1713,7 +1713,7 @@ func (s *sealSuite) TestResealKeyToModeenvWithFdeHookCalled(c *C) { defer dirs.SetRootDir("") mockResealKeyForBootChainsCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdirArg string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdirArg string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { c.Check(rootdirArg, Equals, rootdir) c.Check(method, Equals, device.SealingMethodFDESetupHook) c.Check(expectReseal, Equals, false) @@ -1759,7 +1759,7 @@ func (s *sealSuite) TestResealKeyToModeenvWithFdeHookVerySad(c *C) { defer dirs.SetRootDir("") mockResealKeyForBootChainsCalls := 0 - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdirArg string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdirArg string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { c.Check(rootdirArg, Equals, rootdir) c.Check(method, Equals, device.SealingMethodFDESetupHook) c.Check(expectReseal, Equals, false) @@ -1871,7 +1871,7 @@ func (s *sealSuite) testResealKeyToModeenvWithTryModel(c *C, shimId, grubId stri // set mock key resealing resealKeysCalls := 0 - restore = boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdirArg string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdirArg string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { c.Check(rootdirArg, Equals, rootdir) c.Check(method, Equals, device.SealingMethodTPM) c.Check(expectReseal, Equals, false) diff --git a/boot/systems_test.go b/boot/systems_test.go index 6b1859753ae..b10d53cdc07 100644 --- a/boot/systems_test.go +++ b/boot/systems_test.go @@ -83,7 +83,7 @@ func (s *systemsSuite) mockTrustedBootloaderWithAssetAndChains(c *C, runKernelBf func (s *systemsSuite) SetUpTest(c *C) { s.baseBootenvSuite.SetUpTest(c) - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { return nil }) s.AddCleanup(restore) @@ -145,7 +145,7 @@ func (s *systemsSuite) TestSetTryRecoverySystemEncrypted(c *C) { defer restore() resealCalls := 0 - restore = boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ // bootloader variables have already been modified c.Check(mtbl.SetBootVarsCalls, Equals, 1) @@ -270,7 +270,7 @@ func (s *systemsSuite) TestSetTryRecoverySystemRemodelEncrypted(c *C) { defer restore() resealCalls := 0 - restore = boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ // bootloader variables have already been modified c.Check(mtbl.SetBootVarsCalls, Equals, 1) @@ -375,7 +375,7 @@ func (s *systemsSuite) TestSetTryRecoverySystemSimple(c *C) { } c.Assert(modeenv.WriteTo(""), IsNil) - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { return fmt.Errorf("unexpected call") }) s.AddCleanup(restore) @@ -416,7 +416,7 @@ func (s *systemsSuite) TestSetTryRecoverySystemSetBootVarsErr(c *C) { } c.Assert(modeenv.WriteTo(""), IsNil) - restore := boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { return fmt.Errorf("unexpected call") }) s.AddCleanup(restore) @@ -527,7 +527,7 @@ func (s *systemsSuite) TestSetTryRecoverySystemCleanupOnErrorBeforeReseal(c *C) defer restore() resealCalls := 0 - restore = boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ if cleanupTriggered { return nil @@ -636,7 +636,7 @@ func (s *systemsSuite) TestSetTryRecoverySystemCleanupOnErrorAfterReseal(c *C) { defer restore() resealCalls := 0 - restore = boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ switch resealCalls { case 1: @@ -737,7 +737,7 @@ func (s *systemsSuite) TestSetTryRecoverySystemCleanupError(c *C) { defer restore() resealCalls := 0 - restore = boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ switch resealCalls { case 1: @@ -784,7 +784,7 @@ func (s *systemsSuite) testInspectRecoverySystemOutcomeHappy(c *C, mtbl *bootloa }) defer restore() - restore = boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { return fmt.Errorf("unexpected call") }) defer restore() @@ -980,7 +980,7 @@ func (s *systemsSuite) testClearRecoverySystem(c *C, mtbl *bootloadertest.MockTr defer restore() resealCalls := 0 - restore = boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ switch resealCalls { case 1: @@ -1235,7 +1235,7 @@ func (s *systemsSuite) TestClearRecoverySystemReboot(c *C) { defer restore() resealCalls := 0 - restore = boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ switch resealCalls { case 1: @@ -1365,7 +1365,7 @@ func (s *systemsSuite) testPromoteTriedRecoverySystem(c *C, systemLabel string, defer restore() resealCalls := 0 - restore = boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ switch resealCalls { case 1: @@ -1629,7 +1629,7 @@ func (s *systemsSuite) testDropRecoverySystem(c *C, systemLabel string, tc recov defer restore() resealCalls := 0 - restore = boot.MockResealKeyForBootChains(func(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ switch resealCalls { case 1: diff --git a/osutil/disks/disks.go b/osutil/disks/disks.go index 905a5e1dbc0..6f5ad9cb34b 100644 --- a/osutil/disks/disks.go +++ b/osutil/disks/disks.go @@ -20,6 +20,7 @@ package disks import ( + "errors" "fmt" "strings" ) @@ -237,3 +238,18 @@ func RegisterDeviceMapperBackResolver(name string, f func(dmUUID, dmName []byte) func unregisterDeviceMapperBackResolver(name string) { delete(deviceMapperBackResolvers, name) } + +var ErrNoDmUUID = errors.New("device has no DM_UUID") + +type ErrMountPointNotFound struct { + Path string +} + +func (e ErrMountPointNotFound) Error() string { + return fmt.Sprintf("cannot find mountpoint %q", e.Path) +} + +func (e ErrMountPointNotFound) Is(err error) bool { + _, ok := err.(*ErrMountPointNotFound) + return ok +} diff --git a/osutil/disks/disks_linux.go b/osutil/disks/disks_linux.go index 57516ab5f92..32e9ab478df 100644 --- a/osutil/disks/disks_linux.go +++ b/osutil/disks/disks_linux.go @@ -610,7 +610,7 @@ func partitionPropsFromMountPoint(mountpoint string) (source string, props map[s } } if source == "" { - return "", nil, fmt.Errorf("cannot find mountpoint %q", mountpoint) + return "", nil, ErrMountPointNotFound{Path: mountpoint} } // now we have the partition for this mountpoint, we need to tie that back @@ -976,8 +976,9 @@ func DMCryptUUIDFromMountPoint(mountpoint string) (string, error) { dmUUID, hasDmUUID := props["DM_UUID"] if !hasDmUUID { - return "", fmt.Errorf("device has no DM_UUID") + return "", ErrNoDmUUID } + match := dmUUIDRe.FindStringSubmatchIndex(dmUUID) if match == nil { return "", fmt.Errorf("value of DM_UUID is not recognized") diff --git a/overlord/fdestate/backend/reseal.go b/overlord/fdestate/backend/reseal.go index 7387d9acfb7..1aea97105c2 100644 --- a/overlord/fdestate/backend/reseal.go +++ b/overlord/fdestate/backend/reseal.go @@ -34,7 +34,8 @@ import ( ) var ( - secbootResealKeys = secboot.ResealKeys + secbootResealKeys = secboot.ResealKeys + secbootBuildPCRProtectionProfile = secboot.BuildPCRProtectionProfile ) // MockSecbootResealKeys is only useful in testing. Note that this is a very low @@ -48,8 +49,19 @@ func MockSecbootResealKeys(f func(params *secboot.ResealKeysParams) error) (rest } } +func MockSecbootBuildPCRProtectionProfile(f func(modelParams []*secboot.SealKeyModelParams) (json.RawMessage, error)) (restore func()) { + osutil.MustBeTestBinary("secbootBuildPCRProtectionProfile only can be mocked in tests") + old := secbootBuildPCRProtectionProfile + secbootBuildPCRProtectionProfile = f + return func() { + secbootBuildPCRProtectionProfile = old + } +} + +type stateUpdater func(role string, containerRole string, bootModes []string, models []secboot.ModelForSealing, tpmPCRProfile []byte) error + // ResealKeyForBootChains reseals disk encryption keys with the given bootchains. -func ResealKeyForBootChains(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { +func ResealKeyForBootChains(updateState stateUpdater, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { switch method { case device.SealingMethodFDESetupHook: // FIXME: do something @@ -73,9 +85,10 @@ func ResealKeyForBootChains(method device.SealingMethod, rootdir string, params pbcJSON, _ := json.Marshal(pbc) logger.Debugf("resealing (%d) to boot chains: %s", nextCount, pbcJSON) - if err := resealRunObjectKeys(pbc, authKeyFile, params.RoleToBlName); err != nil { + if err := resealRunObjectKeys(updateState, pbc, authKeyFile, params.RoleToBlName); err != nil { return err } + logger.Debugf("resealing (%d) succeeded", nextCount) bootChainsPath := BootChainsFileUnder(rootdir) @@ -98,7 +111,7 @@ func ResealKeyForBootChains(method device.SealingMethod, rootdir string, params rpbcJSON, _ := json.Marshal(rpbc) logger.Debugf("resealing (%d) to recovery boot chains: %s", nextFallbackCount, rpbcJSON) - if err := resealFallbackObjectKeys(rpbc, authKeyFile, params.RoleToBlName); err != nil { + if err := resealFallbackObjectKeys(updateState, rpbc, authKeyFile, params.RoleToBlName); err != nil { return err } logger.Debugf("fallback resealing (%d) succeeded", nextFallbackCount) @@ -114,18 +127,33 @@ func ResealKeyForBootChains(method device.SealingMethod, rootdir string, params return nil } -func resealRunObjectKeys(pbc boot.PredictableBootChains, authKeyFile string, roleToBlName map[bootloader.Role]string) error { +func resealRunObjectKeys(updateState stateUpdater, pbc boot.PredictableBootChains, authKeyFile string, roleToBlName map[bootloader.Role]string) error { // get model parameters from bootchains modelParams, err := boot.SealKeyModelParams(pbc, roleToBlName) if err != nil { return fmt.Errorf("cannot prepare for key resealing: %v", err) } + numModels := len(modelParams) + if numModels < 1 { + return fmt.Errorf("at least one set of model-specific parameters is required") + } + + pcrProfile, err := secbootBuildPCRProtectionProfile(modelParams) + if err != nil { + return err + } + + var models []secboot.ModelForSealing + for _, m := range modelParams { + models = append(models, m.Model) + } + // list all the key files to reseal keyFiles := []string{device.DataSealedKeyUnder(boot.InitramfsBootEncryptionKeyDir)} resealKeyParams := &secboot.ResealKeysParams{ - ModelParams: modelParams, + PCRProfile: pcrProfile, KeyFiles: keyFiles, TPMPolicyAuthKeyFile: authKeyFile, } @@ -133,16 +161,35 @@ func resealRunObjectKeys(pbc boot.PredictableBootChains, authKeyFile string, rol return fmt.Errorf("cannot reseal the encryption key: %v", err) } + if err := updateState("run+recover", "all", []string{"run", "recover"}, models, pcrProfile); err != nil { + return err + } + return nil } -func resealFallbackObjectKeys(pbc boot.PredictableBootChains, authKeyFile string, roleToBlName map[bootloader.Role]string) error { +func resealFallbackObjectKeys(updateState stateUpdater, pbc boot.PredictableBootChains, authKeyFile string, roleToBlName map[bootloader.Role]string) error { // get model parameters from bootchains modelParams, err := boot.SealKeyModelParams(pbc, roleToBlName) if err != nil { return fmt.Errorf("cannot prepare for fallback key resealing: %v", err) } + numModels := len(modelParams) + if numModels < 1 { + return fmt.Errorf("at least one set of model-specific parameters is required") + } + + pcrProfile, err := secbootBuildPCRProtectionProfile(modelParams) + if err != nil { + return err + } + + var models []secboot.ModelForSealing + for _, m := range modelParams { + models = append(models, m.Model) + } + // list all the key files to reseal keyFiles := []string{ device.FallbackDataSealedKeyUnder(boot.InitramfsSeedEncryptionKeyDir), @@ -150,7 +197,7 @@ func resealFallbackObjectKeys(pbc boot.PredictableBootChains, authKeyFile string } resealKeyParams := &secboot.ResealKeysParams{ - ModelParams: modelParams, + PCRProfile: pcrProfile, KeyFiles: keyFiles, TPMPolicyAuthKeyFile: authKeyFile, } @@ -158,5 +205,9 @@ func resealFallbackObjectKeys(pbc boot.PredictableBootChains, authKeyFile string return fmt.Errorf("cannot reseal the fallback encryption keys: %v", err) } + if err := updateState("recover", "system-save", []string{"recover", "factory-reset"}, models, pcrProfile); err != nil { + return err + } + return nil } diff --git a/overlord/fdestate/backend/reseal_test.go b/overlord/fdestate/backend/reseal_test.go index ec18d908104..f15983c30cc 100644 --- a/overlord/fdestate/backend/reseal_test.go +++ b/overlord/fdestate/backend/reseal_test.go @@ -20,6 +20,7 @@ package backend_test import ( + "encoding/json" "errors" "fmt" "os" @@ -266,21 +267,15 @@ func (s *resealTestSuite) TestTPMResealHappy(c *C) { }, } - resealCalls := 0 - restore := fdeBackend.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error { - resealCalls++ - - c.Check(params.TPMPolicyAuthKeyFile, Equals, filepath.Join(dirs.SnapSaveDir, "device/fde", "tpm-policy-auth-key")) + buildProfileCalls := 0 + restore := fdeBackend.MockSecbootBuildPCRProtectionProfile(func(modelParams []*secboot.SealKeyModelParams) (json.RawMessage, error) { + buildProfileCalls++ - c.Assert(params.ModelParams, HasLen, 1) - mp := params.ModelParams[0] + c.Assert(modelParams, HasLen, 1) + mp := modelParams[0] c.Check(mp.Model.Model(), Equals, model.Model()) - switch resealCalls { + switch buildProfileCalls { case 1: - // Resealing the run+recover key for data partition - c.Check(params.KeyFiles, DeepEquals, []string{ - filepath.Join(s.rootdir, "run/mnt/ubuntu-boot/device/fde/ubuntu-data.sealed-key"), - }) c.Check(mp.EFILoadChains, DeepEquals, []*secboot.LoadChain{ secboot.NewLoadChain(shimBf, secboot.NewLoadChain(assetBf, @@ -291,26 +286,49 @@ func (s *resealTestSuite) TestTPMResealHappy(c *C) { secboot.NewLoadChain(runKernel)))), }) case 2: - // Resealing the recovery key for both data and save partitions - c.Check(params.KeyFiles, DeepEquals, []string{ - filepath.Join(s.rootdir, "run/mnt/ubuntu-seed/device/fde/ubuntu-data.recovery.sealed-key"), - filepath.Join(s.rootdir, "run/mnt/ubuntu-seed/device/fde/ubuntu-save.recovery.sealed-key"), - }) c.Check(mp.EFILoadChains, DeepEquals, []*secboot.LoadChain{ secboot.NewLoadChain(shimBf, secboot.NewLoadChain(assetBf, secboot.NewLoadChain(recoveryKernel))), }) + default: + c.Errorf("unexpected additional call to secboot.BuildPCRProtectionProfile (call # %d)", buildProfileCalls) + } + return []byte(`"serialized-pcr-profile"`), nil + }) + defer restore() + + resealCalls := 0 + restore = fdeBackend.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error { + resealCalls++ + + c.Check(params.TPMPolicyAuthKeyFile, Equals, filepath.Join(dirs.SnapSaveDir, "device/fde", "tpm-policy-auth-key")) + + c.Check(params.PCRProfile, DeepEquals, json.RawMessage(`"serialized-pcr-profile"`)) + switch resealCalls { + case 1: + // Resealing the run+recover key for data partition + c.Check(params.KeyFiles, DeepEquals, []string{ + filepath.Join(s.rootdir, "run/mnt/ubuntu-boot/device/fde/ubuntu-data.sealed-key"), + }) + case 2: + // Resealing the recovery key for both data and save partitions + c.Check(params.KeyFiles, DeepEquals, []string{ + filepath.Join(s.rootdir, "run/mnt/ubuntu-seed/device/fde/ubuntu-data.recovery.sealed-key"), + filepath.Join(s.rootdir, "run/mnt/ubuntu-seed/device/fde/ubuntu-save.recovery.sealed-key"), + }) default: c.Errorf("unexpected additional call to secboot.ResealKey (call # %d)", resealCalls) } return nil }) - defer restore() + updateState := func(role string, containerRole string, bootModes []string, models []secboot.ModelForSealing, tpmPCRProfile []byte) error { + return nil + } const expectReseal = true - err := fdeBackend.ResealKeyForBootChains(device.SealingMethodTPM, s.rootdir, params, expectReseal) + err := fdeBackend.ResealKeyForBootChains(updateState, device.SealingMethodTPM, s.rootdir, params, expectReseal) c.Assert(err, IsNil) c.Check(resealCalls, Equals, 2) @@ -411,16 +429,13 @@ func (s *resealTestSuite) TestResealKeyForBootchainsWithSystemFallback(c *C) { // mock asset cache mockAssetsCache(c, rootdir, "grub", expectedCache) - // set mock key resealing - resealKeysCalls := 0 - restore := fdeBackend.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error { - c.Check(params.TPMPolicyAuthKeyFile, Equals, filepath.Join(dirs.SnapSaveDir, "device/fde", "tpm-policy-auth-key")) - - resealKeysCalls++ - c.Assert(params.ModelParams, HasLen, 1) + buildProfileCalls := 0 + restore := fdeBackend.MockSecbootBuildPCRProtectionProfile(func(modelParams []*secboot.SealKeyModelParams) (json.RawMessage, error) { + buildProfileCalls++ + c.Assert(modelParams, HasLen, 1) // shared parameters - c.Assert(params.ModelParams[0].Model.Model(), Equals, "my-model-uc20") + c.Assert(modelParams[0].Model.Model(), Equals, "my-model-uc20") // recovery parameters shim := bootloader.NewBootFile("", filepath.Join(rootdir, fmt.Sprintf("var/lib/snapd/boot-assets/grub/%s-shim-hash-1", shimId)), bootloader.RoleRecovery) @@ -513,33 +528,70 @@ func (s *resealTestSuite) TestResealKeyForBootchainsWithSystemFallback(c *C) { } checkRunParams := func() { - c.Check(params.KeyFiles, DeepEquals, []string{ - filepath.Join(boot.InitramfsBootEncryptionKeyDir, "ubuntu-data.sealed-key"), - }) - c.Check(params.ModelParams[0].KernelCmdlines, DeepEquals, []string{ + c.Check(modelParams[0].KernelCmdlines, DeepEquals, []string{ "snapd_recovery_mode=recover snapd_recovery_system=20200825 console=ttyS0 console=tty1 panic=-1", "snapd_recovery_mode=run console=ttyS0 console=tty1 panic=-1", }) for _, chain := range possibleChains { - c.Check(params.ModelParams[0].EFILoadChains, ContainsChain, chain) + c.Check(modelParams[0].EFILoadChains, ContainsChain, chain) } for _, chain := range possibleRecoveryChains { - c.Check(params.ModelParams[0].EFILoadChains, ContainsChain, chain) + c.Check(modelParams[0].EFILoadChains, ContainsChain, chain) } } checkRecoveryParams := func() { - c.Check(params.KeyFiles, DeepEquals, []string{ - filepath.Join(boot.InitramfsSeedEncryptionKeyDir, "ubuntu-data.recovery.sealed-key"), - filepath.Join(boot.InitramfsSeedEncryptionKeyDir, "ubuntu-save.recovery.sealed-key"), - }) - c.Check(params.ModelParams[0].KernelCmdlines, DeepEquals, []string{ + c.Check(modelParams[0].KernelCmdlines, DeepEquals, []string{ "snapd_recovery_mode=recover snapd_recovery_system=20200825 console=ttyS0 console=tty1 panic=-1", }) for _, chain := range possibleRecoveryChains { - c.Check(params.ModelParams[0].EFILoadChains, ContainsChain, chain) + c.Check(modelParams[0].EFILoadChains, ContainsChain, chain) + } + } + + switch buildProfileCalls { + case 1: + if !tc.reuseRunPbc { + checkRunParams() + } else if !tc.reuseRecoveryPbc { + checkRecoveryParams() + } else { + c.Errorf("unexpected call to secboot.BuildPCRProtectionProfile (call # %d)", buildProfileCalls) + } + case 2: + if !tc.reuseRecoveryPbc { + checkRecoveryParams() + } else { + c.Errorf("unexpected call to secboot.BuildPCRProtectionProfile (call # %d)", buildProfileCalls) } + default: + c.Errorf("unexpected additional call to secboot.BuildPCRProtectionProfile (call # %d)", buildProfileCalls) + } + + return []byte(`"serialized-pcr-profile"`), nil + }) + defer restore() + + // set mock key resealing + resealKeysCalls := 0 + restore = fdeBackend.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error { + c.Check(params.TPMPolicyAuthKeyFile, Equals, filepath.Join(dirs.SnapSaveDir, "device/fde", "tpm-policy-auth-key")) + + resealKeysCalls++ + c.Check(params.PCRProfile, DeepEquals, json.RawMessage(`"serialized-pcr-profile"`)) + + checkRunParams := func() { + c.Check(params.KeyFiles, DeepEquals, []string{ + filepath.Join(boot.InitramfsBootEncryptionKeyDir, "ubuntu-data.sealed-key"), + }) + } + + checkRecoveryParams := func() { + c.Check(params.KeyFiles, DeepEquals, []string{ + filepath.Join(boot.InitramfsSeedEncryptionKeyDir, "ubuntu-data.recovery.sealed-key"), + filepath.Join(boot.InitramfsSeedEncryptionKeyDir, "ubuntu-save.recovery.sealed-key"), + }) } switch resealKeysCalls { @@ -830,8 +882,11 @@ func (s *resealTestSuite) TestResealKeyForBootchainsWithSystemFallback(c *C) { }, } + updateState := func(role string, containerRole string, bootModes []string, models []secboot.ModelForSealing, tpmPCRProfile []byte) error { + return nil + } const expectReseal = false - err := fdeBackend.ResealKeyForBootChains(device.SealingMethodTPM, rootdir, params, expectReseal) + err := fdeBackend.ResealKeyForBootChains(updateState, device.SealingMethodTPM, rootdir, params, expectReseal) if tc.reuseRunPbc && tc.reuseRecoveryPbc { // did nothing c.Assert(err, IsNil) @@ -889,42 +944,32 @@ func (s *resealTestSuite) TestResealKeyForBootchainsRecoveryKeysForGoodSystemsOn "grubx64.efi-run-grub-hash", }) - // set mock key resealing - resealKeysCalls := 0 - restore := fdeBackend.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error { - c.Check(params.TPMPolicyAuthKeyFile, Equals, filepath.Join(dirs.SnapSaveDir, "device/fde", "tpm-policy-auth-key")) - - resealKeysCalls++ - c.Assert(params.ModelParams, HasLen, 1) + buildProfileCalls := 0 + restore := fdeBackend.MockSecbootBuildPCRProtectionProfile(func(modelParams []*secboot.SealKeyModelParams) (json.RawMessage, error) { + buildProfileCalls++ // shared parameters - c.Assert(params.ModelParams[0].Model.Model(), Equals, "my-model-uc20") - switch resealKeysCalls { + c.Assert(modelParams[0].Model.Model(), Equals, "my-model-uc20") + + switch buildProfileCalls { case 1: // run key - c.Assert(params.KeyFiles, DeepEquals, []string{ - filepath.Join(boot.InitramfsBootEncryptionKeyDir, "ubuntu-data.sealed-key"), - }) - c.Assert(params.ModelParams[0].KernelCmdlines, DeepEquals, []string{ + c.Assert(modelParams[0].KernelCmdlines, DeepEquals, []string{ "snapd_recovery_mode=factory-reset snapd_recovery_system=20200825 console=ttyS0 console=tty1 panic=-1", "snapd_recovery_mode=recover snapd_recovery_system=1234 console=ttyS0 console=tty1 panic=-1", "snapd_recovery_mode=recover snapd_recovery_system=20200825 console=ttyS0 console=tty1 panic=-1", "snapd_recovery_mode=run console=ttyS0 console=tty1 panic=-1", }) // load chains - c.Assert(params.ModelParams[0].EFILoadChains, HasLen, 3) + c.Assert(modelParams[0].EFILoadChains, HasLen, 3) case 2: // recovery keys - c.Assert(params.KeyFiles, DeepEquals, []string{ - filepath.Join(boot.InitramfsSeedEncryptionKeyDir, "ubuntu-data.recovery.sealed-key"), - filepath.Join(boot.InitramfsSeedEncryptionKeyDir, "ubuntu-save.recovery.sealed-key"), - }) - c.Assert(params.ModelParams[0].KernelCmdlines, DeepEquals, []string{ + c.Assert(modelParams[0].KernelCmdlines, DeepEquals, []string{ "snapd_recovery_mode=factory-reset snapd_recovery_system=20200825 console=ttyS0 console=tty1 panic=-1", "snapd_recovery_mode=recover snapd_recovery_system=20200825 console=ttyS0 console=tty1 panic=-1", }) // load chains - c.Assert(params.ModelParams[0].EFILoadChains, HasLen, 1) + c.Assert(modelParams[0].EFILoadChains, HasLen, 1) default: - c.Errorf("unexpected additional call to secboot.ResealKeys (call # %d)", resealKeysCalls) + c.Errorf("unexpected additional call to secboot.BuildPCRProtectionProfile (call # %d)", buildProfileCalls) } // recovery parameters @@ -937,9 +982,9 @@ func (s *resealTestSuite) TestResealKeyForBootchainsRecoveryKeysForGoodSystemsOn runGrub := bootloader.NewBootFile("", filepath.Join(s.rootdir, "var/lib/snapd/boot-assets/grub/grubx64.efi-run-grub-hash"), bootloader.RoleRunMode) runKernel := bootloader.NewBootFile(filepath.Join(s.rootdir, "var/lib/snapd/snaps/pc-kernel_500.snap"), "kernel.efi", bootloader.RoleRunMode) - switch resealKeysCalls { + switch buildProfileCalls { case 1: // run load chain - c.Assert(params.ModelParams[0].EFILoadChains, DeepEquals, []*secboot.LoadChain{ + c.Assert(modelParams[0].EFILoadChains, DeepEquals, []*secboot.LoadChain{ secboot.NewLoadChain(shim, secboot.NewLoadChain(grub, secboot.NewLoadChain(kernelGoodRecovery), @@ -955,7 +1000,7 @@ func (s *resealTestSuite) TestResealKeyForBootchainsRecoveryKeysForGoodSystemsOn )), }) case 2: // recovery load chains - c.Assert(params.ModelParams[0].EFILoadChains, DeepEquals, []*secboot.LoadChain{ + c.Assert(modelParams[0].EFILoadChains, DeepEquals, []*secboot.LoadChain{ secboot.NewLoadChain(shim, secboot.NewLoadChain(grub, secboot.NewLoadChain(kernelGoodRecovery), @@ -963,6 +1008,32 @@ func (s *resealTestSuite) TestResealKeyForBootchainsRecoveryKeysForGoodSystemsOn }) } + return []byte(`"serialized-pcr-profile"`), nil + }) + defer restore() + + // set mock key resealing + resealKeysCalls := 0 + restore = fdeBackend.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error { + c.Check(params.TPMPolicyAuthKeyFile, Equals, filepath.Join(dirs.SnapSaveDir, "device/fde", "tpm-policy-auth-key")) + + resealKeysCalls++ + c.Check(params.PCRProfile, DeepEquals, json.RawMessage(`"serialized-pcr-profile"`)) + + switch resealKeysCalls { + case 1: // run key + c.Assert(params.KeyFiles, DeepEquals, []string{ + filepath.Join(boot.InitramfsBootEncryptionKeyDir, "ubuntu-data.sealed-key"), + }) + case 2: // recovery keys + c.Assert(params.KeyFiles, DeepEquals, []string{ + filepath.Join(boot.InitramfsSeedEncryptionKeyDir, "ubuntu-data.recovery.sealed-key"), + filepath.Join(boot.InitramfsSeedEncryptionKeyDir, "ubuntu-save.recovery.sealed-key"), + }) + default: + c.Errorf("unexpected additional call to secboot.ResealKeys (call # %d)", resealKeysCalls) + } + return nil }) defer restore() @@ -1094,8 +1165,11 @@ func (s *resealTestSuite) TestResealKeyForBootchainsRecoveryKeysForGoodSystemsOn }, } + updateState := func(role string, containerRole string, bootModes []string, models []secboot.ModelForSealing, tpmPCRProfile []byte) error { + return nil + } const expectReseal = false - err := fdeBackend.ResealKeyForBootChains(device.SealingMethodTPM, s.rootdir, params, expectReseal) + err := fdeBackend.ResealKeyForBootChains(updateState, device.SealingMethodTPM, s.rootdir, params, expectReseal) c.Assert(err, IsNil) c.Assert(resealKeysCalls, Equals, 2) @@ -1119,48 +1193,38 @@ func (s *resealTestSuite) testResealKeyForBootchainsWithTryModel(c *C, shimId, g "grubx64.efi-run-grub-hash", }) - // set mock key resealing - resealKeysCalls := 0 - restore := fdeBackend.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error { - c.Check(params.TPMPolicyAuthKeyFile, Equals, filepath.Join(dirs.SnapSaveDir, "device/fde", "tpm-policy-auth-key")) - - resealKeysCalls++ + buildProfileCalls := 0 + restore := fdeBackend.MockSecbootBuildPCRProtectionProfile(func(modelParams []*secboot.SealKeyModelParams) (json.RawMessage, error) { + buildProfileCalls++ - switch resealKeysCalls { + switch buildProfileCalls { case 1: // run key - c.Assert(params.KeyFiles, DeepEquals, []string{ - filepath.Join(boot.InitramfsBootEncryptionKeyDir, "ubuntu-data.sealed-key"), - }) // 2 models, one current and one try model - c.Assert(params.ModelParams, HasLen, 2) + c.Assert(modelParams, HasLen, 2) // shared parameters - c.Assert(params.ModelParams[0].Model.Model(), Equals, "my-model-uc20") - c.Assert(params.ModelParams[0].KernelCmdlines, DeepEquals, []string{ + c.Assert(modelParams[0].Model.Model(), Equals, "my-model-uc20") + c.Assert(modelParams[0].KernelCmdlines, DeepEquals, []string{ "snapd_recovery_mode=factory-reset snapd_recovery_system=20200825 console=ttyS0 console=tty1 panic=-1", "snapd_recovery_mode=recover snapd_recovery_system=20200825 console=ttyS0 console=tty1 panic=-1", "snapd_recovery_mode=run console=ttyS0 console=tty1 panic=-1", }) // 2 load chains (bootloader + run kernel, bootloader + recovery kernel) - c.Assert(params.ModelParams[0].EFILoadChains, HasLen, 2) + c.Assert(modelParams[0].EFILoadChains, HasLen, 2) - c.Assert(params.ModelParams[1].Model.Model(), Equals, "try-my-model-uc20") - c.Assert(params.ModelParams[1].KernelCmdlines, DeepEquals, []string{ + c.Assert(modelParams[1].Model.Model(), Equals, "try-my-model-uc20") + c.Assert(modelParams[1].KernelCmdlines, DeepEquals, []string{ "snapd_recovery_mode=factory-reset snapd_recovery_system=1234 console=ttyS0 console=tty1 panic=-1", "snapd_recovery_mode=recover snapd_recovery_system=1234 console=ttyS0 console=tty1 panic=-1", "snapd_recovery_mode=run console=ttyS0 console=tty1 panic=-1", }) // 2 load chains (bootloader + run kernel, bootloader + recovery kernel) - c.Assert(params.ModelParams[1].EFILoadChains, HasLen, 2) + c.Assert(modelParams[1].EFILoadChains, HasLen, 2) case 2: // recovery keys - c.Assert(params.KeyFiles, DeepEquals, []string{ - filepath.Join(boot.InitramfsSeedEncryptionKeyDir, "ubuntu-data.recovery.sealed-key"), - filepath.Join(boot.InitramfsSeedEncryptionKeyDir, "ubuntu-save.recovery.sealed-key"), - }) // only the current model - c.Assert(params.ModelParams, HasLen, 1) + c.Assert(modelParams, HasLen, 1) // shared parameters - c.Assert(params.ModelParams[0].Model.Model(), Equals, "my-model-uc20") - for _, mp := range params.ModelParams { + c.Assert(modelParams[0].Model.Model(), Equals, "my-model-uc20") + for _, mp := range modelParams { c.Assert(mp.KernelCmdlines, DeepEquals, []string{ "snapd_recovery_mode=factory-reset snapd_recovery_system=20200825 console=ttyS0 console=tty1 panic=-1", "snapd_recovery_mode=recover snapd_recovery_system=20200825 console=ttyS0 console=tty1 panic=-1", @@ -1169,7 +1233,7 @@ func (s *resealTestSuite) testResealKeyForBootchainsWithTryModel(c *C, shimId, g c.Assert(mp.EFILoadChains, HasLen, 1) } default: - c.Errorf("unexpected additional call to secboot.ResealKeys (call # %d)", resealKeysCalls) + c.Errorf("unexpected additional call to secboot.BuildPCRProtectionProfile (call # %d)", buildProfileCalls) } // recovery parameters @@ -1183,13 +1247,13 @@ func (s *resealTestSuite) testResealKeyForBootchainsWithTryModel(c *C, shimId, g runKernel := bootloader.NewBootFile(filepath.Join(s.rootdir, "var/lib/snapd/snaps/pc-kernel_500.snap"), "kernel.efi", bootloader.RoleRunMode) // verify the load chains, which are identical for both models - switch resealKeysCalls { + switch buildProfileCalls { case 1: // run load chain for 2 models, current and a try model - c.Assert(params.ModelParams, HasLen, 2) + c.Assert(modelParams, HasLen, 2) // each load chain has either the run kernel (shared for // both), or the kernel of the respective recovery // system - c.Assert(params.ModelParams[0].EFILoadChains, DeepEquals, []*secboot.LoadChain{ + c.Assert(modelParams[0].EFILoadChains, DeepEquals, []*secboot.LoadChain{ secboot.NewLoadChain(shim, secboot.NewLoadChain(grub, secboot.NewLoadChain(kernelOldRecovery), @@ -1200,7 +1264,7 @@ func (s *resealTestSuite) testResealKeyForBootchainsWithTryModel(c *C, shimId, g secboot.NewLoadChain(runKernel)), )), }) - c.Assert(params.ModelParams[1].EFILoadChains, DeepEquals, []*secboot.LoadChain{ + c.Assert(modelParams[1].EFILoadChains, DeepEquals, []*secboot.LoadChain{ secboot.NewLoadChain(shim, secboot.NewLoadChain(grub, secboot.NewLoadChain(kernelNewRecovery), @@ -1212,10 +1276,10 @@ func (s *resealTestSuite) testResealKeyForBootchainsWithTryModel(c *C, shimId, g )), }) case 2: // recovery load chains, only for the current model - c.Assert(params.ModelParams, HasLen, 1) + c.Assert(modelParams, HasLen, 1) // load chain with a kernel from a recovery system that // matches the current model only - c.Assert(params.ModelParams[0].EFILoadChains, DeepEquals, []*secboot.LoadChain{ + c.Assert(modelParams[0].EFILoadChains, DeepEquals, []*secboot.LoadChain{ secboot.NewLoadChain(shim, secboot.NewLoadChain(grub, secboot.NewLoadChain(kernelOldRecovery), @@ -1223,6 +1287,32 @@ func (s *resealTestSuite) testResealKeyForBootchainsWithTryModel(c *C, shimId, g }) } + return []byte(`"serialized-pcr-profile"`), nil + }) + defer restore() + + // set mock key resealing + resealKeysCalls := 0 + restore = fdeBackend.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error { + c.Check(params.TPMPolicyAuthKeyFile, Equals, filepath.Join(dirs.SnapSaveDir, "device/fde", "tpm-policy-auth-key")) + + resealKeysCalls++ + c.Check(params.PCRProfile, DeepEquals, json.RawMessage(`"serialized-pcr-profile"`)) + + switch resealKeysCalls { + case 1: // run key + c.Assert(params.KeyFiles, DeepEquals, []string{ + filepath.Join(boot.InitramfsBootEncryptionKeyDir, "ubuntu-data.sealed-key"), + }) + case 2: // recovery keys + c.Assert(params.KeyFiles, DeepEquals, []string{ + filepath.Join(boot.InitramfsSeedEncryptionKeyDir, "ubuntu-data.recovery.sealed-key"), + filepath.Join(boot.InitramfsSeedEncryptionKeyDir, "ubuntu-save.recovery.sealed-key"), + }) + default: + c.Errorf("unexpected additional call to secboot.ResealKeys (call # %d)", resealKeysCalls) + } + return nil }) defer restore() @@ -1344,8 +1434,11 @@ func (s *resealTestSuite) testResealKeyForBootchainsWithTryModel(c *C, shimId, g }, } + updateState := func(role string, containerRole string, bootModes []string, models []secboot.ModelForSealing, tpmPCRProfile []byte) error { + return nil + } const expectReseal = false - err := fdeBackend.ResealKeyForBootChains(device.SealingMethodTPM, s.rootdir, params, expectReseal) + err := fdeBackend.ResealKeyForBootChains(updateState, device.SealingMethodTPM, s.rootdir, params, expectReseal) c.Assert(err, IsNil) c.Assert(resealKeysCalls, Equals, 2) @@ -1397,22 +1490,40 @@ func (s *resealTestSuite) TestResealKeyForBootchainsFallbackCmdline(c *C) { bootloader.Force(mtbl) defer bootloader.Force(nil) - // set mock key resealing - resealKeysCalls := 0 - restore := fdeBackend.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error { - resealKeysCalls++ - c.Assert(params.ModelParams, HasLen, 1) - c.Logf("reseal: %+v", params) - switch resealKeysCalls { + buildProfileCalls := 0 + restore := fdeBackend.MockSecbootBuildPCRProtectionProfile(func(modelParams []*secboot.SealKeyModelParams) (json.RawMessage, error) { + buildProfileCalls++ + + c.Assert(modelParams, HasLen, 1) + + switch buildProfileCalls { case 1: - c.Assert(params.ModelParams[0].KernelCmdlines, DeepEquals, []string{ + c.Assert(modelParams[0].KernelCmdlines, DeepEquals, []string{ "snapd_recovery_mode=recover snapd_recovery_system=20200825 static cmdline", "snapd_recovery_mode=run static cmdline", }) case 2: - c.Assert(params.ModelParams[0].KernelCmdlines, DeepEquals, []string{ + c.Assert(modelParams[0].KernelCmdlines, DeepEquals, []string{ "snapd_recovery_mode=recover snapd_recovery_system=20200825 static cmdline", }) + default: + c.Fatalf("unexpected number of build profile calls, %v", modelParams) + } + + return []byte(`"serialized-pcr-profile"`), nil + }) + defer restore() + + // set mock key resealing + resealKeysCalls := 0 + restore = fdeBackend.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error { + resealKeysCalls++ + + c.Check(params.PCRProfile, DeepEquals, json.RawMessage(`"serialized-pcr-profile"`)) + c.Logf("reseal: %+v", params) + switch resealKeysCalls { + case 1: + case 2: default: c.Fatalf("unexpected number of reseal calls, %v", params) } @@ -1496,8 +1607,11 @@ func (s *resealTestSuite) TestResealKeyForBootchainsFallbackCmdline(c *C) { }, } + updateState := func(role string, containerRole string, bootModes []string, models []secboot.ModelForSealing, tpmPCRProfile []byte) error { + return nil + } const expectReseal = false - err = fdeBackend.ResealKeyForBootChains(device.SealingMethodTPM, s.rootdir, params, expectReseal) + err = fdeBackend.ResealKeyForBootChains(updateState, device.SealingMethodTPM, s.rootdir, params, expectReseal) c.Assert(err, IsNil) c.Assert(resealKeysCalls, Equals, 2) diff --git a/overlord/fdestate/export_test.go b/overlord/fdestate/export_test.go index fa91b2c3cba..ae6e884b402 100644 --- a/overlord/fdestate/export_test.go +++ b/overlord/fdestate/export_test.go @@ -20,3 +20,5 @@ package fdestate var FdeMgr = fdeMgr + +var UpdateParameters = updateParameters diff --git a/overlord/fdestate/fdemgr.go b/overlord/fdestate/fdemgr.go index c97680973db..fd8d3e69986 100644 --- a/overlord/fdestate/fdemgr.go +++ b/overlord/fdestate/fdemgr.go @@ -26,6 +26,7 @@ import ( "github.com/snapcore/snapd/gadget/device" "github.com/snapcore/snapd/overlord/fdestate/backend" "github.com/snapcore/snapd/overlord/state" + "github.com/snapcore/snapd/secboot" ) // FDEManager is responsible for managing full disk encryption keys. @@ -50,11 +51,20 @@ func Manager(st *state.State, runner *state.TaskRunner) *FDEManager { } func (m *FDEManager) Ensure() error { - return nil + m.state.Lock() + defer m.state.Unlock() + return ensureState(m.state) } -func (m *FDEManager) resealKeyForBootChains(method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { - return backend.ResealKeyForBootChains(method, rootdir, params, expectReseal) +func (m *FDEManager) resealKeyForBootChains(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + doUpdate := func(role string, containerRole string, bootModes []string, models []secboot.ModelForSealing, tpmPCRProfile []byte) error { + if !locked { + m.state.Lock() + defer m.state.Unlock() + } + return updateParameters(m.state, role, containerRole, bootModes, models, tpmPCRProfile) + } + return backend.ResealKeyForBootChains(doUpdate, method, rootdir, params, expectReseal) } func fdeMgr(st *state.State) *FDEManager { diff --git a/overlord/fdestate/fdemgr_test.go b/overlord/fdestate/fdemgr_test.go index 3f53e2b17ce..9e6886a15aa 100644 --- a/overlord/fdestate/fdemgr_test.go +++ b/overlord/fdestate/fdemgr_test.go @@ -20,12 +20,18 @@ package fdestate_test import ( + "crypto" + "encoding/json" + "fmt" "testing" . "gopkg.in/check.v1" + "github.com/snapcore/snapd/asserts" + "github.com/snapcore/snapd/dirs" "github.com/snapcore/snapd/overlord/fdestate" "github.com/snapcore/snapd/overlord/state" + "github.com/snapcore/snapd/secboot" ) func TestFDE(t *testing.T) { TestingT(t) } @@ -37,6 +43,102 @@ var _ = Suite(&fdeMgrSuite{}) func (s *fdeMgrSuite) TestGetManagerFromState(c *C) { st := state.New(nil) + defer fdestate.MockDmCryptUUIDFromMountPoint(func(mountpoint string) (string, error) { + switch mountpoint { + case dirs.GlobalRootDir: + return "aaa", nil + case dirs.SnapSaveDir: + return "bbb", nil + } + return "", fmt.Errorf("unknown mount point") + })() + + defer fdestate.MockGetPrimaryKeyHash(func(devicePath string, alg crypto.Hash) ([]byte, []byte, error) { + c.Check(devicePath, Equals, "/dev/disk/by-uuid/aaa") + c.Check(alg, Equals, crypto.Hash(crypto.SHA256)) + return []byte{1, 2, 3, 4}, []byte{5, 6, 7, 8}, nil + })() + + defer fdestate.MockVerifyPrimaryKeyHash(func(devicePath string, alg crypto.Hash, salt, digest []byte) (bool, error) { + c.Check(devicePath, Equals, "/dev/disk/by-uuid/bbb") + c.Check(alg, Equals, crypto.Hash(crypto.SHA256)) + c.Check(salt, DeepEquals, []byte{1, 2, 3, 4}) + c.Check(digest, DeepEquals, []byte{5, 6, 7, 8}) + return true, nil + })() + + runner := state.NewTaskRunner(st) + manager := fdestate.Manager(st, runner) + c.Assert(manager.Ensure(), IsNil) + st.Lock() + defer st.Unlock() + foundManager := fdestate.FdeMgr(st) + c.Check(foundManager, Equals, manager) + + var fdeSt fdestate.FdeState + err := st.Get("fde", &fdeSt) + c.Assert(err, IsNil) + primaryKey, hasPrimaryKey := fdeSt.PrimaryKeys[0] + c.Assert(hasPrimaryKey, Equals, true) + c.Check(crypto.Hash(primaryKey.Digest.Algorithm), Equals, crypto.Hash(crypto.SHA256)) + c.Check(primaryKey.Digest.Salt, DeepEquals, []byte{1, 2, 3, 4}) + c.Check(primaryKey.Digest.Digest, DeepEquals, []byte{5, 6, 7, 8}) +} + +type mockModel struct { +} + +func (m *mockModel) Series() string { + return "mock-series" +} + +func (m *mockModel) BrandID() string { + return "mock-brand" +} + +func (m *mockModel) Model() string { + return "mock-model" +} + +func (m *mockModel) Classic() bool { + return false +} + +func (m *mockModel) Grade() asserts.ModelGrade { + return asserts.ModelSigned +} + +func (m *mockModel) SignKeyID() string { + return "mock-key" +} + +func (s *fdeMgrSuite) TestUpdateState(c *C) { + st := state.New(nil) + + defer fdestate.MockDmCryptUUIDFromMountPoint(func(mountpoint string) (string, error) { + switch mountpoint { + case dirs.GlobalRootDir: + return "aaa", nil + case dirs.SnapSaveDir: + return "bbb", nil + } + return "", fmt.Errorf("unknown mount point") + })() + + defer fdestate.MockGetPrimaryKeyHash(func(devicePath string, alg crypto.Hash) ([]byte, []byte, error) { + c.Check(devicePath, Equals, "/dev/disk/by-uuid/aaa") + c.Check(alg, Equals, crypto.Hash(crypto.SHA256)) + return []byte{1, 2, 3, 4}, []byte{5, 6, 7, 8}, nil + })() + + defer fdestate.MockVerifyPrimaryKeyHash(func(devicePath string, alg crypto.Hash, salt, digest []byte) (bool, error) { + c.Check(devicePath, Equals, "/dev/disk/by-uuid/bbb") + c.Check(alg, Equals, crypto.Hash(crypto.SHA256)) + c.Check(salt, DeepEquals, []byte{1, 2, 3, 4}) + c.Check(digest, DeepEquals, []byte{5, 6, 7, 8}) + return true, nil + })() + runner := state.NewTaskRunner(st) manager := fdestate.Manager(st, runner) c.Assert(manager.Ensure(), IsNil) @@ -44,4 +146,23 @@ func (s *fdeMgrSuite) TestGetManagerFromState(c *C) { defer st.Unlock() foundManager := fdestate.FdeMgr(st) c.Check(foundManager, Equals, manager) + + models := []secboot.ModelForSealing{ + &mockModel{}, + } + + fdestate.UpdateParameters(st, "run+recover", "container-role", []string{"run"}, models, json.RawMessage(`"serialized-profile"`)) + + var fdeSt fdestate.FdeState + err := st.Get("fde", &fdeSt) + c.Assert(err, IsNil) + runRecoverRole, hasRunRecoverRole := fdeSt.KeyslotRoles["run+recover"] + c.Assert(hasRunRecoverRole, Equals, true) + containerRole, hasContainerRole := runRecoverRole.Parameters["container-role"] + c.Assert(hasContainerRole, Equals, true) + + c.Assert(containerRole.Models, HasLen, 1) + c.Check(containerRole.Models[0].Model(), Equals, "mock-model") + c.Check(containerRole.BootModes, DeepEquals, []string{"run"}) + c.Check(containerRole.Tpm2PCRProfile, DeepEquals, json.RawMessage(`"serialized-profile"`)) } diff --git a/overlord/fdestate/fdestate.go b/overlord/fdestate/fdestate.go new file mode 100644 index 00000000000..2ab7540c235 --- /dev/null +++ b/overlord/fdestate/fdestate.go @@ -0,0 +1,305 @@ +// -*- 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 fdestate + +import ( + "crypto" + "encoding/json" + "errors" + "fmt" + + "github.com/snapcore/snapd/asserts" + "github.com/snapcore/snapd/dirs" + "github.com/snapcore/snapd/osutil" + "github.com/snapcore/snapd/osutil/disks" + "github.com/snapcore/snapd/overlord/state" + "github.com/snapcore/snapd/secboot" +) + +var ( + disksDmCryptUUIDFromMountPoint = disks.DmCryptUUIDFromMountPoint + secbootGetPrimaryKeyHash = secboot.GetPrimaryKeyHash + secbootVerifyPrimaryKeyHash = secboot.VerifyPrimaryKeyHash +) + +type Model struct { + SeriesValue string `json:"series"` + BrandIDValue string `json:"brand-id"` + ModelValue string `json:"model"` + ClassicValue bool `json:"classic"` + GradeValue asserts.ModelGrade `json:"grade"` + SignKeyIDValue string `json:"sign-key-id"` +} + +func (m *Model) Series() string { + return m.SeriesValue +} + +func (m *Model) BrandID() string { + return m.BrandIDValue +} + +func (m *Model) Model() string { + return m.ModelValue +} + +func (m *Model) Classic() bool { + return m.ClassicValue +} + +func (m *Model) Grade() asserts.ModelGrade { + return m.GradeValue +} + +func (m *Model) SignKeyID() string { + return m.SignKeyIDValue +} + +func newModel(m secboot.ModelForSealing) Model { + return Model{ + SeriesValue: m.Series(), + BrandIDValue: m.BrandID(), + ModelValue: m.Model(), + ClassicValue: m.Classic(), + GradeValue: m.Grade(), + SignKeyIDValue: m.SignKeyID(), + } +} + +var _ secboot.ModelForSealing = (*Model)(nil) + +type KeyroleSlotParameters struct { + Models []Model `json:"models,omitempty"` + BootModes []string `json:"boot-modes,omitempty"` + Tpm2PCRProfile json.RawMessage `json:"tpm2-pcr-profile,omitempty"` +} + +type KeyslotRoleInfo struct { + // PrimaryKeyId is the ID for the primary key found in + // PrimaryKeys field of FdeState + PrimaryKeyId int `json:"primary-key-id"` + // Parameters is indexed by container role name + Parameters map[string]KeyroleSlotParameters `json:"params,omitempty"` + // Tpm2PCRPolicyRevocationCounter is the handle for the TPM + // policy revocation counter. A value of 0 means it is not + // set. + Tpm2PCRPolicyRevocationCounter uint32 `json:"tpm2-pcr-policy-revocation-counter,omitempty"` +} + +type hashAlg crypto.Hash + +func (h *hashAlg) UnmarshalJSON(b []byte) error { + var s string + if err := json.Unmarshal(b, &s); err != nil { + return err + } + switch s { + case "sha256": + *h = hashAlg(crypto.SHA256) + default: + return fmt.Errorf("Unknown algorithm %s", s) + } + + return nil +} + +func (h hashAlg) MarshalJSON() ([]byte, error) { + switch crypto.Hash(h) { + case crypto.SHA256: + return json.Marshal("sha256") + default: + return nil, fmt.Errorf("Unknown algorithm %v", h) + } +} + +type KeyDigest struct { + // Algorithm is the algorithm for + Algorithm hashAlg `json:"alg"` + // Salt is the salt for the HMAC digest + Salt []byte `json:"salt"` + // Digest is the result of `HMAC(key, salt)` + Digest []byte `json:"digest"` +} + +const defaultHashAlg = crypto.SHA256 + +func getPrimaryKeyDigest(devicePath string) (KeyDigest, error) { + salt, digest, err := secbootGetPrimaryKeyHash(devicePath, crypto.Hash(defaultHashAlg)) + if err != nil { + return KeyDigest{}, err + } + + return KeyDigest{ + Algorithm: hashAlg(defaultHashAlg), + Salt: salt, + Digest: digest, + }, nil +} + +func (kd *KeyDigest) verifyPrimaryKeyDigest(devicePath string) (bool, error) { + return secbootVerifyPrimaryKeyHash(devicePath, crypto.Hash(kd.Algorithm), kd.Salt, kd.Digest) +} + +type PrimaryKeyInfo struct { + Digest KeyDigest `json:"digest"` +} + +type FdeState struct { + // PrimaryKeys are the keys on the system. Key with ID 0 is + // reserved for snapd and is populated on first boot. Other + // IDs are for externally managed keys. + PrimaryKeys map[int]PrimaryKeyInfo `json:"primary-keys"` + + // KeyslotRoles are all keyslot roles indexed by the role name + KeyslotRoles map[string]KeyslotRoleInfo `json:"keyslot-roles"` +} + +const fdeStateKey = "fde" + +func ensureState(st *state.State) error { + var s FdeState + err := st.Get(fdeStateKey, &s) + if err == nil { + // TODO: Do we need to do something in recover? + return nil + } + + if !errors.Is(err, state.ErrNoState) { + return err + } + + dataUUID, dataErr := disksDmCryptUUIDFromMountPoint(dirs.GlobalRootDir) + saveUUID, saveErr := disksDmCryptUUIDFromMountPoint(dirs.SnapSaveDir) + if errors.Is(saveErr, &disks.ErrMountPointNotFound{}) { + // TODO: do we need to care about old cases where there is no save partition? + return nil + } + + if errors.Is(dataErr, disks.ErrNoDmUUID) && errors.Is(saveErr, disks.ErrNoDmUUID) { + return nil + } + if dataErr != nil { + return dataErr + } + if saveErr != nil { + return saveErr + } + + digest, err := getPrimaryKeyDigest(fmt.Sprintf("/dev/disk/by-uuid/%s", dataUUID)) + if err != nil { + return err + } + sameDigest, err := digest.verifyPrimaryKeyDigest(fmt.Sprintf("/dev/disk/by-uuid/%s", saveUUID)) + if err != nil { + return err + } + if !sameDigest { + return fmt.Errorf("Primary key for data and save partition are not the same") + } + + s.PrimaryKeys = map[int]PrimaryKeyInfo{ + 0: { + Digest: digest, + }, + } + + // Note that Parameters will be updated on first update + s.KeyslotRoles = map[string]KeyslotRoleInfo{ + "run+recover": { + PrimaryKeyId: 0, + // FIXME: this might be + // AltRunObjectPCRPolicyCounterHandle after + // factory reset, but this is supposed to be + // removed. + Tpm2PCRPolicyRevocationCounter: secboot.RunObjectPCRPolicyCounterHandle, + }, + "recover": { + PrimaryKeyId: 0, + Tpm2PCRPolicyRevocationCounter: secboot.FallbackObjectPCRPolicyCounterHandle, + }, + } + + st.Set(fdeStateKey, s) + + return nil +} + +func updateParameters(st *state.State, role string, containerRole string, bootModes []string, models []secboot.ModelForSealing, tpmPCRProfile []byte) error { + var s FdeState + err := st.Get(fdeStateKey, &s) + if err != nil { + return err + } + + roleInfo, hasRole := s.KeyslotRoles[role] + if !hasRole { + return fmt.Errorf("Cannot find role %s", role) + } + + var convertedModels []Model + for _, model := range models { + convertedModels = append(convertedModels, newModel(model)) + } + + if roleInfo.Parameters == nil { + roleInfo.Parameters = make(map[string]KeyroleSlotParameters) + } + roleInfo.Parameters[containerRole] = KeyroleSlotParameters{ + Models: convertedModels, + BootModes: bootModes, + Tpm2PCRProfile: tpmPCRProfile, + } + + s.KeyslotRoles[role] = roleInfo + + st.Set(fdeStateKey, s) + + return nil +} + +func MockDmCryptUUIDFromMountPoint(f func(mountpoint string) (string, error)) (restore func()) { + osutil.MustBeTestBinary("mocking DmCryptUUIDFromMountPoint can be done only from tests") + + old := disksDmCryptUUIDFromMountPoint + disksDmCryptUUIDFromMountPoint = f + return func() { + disksDmCryptUUIDFromMountPoint = old + } +} + +func MockGetPrimaryKeyHash(f func(devicePath string, alg crypto.Hash) ([]byte, []byte, error)) (restore func()) { + osutil.MustBeTestBinary("mocking GetPrimaryKeyHash can be done only from tests") + + old := secbootGetPrimaryKeyHash + secbootGetPrimaryKeyHash = f + return func() { + secbootGetPrimaryKeyHash = old + } +} + +func MockVerifyPrimaryKeyHash(f func(devicePath string, alg crypto.Hash, salt []byte, digest []byte) (bool, error)) (restore func()) { + osutil.MustBeTestBinary("mocking VerifyPrimaryKeyHash can be done only from tests") + + old := secbootVerifyPrimaryKeyHash + secbootVerifyPrimaryKeyHash = f + return func() { + secbootVerifyPrimaryKeyHash = old + } +} diff --git a/overlord/managers_test.go b/overlord/managers_test.go index 8ed14c9f5aa..bb011352621 100644 --- a/overlord/managers_test.go +++ b/overlord/managers_test.go @@ -24,6 +24,7 @@ package overlord_test import ( "bytes" "context" + "crypto" "encoding/json" "errors" "fmt" @@ -74,6 +75,7 @@ import ( "github.com/snapcore/snapd/overlord/configstate/config" "github.com/snapcore/snapd/overlord/devicestate" "github.com/snapcore/snapd/overlord/devicestate/devicestatetest" + "github.com/snapcore/snapd/overlord/fdestate" fdeBackend "github.com/snapcore/snapd/overlord/fdestate/backend" "github.com/snapcore/snapd/overlord/hookstate" "github.com/snapcore/snapd/overlord/hookstate/ctlcmd" @@ -7578,6 +7580,30 @@ func (s *mgrsSuiteCore) testRemodelUC20WithRecoverySystem(c *C, encrypted bool) c.Check(devicestate.RemodelingChange(st), NotNil) + restore = fdeBackend.MockSecbootBuildPCRProtectionProfile(func(modelParams []*secboot.SealKeyModelParams) (json.RawMessage, error) { + return []byte(`"some-profile"`), nil + }) + defer restore() + + restore = fdestate.MockDmCryptUUIDFromMountPoint(func(mountpoint string) (string, error) { + if mountpoint == dirs.GlobalRootDir { + return "root-uuid", nil + } else { + return "save-uuid", nil + } + }) + defer restore() + + restore = fdestate.MockGetPrimaryKeyHash(func(devicePath string, alg crypto.Hash) ([]byte, []byte, error) { + return []byte("aaaa"), []byte("bbbb"), nil + }) + defer restore() + + restore = fdestate.MockVerifyPrimaryKeyHash(func(devicePath string, alg crypto.Hash, salt []byte, digest []byte) (bool, error) { + return true, nil + }) + defer restore() + st.Unlock() err = s.o.Settle(settleTimeout) st.Lock() diff --git a/secboot/secboot.go b/secboot/secboot.go index b488939f0b5..6e2747ea394 100644 --- a/secboot/secboot.go +++ b/secboot/secboot.go @@ -25,6 +25,8 @@ package secboot // Debian does run "go list" without any support for passing -tags. import ( + "encoding/json" + "github.com/snapcore/snapd/asserts" "github.com/snapcore/snapd/bootloader" "github.com/snapcore/snapd/dirs" @@ -134,7 +136,7 @@ type SealKeysWithFDESetupHookParams struct { type ResealKeysParams struct { // The snap model parameters - ModelParams []*SealKeyModelParams + PCRProfile json.RawMessage // The path to the sealed key files KeyFiles []string // The path to the authorization policy update key file (only relevant for TPM) diff --git a/secboot/secboot_dummy.go b/secboot/secboot_dummy.go index 5f16ce81fae..2cb0593d433 100644 --- a/secboot/secboot_dummy.go +++ b/secboot/secboot_dummy.go @@ -21,6 +21,8 @@ package secboot import ( + "crypto" + "encoding/json" "errors" "github.com/snapcore/snapd/kernel/fde" @@ -85,3 +87,15 @@ func RenameOrDeleteKeys(node string, renames map[string]string) error { func DeleteKeys(node string, matches map[string]bool) error { return errBuildWithoutSecboot } + +func BuildPCRProtectionProfile(modelParams []*SealKeyModelParams) (json.RawMessage, error) { + return nil, errBuildWithoutSecboot +} + +func GetPrimaryKeyHash(devicePath string, alg crypto.Hash) ([]byte, []byte, error) { + return nil, nil, errBuildWithoutSecboot +} + +func VerifyPrimaryKeyHash(devicePath string, alg crypto.Hash, salt []byte, digest []byte) (bool, error) { + return false, errBuildWithoutSecboot +} diff --git a/secboot/secboot_sb.go b/secboot/secboot_sb.go index d09c4e56fdf..95d3f209862 100644 --- a/secboot/secboot_sb.go +++ b/secboot/secboot_sb.go @@ -21,8 +21,12 @@ package secboot import ( + "crypto" + "crypto/hmac" + "crypto/rand" "errors" "fmt" + "hash" "path/filepath" sb "github.com/snapcore/secboot" @@ -291,3 +295,32 @@ func DeleteKeys(node string, matches map[string]bool) error { return nil } + +func GetPrimaryKeyHash(devicePath string, alg crypto.Hash) ([]byte, []byte, error) { + const remove = false + p, err := sb.GetPrimaryKeyFromKernel(keyringPrefix, devicePath, remove) + if err != nil { + return nil, nil, err + } + + var salt [32]byte + if _, err := rand.Read(salt[:]); err != nil { + return nil, nil, err + } + + h := hmac.New(func() hash.Hash { return alg.New() }, salt[:]) + h.Write(p) + return salt[:], h.Sum(nil), nil +} + +func VerifyPrimaryKeyHash(devicePath string, alg crypto.Hash, salt []byte, digest []byte) (bool, error) { + const remove = false + p, err := sb.GetPrimaryKeyFromKernel(keyringPrefix, devicePath, remove) + if err != nil { + return false, err + } + + h := hmac.New(func() hash.Hash { return alg.New() }, salt[:]) + h.Write(p) + return hmac.Equal(h.Sum(nil), digest), nil +} diff --git a/secboot/secboot_sb_test.go b/secboot/secboot_sb_test.go index 7f555951d57..175107bd9dc 100644 --- a/secboot/secboot_sb_test.go +++ b/secboot/secboot_sb_test.go @@ -1125,6 +1125,7 @@ func (s *secbootSuite) TestResealKey(c *C) { revokeCalls int expectedErr string oldKeyFiles bool + buildProfileErr string }{ // happy case {tpmEnabled: true, resealCalls: 1, expectedErr: ""}, @@ -1134,10 +1135,10 @@ func (s *secbootSuite) TestResealKey(c *C) { // unhappy cases {tpmErr: mockErr, expectedErr: "cannot connect to TPM: some error"}, {tpmEnabled: false, expectedErr: "TPM device is not enabled"}, - {tpmEnabled: true, missingFile: true, expectedErr: "cannot build new PCR protection profile: cannot build EFI image load sequences: file .*\\/file.efi does not exist"}, - {tpmEnabled: true, addPCRProfileErr: mockErr, expectedErr: "cannot build new PCR protection profile: cannot add EFI secure boot and boot manager policy profiles: some error"}, - {tpmEnabled: true, addSystemdEFIStubErr: mockErr, expectedErr: "cannot build new PCR protection profile: cannot add systemd EFI stub profile: some error"}, - {tpmEnabled: true, addSnapModelErr: mockErr, expectedErr: "cannot build new PCR protection profile: cannot add snap model profile: some error"}, + {tpmEnabled: true, missingFile: true, buildProfileErr: `cannot build EFI image load sequences: file .*\/file.efi does not exist`}, + {tpmEnabled: true, addPCRProfileErr: mockErr, buildProfileErr: `cannot add EFI secure boot and boot manager policy profiles: some error`}, + {tpmEnabled: true, addSystemdEFIStubErr: mockErr, buildProfileErr: `cannot add systemd EFI stub profile: some error`}, + {tpmEnabled: true, addSnapModelErr: mockErr, buildProfileErr: `cannot add snap model profile: some error`}, {tpmEnabled: true, readSealedKeyObjectErr: mockErr, expectedErr: "cannot read key file .*: some error"}, {tpmEnabled: true, resealErr: mockErr, resealCalls: 1, expectedErr: "cannot update legacy PCR protection policy: some error", oldKeyFiles: true}, {tpmEnabled: true, resealErr: mockErr, resealCalls: 1, expectedErr: "cannot update PCR protection policy: some error"}, @@ -1155,17 +1156,66 @@ func (s *secbootSuite) TestResealKey(c *C) { c.Assert(err, IsNil) } + modelParams := []*secboot.SealKeyModelParams{ + { + EFILoadChains: []*secboot.LoadChain{secboot.NewLoadChain(mockEFI)}, + KernelCmdlines: []string{"cmdline"}, + Model: &asserts.Model{}, + }, + } + + sequences := sb_efi.NewImageLoadSequences().Append( + sb_efi.NewImageLoadActivity( + sb_efi.NewFileImage(mockEFI.Path), + ), + ) + + addPCRProfileCalls := 0 + restore := secboot.MockSbEfiAddPCRProfile(func(pcrAlg tpm2.HashAlgorithmId, branch *sb_tpm2.PCRProtectionProfileBranch, loadSequences *sb_efi.ImageLoadSequences, options ...sb_efi.PCRProfileOption) error { + addPCRProfileCalls++ + c.Assert(pcrAlg, Equals, tpm2.HashAlgorithmSHA256) + c.Assert(loadSequences, DeepEquals, sequences) + return tc.addPCRProfileErr + }) + defer restore() + + // mock adding snap model profile + addSnapModelCalls := 0 + restore = secboot.MockSbAddSnapModelProfile(func(profile *sb_tpm2.PCRProtectionProfileBranch, params *sb_tpm2.SnapModelProfileParams) error { + addSnapModelCalls++ + //c.Assert(profile, Equals, pcrProfile) + c.Assert(params.PCRAlgorithm, Equals, tpm2.HashAlgorithmSHA256) + c.Assert(params.PCRIndex, Equals, 12) + c.Assert(params.Models[0], DeepEquals, modelParams[0].Model) + return tc.addSnapModelErr + }) + defer restore() + + // mock adding systemd EFI stub profile + addSystemdEfiStubCalls := 0 + restore = secboot.MockSbEfiAddSystemdStubProfile(func(profile *sb_tpm2.PCRProtectionProfileBranch, params *sb_efi.SystemdStubProfileParams) error { + addSystemdEfiStubCalls++ + //c.Assert(profile, Equals, pcrProfile) + c.Assert(params.PCRAlgorithm, Equals, tpm2.HashAlgorithmSHA256) + c.Assert(params.PCRIndex, Equals, 12) + c.Assert(params.KernelCmdlines, DeepEquals, modelParams[0].KernelCmdlines) + return tc.addSystemdEFIStubErr + }) + defer restore() + + pcrProfile, err := secboot.BuildPCRProtectionProfile(modelParams) + if len(tc.buildProfileErr) > 0 { + c.Assert(err, ErrorMatches, tc.buildProfileErr) + continue + } else { + c.Assert(err, IsNil) + } + tmpdir := c.MkDir() keyFile := filepath.Join(tmpdir, "keyfile") keyFile2 := filepath.Join(tmpdir, "keyfile2") myParams := &secboot.ResealKeysParams{ - ModelParams: []*secboot.SealKeyModelParams{ - { - EFILoadChains: []*secboot.LoadChain{secboot.NewLoadChain(mockEFI)}, - KernelCmdlines: []string{"cmdline"}, - Model: &asserts.Model{}, - }, - }, + PCRProfile: pcrProfile, KeyFiles: []string{keyFile, keyFile2}, TPMPolicyAuthKeyFile: mockTPMPolicyAuthKeyFile, } @@ -1194,12 +1244,6 @@ func (s *secbootSuite) TestResealKey(c *C) { } } - sequences := sb_efi.NewImageLoadSequences().Append( - sb_efi.NewImageLoadActivity( - sb_efi.NewFileImage(mockEFI.Path), - ), - ) - // mock TPM connection tpm, restore := mockSbTPMConnection(c, tc.tpmErr) defer restore() @@ -1210,39 +1254,6 @@ func (s *secbootSuite) TestResealKey(c *C) { }) defer restore() - addPCRProfileCalls := 0 - restore = secboot.MockSbEfiAddPCRProfile(func(pcrAlg tpm2.HashAlgorithmId, branch *sb_tpm2.PCRProtectionProfileBranch, loadSequences *sb_efi.ImageLoadSequences, options ...sb_efi.PCRProfileOption) error { - addPCRProfileCalls++ - c.Assert(pcrAlg, Equals, tpm2.HashAlgorithmSHA256) - c.Assert(loadSequences, DeepEquals, sequences) - return tc.addPCRProfileErr - }) - defer restore() - - // mock adding systemd EFI stub profile - addSystemdEfiStubCalls := 0 - restore = secboot.MockSbEfiAddSystemdStubProfile(func(profile *sb_tpm2.PCRProtectionProfileBranch, params *sb_efi.SystemdStubProfileParams) error { - addSystemdEfiStubCalls++ - //c.Assert(profile, Equals, pcrProfile) - c.Assert(params.PCRAlgorithm, Equals, tpm2.HashAlgorithmSHA256) - c.Assert(params.PCRIndex, Equals, 12) - c.Assert(params.KernelCmdlines, DeepEquals, myParams.ModelParams[0].KernelCmdlines) - return tc.addSystemdEFIStubErr - }) - defer restore() - - // mock adding snap model profile - addSnapModelCalls := 0 - restore = secboot.MockSbAddSnapModelProfile(func(profile *sb_tpm2.PCRProtectionProfileBranch, params *sb_tpm2.SnapModelProfileParams) error { - addSnapModelCalls++ - //c.Assert(profile, Equals, pcrProfile) - c.Assert(params.PCRAlgorithm, Equals, tpm2.HashAlgorithmSHA256) - c.Assert(params.PCRIndex, Equals, 12) - c.Assert(params.Models[0], DeepEquals, myParams.ModelParams[0].Model) - return tc.addSnapModelErr - }) - defer restore() - // mock ReadSealedKeyObject readSealedKeyObjectCalls := 0 restore = secboot.MockReadKeyFile(func(keyfile string) (*sb.KeyData, *sb_tpm2.SealedKeyObject, error) { diff --git a/secboot/secboot_tpm.go b/secboot/secboot_tpm.go index 398a7a8de2f..8438df33430 100644 --- a/secboot/secboot_tpm.go +++ b/secboot/secboot_tpm.go @@ -23,6 +23,7 @@ package secboot import ( "bytes" "crypto/rand" + "encoding/json" "errors" "fmt" "io" @@ -485,10 +486,6 @@ func SealKeys(keys []SealKeyRequest, params *SealKeysParams) ([]byte, error) { // ResealKeys updates the PCR protection policy for the sealed encryption keys // according to the specified parameters. func ResealKeys(params *ResealKeysParams) error { - numModels := len(params.ModelParams) - if numModels < 1 { - return fmt.Errorf("at least one set of model-specific parameters is required") - } numSealedKeyObjects := len(params.KeyFiles) if numSealedKeyObjects < 1 { return fmt.Errorf("at least one key file is required") @@ -503,9 +500,9 @@ func ResealKeys(params *ResealKeysParams) error { return fmt.Errorf("TPM device is not enabled") } - pcrProfile, err := buildPCRProtectionProfile(params.ModelParams) - if err != nil { - return fmt.Errorf("cannot build new PCR protection profile: %w", err) + var pcrProfile sb_tpm2.PCRProtectionProfile + if err := json.Unmarshal(params.PCRProfile, &pcrProfile); err != nil { + return err } authKey, err := os.ReadFile(params.TPMPolicyAuthKeyFile) @@ -537,7 +534,7 @@ func ResealKeys(params *ResealKeysParams) error { } if hasOldObject { - if err := sbUpdateKeyPCRProtectionPolicyMultiple(tpm, sealedKeyObjects, authKey, pcrProfile); err != nil { + if err := sbUpdateKeyPCRProtectionPolicyMultiple(tpm, sealedKeyObjects, authKey, &pcrProfile); err != nil { return fmt.Errorf("cannot update legacy PCR protection policy: %w", err) } @@ -555,7 +552,7 @@ func ResealKeys(params *ResealKeysParams) error { } } else { // TODO: find out which context when revocation should happen - if err := sbUpdateKeyDataPCRProtectionPolicy(tpm, authKey, pcrProfile, sb_tpm2.NoNewPCRPolicyVersion, keyDatas...); err != nil { + if err := sbUpdateKeyDataPCRProtectionPolicy(tpm, authKey, &pcrProfile, sb_tpm2.NoNewPCRPolicyVersion, keyDatas...); err != nil { return fmt.Errorf("cannot update PCR protection policy: %w", err) } @@ -628,6 +625,14 @@ func buildPCRProtectionProfile(modelParams []*SealKeyModelParams) (*sb_tpm2.PCRP return pcrProfile, nil } +func BuildPCRProtectionProfile(modelParams []*SealKeyModelParams) (json.RawMessage, error) { + pcrProfile, err := buildPCRProtectionProfile(modelParams) + if err != nil { + return nil, err + } + return json.Marshal(pcrProfile) +} + func tpmProvision(tpm *sb_tpm2.Connection, mode TPMProvisionMode, lockoutAuthFile string) error { var currentLockoutAuth []byte if mode == TPMPartialReprovision { From 87ca651812adde0bfa8f81d72f81b7fff5d40637 Mon Sep 17 00:00:00 2001 From: Valentin David Date: Tue, 1 Oct 2024 12:08:06 +0200 Subject: [PATCH 2/9] fixup! overlord/fdestate: keep FDE state up to date --- overlord/fdestate/backend/reseal.go | 2 +- overlord/fdestate/backend/reseal_test.go | 21 ++++++++++----------- overlord/managers_test.go | 2 +- secboot/secboot.go | 4 +++- secboot/secboot_tpm.go | 2 +- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/overlord/fdestate/backend/reseal.go b/overlord/fdestate/backend/reseal.go index 1aea97105c2..6e0da6de4a3 100644 --- a/overlord/fdestate/backend/reseal.go +++ b/overlord/fdestate/backend/reseal.go @@ -49,7 +49,7 @@ func MockSecbootResealKeys(f func(params *secboot.ResealKeysParams) error) (rest } } -func MockSecbootBuildPCRProtectionProfile(f func(modelParams []*secboot.SealKeyModelParams) (json.RawMessage, error)) (restore func()) { +func MockSecbootBuildPCRProtectionProfile(f func(modelParams []*secboot.SealKeyModelParams) (secboot.SerializedPCRProfile, error)) (restore func()) { osutil.MustBeTestBinary("secbootBuildPCRProtectionProfile only can be mocked in tests") old := secbootBuildPCRProtectionProfile secbootBuildPCRProtectionProfile = f diff --git a/overlord/fdestate/backend/reseal_test.go b/overlord/fdestate/backend/reseal_test.go index f15983c30cc..75710fedcb1 100644 --- a/overlord/fdestate/backend/reseal_test.go +++ b/overlord/fdestate/backend/reseal_test.go @@ -20,7 +20,6 @@ package backend_test import ( - "encoding/json" "errors" "fmt" "os" @@ -268,7 +267,7 @@ func (s *resealTestSuite) TestTPMResealHappy(c *C) { } buildProfileCalls := 0 - restore := fdeBackend.MockSecbootBuildPCRProtectionProfile(func(modelParams []*secboot.SealKeyModelParams) (json.RawMessage, error) { + restore := fdeBackend.MockSecbootBuildPCRProtectionProfile(func(modelParams []*secboot.SealKeyModelParams) (secboot.SerializedPCRProfile, error) { buildProfileCalls++ c.Assert(modelParams, HasLen, 1) @@ -304,7 +303,7 @@ func (s *resealTestSuite) TestTPMResealHappy(c *C) { c.Check(params.TPMPolicyAuthKeyFile, Equals, filepath.Join(dirs.SnapSaveDir, "device/fde", "tpm-policy-auth-key")) - c.Check(params.PCRProfile, DeepEquals, json.RawMessage(`"serialized-pcr-profile"`)) + c.Check(params.PCRProfile, DeepEquals, secboot.SerializedPCRProfile(`"serialized-pcr-profile"`)) switch resealCalls { case 1: // Resealing the run+recover key for data partition @@ -430,7 +429,7 @@ func (s *resealTestSuite) TestResealKeyForBootchainsWithSystemFallback(c *C) { mockAssetsCache(c, rootdir, "grub", expectedCache) buildProfileCalls := 0 - restore := fdeBackend.MockSecbootBuildPCRProtectionProfile(func(modelParams []*secboot.SealKeyModelParams) (json.RawMessage, error) { + restore := fdeBackend.MockSecbootBuildPCRProtectionProfile(func(modelParams []*secboot.SealKeyModelParams) (secboot.SerializedPCRProfile, error) { buildProfileCalls++ c.Assert(modelParams, HasLen, 1) @@ -579,7 +578,7 @@ func (s *resealTestSuite) TestResealKeyForBootchainsWithSystemFallback(c *C) { c.Check(params.TPMPolicyAuthKeyFile, Equals, filepath.Join(dirs.SnapSaveDir, "device/fde", "tpm-policy-auth-key")) resealKeysCalls++ - c.Check(params.PCRProfile, DeepEquals, json.RawMessage(`"serialized-pcr-profile"`)) + c.Check(params.PCRProfile, DeepEquals, secboot.SerializedPCRProfile(`"serialized-pcr-profile"`)) checkRunParams := func() { c.Check(params.KeyFiles, DeepEquals, []string{ @@ -945,7 +944,7 @@ func (s *resealTestSuite) TestResealKeyForBootchainsRecoveryKeysForGoodSystemsOn }) buildProfileCalls := 0 - restore := fdeBackend.MockSecbootBuildPCRProtectionProfile(func(modelParams []*secboot.SealKeyModelParams) (json.RawMessage, error) { + restore := fdeBackend.MockSecbootBuildPCRProtectionProfile(func(modelParams []*secboot.SealKeyModelParams) (secboot.SerializedPCRProfile, error) { buildProfileCalls++ // shared parameters @@ -1018,7 +1017,7 @@ func (s *resealTestSuite) TestResealKeyForBootchainsRecoveryKeysForGoodSystemsOn c.Check(params.TPMPolicyAuthKeyFile, Equals, filepath.Join(dirs.SnapSaveDir, "device/fde", "tpm-policy-auth-key")) resealKeysCalls++ - c.Check(params.PCRProfile, DeepEquals, json.RawMessage(`"serialized-pcr-profile"`)) + c.Check(params.PCRProfile, DeepEquals, secboot.SerializedPCRProfile(`"serialized-pcr-profile"`)) switch resealKeysCalls { case 1: // run key @@ -1194,7 +1193,7 @@ func (s *resealTestSuite) testResealKeyForBootchainsWithTryModel(c *C, shimId, g }) buildProfileCalls := 0 - restore := fdeBackend.MockSecbootBuildPCRProtectionProfile(func(modelParams []*secboot.SealKeyModelParams) (json.RawMessage, error) { + restore := fdeBackend.MockSecbootBuildPCRProtectionProfile(func(modelParams []*secboot.SealKeyModelParams) (secboot.SerializedPCRProfile, error) { buildProfileCalls++ switch buildProfileCalls { @@ -1297,7 +1296,7 @@ func (s *resealTestSuite) testResealKeyForBootchainsWithTryModel(c *C, shimId, g c.Check(params.TPMPolicyAuthKeyFile, Equals, filepath.Join(dirs.SnapSaveDir, "device/fde", "tpm-policy-auth-key")) resealKeysCalls++ - c.Check(params.PCRProfile, DeepEquals, json.RawMessage(`"serialized-pcr-profile"`)) + c.Check(params.PCRProfile, DeepEquals, secboot.SerializedPCRProfile(`"serialized-pcr-profile"`)) switch resealKeysCalls { case 1: // run key @@ -1491,7 +1490,7 @@ func (s *resealTestSuite) TestResealKeyForBootchainsFallbackCmdline(c *C) { defer bootloader.Force(nil) buildProfileCalls := 0 - restore := fdeBackend.MockSecbootBuildPCRProtectionProfile(func(modelParams []*secboot.SealKeyModelParams) (json.RawMessage, error) { + restore := fdeBackend.MockSecbootBuildPCRProtectionProfile(func(modelParams []*secboot.SealKeyModelParams) (secboot.SerializedPCRProfile, error) { buildProfileCalls++ c.Assert(modelParams, HasLen, 1) @@ -1519,7 +1518,7 @@ func (s *resealTestSuite) TestResealKeyForBootchainsFallbackCmdline(c *C) { restore = fdeBackend.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error { resealKeysCalls++ - c.Check(params.PCRProfile, DeepEquals, json.RawMessage(`"serialized-pcr-profile"`)) + c.Check(params.PCRProfile, DeepEquals, secboot.SerializedPCRProfile(`"serialized-pcr-profile"`)) c.Logf("reseal: %+v", params) switch resealKeysCalls { case 1: diff --git a/overlord/managers_test.go b/overlord/managers_test.go index bb011352621..f5b361d6392 100644 --- a/overlord/managers_test.go +++ b/overlord/managers_test.go @@ -7580,7 +7580,7 @@ func (s *mgrsSuiteCore) testRemodelUC20WithRecoverySystem(c *C, encrypted bool) c.Check(devicestate.RemodelingChange(st), NotNil) - restore = fdeBackend.MockSecbootBuildPCRProtectionProfile(func(modelParams []*secboot.SealKeyModelParams) (json.RawMessage, error) { + restore = fdeBackend.MockSecbootBuildPCRProtectionProfile(func(modelParams []*secboot.SealKeyModelParams) (secboot.SerializedPCRProfile, error) { return []byte(`"some-profile"`), nil }) defer restore() diff --git a/secboot/secboot.go b/secboot/secboot.go index 6e2747ea394..9a45171e226 100644 --- a/secboot/secboot.go +++ b/secboot/secboot.go @@ -134,9 +134,11 @@ type SealKeysWithFDESetupHookParams struct { AuxKeyFile string } +type SerializedPCRProfile json.RawMessage + type ResealKeysParams struct { // The snap model parameters - PCRProfile json.RawMessage + PCRProfile SerializedPCRProfile // The path to the sealed key files KeyFiles []string // The path to the authorization policy update key file (only relevant for TPM) diff --git a/secboot/secboot_tpm.go b/secboot/secboot_tpm.go index 8438df33430..c8632c6a057 100644 --- a/secboot/secboot_tpm.go +++ b/secboot/secboot_tpm.go @@ -625,7 +625,7 @@ func buildPCRProtectionProfile(modelParams []*SealKeyModelParams) (*sb_tpm2.PCRP return pcrProfile, nil } -func BuildPCRProtectionProfile(modelParams []*SealKeyModelParams) (json.RawMessage, error) { +func BuildPCRProtectionProfile(modelParams []*SealKeyModelParams) (SerializedPCRProfile, error) { pcrProfile, err := buildPCRProtectionProfile(modelParams) if err != nil { return nil, err From c7de6a788a15e2ba292acb51fc78a752c36a2e56 Mon Sep 17 00:00:00 2001 From: Valentin David Date: Tue, 1 Oct 2024 12:43:16 +0200 Subject: [PATCH 3/9] fixup! overlord/fdestate: keep FDE state up to date --- overlord/fdestate/fdemgr.go | 6 +++- overlord/fdestate/fdemgr_test.go | 4 +-- overlord/fdestate/fdestate.go | 2 +- overlord/managers_test.go | 55 +++++++++++++++++--------------- 4 files changed, 38 insertions(+), 29 deletions(-) diff --git a/overlord/fdestate/fdemgr.go b/overlord/fdestate/fdemgr.go index fd8d3e69986..aeefc4cb226 100644 --- a/overlord/fdestate/fdemgr.go +++ b/overlord/fdestate/fdemgr.go @@ -51,9 +51,13 @@ func Manager(st *state.State, runner *state.TaskRunner) *FDEManager { } func (m *FDEManager) Ensure() error { + return nil +} + +func (m *FDEManager) StartUp() error { m.state.Lock() defer m.state.Unlock() - return ensureState(m.state) + return initializeState(m.state) } func (m *FDEManager) resealKeyForBootChains(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { diff --git a/overlord/fdestate/fdemgr_test.go b/overlord/fdestate/fdemgr_test.go index 9e6886a15aa..4649248f14d 100644 --- a/overlord/fdestate/fdemgr_test.go +++ b/overlord/fdestate/fdemgr_test.go @@ -69,7 +69,7 @@ func (s *fdeMgrSuite) TestGetManagerFromState(c *C) { runner := state.NewTaskRunner(st) manager := fdestate.Manager(st, runner) - c.Assert(manager.Ensure(), IsNil) + c.Assert(manager.StartUp(), IsNil) st.Lock() defer st.Unlock() foundManager := fdestate.FdeMgr(st) @@ -141,7 +141,7 @@ func (s *fdeMgrSuite) TestUpdateState(c *C) { runner := state.NewTaskRunner(st) manager := fdestate.Manager(st, runner) - c.Assert(manager.Ensure(), IsNil) + c.Assert(manager.StartUp(), IsNil) st.Lock() defer st.Unlock() foundManager := fdestate.FdeMgr(st) diff --git a/overlord/fdestate/fdestate.go b/overlord/fdestate/fdestate.go index 2ab7540c235..cc041b88daa 100644 --- a/overlord/fdestate/fdestate.go +++ b/overlord/fdestate/fdestate.go @@ -173,7 +173,7 @@ type FdeState struct { const fdeStateKey = "fde" -func ensureState(st *state.State) error { +func initializeState(st *state.State) error { var s FdeState err := st.Get(fdeStateKey, &s) if err == nil { diff --git a/overlord/managers_test.go b/overlord/managers_test.go index f5b361d6392..f98f89f4f11 100644 --- a/overlord/managers_test.go +++ b/overlord/managers_test.go @@ -7429,6 +7429,35 @@ func (s *mgrsSuiteCore) testRemodelUC20WithRecoverySystem(c *C, encrypted bool) mockServer := s.mockStore(c) defer mockServer.Close() + restore = fdeBackend.MockSecbootBuildPCRProtectionProfile(func(modelParams []*secboot.SealKeyModelParams) (secboot.SerializedPCRProfile, error) { + return []byte(`"some-profile"`), nil + }) + defer restore() + + restore = fdestate.MockDmCryptUUIDFromMountPoint(func(mountpoint string) (string, error) { + if mountpoint == dirs.GlobalRootDir { + return "root-uuid", nil + } else { + return "save-uuid", nil + } + }) + defer restore() + + restore = fdestate.MockGetPrimaryKeyHash(func(devicePath string, alg crypto.Hash) ([]byte, []byte, error) { + return []byte("aaaa"), []byte("bbbb"), nil + }) + defer restore() + + restore = fdestate.MockVerifyPrimaryKeyHash(func(devicePath string, alg crypto.Hash, salt []byte, digest []byte) (bool, error) { + return true, nil + }) + defer restore() + + fdemgr := s.o.FDEManager() + c.Assert(fdemgr, NotNil) + err := fdemgr.StartUp() + c.Assert(err, IsNil) + st := s.o.State() st.Lock() defer st.Unlock() @@ -7445,7 +7474,7 @@ func (s *mgrsSuiteCore) testRemodelUC20WithRecoverySystem(c *C, encrypted bool) s.serveSnap(snapPath, "22") } - err := assertstate.Add(st, s.devAcct) + err = assertstate.Add(st, s.devAcct) c.Assert(err, IsNil) // snaps in state pcInfo := s.makeInstalledSnapInStateForRemodel(c, "pc", snap.R(1), "20/stable") @@ -7580,30 +7609,6 @@ func (s *mgrsSuiteCore) testRemodelUC20WithRecoverySystem(c *C, encrypted bool) c.Check(devicestate.RemodelingChange(st), NotNil) - restore = fdeBackend.MockSecbootBuildPCRProtectionProfile(func(modelParams []*secboot.SealKeyModelParams) (secboot.SerializedPCRProfile, error) { - return []byte(`"some-profile"`), nil - }) - defer restore() - - restore = fdestate.MockDmCryptUUIDFromMountPoint(func(mountpoint string) (string, error) { - if mountpoint == dirs.GlobalRootDir { - return "root-uuid", nil - } else { - return "save-uuid", nil - } - }) - defer restore() - - restore = fdestate.MockGetPrimaryKeyHash(func(devicePath string, alg crypto.Hash) ([]byte, []byte, error) { - return []byte("aaaa"), []byte("bbbb"), nil - }) - defer restore() - - restore = fdestate.MockVerifyPrimaryKeyHash(func(devicePath string, alg crypto.Hash, salt []byte, digest []byte) (bool, error) { - return true, nil - }) - defer restore() - st.Unlock() err = s.o.Settle(settleTimeout) st.Lock() From 68e99e8539c9541857b5001231da5033ac4b6177 Mon Sep 17 00:00:00 2001 From: Valentin David Date: Tue, 1 Oct 2024 13:34:47 +0200 Subject: [PATCH 4/9] fixup! overlord/fdestate: keep FDE state up to date --- secboot/secboot_dummy.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/secboot/secboot_dummy.go b/secboot/secboot_dummy.go index 2cb0593d433..c69881b0a93 100644 --- a/secboot/secboot_dummy.go +++ b/secboot/secboot_dummy.go @@ -22,7 +22,6 @@ package secboot import ( "crypto" - "encoding/json" "errors" "github.com/snapcore/snapd/kernel/fde" @@ -88,7 +87,7 @@ func DeleteKeys(node string, matches map[string]bool) error { return errBuildWithoutSecboot } -func BuildPCRProtectionProfile(modelParams []*SealKeyModelParams) (json.RawMessage, error) { +func BuildPCRProtectionProfile(modelParams []*SealKeyModelParams) (SerializedPCRProfile, error) { return nil, errBuildWithoutSecboot } From b178e11d82afd5de3531132eaffc82087abfd36b Mon Sep 17 00:00:00 2001 From: Valentin David Date: Tue, 1 Oct 2024 13:35:31 +0200 Subject: [PATCH 5/9] fixup! overlord/fdestate: keep FDE state up to date --- boot/assets_test.go | 16 ++++++------- boot/boot_test.go | 48 ++++++++++++++++++------------------- boot/export_test.go | 2 +- boot/model_test.go | 24 +++++++++---------- boot/seal.go | 14 ++++------- boot/seal_test.go | 12 +++++----- boot/systems_test.go | 26 ++++++++++---------- overlord/fdestate/fdemgr.go | 8 +++++-- 8 files changed, 74 insertions(+), 76 deletions(-) diff --git a/boot/assets_test.go b/boot/assets_test.go index fe42c46ab8f..f1a96562afb 100644 --- a/boot/assets_test.go +++ b/boot/assets_test.go @@ -53,7 +53,7 @@ var _ = Suite(&assetsSuite{}) func (s *assetsSuite) SetUpTest(c *C) { s.baseBootenvSuite.SetUpTest(c) - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { return nil }) s.AddCleanup(restore) @@ -788,7 +788,7 @@ func (s *assetsSuite) testUpdateObserverUpdateMockedWithReseal(c *C, seedRole st // everything is set up, trigger a reseal resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return nil }) @@ -893,7 +893,7 @@ func (s *assetsSuite) TestUpdateObserverUpdateExistingAssetMocked(c *C) { // everything is set up, trigger reseal resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return nil }) @@ -1649,7 +1649,7 @@ func (s *assetsSuite) TestUpdateObserverCanceledSimpleAfterBackupMocked(c *C) { "shim": []string{shimHash}, }) resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return nil }) @@ -1809,7 +1809,7 @@ func (s *assetsSuite) TestUpdateObserverCanceledNoActionsMocked(c *C) { obs, _ := s.uc20UpdateObserverEncryptedSystemMockedBootloader(c) resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return nil }) @@ -2561,7 +2561,7 @@ func (s *assetsSuite) TestUpdateObserverReseal(c *C) { // everything is set up, trigger a reseal resealCalls := 0 - restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ c.Assert(params.RunModeBootChains, HasLen, 1) @@ -2713,7 +2713,7 @@ func (s *assetsSuite) TestUpdateObserverCanceledReseal(c *C) { resealCalls := 0 - restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ c.Assert(params.RunModeBootChains, HasLen, 1) @@ -2846,7 +2846,7 @@ func (s *assetsSuite) TestUpdateObserverUpdateMockedNonEncryption(c *C) { // make sure that no reseal is triggered resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return nil }) diff --git a/boot/boot_test.go b/boot/boot_test.go index cf64c23c9c5..c57585af6e8 100644 --- a/boot/boot_test.go +++ b/boot/boot_test.go @@ -137,7 +137,7 @@ type baseBootenv20Suite struct { func (s *baseBootenv20Suite) SetUpTest(c *C) { s.baseBootenvSuite.SetUpTest(c) - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { return nil }) s.AddCleanup(restore) @@ -1121,7 +1121,7 @@ func (s *bootenv20Suite) TestCoreParticipant20SetNextNewKernelSnapWithReseal(c * defer r() resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ c.Assert(params.RunModeBootChains, HasLen, 2) @@ -1241,7 +1241,7 @@ func (s *bootenv20Suite) TestCoreParticipant20SetNextNewUnassertedKernelSnapWith defer r() resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ c.Assert(params.RunModeBootChains, HasLen, 2) @@ -1362,7 +1362,7 @@ func (s *bootenv20Suite) TestCoreParticipant20SetNextSameKernelSnapNoReseal(c *C defer r() resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return nil }) @@ -1459,7 +1459,7 @@ func (s *bootenv20Suite) TestCoreParticipant20SetNextSameUnassertedKernelSnapNoR defer r() resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return nil }) @@ -2058,7 +2058,7 @@ func (s *bootenv20Suite) TestMarkBootSuccessful20KernelUpdateWithReseal(c *C) { defer r() resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ c.Assert(params.RunModeBootChains, HasLen, 1) @@ -2292,7 +2292,7 @@ func (s *bootenv20Suite) TestMarkBootSuccessful20BootAssetsUpdateHappy(c *C) { c.Assert(coreDev.HasModeenv(), Equals, true) resealCalls := 0 - restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ c.Assert(params.RunModeBootChains, HasLen, 1) @@ -2453,7 +2453,7 @@ func (s *bootenv20Suite) TestMarkBootSuccessful20BootAssetsStableStateHappy(c *C c.Assert(coreDev.HasModeenv(), Equals, true) resealCalls := 0 - restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return nil }) @@ -2567,7 +2567,7 @@ func (s *bootenv20Suite) TestMarkBootSuccessful20BootUnassertedKernelAssetsStabl c.Assert(coreDev.HasModeenv(), Equals, true) resealCalls := 0 - restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return nil }) @@ -3156,7 +3156,7 @@ var _ = Suite(&bootConfigSuite{}) func (s *bootConfigSuite) SetUpTest(c *C) { s.baseBootenvSuite.SetUpTest(c) - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { return nil }) s.AddCleanup(restore) @@ -3193,7 +3193,7 @@ func (s *bootConfigSuite) TestBootConfigUpdateHappyNoKeysNoReseal(c *C) { c.Assert(m.WriteTo(""), IsNil) resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return nil }) @@ -3245,7 +3245,7 @@ func (s *bootConfigSuite) testBootConfigUpdateHappyWithReseal(c *C, cmdlineAppen newCmdline := strutil.JoinNonEmpty([]string{ "snapd_recovery_mode=run mocked candidate panic=-1", cmdlineAppend}, " ") resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ c.Assert(params, NotNil) @@ -3305,7 +3305,7 @@ func (s *bootConfigSuite) testBootConfigUpdateHappyNoChange(c *C, cmdlineAppend c.Assert(m.WriteTo(""), IsNil) resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return nil }) @@ -3470,7 +3470,7 @@ volumes: c.Assert(m.WriteTo(""), IsNil) resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ c.Assert(params, NotNil) @@ -3536,7 +3536,7 @@ volumes: // reseal does not happen, because the gadget overrides the static // command line which is part of boot config, thus there's no resulting // change in the command lines tracked in modeenv and no need to reseal - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return fmt.Errorf("unexpected call") }) @@ -3573,7 +3573,7 @@ var _ = Suite(&bootKernelCommandLineSuite{}) func (s *bootKernelCommandLineSuite) SetUpTest(c *C) { s.baseBootenvSuite.SetUpTest(c) - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { return nil }) s.AddCleanup(restore) @@ -3633,7 +3633,7 @@ func (s *bootKernelCommandLineSuite) SetUpTest(c *C) { s.resealCommandLines = nil s.resealCalls = 0 - restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { s.resealCalls++ c.Assert(params, NotNil) c.Assert(params.RunModeBootChains, HasLen, 0) @@ -3909,7 +3909,7 @@ volumes: c.Assert(s.modeenvWithEncryption.WriteTo(""), IsNil) resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return fmt.Errorf("reseal fails") }) @@ -4053,7 +4053,7 @@ func (s *bootKernelCommandLineSuite) TestCommandLineUpdateUC20OverSpuriousReboot s.stampSealedKeys(c, dirs.GlobalRootDir) resealPanic := false - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { s.resealCalls++ c.Logf("reseal call %v", s.resealCalls) c.Assert(params, NotNil) @@ -4636,7 +4636,7 @@ func (s *bootenv20Suite) TestCoreParticipant20UndoKernelSnapInstallNewWithReseal defer r() resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ c.Assert(params.RunModeBootChains, HasLen, 1) @@ -4747,7 +4747,7 @@ func (s *bootenv20Suite) TestCoreParticipant20UndoUnassertedKernelSnapInstallNew defer r() resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ c.Assert(params.RunModeBootChains, HasLen, 1) @@ -4859,7 +4859,7 @@ func (s *bootenv20Suite) TestCoreParticipant20UndoKernelSnapInstallSameNoReseal( defer r() resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return nil }) @@ -4956,7 +4956,7 @@ func (s *bootenv20Suite) TestCoreParticipant20UndoUnassertedKernelSnapInstallSam defer r() resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return nil }) @@ -5092,7 +5092,7 @@ func (s *bootenv20Suite) TestCoreParticipant20UndoBaseSnapInstallNewNoReseal(c * model := coreDev.Model() resealCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ return nil }) diff --git a/boot/export_test.go b/boot/export_test.go index dd18c8ed92f..51e500e5515 100644 --- a/boot/export_test.go +++ b/boot/export_test.go @@ -226,7 +226,7 @@ func EnableTestingRebootFunction() (restore func()) { return func() { testingRebootItself = false } } -func MockResealKeyForBootChains(f func(locked bool, method device.SealingMethod, rootdir string, params *ResealKeyForBootChainsParams, expectReseal bool) error) (restore func()) { +func MockResealKeyForBootChains(f func(unlocker Unlocker, method device.SealingMethod, rootdir string, params *ResealKeyForBootChainsParams, expectReseal bool) error) (restore func()) { old := ResealKeyForBootChains ResealKeyForBootChains = f return func() { diff --git a/boot/model_test.go b/boot/model_test.go index 398d5638c3f..d78c1e8e9b8 100644 --- a/boot/model_test.go +++ b/boot/model_test.go @@ -89,7 +89,7 @@ func makeEncodableModel(signingAccounts *assertstest.SigningAccounts, overrides func (s *modelSuite) SetUpTest(c *C) { s.baseBootenvSuite.SetUpTest(c) - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { return nil }) s.AddCleanup(restore) @@ -204,7 +204,7 @@ func (s *modelSuite) TestDeviceChangeHappy(c *C) { "model: my-model-uc20\n") resealKeysCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealKeysCalls++ m, err := boot.ReadModeenv("") c.Assert(err, IsNil) @@ -242,7 +242,7 @@ func (s *modelSuite) TestDeviceChangeHappy(c *C) { err = boot.DeviceChange(s.oldUc20dev, s.newUc20dev, u.unlocker) c.Assert(err, IsNil) c.Assert(resealKeysCalls, Equals, 2) - c.Check(u.unlocked, Equals, 2) + c.Check(u.unlocked, Equals, 0) c.Check(filepath.Join(boot.InitramfsUbuntuBootDir, "device/model"), testutil.FileContains, "model: my-new-model-uc20\n") @@ -267,7 +267,7 @@ func (s *modelSuite) TestDeviceChangeUnhappyFirstReseal(c *C) { "model: my-model-uc20\n") resealKeysCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealKeysCalls++ m, err := boot.ReadModeenv("") c.Assert(err, IsNil) @@ -318,7 +318,7 @@ func (s *modelSuite) TestDeviceChangeUnhappyFirstSwapModelFile(c *C) { "model: my-model-uc20\n") resealKeysCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealKeysCalls++ m, err := boot.ReadModeenv("") c.Assert(err, IsNil) @@ -371,7 +371,7 @@ func (s *modelSuite) TestDeviceChangeUnhappySecondReseal(c *C) { "model: my-model-uc20\n") resealKeysCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealKeysCalls++ m, err := boot.ReadModeenv("") c.Assert(err, IsNil) @@ -467,7 +467,7 @@ func (s *modelSuite) TestDeviceChangeRebootBeforeNewModel(c *C) { "model: my-model-uc20\n") resealKeysCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealKeysCalls++ c.Logf("reseal key call: %v", resealKeysCalls) m, err := boot.ReadModeenv("") @@ -586,7 +586,7 @@ func (s *modelSuite) TestDeviceChangeRebootAfterNewModelFileWrite(c *C) { "model: my-model-uc20\n") resealKeysCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealKeysCalls++ // timeline & calls: // 1 - pre reboot, run & recovery keys, try model set @@ -706,7 +706,7 @@ func (s *modelSuite) TestDeviceChangeRebootPostSameModel(c *C) { "model: my-model-uc20\n") resealKeysCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealKeysCalls++ c.Logf("reseal key call: %v", resealKeysCalls) m, err := boot.ReadModeenv("") @@ -849,7 +849,7 @@ func (s *modelSuite) testDeviceChangeUnhappyMockedWriteModelToBoot(c *C, tc unha writeModelToBootCalls := 0 resealKeysCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealKeysCalls++ m, err := boot.ReadModeenv("") c.Assert(err, IsNil) @@ -972,7 +972,7 @@ func (s *modelSuite) TestDeviceChangeUnhappyFailReseaWithSwappedModelMockedWrite writeModelToBootCalls := 0 resealKeysCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealKeysCalls++ if resealKeysCalls == 2 { m, err := boot.ReadModeenv("") @@ -1059,7 +1059,7 @@ func (s *modelSuite) TestDeviceChangeRebootRestoreModelKeyChangeMockedWriteModel })) resealKeysCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealKeysCalls++ c.Logf("reseal key call: %v", resealKeysCalls) m, err := boot.ReadModeenv("") diff --git a/boot/seal.go b/boot/seal.go index 2e253374929..c3b68b4b69a 100644 --- a/boot/seal.go +++ b/boot/seal.go @@ -251,13 +251,7 @@ func resealKeyToModeenvImpl(rootdir string, modeenv *Modeenv, expectReseal bool, return err } - locked := true - if unlocker != nil { - // unlock/relock global state - defer unlocker()() - locked = false - } - return resealKeyToModeenvForMethod(locked, method, rootdir, modeenv, expectReseal) + return resealKeyToModeenvForMethod(unlocker, method, rootdir, modeenv, expectReseal) } type ResealKeyForBootChainsParams struct { @@ -272,7 +266,7 @@ type ResealKeyForBootChainsParams struct { RoleToBlName map[bootloader.Role]string } -func resealKeyToModeenvForMethod(locked bool, method device.SealingMethod, rootdir string, modeenv *Modeenv, expectReseal bool) error { +func resealKeyToModeenvForMethod(unlocker Unlocker, method device.SealingMethod, rootdir string, modeenv *Modeenv, expectReseal bool) error { // this is just optimization. If the backend does not need it, we should not calculate it. requiresBootChains := true switch method { @@ -350,10 +344,10 @@ func resealKeyToModeenvForMethod(locked bool, method device.SealingMethod, rootd } } - return ResealKeyForBootChains(locked, method, rootdir, params, expectReseal) + return ResealKeyForBootChains(unlocker, method, rootdir, params, expectReseal) } -func resealKeyForBootChainsImpl(locked bool, method device.SealingMethod, rootdir string, params *ResealKeyForBootChainsParams, expectReseal bool) error { +func resealKeyForBootChainsImpl(unlocker Unlocker, method device.SealingMethod, rootdir string, params *ResealKeyForBootChainsParams, expectReseal bool) error { return fmt.Errorf("FDE manager was not started") } diff --git a/boot/seal_test.go b/boot/seal_test.go index 67fe0e84f79..3e6761a8d6e 100644 --- a/boot/seal_test.go +++ b/boot/seal_test.go @@ -560,7 +560,7 @@ func (s *sealSuite) TestResealKeyToModeenvWithSystemFallback(c *C) { // set mock key resealing resealKeysCalls := 0 - restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdirArg string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdirArg string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealKeysCalls++ c.Check(method, Equals, device.SealingMethodTPM) @@ -654,7 +654,7 @@ func (s *sealSuite) TestResealKeyToModeenvRecoveryKeysForGoodSystemsOnly(c *C) { // set mock key resealing resealKeysCalls := 0 - restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdirArg string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdirArg string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealKeysCalls++ c.Check(method, Equals, device.SealingMethodTPM) @@ -869,7 +869,7 @@ func (s *sealSuite) TestResealKeyToModeenvFallbackCmdline(c *C) { // set mock key resealing resealKeysCalls := 0 - restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdirArg string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdirArg string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { c.Check(rootdirArg, Equals, rootdir) c.Check(method, Equals, device.SealingMethodTPM) c.Check(expectReseal, Equals, false) @@ -1713,7 +1713,7 @@ func (s *sealSuite) TestResealKeyToModeenvWithFdeHookCalled(c *C) { defer dirs.SetRootDir("") mockResealKeyForBootChainsCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdirArg string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdirArg string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { c.Check(rootdirArg, Equals, rootdir) c.Check(method, Equals, device.SealingMethodFDESetupHook) c.Check(expectReseal, Equals, false) @@ -1759,7 +1759,7 @@ func (s *sealSuite) TestResealKeyToModeenvWithFdeHookVerySad(c *C) { defer dirs.SetRootDir("") mockResealKeyForBootChainsCalls := 0 - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdirArg string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdirArg string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { c.Check(rootdirArg, Equals, rootdir) c.Check(method, Equals, device.SealingMethodFDESetupHook) c.Check(expectReseal, Equals, false) @@ -1871,7 +1871,7 @@ func (s *sealSuite) testResealKeyToModeenvWithTryModel(c *C, shimId, grubId stri // set mock key resealing resealKeysCalls := 0 - restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdirArg string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdirArg string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { c.Check(rootdirArg, Equals, rootdir) c.Check(method, Equals, device.SealingMethodTPM) c.Check(expectReseal, Equals, false) diff --git a/boot/systems_test.go b/boot/systems_test.go index b10d53cdc07..53fc8ad5b93 100644 --- a/boot/systems_test.go +++ b/boot/systems_test.go @@ -83,7 +83,7 @@ func (s *systemsSuite) mockTrustedBootloaderWithAssetAndChains(c *C, runKernelBf func (s *systemsSuite) SetUpTest(c *C) { s.baseBootenvSuite.SetUpTest(c) - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { return nil }) s.AddCleanup(restore) @@ -145,7 +145,7 @@ func (s *systemsSuite) TestSetTryRecoverySystemEncrypted(c *C) { defer restore() resealCalls := 0 - restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ // bootloader variables have already been modified c.Check(mtbl.SetBootVarsCalls, Equals, 1) @@ -270,7 +270,7 @@ func (s *systemsSuite) TestSetTryRecoverySystemRemodelEncrypted(c *C) { defer restore() resealCalls := 0 - restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ // bootloader variables have already been modified c.Check(mtbl.SetBootVarsCalls, Equals, 1) @@ -375,7 +375,7 @@ func (s *systemsSuite) TestSetTryRecoverySystemSimple(c *C) { } c.Assert(modeenv.WriteTo(""), IsNil) - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { return fmt.Errorf("unexpected call") }) s.AddCleanup(restore) @@ -416,7 +416,7 @@ func (s *systemsSuite) TestSetTryRecoverySystemSetBootVarsErr(c *C) { } c.Assert(modeenv.WriteTo(""), IsNil) - restore := boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore := boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { return fmt.Errorf("unexpected call") }) s.AddCleanup(restore) @@ -527,7 +527,7 @@ func (s *systemsSuite) TestSetTryRecoverySystemCleanupOnErrorBeforeReseal(c *C) defer restore() resealCalls := 0 - restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ if cleanupTriggered { return nil @@ -636,7 +636,7 @@ func (s *systemsSuite) TestSetTryRecoverySystemCleanupOnErrorAfterReseal(c *C) { defer restore() resealCalls := 0 - restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ switch resealCalls { case 1: @@ -737,7 +737,7 @@ func (s *systemsSuite) TestSetTryRecoverySystemCleanupError(c *C) { defer restore() resealCalls := 0 - restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ switch resealCalls { case 1: @@ -784,7 +784,7 @@ func (s *systemsSuite) testInspectRecoverySystemOutcomeHappy(c *C, mtbl *bootloa }) defer restore() - restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { return fmt.Errorf("unexpected call") }) defer restore() @@ -980,7 +980,7 @@ func (s *systemsSuite) testClearRecoverySystem(c *C, mtbl *bootloadertest.MockTr defer restore() resealCalls := 0 - restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ switch resealCalls { case 1: @@ -1235,7 +1235,7 @@ func (s *systemsSuite) TestClearRecoverySystemReboot(c *C) { defer restore() resealCalls := 0 - restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ switch resealCalls { case 1: @@ -1365,7 +1365,7 @@ func (s *systemsSuite) testPromoteTriedRecoverySystem(c *C, systemLabel string, defer restore() resealCalls := 0 - restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ switch resealCalls { case 1: @@ -1629,7 +1629,7 @@ func (s *systemsSuite) testDropRecoverySystem(c *C, systemLabel string, tc recov defer restore() resealCalls := 0 - restore = boot.MockResealKeyForBootChains(func(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { + restore = boot.MockResealKeyForBootChains(func(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { resealCalls++ switch resealCalls { case 1: diff --git a/overlord/fdestate/fdemgr.go b/overlord/fdestate/fdemgr.go index aeefc4cb226..deab52ecbe2 100644 --- a/overlord/fdestate/fdemgr.go +++ b/overlord/fdestate/fdemgr.go @@ -60,14 +60,18 @@ func (m *FDEManager) StartUp() error { return initializeState(m.state) } -func (m *FDEManager) resealKeyForBootChains(locked bool, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { +func (m *FDEManager) resealKeyForBootChains(unlocker boot.Unlocker, method device.SealingMethod, rootdir string, params *boot.ResealKeyForBootChainsParams, expectReseal bool) error { doUpdate := func(role string, containerRole string, bootModes []string, models []secboot.ModelForSealing, tpmPCRProfile []byte) error { - if !locked { + if unlocker != nil { m.state.Lock() defer m.state.Unlock() } return updateParameters(m.state, role, containerRole, bootModes, models, tpmPCRProfile) } + if unlocker != nil { + locker := unlocker() + defer locker() + } return backend.ResealKeyForBootChains(doUpdate, method, rootdir, params, expectReseal) } From c48cd0c15898de03d418640e720204377bfd17fb Mon Sep 17 00:00:00 2001 From: Valentin David Date: Wed, 2 Oct 2024 12:56:58 +0200 Subject: [PATCH 6/9] fixup! overlord/fdestate: keep FDE state up to date --- osutil/disks/disks.go | 5 ++++ osutil/disks/disks_linux_test.go | 6 +++- overlord/fdestate/fdemgr.go | 2 ++ overlord/fdestate/fdemgr_test.go | 9 +++--- overlord/fdestate/fdestate.go | 47 ++++++++++++++++++++++---------- overlord/managers_test.go | 2 +- 6 files changed, 49 insertions(+), 22 deletions(-) diff --git a/osutil/disks/disks.go b/osutil/disks/disks.go index 6f5ad9cb34b..4e4cdfa94d5 100644 --- a/osutil/disks/disks.go +++ b/osutil/disks/disks.go @@ -239,9 +239,14 @@ func unregisterDeviceMapperBackResolver(name string) { delete(deviceMapperBackResolvers, name) } +// ErrNoDmUUID is return by DMCryptUUIDFromMountPoint when the device +// at the mount point is not a device mapper device var ErrNoDmUUID = errors.New("device has no DM_UUID") +// ErrMountPointNotFound is return by DMCryptUUIDFromMountPoint a path +// is not a mount point type ErrMountPointNotFound struct { + // Path is the path for which a mount point was assumed Path string } diff --git a/osutil/disks/disks_linux_test.go b/osutil/disks/disks_linux_test.go index 11890b947ec..f3800985339 100644 --- a/osutil/disks/disks_linux_test.go +++ b/osutil/disks/disks_linux_test.go @@ -1854,7 +1854,10 @@ func (s *diskSuite) TestDMCryptUUIDFromMountPointErrs(c *C) { defer restore() _, err := disks.DMCryptUUIDFromMountPoint("/run/mnt/blah") - c.Assert(err, ErrorMatches, "cannot find mountpoint \"/run/mnt/blah\"") + c.Assert(err, ErrorMatches, `cannot find mountpoint "/run/mnt/blah"`) + var mountPointErr disks.ErrMountPointNotFound + c.Assert(errors.As(err, &mountPointErr), Equals, true) + c.Check(mountPointErr.Path, Equals, "/run/mnt/blah") restore = osutil.MockMountInfo(`130 30 42:1 / /run/mnt/point rw,relatime shared:54 - ext4 /dev/vda4 rw `) @@ -1872,6 +1875,7 @@ func (s *diskSuite) TestDMCryptUUIDFromMountPointErrs(c *C) { _, err = disks.DMCryptUUIDFromMountPoint("/run/mnt/point") c.Assert(err, ErrorMatches, "device has no DM_UUID") + c.Assert(err, Equals, disks.ErrNoDmUUID) restore = disks.MockUdevPropertiesForDevice(func(typeOpt, dev string) (map[string]string, error) { c.Assert(typeOpt, Equals, "--name") diff --git a/overlord/fdestate/fdemgr.go b/overlord/fdestate/fdemgr.go index deab52ecbe2..ec477f6d4f4 100644 --- a/overlord/fdestate/fdemgr.go +++ b/overlord/fdestate/fdemgr.go @@ -50,10 +50,12 @@ func Manager(st *state.State, runner *state.TaskRunner) *FDEManager { return m } +// Ensure implements StateManager.Ensure func (m *FDEManager) Ensure() error { return nil } +// StartUp implements StateStarterUp.Startup func (m *FDEManager) StartUp() error { m.state.Lock() defer m.state.Unlock() diff --git a/overlord/fdestate/fdemgr_test.go b/overlord/fdestate/fdemgr_test.go index 4649248f14d..a103906ef10 100644 --- a/overlord/fdestate/fdemgr_test.go +++ b/overlord/fdestate/fdemgr_test.go @@ -21,7 +21,6 @@ package fdestate_test import ( "crypto" - "encoding/json" "fmt" "testing" @@ -43,7 +42,7 @@ var _ = Suite(&fdeMgrSuite{}) func (s *fdeMgrSuite) TestGetManagerFromState(c *C) { st := state.New(nil) - defer fdestate.MockDmCryptUUIDFromMountPoint(func(mountpoint string) (string, error) { + defer fdestate.MockDMCryptUUIDFromMountPoint(func(mountpoint string) (string, error) { switch mountpoint { case dirs.GlobalRootDir: return "aaa", nil @@ -115,7 +114,7 @@ func (m *mockModel) SignKeyID() string { func (s *fdeMgrSuite) TestUpdateState(c *C) { st := state.New(nil) - defer fdestate.MockDmCryptUUIDFromMountPoint(func(mountpoint string) (string, error) { + defer fdestate.MockDMCryptUUIDFromMountPoint(func(mountpoint string) (string, error) { switch mountpoint { case dirs.GlobalRootDir: return "aaa", nil @@ -151,7 +150,7 @@ func (s *fdeMgrSuite) TestUpdateState(c *C) { &mockModel{}, } - fdestate.UpdateParameters(st, "run+recover", "container-role", []string{"run"}, models, json.RawMessage(`"serialized-profile"`)) + fdestate.UpdateParameters(st, "run+recover", "container-role", []string{"run"}, models, secboot.SerializedPCRProfile(`"serialized-profile"`)) var fdeSt fdestate.FdeState err := st.Get("fde", &fdeSt) @@ -164,5 +163,5 @@ func (s *fdeMgrSuite) TestUpdateState(c *C) { c.Assert(containerRole.Models, HasLen, 1) c.Check(containerRole.Models[0].Model(), Equals, "mock-model") c.Check(containerRole.BootModes, DeepEquals, []string{"run"}) - c.Check(containerRole.Tpm2PCRProfile, DeepEquals, json.RawMessage(`"serialized-profile"`)) + c.Check(containerRole.Tpm2PCRProfile, DeepEquals, secboot.SerializedPCRProfile(`"serialized-profile"`)) } diff --git a/overlord/fdestate/fdestate.go b/overlord/fdestate/fdestate.go index cc041b88daa..02c8a3ec245 100644 --- a/overlord/fdestate/fdestate.go +++ b/overlord/fdestate/fdestate.go @@ -34,11 +34,12 @@ import ( ) var ( - disksDmCryptUUIDFromMountPoint = disks.DmCryptUUIDFromMountPoint + disksDMCryptUUIDFromMountPoint = disks.DMCryptUUIDFromMountPoint secbootGetPrimaryKeyHash = secboot.GetPrimaryKeyHash secbootVerifyPrimaryKeyHash = secboot.VerifyPrimaryKeyHash ) +// Model is a json serializable secboot.ModelForSealing type Model struct { SeriesValue string `json:"series"` BrandIDValue string `json:"brand-id"` @@ -48,26 +49,32 @@ type Model struct { SignKeyIDValue string `json:"sign-key-id"` } +// Series implements secboot.ModelForSealing.Series func (m *Model) Series() string { return m.SeriesValue } +// BrandID implements secboot.ModelForSealing.BrandID func (m *Model) BrandID() string { return m.BrandIDValue } +// Model implements secboot.ModelForSealing.Model func (m *Model) Model() string { return m.ModelValue } +// Classic implements secboot.ModelForSealing.Classic func (m *Model) Classic() bool { return m.ClassicValue } +// Grade implements secboot.ModelForSealing.Grade func (m *Model) Grade() asserts.ModelGrade { return m.GradeValue } +// SignKeyID implements secboot.ModelForSealing.SignKeyID func (m *Model) SignKeyID() string { return m.SignKeyIDValue } @@ -85,18 +92,23 @@ func newModel(m secboot.ModelForSealing) Model { var _ secboot.ModelForSealing = (*Model)(nil) -type KeyroleSlotParameters struct { - Models []Model `json:"models,omitempty"` - BootModes []string `json:"boot-modes,omitempty"` - Tpm2PCRProfile json.RawMessage `json:"tpm2-pcr-profile,omitempty"` +// KeyslotRoleParameters stores upgradeable paramaters for a keyslot role +type KeyslotRoleParameters struct { + // Models are the optional list of approved models + Models []Model `json:"models,omitempty"` + // BootModes are the optional list of approved modes (run, recover, ...) + BootModes []string `json:"boot-modes,omitempty"` + // Tpm2PCRProfile is an optional serialized PCR profile + Tpm2PCRProfile secboot.SerializedPCRProfile `json:"tpm2-pcr-profile,omitempty"` } +// KeyslotRoleInfo stores information about a key slot role type KeyslotRoleInfo struct { // PrimaryKeyId is the ID for the primary key found in // PrimaryKeys field of FdeState PrimaryKeyId int `json:"primary-key-id"` // Parameters is indexed by container role name - Parameters map[string]KeyroleSlotParameters `json:"params,omitempty"` + Parameters map[string]KeyslotRoleParameters `json:"params,omitempty"` // Tpm2PCRPolicyRevocationCounter is the handle for the TPM // policy revocation counter. A value of 0 means it is not // set. @@ -105,6 +117,7 @@ type KeyslotRoleInfo struct { type hashAlg crypto.Hash +// UnmarshalJSON implements json.Unmarshaler.UnmarshalJSON func (h *hashAlg) UnmarshalJSON(b []byte) error { var s string if err := json.Unmarshal(b, &s); err != nil { @@ -120,6 +133,7 @@ func (h *hashAlg) UnmarshalJSON(b []byte) error { return nil } +// MarshalJSON implements json.Marshaler.MarshalJSON func (h hashAlg) MarshalJSON() ([]byte, error) { switch crypto.Hash(h) { case crypto.SHA256: @@ -129,6 +143,7 @@ func (h hashAlg) MarshalJSON() ([]byte, error) { } } +// KeyDigest stores a HMAC(key, salt) of a key type KeyDigest struct { // Algorithm is the algorithm for Algorithm hashAlg `json:"alg"` @@ -157,10 +172,12 @@ func (kd *KeyDigest) verifyPrimaryKeyDigest(devicePath string) (bool, error) { return secbootVerifyPrimaryKeyHash(devicePath, crypto.Hash(kd.Algorithm), kd.Salt, kd.Digest) } +// PrimaryKeyInfo provides information about a primary key that is used to manage key slots type PrimaryKeyInfo struct { Digest KeyDigest `json:"digest"` } +// FdeState is the root persistent FDE state type FdeState struct { // PrimaryKeys are the keys on the system. Key with ID 0 is // reserved for snapd and is populated on first boot. Other @@ -185,8 +202,8 @@ func initializeState(st *state.State) error { return err } - dataUUID, dataErr := disksDmCryptUUIDFromMountPoint(dirs.GlobalRootDir) - saveUUID, saveErr := disksDmCryptUUIDFromMountPoint(dirs.SnapSaveDir) + dataUUID, dataErr := disksDMCryptUUIDFromMountPoint(dirs.GlobalRootDir) + saveUUID, saveErr := disksDMCryptUUIDFromMountPoint(dirs.SnapSaveDir) if errors.Is(saveErr, &disks.ErrMountPointNotFound{}) { // TODO: do we need to care about old cases where there is no save partition? return nil @@ -259,9 +276,9 @@ func updateParameters(st *state.State, role string, containerRole string, bootMo } if roleInfo.Parameters == nil { - roleInfo.Parameters = make(map[string]KeyroleSlotParameters) + roleInfo.Parameters = make(map[string]KeyslotRoleParameters) } - roleInfo.Parameters[containerRole] = KeyroleSlotParameters{ + roleInfo.Parameters[containerRole] = KeyslotRoleParameters{ Models: convertedModels, BootModes: bootModes, Tpm2PCRProfile: tpmPCRProfile, @@ -274,13 +291,13 @@ func updateParameters(st *state.State, role string, containerRole string, bootMo return nil } -func MockDmCryptUUIDFromMountPoint(f func(mountpoint string) (string, error)) (restore func()) { - osutil.MustBeTestBinary("mocking DmCryptUUIDFromMountPoint can be done only from tests") +func MockDMCryptUUIDFromMountPoint(f func(mountpoint string) (string, error)) (restore func()) { + osutil.MustBeTestBinary("mocking DMCryptUUIDFromMountPoint can be done only from tests") - old := disksDmCryptUUIDFromMountPoint - disksDmCryptUUIDFromMountPoint = f + old := disksDMCryptUUIDFromMountPoint + disksDMCryptUUIDFromMountPoint = f return func() { - disksDmCryptUUIDFromMountPoint = old + disksDMCryptUUIDFromMountPoint = old } } diff --git a/overlord/managers_test.go b/overlord/managers_test.go index f98f89f4f11..0a4e98034da 100644 --- a/overlord/managers_test.go +++ b/overlord/managers_test.go @@ -7434,7 +7434,7 @@ func (s *mgrsSuiteCore) testRemodelUC20WithRecoverySystem(c *C, encrypted bool) }) defer restore() - restore = fdestate.MockDmCryptUUIDFromMountPoint(func(mountpoint string) (string, error) { + restore = fdestate.MockDMCryptUUIDFromMountPoint(func(mountpoint string) (string, error) { if mountpoint == dirs.GlobalRootDir { return "root-uuid", nil } else { From 4adc79b086bc1f41d878f1a2bff185b7c1d9f206 Mon Sep 17 00:00:00 2001 From: Valentin David Date: Wed, 2 Oct 2024 12:57:45 +0200 Subject: [PATCH 7/9] amend! overlord/fdestate: keep FDE state up to date overlord/fdestate: keep FDE state up to date StartUp() initializes the empty profiles, and reseal updates them. From 5d69be1417e209e1125d2f034ac8803b4c49be30 Mon Sep 17 00:00:00 2001 From: Valentin David Date: Wed, 2 Oct 2024 13:23:29 +0200 Subject: [PATCH 8/9] fixup! overlord/fdestate: keep FDE state up to date --- overlord/fdestate/fdestate.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/overlord/fdestate/fdestate.go b/overlord/fdestate/fdestate.go index 02c8a3ec245..ec23209b6bf 100644 --- a/overlord/fdestate/fdestate.go +++ b/overlord/fdestate/fdestate.go @@ -92,7 +92,7 @@ func newModel(m secboot.ModelForSealing) Model { var _ secboot.ModelForSealing = (*Model)(nil) -// KeyslotRoleParameters stores upgradeable paramaters for a keyslot role +// KeyslotRoleParameters stores upgradeable parameters for a keyslot role type KeyslotRoleParameters struct { // Models are the optional list of approved models Models []Model `json:"models,omitempty"` From 4d220cf733437f5dbad35cadf656486d8138940e Mon Sep 17 00:00:00 2001 From: Valentin David Date: Thu, 3 Oct 2024 10:59:14 +0200 Subject: [PATCH 9/9] fixup! overlord/fdestate: keep FDE state up to date --- secboot/secboot_tpm.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/secboot/secboot_tpm.go b/secboot/secboot_tpm.go index c8632c6a057..294c4595b9d 100644 --- a/secboot/secboot_tpm.go +++ b/secboot/secboot_tpm.go @@ -625,6 +625,8 @@ func buildPCRProtectionProfile(modelParams []*SealKeyModelParams) (*sb_tpm2.PCRP return pcrProfile, nil } +// BuildPCRProtectionProfile builds and serializes a PCR profile from +// a list of SealKeyModelParams func BuildPCRProtectionProfile(modelParams []*SealKeyModelParams) (SerializedPCRProfile, error) { pcrProfile, err := buildPCRProtectionProfile(modelParams) if err != nil {