Skip to content

Commit

Permalink
secboot,overlord/fdestate: seal with boot mode for FDE hooks
Browse files Browse the repository at this point in the history
Set the authorized boot modes for FDE hook keys. For now
the run+recover key allows "run" and "recover", while
the recover key allows "recover" and "factory-reset".
  • Loading branch information
valentindavid committed Feb 12, 2025
1 parent 65e98f1 commit 685a615
Show file tree
Hide file tree
Showing 10 changed files with 56 additions and 14 deletions.
2 changes: 1 addition & 1 deletion overlord/fdestate/backend/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (
"github.com/snapcore/snapd/secboot"
)

func MockSecbootResealKeysWithFDESetupHook(f func(keys []secboot.KeyDataLocation, primaryKeyFile string, models []secboot.ModelForSealing) error) (restore func()) {
func MockSecbootResealKeysWithFDESetupHook(f func(keys []secboot.KeyDataLocation, primaryKeyFile string, models []secboot.ModelForSealing, bootModes []string) error) (restore func()) {
old := secbootResealKeysWithFDESetupHook
secbootResealKeysWithFDESetupHook = f
return func() {
Expand Down
4 changes: 2 additions & 2 deletions overlord/fdestate/backend/reseal.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,12 +155,12 @@ func doReseal(manager FDEStateManager, method device.SealingMethod, rootdir stri
case device.SealingMethodFDESetupHook:
primaryKeyFile := filepath.Join(boot.InstallHostFDESaveDir, "aux-key")
if runParams != nil {
if err := secbootResealKeysWithFDESetupHook(runKeys, primaryKeyFile, runParams.Models); err != nil {
if err := secbootResealKeysWithFDESetupHook(runKeys, primaryKeyFile, runParams.Models, runParams.BootModes); err != nil {
return err
}
}
if recoveryParams != nil {
if err := secbootResealKeysWithFDESetupHook(recoveryKeys, primaryKeyFile, recoveryParams.Models); err != nil {
if err := secbootResealKeysWithFDESetupHook(recoveryKeys, primaryKeyFile, recoveryParams.Models, recoveryParams.BootModes); err != nil {
return err
}
}
Expand Down
2 changes: 1 addition & 1 deletion overlord/fdestate/backend/reseal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1793,7 +1793,7 @@ func (s *resealTestSuite) TestHooksResealHappy(c *C) {
}

resealCalls := 0
restore := fdeBackend.MockSecbootResealKeysWithFDESetupHook(func(keys []secboot.KeyDataLocation, primaryKeyFile string, models []secboot.ModelForSealing) error {
restore := fdeBackend.MockSecbootResealKeysWithFDESetupHook(func(keys []secboot.KeyDataLocation, primaryKeyFile string, models []secboot.ModelForSealing, bootModes []string) error {
resealCalls++

switch resealCalls {
Expand Down
5 changes: 5 additions & 0 deletions overlord/fdestate/backend/seal.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ func runKeySealRequests(key secboot.BootstrappedContainer, useTokens bool) []sec
KeyName: "ubuntu-data",
SlotName: "default",
KeyFile: keyFile,
BootModes: []string{"run", "recover"},
},
}
}
Expand All @@ -83,12 +84,16 @@ 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"},
},
{
BootstrappedContainer: saveKey,
KeyName: "ubuntu-save",
SlotName: "default-fallback",
KeyFile: saveFallbackKey,
BootModes: []string{"recover", "factory-reset"},
},
}
}
Expand Down
6 changes: 3 additions & 3 deletions overlord/fdestate/backend/seal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ func (s *sealSuite) TestSealKeyForBootChains(c *C) {
// the run object seals only the ubuntu-data key
c.Check(params.TPMPolicyAuthKeyFile, Equals, filepath.Join(boot.InstallHostFDESaveDir, "tpm-policy-auth-key"))

expectedSKR := secboot.SealKeyRequest{BootstrappedContainer: myKey, KeyName: "ubuntu-data", SlotName: "default"}
expectedSKR := secboot.SealKeyRequest{BootstrappedContainer: myKey, KeyName: "ubuntu-data", SlotName: "default", BootModes: []string{"run", "recover"}}
if tc.disableTokens {
expectedSKR.KeyFile = filepath.Join(rootdir, "run/mnt/ubuntu-boot/device/fde/ubuntu-data.sealed-key")
}
Expand All @@ -179,8 +179,8 @@ 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"}
expectedSaveSKR := secboot.SealKeyRequest{BootstrappedContainer: myKey2, KeyName: "ubuntu-save", SlotName: "default-fallback"}
expectedDataSKR := secboot.SealKeyRequest{BootstrappedContainer: myKey, KeyName: "ubuntu-data", SlotName: "default-fallback", BootModes: []string{"recover", "factory-reset"}}
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")
if tc.factoryReset {
Expand Down
8 changes: 8 additions & 0 deletions secboot/export_sb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,14 @@ func MockSetAuthorizedSnapModelsOnHooksKeydata(f func(kd *sb_hooks.KeyData, rand
}
}

func MockSetAuthorizedBootModesOnHooksKeydata(f func(kd *sb_hooks.KeyData, rand io.Reader, key sb.PrimaryKey, bootModes ...string) error) (restore func()) {
old := setAuthorizedBootModesOnHooksKeydata
setAuthorizedBootModesOnHooksKeydata = f
return func() {
setAuthorizedBootModesOnHooksKeydata = old
}
}

type DefaultKeyLoader = defaultKeyLoader

var ReadKeyFile = readKeyFile
Expand Down
2 changes: 2 additions & 0 deletions secboot/secboot.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ type SealKeyRequest struct {
// The file to store the key data. If empty, the key data will
// be saved to the token.
KeyFile string
// The boot modes allow (i.e. snapd_recovery_mode kernel parameter)
BootModes []string
}

// ModelForSealing provides information about the model for use in the context
Expand Down
2 changes: 1 addition & 1 deletion secboot/secboot_dummy.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func VerifyPrimaryKeyDigest(devicePath string, alg crypto.Hash, salt []byte, dig
return false, errBuildWithoutSecboot
}

func ResealKeysWithFDESetupHook(keys []KeyDataLocation, primaryKeyFile string, models []ModelForSealing) error {
func ResealKeysWithFDESetupHook(keys []KeyDataLocation, primaryKeyFile string, models []ModelForSealing, bootModes []string) error {
return errBuildWithoutSecboot
}

Expand Down
13 changes: 11 additions & 2 deletions secboot/secboot_hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func SealKeysWithFDESetupHook(runHook fde.RunSetupHookFunc, keys []SealKeyReques
AuthorizedSnapModels: []sb.SnapModel{
params.Model,
},
// TODO:FDEM:FIX: add boot modes
AuthorizedBootModes: skr.BootModes,
}

protectedKey, primaryKeyOut, unlockKey, err := sb_hooks.NewProtectedKey(rand.Reader, params)
Expand Down Expand Up @@ -132,9 +132,15 @@ func setAuthorizedSnapModelsOnHooksKeydataImpl(kd *sb_hooks.KeyData, rand io.Rea

var setAuthorizedSnapModelsOnHooksKeydata = setAuthorizedSnapModelsOnHooksKeydataImpl

func setAuthorizedBootModesOnHooksKeydataImpl(kd *sb_hooks.KeyData, rand io.Reader, key sb.PrimaryKey, bootmodes ...string) error {
return kd.SetAuthorizedBootModes(rand, key, bootmodes...)
}

var setAuthorizedBootModesOnHooksKeydata = setAuthorizedBootModesOnHooksKeydataImpl

// ResealKeysWithFDESetupHook updates hook based keydatas for given
// files with a specific list of models
func ResealKeysWithFDESetupHook(keys []KeyDataLocation, primaryKeyFile string, models []ModelForSealing) error {
func ResealKeysWithFDESetupHook(keys []KeyDataLocation, primaryKeyFile string, models []ModelForSealing, bootModes []string) error {
// TODO:FDEM:FIX: load primary key from keyring when available
primaryKeyBuf, err := os.ReadFile(primaryKeyFile)
if err != nil {
Expand Down Expand Up @@ -187,6 +193,9 @@ func ResealKeysWithFDESetupHook(keys []KeyDataLocation, primaryKeyFile string, m
if err := setAuthorizedSnapModelsOnHooksKeydata(hooksKeyData, rand.Reader, primaryKey, sbModels...); err != nil {
return err
}
if err := setAuthorizedBootModesOnHooksKeydata(hooksKeyData, rand.Reader, primaryKey, bootModes...); err != nil {
return err
}
}

if err := keyData.WriteAtomic(keyDataWriter); err != nil {
Expand Down
26 changes: 22 additions & 4 deletions secboot/secboot_sb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2849,7 +2849,7 @@ func (s *secbootSuite) TestResealKeysWithFDESetupHookV1(c *C) {
KeyFile: key1Fn,
}

err = secboot.ResealKeysWithFDESetupHook([]secboot.KeyDataLocation{key1Location}, auxKeyFn, []secboot.ModelForSealing{m})
err = secboot.ResealKeysWithFDESetupHook([]secboot.KeyDataLocation{key1Location}, auxKeyFn, []secboot.ModelForSealing{m}, []string{"run"})
c.Assert(err, IsNil)

// Nothing should have happened. But we make sure that they key is still there untouched.
Expand Down Expand Up @@ -2890,7 +2890,7 @@ func (s *secbootSuite) TestResealKeysWithFDESetupHookV2(c *C) {
KeyFile: key1Fn,
}

err = secboot.ResealKeysWithFDESetupHook([]secboot.KeyDataLocation{key1Location}, auxKeyFn, []secboot.ModelForSealing{m})
err = secboot.ResealKeysWithFDESetupHook([]secboot.KeyDataLocation{key1Location}, auxKeyFn, []secboot.ModelForSealing{m}, []string{"run"})
c.Assert(err, IsNil)

afterReader, err := sb.NewFileKeyDataReader(key1Fn)
Expand Down Expand Up @@ -2965,15 +2965,24 @@ func (s *secbootSuite) TestResealKeysWithFDESetupHook(c *C) {
return nil
})

bootModesSet := 0
secboot.MockSetAuthorizedBootModesOnHooksKeydata(func(kd *sb_hooks.KeyData, rand io.Reader, key sb.PrimaryKey, bootModes ...string) error {
bootModesSet++
c.Check([]byte(key), DeepEquals, primaryKey)
c.Check(bootModes, DeepEquals, []string{"some-mode"})
return nil
})

key1Location := secboot.KeyDataLocation{
DevicePath: "/dev/somedevice",
SlotName: "token-name",
KeyFile: key1Fn,
}

err = secboot.ResealKeysWithFDESetupHook([]secboot.KeyDataLocation{key1Location}, primaryKeyFn, []secboot.ModelForSealing{newModel})
err = secboot.ResealKeysWithFDESetupHook([]secboot.KeyDataLocation{key1Location}, primaryKeyFn, []secboot.ModelForSealing{newModel}, []string{"some-mode"})
c.Assert(err, IsNil)
c.Check(modelSet, Equals, 1)
c.Check(bootModesSet, Equals, 1)
c.Check(tokenWritten, Equals, 1)
}

Expand Down Expand Up @@ -3030,15 +3039,24 @@ func (s *secbootSuite) TestResealKeysWithFDESetupHookFromFile(c *C) {
return nil
})

bootModesSet := 0
secboot.MockSetAuthorizedBootModesOnHooksKeydata(func(kd *sb_hooks.KeyData, rand io.Reader, key sb.PrimaryKey, bootModes ...string) error {
bootModesSet++
c.Check([]byte(key), DeepEquals, primaryKey)
c.Check(bootModes, DeepEquals, []string{"some-mode"})
return nil
})

key1Location := secboot.KeyDataLocation{
DevicePath: "/dev/foo",
SlotName: "default",
KeyFile: key1Fn,
}

err = secboot.ResealKeysWithFDESetupHook([]secboot.KeyDataLocation{key1Location}, primaryKeyFn, []secboot.ModelForSealing{newModel})
err = secboot.ResealKeysWithFDESetupHook([]secboot.KeyDataLocation{key1Location}, primaryKeyFn, []secboot.ModelForSealing{newModel}, []string{"some-mode"})
c.Assert(err, IsNil)
c.Check(modelSet, Equals, 1)
c.Check(bootModesSet, Equals, 1)
// We tried to read key data from token
c.Check(readKeyTokenCalls, Equals, 1)
}
Expand Down

0 comments on commit 685a615

Please sign in to comment.