Skip to content

Commit

Permalink
many: update secboot and use new key format
Browse files Browse the repository at this point in the history
  • Loading branch information
valentindavid committed Aug 26, 2024
1 parent 1e0707b commit 0940e30
Show file tree
Hide file tree
Showing 50 changed files with 1,534 additions and 697 deletions.
2 changes: 1 addition & 1 deletion boot/bootchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ type BootChain struct {
KernelBootFile bootloader.BootFile `json:"-"`
}

func (b *BootChain) modelForSealing() *modelForSealing {
func (b *BootChain) ModelForSealing() *modelForSealing {
return &modelForSealing{
brandID: b.BrandID,
model: b.Model,
Expand Down
8 changes: 5 additions & 3 deletions boot/export_sb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
package boot

import (
"context"

"github.com/canonical/go-efilib"
"github.com/canonical/go-efilib/linux"

Expand All @@ -33,19 +35,19 @@ var (
SetEfiBootOrderVariable = setEfiBootOrderVariable
)

func MockEfiListVariables(f func() ([]efi.VariableDescriptor, error)) (restore func()) {
func MockEfiListVariables(f func(ctx context.Context) ([]efi.VariableDescriptor, error)) (restore func()) {
restore = testutil.Backup(&efiListVariables)
efiListVariables = f
return restore
}

func MockEfiReadVariable(f func(name string, guid efi.GUID) ([]byte, efi.VariableAttributes, error)) (restore func()) {
func MockEfiReadVariable(f func(ctx context.Context, name string, guid efi.GUID) ([]byte, efi.VariableAttributes, error)) (restore func()) {
restore = testutil.Backup(&efiReadVariable)
efiReadVariable = f
return restore
}

func MockEfiWriteVariable(f func(name string, guid efi.GUID, attrs efi.VariableAttributes, data []byte) error) (restore func()) {
func MockEfiWriteVariable(f func(ctx context.Context, name string, guid efi.GUID, attrs efi.VariableAttributes, data []byte) error) (restore func()) {
restore = testutil.Backup(&efiWriteVariable)
efiWriteVariable = f
return restore
Expand Down
4 changes: 2 additions & 2 deletions boot/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ func MockSecbootProvisionTPM(f func(mode secboot.TPMProvisionMode, lockoutAuthFi
return restore
}

func MockSecbootSealKeys(f func(keys []secboot.SealKeyRequest, params *secboot.SealKeysParams) error) (restore func()) {
func MockSecbootSealKeys(f func(keys []secboot.SealKeyRequest, params *secboot.SealKeysParams) ([]byte, error)) (restore func()) {
old := secbootSealKeys
secbootSealKeys = f
return func() {
Expand Down Expand Up @@ -206,7 +206,7 @@ func SetBootFlagsInBootloader(flags []string, rootDir string) error {
}

func (b *BootChain) SecbootModelForSealing() secboot.ModelForSealing {
return b.modelForSealing()
return b.ModelForSealing()
}

func (b *BootChain) SetKernelBootFile(kbf bootloader.BootFile) {
Expand Down
12 changes: 6 additions & 6 deletions boot/makebootable_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -694,7 +694,7 @@ version: 5.0

// set mock key sealing
sealKeysCalls := 0
restore = boot.MockSecbootSealKeys(func(keys []secboot.SealKeyRequest, params *secboot.SealKeysParams) error {
restore = boot.MockSecbootSealKeys(func(keys []secboot.SealKeyRequest, params *secboot.SealKeysParams) ([]byte, error) {
c.Assert(provisionCalls, Equals, 1, Commentf("TPM must have been provisioned before"))
sealKeysCalls++
switch sealKeysCalls {
Expand Down Expand Up @@ -778,7 +778,7 @@ version: 5.0

c.Assert(params.ModelParams[0].Model.Model(), Equals, "my-model-uc20")

return nil
return nil, nil
})
defer restore()

Expand Down Expand Up @@ -1170,7 +1170,7 @@ version: 5.0
defer restore()
// set mock key sealing
sealKeysCalls := 0
restore = boot.MockSecbootSealKeys(func(keys []secboot.SealKeyRequest, params *secboot.SealKeysParams) error {
restore = boot.MockSecbootSealKeys(func(keys []secboot.SealKeyRequest, params *secboot.SealKeysParams) ([]byte, error) {
sealKeysCalls++
switch sealKeysCalls {
case 1:
Expand Down Expand Up @@ -1208,7 +1208,7 @@ version: 5.0
})
c.Assert(params.ModelParams[0].Model.Model(), Equals, "my-model-uc20")

return fmt.Errorf("seal error")
return nil, fmt.Errorf("seal error")
})
defer restore()

Expand Down Expand Up @@ -1360,7 +1360,7 @@ version: 5.0
defer restore()
// set mock key sealing
sealKeysCalls := 0
restore = boot.MockSecbootSealKeys(func(keys []secboot.SealKeyRequest, params *secboot.SealKeysParams) error {
restore = boot.MockSecbootSealKeys(func(keys []secboot.SealKeyRequest, params *secboot.SealKeysParams) ([]byte, error) {
sealKeysCalls++
switch sealKeysCalls {
case 1, 2:
Expand All @@ -1384,7 +1384,7 @@ version: 5.0

c.Assert(params.ModelParams[0].Model.Model(), Equals, "my-model-uc20")

return nil
return nil, nil
})
defer restore()

Expand Down
73 changes: 40 additions & 33 deletions boot/seal.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@
package boot

import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"fmt"
"os"
"path/filepath"
Expand All @@ -35,7 +32,6 @@ import (
"github.com/snapcore/snapd/logger"
"github.com/snapcore/snapd/osutil"
"github.com/snapcore/snapd/secboot"
"github.com/snapcore/snapd/secboot/keys"
"github.com/snapcore/snapd/seed"
"github.com/snapcore/snapd/snap"
"github.com/snapcore/snapd/strutil"
Expand Down Expand Up @@ -150,6 +146,7 @@ func runKeySealRequests(key secboot.BootstrappedContainer) []secboot.SealKeyRequ
{
BootstrappedContainer: key,
KeyName: "ubuntu-data",
SlotName: "default",
KeyFile: device.DataSealedKeyUnder(InitramfsBootEncryptionKeyDir),
},
}
Expand All @@ -168,30 +165,21 @@ func fallbackKeySealRequests(key, saveKey secboot.BootstrappedContainer, factory
{
BootstrappedContainer: key,
KeyName: "ubuntu-data",
SlotName: "default-fallback",
KeyFile: device.FallbackDataSealedKeyUnder(InitramfsSeedEncryptionKeyDir),
},
{
BootstrappedContainer: saveKey,
KeyName: "ubuntu-save",
SlotName: "default-fallback",
KeyFile: saveFallbackKey,
},
}
}

func sealKeyToModeenvUsingFDESetupHook(key, saveKey secboot.BootstrappedContainer, model *asserts.Model, modeenv *Modeenv, flags sealKeyToModeenvFlags) error {
// XXX: Move the auxKey creation to a more generic place, see
// PR#10123 for a possible way of doing this. However given
// that the equivalent key for the TPM case is also created in
// sealKeyToModeenvUsingTPM more symetric to create the auxKey
// here and when we also move TPM to use the auxKey to move
// the creation of it.
auxKey, err := keys.NewAuxKey()
if err != nil {
return fmt.Errorf("cannot create aux key: %v", err)
}
params := secboot.SealKeysWithFDESetupHookParams{
Model: modeenv.ModelForSealing(),
AuxKey: auxKey,
AuxKeyFile: filepath.Join(InstallHostFDESaveDir, "aux-key"),
}
factoryReset := flags.FactoryReset
Expand All @@ -204,6 +192,18 @@ func sealKeyToModeenvUsingFDESetupHook(key, saveKey secboot.BootstrappedContaine
return err
}

for _, container := range []secboot.BootstrappedContainer{
key,
saveKey,
} {
if container != nil {
if err := container.RemoveBootstrapKey(); err != nil {
// This could be a warning
return err
}
}
}

return nil
}

Expand Down Expand Up @@ -261,12 +261,6 @@ func sealKeyToModeenvUsingSecboot(key, saveKey secboot.BootstrappedContainer, mo
// the boot chains we seal the fallback object to
rpbc := ToPredictableBootChains(recoveryBootChains)

// gets written to a file by sealRunObjectKeys()
authKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return fmt.Errorf("cannot generate key for signing dynamic authorization policies: %v", err)
}

runObjectKeyPCRHandle := uint32(secboot.RunObjectPCRPolicyCounterHandle)
fallbackObjectKeyPCRHandle := uint32(secboot.FallbackObjectPCRPolicyCounterHandle)
if flags.FactoryReset {
Expand Down Expand Up @@ -311,17 +305,29 @@ func sealKeyToModeenvUsingSecboot(key, saveKey secboot.BootstrappedContainer, mo

// TODO: refactor sealing functions to take a struct instead of so many
// parameters
err = sealRunObjectKeys(key, pbc, authKey, roleToBlName, runObjectKeyPCRHandle)
primaryKey, err := sealRunObjectKeys(key, pbc, roleToBlName, runObjectKeyPCRHandle)
if err != nil {
return err
}

err = sealFallbackObjectKeys(key, saveKey, rpbc, authKey, roleToBlName, flags.FactoryReset,
err = sealFallbackObjectKeys(key, saveKey, rpbc, primaryKey, roleToBlName, flags.FactoryReset,
fallbackObjectKeyPCRHandle)
if err != nil {
return err
}

for _, container := range []secboot.BootstrappedContainer{
key,
saveKey,
} {
if container != nil {
if err := container.RemoveBootstrapKey(); err != nil {
// This could be a warning
return err
}
}
}

if err := device.StampSealedKeys(InstallHostWritableDir(model), device.SealingMethodTPM); err != nil {
return err
}
Expand Down Expand Up @@ -350,15 +356,15 @@ func usesAltPCRHandles() (bool, error) {
return handle == secboot.AltFallbackObjectPCRPolicyCounterHandle, nil
}

func sealRunObjectKeys(key secboot.BootstrappedContainer, pbc PredictableBootChains, authKey *ecdsa.PrivateKey, roleToBlName map[bootloader.Role]string, pcrHandle uint32) error {
func sealRunObjectKeys(key secboot.BootstrappedContainer, pbc PredictableBootChains, roleToBlName map[bootloader.Role]string, pcrHandle uint32) ([]byte, error) {
modelParams, err := SealKeyModelParams(pbc, roleToBlName)
if err != nil {
return fmt.Errorf("cannot prepare for key sealing: %v", err)
return nil, fmt.Errorf("cannot prepare for key sealing: %v", err)
}

sealKeyParams := &secboot.SealKeysParams{
ModelParams: modelParams,
TPMPolicyAuthKey: authKey,
PrimaryKey: nil,
TPMPolicyAuthKeyFile: filepath.Join(InstallHostFDESaveDir, "tpm-policy-auth-key"),
PCRPolicyCounterHandle: pcrHandle,
}
Expand All @@ -369,30 +375,31 @@ func sealRunObjectKeys(key secboot.BootstrappedContainer, pbc PredictableBootCha
// path only unseals one object because unsealing is expensive.
// Furthermore, the run object key is stored on ubuntu-boot so that we do not
// need to continually write/read keys from ubuntu-seed.
if err := secbootSealKeys(runKeySealRequests(key), sealKeyParams); err != nil {
return fmt.Errorf("cannot seal the encryption keys: %v", err)
primaryKey, err := secbootSealKeys(runKeySealRequests(key), sealKeyParams)
if err != nil {
return nil, fmt.Errorf("cannot seal the encryption keys: %v", err)
}

return nil
return primaryKey, nil
}

func sealFallbackObjectKeys(key, saveKey secboot.BootstrappedContainer, pbc PredictableBootChains, authKey *ecdsa.PrivateKey, roleToBlName map[bootloader.Role]string, factoryReset bool, pcrHandle uint32) error {
func sealFallbackObjectKeys(key, saveKey secboot.BootstrappedContainer, pbc PredictableBootChains, primaryKey []byte, roleToBlName map[bootloader.Role]string, factoryReset bool, pcrHandle uint32) error {
// also seal the keys to the recovery bootchains as a fallback
modelParams, err := SealKeyModelParams(pbc, roleToBlName)
if err != nil {
return fmt.Errorf("cannot prepare for fallback key sealing: %v", err)
}
sealKeyParams := &secboot.SealKeysParams{
ModelParams: modelParams,
TPMPolicyAuthKey: authKey,
PrimaryKey: primaryKey,
PCRPolicyCounterHandle: pcrHandle,
}
logger.Debugf("sealing fallback key with PCR handle: %#x", sealKeyParams.PCRPolicyCounterHandle)
// The fallback object contains the ubuntu-data and ubuntu-save keys. The
// key files are stored on ubuntu-seed, separate from ubuntu-data so they
// can be used if ubuntu-data and ubuntu-boot are corrupted or unavailable.

if err := secbootSealKeys(fallbackKeySealRequests(key, saveKey, factoryReset), sealKeyParams); err != nil {
if _, err := secbootSealKeys(fallbackKeySealRequests(key, saveKey, factoryReset), sealKeyParams); err != nil {
return fmt.Errorf("cannot seal the fallback encryption keys: %v", err)
}

Expand Down Expand Up @@ -800,7 +807,7 @@ func SealKeyModelParams(pbc PredictableBootChains, roleToBlName map[bootloader.R
modelParams := make([]*secboot.SealKeyModelParams, 0, len(pbc))

for _, bc := range pbc {
modelForSealing := bc.modelForSealing()
modelForSealing := bc.ModelForSealing()
modelID := modelUniqueID(modelForSealing)
const expectNew = false
loadChains, err := bootAssetsToLoadChains(bc.AssetChain, bc.KernelBootFile, roleToBlName, expectNew)
Expand Down
18 changes: 4 additions & 14 deletions boot/seal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ func (s *sealSuite) TestSealKeyToModeenv(c *C) {

// set mock key sealing
sealKeysCalls := 0
restore = boot.MockSecbootSealKeys(func(keys []secboot.SealKeyRequest, params *secboot.SealKeysParams) error {
restore = boot.MockSecbootSealKeys(func(keys []secboot.SealKeyRequest, params *secboot.SealKeysParams) ([]byte, error) {
c.Assert(provisionCalls, Equals, 1, Commentf("TPM must have been provisioned before"))
sealKeysCalls++
switch sealKeysCalls {
Expand All @@ -280,7 +280,7 @@ func (s *sealSuite) TestSealKeyToModeenv(c *C) {
c.Check(params.TPMPolicyAuthKeyFile, Equals, filepath.Join(boot.InstallHostFDESaveDir, "tpm-policy-auth-key"))

dataKeyFile := filepath.Join(rootdir, "/run/mnt/ubuntu-boot/device/fde/ubuntu-data.sealed-key")
c.Check(keys, DeepEquals, []secboot.SealKeyRequest{{BootstrappedContainer: myKey, KeyName: "ubuntu-data", KeyFile: dataKeyFile}})
c.Check(keys, DeepEquals, []secboot.SealKeyRequest{{BootstrappedContainer: myKey, KeyName: "ubuntu-data", SlotName: "default", KeyFile: dataKeyFile}})
if tc.pcrHandleOfKey == secboot.FallbackObjectPCRPolicyCounterHandle {
c.Check(params.PCRPolicyCounterHandle, Equals, secboot.AltRunObjectPCRPolicyCounterHandle)
} else {
Expand All @@ -296,7 +296,7 @@ func (s *sealSuite) TestSealKeyToModeenv(c *C) {
// during factory reset we use a different key location
saveKeyFile = filepath.Join(rootdir, "/run/mnt/ubuntu-seed/device/fde/ubuntu-save.recovery.sealed-key.factory-reset")
}
c.Check(keys, DeepEquals, []secboot.SealKeyRequest{{BootstrappedContainer: myKey, KeyName: "ubuntu-data", KeyFile: dataKeyFile}, {BootstrappedContainer: myKey2, KeyName: "ubuntu-save", KeyFile: saveKeyFile}})
c.Check(keys, DeepEquals, []secboot.SealKeyRequest{{BootstrappedContainer: myKey, KeyName: "ubuntu-data", SlotName: "default-fallback", KeyFile: dataKeyFile}, {BootstrappedContainer: myKey2, KeyName: "ubuntu-save", SlotName: "default-fallback", KeyFile: saveKeyFile}})
if tc.pcrHandleOfKey == secboot.FallbackObjectPCRPolicyCounterHandle {
c.Check(params.PCRPolicyCounterHandle, Equals, secboot.AltFallbackObjectPCRPolicyCounterHandle)
} else {
Expand Down Expand Up @@ -348,7 +348,7 @@ func (s *sealSuite) TestSealKeyToModeenv(c *C) {
}
c.Assert(params.ModelParams[0].Model.Model(), Equals, "my-model-uc20")

return tc.sealErr
return nil, tc.sealErr
})
defer restore()

Expand Down Expand Up @@ -1859,16 +1859,6 @@ func (s *sealSuite) TestSealToModeenvWithFdeHookHappy(c *C) {
{Key: []byte{1, 2, 3, 4}, KeyName: "ubuntu-data"},
{Key: []byte{1, 2, 3, 4}, KeyName: "ubuntu-save"},
})
// check that the sealed keys got written to the expected places
for i, p := range []string{
filepath.Join(boot.InitramfsBootEncryptionKeyDir, "ubuntu-data.sealed-key"),
filepath.Join(boot.InitramfsSeedEncryptionKeyDir, "ubuntu-data.recovery.sealed-key"),
filepath.Join(boot.InitramfsSeedEncryptionKeyDir, "ubuntu-save.recovery.sealed-key"),
} {
// Check for a valid platform handle, encrypted payload (base64)
mockedSealedKey := []byte(fmt.Sprintf("key-%v", strconv.Itoa(i+1)))
c.Check(keyToSave[p], DeepEquals, mockedSealedKey)
}

marker := filepath.Join(dirs.SnapFDEDirUnder(filepath.Join(dirs.GlobalRootDir, "/run/mnt/ubuntu-data/system-data")), "sealed-keys")
c.Check(marker, testutil.FileEquals, "fde-setup-hook")
Expand Down
10 changes: 5 additions & 5 deletions boot/setefibootvars_sb.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func constructLoadOption(description string, assetPath string, optionalData []by
// number should be written. If a different error occurs, returns that error,
// and the returned boot number should be ignored.
func findMatchingBootOption(optionData []byte) (uint16, error) {
variables, err := efiListVariables()
variables, err := efiListVariables(efi.DefaultVarContext)
if err != nil {
return 0, err
}
Expand All @@ -101,7 +101,7 @@ func findMatchingBootOption(optionData []byte) (uint16, error) {
}
// Since we never overwrite an existing variable, we can ignore
// variable attributes when reading the variable
varData, _, err := efiReadVariable(varName, varGUID)
varData, _, err := efiReadVariable(efi.DefaultVarContext, varName, varGUID)
if err != nil {
return 0, err
}
Expand Down Expand Up @@ -135,7 +135,7 @@ func setEfiBootOptionVariable(loadOptionData []byte) (uint16, error) {
return 0, err
}
varName := fmt.Sprintf("Boot%04X", bootNum)
err = efiWriteVariable(varName, efi.GlobalVariable, defaultVarAttrs, loadOptionData)
err = efiWriteVariable(efi.DefaultVarContext, varName, efi.GlobalVariable, defaultVarAttrs, loadOptionData)
return bootNum, err
}

Expand All @@ -144,7 +144,7 @@ func setEfiBootOptionVariable(loadOptionData []byte) (uint16, error) {
// (and removes it from later in the list if it occurs) and writes the
// list as the new BootOrder variable.
func setEfiBootOrderVariable(newBootNum uint16) error {
origData, attrs, err := efiReadVariable("BootOrder", efi.GlobalVariable)
origData, attrs, err := efiReadVariable(efi.DefaultVarContext, "BootOrder", efi.GlobalVariable)
if err == efi.ErrVarNotExist {
attrs = defaultVarAttrs
origData = make([]byte, 0)
Expand Down Expand Up @@ -177,7 +177,7 @@ func setEfiBootOrderVariable(newBootNum uint16) error {
copy(newData[2:], origData[:optionOffset])
copy(newData[optionOffset+2:], origData[optionOffset+2:])
}
return efiWriteVariable("BootOrder", efi.GlobalVariable, attrs, newData)
return efiWriteVariable(efi.DefaultVarContext, "BootOrder", efi.GlobalVariable, attrs, newData)
}

// SetEfiBootVariables sets the Boot#### and BootOrder variables for the given
Expand Down
Loading

0 comments on commit 0940e30

Please sign in to comment.