Skip to content

Commit

Permalink
overlord/fdestate/backend: split profiles for data and save partitions
Browse files Browse the repository at this point in the history
  • Loading branch information
valentindavid committed Feb 11, 2025
1 parent 8864d86 commit 33ba6bf
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 64 deletions.
121 changes: 72 additions & 49 deletions overlord/fdestate/backend/reseal.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,76 +119,85 @@ func getUniqueModels(bootChains []boot.BootChain) []secboot.ModelForSealing {
return models
}

type resealParamsAndLocation struct {
params *SealingParameters
location secboot.KeyDataLocation
}

func doReseal(manager FDEStateManager, method device.SealingMethod, rootdir string) error {
runParams, err := manager.Get("run+recover", "all")
runParamsData, err := manager.Get("run+recover", "system-data")
if err != nil {
return err
}

recoveryParams, err := manager.Get("recover", "system-save")
recoveryParamsSave, err := manager.Get("recover", "system-save")
if err != nil {
return err
}

runKeys := []secboot.KeyDataLocation{
{
DevicePath: "/dev/disk/by-partlabel/ubuntu-data",
SlotName: "default",
KeyFile: device.DataSealedKeyUnder(boot.InitramfsBootEncryptionKeyDir),
},
recoveryParamsData, err := manager.Get("recover", "system-data")
if err != nil {
return err

Check warning on line 140 in overlord/fdestate/backend/reseal.go

View check run for this annotation

Codecov / codecov/patch

overlord/fdestate/backend/reseal.go#L140

Added line #L140 was not covered by tests
}

recoveryKeys := []secboot.KeyDataLocation{
{
DevicePath: "/dev/disk/by-partlabel/ubuntu-data",
SlotName: "default-fallback",
KeyFile: device.FallbackDataSealedKeyUnder(boot.InitramfsSeedEncryptionKeyDir),
},
{
DevicePath: "/dev/disk/by-partlabel/ubuntu-save",
SlotName: "default-fallback",
KeyFile: device.FallbackSaveSealedKeyUnder(boot.InitramfsSeedEncryptionKeyDir),
},
var keys []resealParamsAndLocation

if runParamsData != nil {
keys = append(keys, resealParamsAndLocation{
params: runParamsData,
location: secboot.KeyDataLocation{
DevicePath: "/dev/disk/by-partlabel/ubuntu-data",
SlotName: "default",
KeyFile: device.DataSealedKeyUnder(boot.InitramfsBootEncryptionKeyDir),
},
})
}

if recoveryParamsData != nil {
keys = append(keys, resealParamsAndLocation{
params: recoveryParamsData,
location: secboot.KeyDataLocation{
DevicePath: "/dev/disk/by-partlabel/ubuntu-data",
SlotName: "default-fallback",
KeyFile: device.FallbackDataSealedKeyUnder(boot.InitramfsSeedEncryptionKeyDir),
},
})
}

if recoveryParamsSave != nil {
keys = append(keys, resealParamsAndLocation{
params: recoveryParamsSave,
location: secboot.KeyDataLocation{
DevicePath: "/dev/disk/by-partlabel/ubuntu-save",
SlotName: "default-fallback",
KeyFile: device.FallbackSaveSealedKeyUnder(boot.InitramfsSeedEncryptionKeyDir),
},
})
}

switch method {
case device.SealingMethodFDESetupHook:
primaryKeyFile := filepath.Join(boot.InstallHostFDESaveDir, "aux-key")
if runParams != nil {
if err := secbootResealKeysWithFDESetupHook(runKeys, primaryKeyFile, runParams.Models, runParams.BootModes); err != nil {
return err
}
}
if recoveryParams != nil {
if err := secbootResealKeysWithFDESetupHook(recoveryKeys, primaryKeyFile, recoveryParams.Models, recoveryParams.BootModes); err != nil {
for _, key := range keys {
if err := secbootResealKeysWithFDESetupHook([]secboot.KeyDataLocation{key.location}, primaryKeyFile, key.params.Models, key.params.BootModes); err != nil {
return err
}
}
return nil
case device.SealingMethodTPM, device.SealingMethodLegacyTPM:
saveFDEDir := dirs.SnapFDEDirUnderSave(dirs.SnapSaveDirUnder(rootdir))
authKeyFile := filepath.Join(saveFDEDir, "tpm-policy-auth-key")
if runParams != nil {
runResealKeyParams := &secboot.ResealKeysParams{
PCRProfile: runParams.TpmPCRProfile,
Keys: runKeys,
for _, key := range keys {
keyParams := &secboot.ResealKeysParams{
PCRProfile: key.params.TpmPCRProfile,
Keys: []secboot.KeyDataLocation{key.location},
TPMPolicyAuthKeyFile: authKeyFile,
}

if err := secbootResealKeys(runResealKeyParams); err != nil {
if err := secbootResealKeys(keyParams); err != nil {
return fmt.Errorf("cannot reseal the encryption key: %v", err)
}
}
if recoveryParams != nil {
recoveryResealKeyParams := &secboot.ResealKeysParams{
PCRProfile: recoveryParams.TpmPCRProfile,
Keys: recoveryKeys,
TPMPolicyAuthKeyFile: authKeyFile,
}
if err := secbootResealKeys(recoveryResealKeyParams); err != nil {
return fmt.Errorf("cannot reseal the fallback encryption keys: %v", err)
}
}
return nil
default:
return fmt.Errorf("unknown key sealing method: %q", method)
Expand All @@ -208,11 +217,19 @@ func recalculateParamatersFDEHook(manager FDEStateManager, method device.Sealing
return err
}

recoveryParams := &SealingParameters{
recoveryParamsData := &SealingParameters{
BootModes: []string{"recover"},
Models: recoveryModels,
}
if err := manager.Update("recover", "system-data", recoveryParamsData); err != nil {
return err
}

Check warning on line 226 in overlord/fdestate/backend/reseal.go

View check run for this annotation

Codecov / codecov/patch

overlord/fdestate/backend/reseal.go#L225-L226

Added lines #L225 - L226 were not covered by tests

recoveryParamsSave := &SealingParameters{
BootModes: []string{"recover", "factory-reset"},
Models: recoveryModels,
}
if err := manager.Update("recover", "system-save", recoveryParams); err != nil {
if err := manager.Update("recover", "system-save", recoveryParamsSave); err != nil {
return err
}

Expand Down Expand Up @@ -417,17 +434,23 @@ func updateFallbackProtectionProfile(
models = append(models, m.Model)
}

// TODO:FDEM:FIX: We are missing recover for system-data, for
// "recover" boot mode. It is different from the run+recover
// as this should only include working models.

params := &SealingParameters{
saveParams := &SealingParameters{
BootModes: []string{"recover", "factory-reset"},
Models: models,
TpmPCRProfile: pcrProfile,
}
// TODO:FDEM: use constants for "recover" (the first parameter) and "system-save"
if err := manager.Update("recover", "system-save", params); err != nil {
if err := manager.Update("recover", "system-save", saveParams); err != nil {
return err
}

Check warning on line 445 in overlord/fdestate/backend/reseal.go

View check run for this annotation

Codecov / codecov/patch

overlord/fdestate/backend/reseal.go#L444-L445

Added lines #L444 - L445 were not covered by tests

dataParams := &SealingParameters{
BootModes: []string{"recover"},
Models: models,
TpmPCRProfile: pcrProfile,
}
// TODO:FDEM: use constants for "recover" (the first parameter) and "system-data"
if err := manager.Update("recover", "system-data", dataParams); err != nil {
return err
}

Expand Down
47 changes: 36 additions & 11 deletions overlord/fdestate/backend/reseal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -359,13 +359,17 @@ func (s *resealTestSuite) TestTPMResealHappy(c *C) {
},
})
case 2:
// Resealing the recovery key for both data and save partitions
// Resealing the recovery key for both data partition
c.Check(params.Keys, DeepEquals, []secboot.KeyDataLocation{
{
DevicePath: "/dev/disk/by-partlabel/ubuntu-data",
SlotName: "default-fallback",
KeyFile: filepath.Join(s.rootdir, "run/mnt/ubuntu-seed/device/fde/ubuntu-data.recovery.sealed-key"),
},
})
case 3:
// Resealing the recovery key for both save partition
c.Check(params.Keys, DeepEquals, []secboot.KeyDataLocation{
{
DevicePath: "/dev/disk/by-partlabel/ubuntu-save",
SlotName: "default-fallback",
Expand All @@ -384,7 +388,7 @@ func (s *resealTestSuite) TestTPMResealHappy(c *C) {
err := fdeBackend.ResealKeyForBootChains(myState, device.SealingMethodTPM, s.rootdir, params, expectReseal)
c.Assert(err, IsNil)

c.Check(resealCalls, Equals, 2)
c.Check(resealCalls, Equals, 3)

pbc, cnt, err := boot.ReadBootChains(filepath.Join(dirs.SnapFDEDir, "boot-chains"))
c.Assert(err, IsNil)
Expand Down Expand Up @@ -663,13 +667,18 @@ func (s *resealTestSuite) TestResealKeyForBootchainsWithSystemFallback(c *C) {
})
}

checkRecoveryParams := func() {
checkRecoveryParamsData := func() {
c.Check(params.Keys, DeepEquals, []secboot.KeyDataLocation{
{
DevicePath: "/dev/disk/by-partlabel/ubuntu-data",
SlotName: "default-fallback",
KeyFile: filepath.Join(boot.InitramfsSeedEncryptionKeyDir, "ubuntu-data.recovery.sealed-key"),
},
})
}

checkRecoveryParamsSave := func() {
c.Check(params.Keys, DeepEquals, []secboot.KeyDataLocation{
{
DevicePath: "/dev/disk/by-partlabel/ubuntu-save",
SlotName: "default-fallback",
Expand All @@ -682,7 +691,9 @@ func (s *resealTestSuite) TestResealKeyForBootchainsWithSystemFallback(c *C) {
case 1:
checkRunParams()
case 2:
checkRecoveryParams()
checkRecoveryParamsData()
case 3:
checkRecoveryParamsSave()
default:
c.Errorf("unexpected additional call to secboot.ResealKeys (call # %d)", resealKeysCalls)
}
Expand Down Expand Up @@ -967,7 +978,7 @@ func (s *resealTestSuite) TestResealKeyForBootchainsWithSystemFallback(c *C) {
// mocked error is returned on first reseal
c.Assert(resealKeysCalls, Equals, 1)
} else {
c.Assert(resealKeysCalls, Equals, 2)
c.Assert(resealKeysCalls, Equals, 3)
}
if tc.err != "" {
continue
Expand Down Expand Up @@ -1110,6 +1121,9 @@ func (s *resealTestSuite) TestResealKeyForBootchainsRecoveryKeysForGoodSystemsOn
SlotName: "default-fallback",
KeyFile: filepath.Join(s.rootdir, "run/mnt/ubuntu-seed/device/fde/ubuntu-data.recovery.sealed-key"),
},
})
case 3:
c.Check(params.Keys, DeepEquals, []secboot.KeyDataLocation{
{
DevicePath: "/dev/disk/by-partlabel/ubuntu-save",
SlotName: "default-fallback",
Expand Down Expand Up @@ -1255,7 +1269,7 @@ func (s *resealTestSuite) TestResealKeyForBootchainsRecoveryKeysForGoodSystemsOn
const expectReseal = false
err := fdeBackend.ResealKeyForBootChains(myState, device.SealingMethodTPM, s.rootdir, params, expectReseal)
c.Assert(err, IsNil)
c.Assert(resealKeysCalls, Equals, 2)
c.Assert(resealKeysCalls, Equals, 3)

// verify the boot chains data file for run key
runPbc, cnt, err := boot.ReadBootChains(filepath.Join(dirs.SnapFDEDir, "boot-chains"))
Expand Down Expand Up @@ -1430,6 +1444,9 @@ func (s *resealTestSuite) testResealKeyForBootchainsWithTryModel(c *C, shimId, g
SlotName: "default-fallback",
KeyFile: filepath.Join(s.rootdir, "run/mnt/ubuntu-seed/device/fde/ubuntu-data.recovery.sealed-key"),
},
})
case 3:
c.Assert(params.Keys, DeepEquals, []secboot.KeyDataLocation{
{
DevicePath: "/dev/disk/by-partlabel/ubuntu-save",
SlotName: "default-fallback",
Expand Down Expand Up @@ -1565,7 +1582,7 @@ func (s *resealTestSuite) testResealKeyForBootchainsWithTryModel(c *C, shimId, g
const expectReseal = false
err := fdeBackend.ResealKeyForBootChains(myState, device.SealingMethodTPM, s.rootdir, params, expectReseal)
c.Assert(err, IsNil)
c.Assert(resealKeysCalls, Equals, 2)
c.Assert(resealKeysCalls, Equals, 3)

// verify the boot chains data file for run key
runPbc, cnt, err := boot.ReadBootChains(filepath.Join(dirs.SnapFDEDir, "boot-chains"))
Expand Down Expand Up @@ -1653,6 +1670,7 @@ func (s *resealTestSuite) TestResealKeyForBootchainsFallbackCmdline(c *C) {
switch resealKeysCalls {
case 1:
case 2:
case 3:
default:
c.Fatalf("unexpected number of reseal calls, %v", params)
}
Expand Down Expand Up @@ -1740,7 +1758,7 @@ func (s *resealTestSuite) TestResealKeyForBootchainsFallbackCmdline(c *C) {
const expectReseal = false
err = fdeBackend.ResealKeyForBootChains(myState, device.SealingMethodTPM, s.rootdir, params, expectReseal)
c.Assert(err, IsNil)
c.Assert(resealKeysCalls, Equals, 2)
c.Assert(resealKeysCalls, Equals, 3)

// verify the boot chains data file
pbc, cnt, err := boot.ReadBootChains(filepath.Join(dirs.SnapFDEDir, "boot-chains"))
Expand Down Expand Up @@ -1810,13 +1828,20 @@ func (s *resealTestSuite) TestHooksResealHappy(c *C) {
c.Assert(models, HasLen, 1)
c.Check(models[0].Model(), Equals, model.Model())
case 2:
// Resealing the recovery key for both data and save partitions
// Resealing the recovery key for both data partition
c.Check(keys, DeepEquals, []secboot.KeyDataLocation{
{
DevicePath: "/dev/disk/by-partlabel/ubuntu-data",
SlotName: "default-fallback",
KeyFile: filepath.Join(s.rootdir, "run/mnt/ubuntu-seed/device/fde/ubuntu-data.recovery.sealed-key"),
},
})
c.Check(primaryKeyFile, Equals, filepath.Join(s.rootdir, "run/mnt/ubuntu-save/device/fde/aux-key"))
c.Assert(models, HasLen, 1)
c.Check(models[0].Model(), Equals, model.Model())
case 3:
// Resealing the recovery key for both save partition
c.Check(keys, DeepEquals, []secboot.KeyDataLocation{
{
DevicePath: "/dev/disk/by-partlabel/ubuntu-save",
SlotName: "default-fallback",
Expand All @@ -1839,7 +1864,7 @@ func (s *resealTestSuite) TestHooksResealHappy(c *C) {
err := fdeBackend.ResealKeyForBootChains(myState, device.SealingMethodFDESetupHook, s.rootdir, params, expectReseal)
c.Assert(err, IsNil)

c.Check(resealCalls, Equals, 2)
c.Check(resealCalls, Equals, 3)
}

func (s *resealTestSuite) TestResealKeyForSignatureDBUpdate(c *C) {
Expand Down Expand Up @@ -1982,5 +2007,5 @@ func (s *resealTestSuite) TestResealKeyForSignatureDBUpdate(c *C) {

// reseal was called
c.Check(buildProfileCalls, Equals, 3)
c.Check(resealKeysCalls, Equals, 2)
c.Check(resealKeysCalls, Equals, 3)
}
4 changes: 1 addition & 3 deletions overlord/fdestate/backend/seal.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,7 @@ func fallbackKeySealRequests(key, saveKey secboot.BootstrappedContainer, factory
KeyName: "ubuntu-data",
SlotName: "default-fallback",
KeyFile: dataFallbackKey,
// TODO:FDEM:FIX we should not not have "factory-reset" here, but for now
// we want to have the same as the pcr profile
BootModes: []string{"recover", "factory-reset"},
BootModes: []string{"recover"},
},
{
BootstrappedContainer: saveKey,
Expand Down
2 changes: 1 addition & 1 deletion overlord/fdestate/backend/seal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ func (s *sealSuite) TestSealKeyForBootChains(c *C) {
// the fallback object seals the ubuntu-data and the ubuntu-save keys
c.Check(params.TPMPolicyAuthKeyFile, Equals, "")

expectedDataSKR := secboot.SealKeyRequest{BootstrappedContainer: myKey, KeyName: "ubuntu-data", SlotName: "default-fallback", BootModes: []string{"recover", "factory-reset"}}
expectedDataSKR := secboot.SealKeyRequest{BootstrappedContainer: myKey, KeyName: "ubuntu-data", SlotName: "default-fallback", BootModes: []string{"recover"}}
expectedSaveSKR := secboot.SealKeyRequest{BootstrappedContainer: myKey2, KeyName: "ubuntu-save", SlotName: "default-fallback", BootModes: []string{"recover", "factory-reset"}}
if tc.disableTokens {
expectedDataSKR.KeyFile = filepath.Join(rootdir, "run/mnt/ubuntu-seed/device/fde/ubuntu-data.recovery.sealed-key")
Expand Down
5 changes: 5 additions & 0 deletions secboot/secboot.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,11 @@ type ResealKeysParams struct {
TPMPolicyAuthKeyFile string
}

type ResealKeysParamsForHooks struct {
Models []ModelForSealing
BootModes []string
}

// UnlockVolumeUsingSealedKeyOptions contains options for unlocking encrypted
// volumes using keys sealed to the TPM.
type UnlockVolumeUsingSealedKeyOptions struct {
Expand Down

0 comments on commit 33ba6bf

Please sign in to comment.