diff --git a/secboot/encrypt_sb.go b/secboot/encrypt_sb.go index edbfe385753..74e16a32e85 100644 --- a/secboot/encrypt_sb.go +++ b/secboot/encrypt_sb.go @@ -23,6 +23,7 @@ package secboot import ( "bytes" "encoding/json" + "errors" "fmt" "io" "os" @@ -165,18 +166,18 @@ func EnsureRecoveryKey(keyFile string, rkeyDevs []RecoveryKeyDevice) (keys.Recov for _, device := range newDevices { var unlockKey []byte - if device.keyFile != "" { + const defaultPrefix = "ubuntu-fde" + // always check keyring first for unlock key, otherwise fallback to using key file + key, err := sbGetDiskUnlockKeyFromKernel(defaultPrefix, device.node, false) + if errors.Is(err, sb.ErrKernelKeyNotFound) && device.keyFile != "" { key, err := os.ReadFile(device.keyFile) if err != nil { - return keys.RecoveryKey{}, fmt.Errorf("cannot get key from '%s': %v", device.keyFile, err) + return keys.RecoveryKey{}, fmt.Errorf("cannot get key from kernel keyring or %q: %v", device.keyFile, err) } unlockKey = key + } else if err != nil { + return keys.RecoveryKey{}, fmt.Errorf("cannot get key for unlocked disk: %v", err) } else { - const defaultPrefix = "ubuntu-fde" - key, err := sbGetDiskUnlockKeyFromKernel(defaultPrefix, device.node, false) - if err != nil { - return keys.RecoveryKey{}, fmt.Errorf("cannot get key for unlocked disk: %v", err) - } unlockKey = key } diff --git a/secboot/encrypt_sb_test.go b/secboot/encrypt_sb_test.go index d37250d8b52..54cf6a5ed91 100644 --- a/secboot/encrypt_sb_test.go +++ b/secboot/encrypt_sb_test.go @@ -350,7 +350,9 @@ func (s *keymgrSuite) TestEnsureRecoveryKey(c *C) { defer secboot.MockListLUKS2ContainerRecoveryKeyNames(func(devicePath string) ([]string, error) { return []string{}, nil })() + keyringCalled := 0 defer secboot.MockGetDiskUnlockKeyFromKernel(func(prefix string, devicePath string, remove bool) (sb.DiskUnlockKey, error) { + keyringCalled++ return []byte{1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4}, nil })() defer secboot.MockAddLUKS2ContainerRecoveryKey(func(devicePath string, keyslotName string, existingKey sb.DiskUnlockKey, recoveryKey sb.RecoveryKey) error { @@ -365,6 +367,8 @@ func (s *keymgrSuite) TestEnsureRecoveryKey(c *C) { {Mountpoint: "/bar", AuthorizingKeyFile: keyFilePath}, }) c.Assert(err, IsNil) + // Make sure that keyring is checked first for the unlock keys + c.Check(keyringCalled, Equals, 2) c.Check(udevadmCmd.Calls(), DeepEquals, [][]string{ {"udevadm", "info", "--query", "property", "--name", "/dev/mapper/foo"}, {"udevadm", "info", "--query", "property", "--name", "/dev/mapper/bar"}, @@ -388,6 +392,8 @@ func (s *keymgrSuite) TestEnsureRecoveryKey(c *C) { {Mountpoint: "/bar", AuthorizingKeyFile: keyFilePath}, }) c.Assert(err, IsNil) + // Make sure that keyring is checked first for the unlock keys + c.Check(keyringCalled, Equals, 4) recovery, err := os.ReadFile(filepath.Join(s.d, "recovery.key")) c.Assert(err, IsNil) @@ -395,6 +401,36 @@ func (s *keymgrSuite) TestEnsureRecoveryKey(c *C) { c.Check(recovery, DeepEquals, originalRecovery) } +func (s *keymgrSuite) TestEnsureRecoveryKeyFallback(c *C) { + s.mocksForDeviceMounts(c) + + defer secboot.MockListLUKS2ContainerUnlockKeyNames(func(devicePath string) ([]string, error) { + return []string{"default-fallback"}, nil + })() + defer secboot.MockListLUKS2ContainerRecoveryKeyNames(func(devicePath string) ([]string, error) { + return []string{}, nil + })() + keyringCalled := 0 + defer secboot.MockGetDiskUnlockKeyFromKernel(func(prefix string, devicePath string, remove bool) (sb.DiskUnlockKey, error) { + keyringCalled++ + return nil, sb.ErrKernelKeyNotFound + })() + defer secboot.MockAddLUKS2ContainerRecoveryKey(func(devicePath string, keyslotName string, existingKey sb.DiskUnlockKey, recoveryKey sb.RecoveryKey) error { + // Verify unlock key directly came from keyfile + c.Assert(existingKey, DeepEquals, sb.DiskUnlockKey([]byte{9, 8, 7, 1, 2, 3})) + return nil + })() + + keyFilePath := filepath.Join(c.MkDir(), "key.file") + err := os.WriteFile(keyFilePath, []byte{9, 8, 7, 1, 2, 3}, 0644) + c.Assert(err, IsNil) + _, err = secboot.EnsureRecoveryKey(filepath.Join(s.d, "recovery.key"), []secboot.RecoveryKeyDevice{ + {Mountpoint: "/bar", AuthorizingKeyFile: keyFilePath}, + }) + c.Assert(err, IsNil) + c.Check(keyringCalled, Equals, 1) +} + func (s *keymgrSuite) TestEnsureRecoveryKeyLegacy(c *C) { udevadmCmd := s.mocksForDeviceMounts(c)