diff options
| author | Michael Vogt <mvo@ubuntu.com> | 2022-06-13 11:08:59 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-06-13 11:08:59 +0200 |
| commit | 95dc06253504288da1d1e0fe00d739107b482618 (patch) | |
| tree | 981f4f0890e9ff7bb27fbd9bc3107a9cc4743f03 | |
| parent | bf49d2065462cc62601e555c6075eaff1d02af11 (diff) | |
| parent | 8f26223cd0806360b3a1a434cbfc9b4d423e8538 (diff) | |
Merge pull request #11865 from bboozzoo/bboozzoo/secboot-stage-transition
secboot: stage and transition encryption keys
| -rw-r--r-- | secboot/encrypt_dummy.go | 6 | ||||
| -rw-r--r-- | secboot/encrypt_sb.go | 43 | ||||
| -rw-r--r-- | secboot/encrypt_sb_test.go | 90 |
3 files changed, 120 insertions, 19 deletions
diff --git a/secboot/encrypt_dummy.go b/secboot/encrypt_dummy.go index a531d5530e..d36a809dca 100644 --- a/secboot/encrypt_dummy.go +++ b/secboot/encrypt_dummy.go @@ -33,6 +33,10 @@ func RemoveRecoveryKeys(map[RecoveryKeyDevice]string) error { return errBuildWithoutSecboot } -func ChangeEncryptionKey(node string, key keys.EncryptionKey) error { +func StageEncryptionKeyChange(node string, key keys.EncryptionKey) error { + return errBuildWithoutSecboot +} + +func TransitionEncryptionKeyChange(mountpoint string, key keys.EncryptionKey) error { return errBuildWithoutSecboot } diff --git a/secboot/encrypt_sb.go b/secboot/encrypt_sb.go index 37368ad64a..f6fe348c08 100644 --- a/secboot/encrypt_sb.go +++ b/secboot/encrypt_sb.go @@ -170,16 +170,18 @@ func RemoveRecoveryKeys(rkeyDevToKey map[RecoveryKeyDevice]string) error { return nil } -// ChangeEncryptionKey changes the main encryption key of a given device to the -// new key. -func ChangeEncryptionKey(node string, key keys.EncryptionKey) error { +// StageEncryptionKeyChange stages a new encryption key for a given encrypted +// device. The new key is added into a temporary slot. To complete the +// encryption key change process, a call to TransitionEncryptionKeyChange is +// needed. +func StageEncryptionKeyChange(node string, key keys.EncryptionKey) error { partitionUUID, err := disks.PartitionUUID(node) if err != nil { return fmt.Errorf("cannot get UUID of partition %v: %v", node, err) } dev := filepath.Join("/dev/disk/by-partuuid", partitionUUID) - logger.Debugf("changing encryption key on device: %v", dev) + logger.Debugf("stage encryption key change on device: %v", dev) var buf bytes.Buffer err = json.NewEncoder(&buf).Encode(struct { @@ -194,6 +196,39 @@ func ChangeEncryptionKey(node string, key keys.EncryptionKey) error { command := []string{ "change-encryption-key", "--device", dev, + "--stage", + } + + if err := runSnapFDEKeymgr(command, &buf); err != nil { + return fmt.Errorf("cannot run FDE key manager tool: %v", err) + } + return nil +} + +// TransitionEncryptionKeyChange transitions the encryption key on an encrypted +// device corresponding to the given mount point. The change is authorized using +// the new key, thus a prior call to StageEncryptionKeyChange must be done. +func TransitionEncryptionKeyChange(mountpoint string, key keys.EncryptionKey) error { + dev, err := devByPartUUIDFromMount(mountpoint) + if err != nil { + return fmt.Errorf("cannot find matching device: %v", err) + } + logger.Debugf("transition encryption key change on device: %v", dev) + + var buf bytes.Buffer + err = json.NewEncoder(&buf).Encode(struct { + Key []byte `json:"key"` + }{ + Key: key, + }) + if err != nil { + return fmt.Errorf("cannot encode key for the FDE key manager tool: %v", err) + } + + command := []string{ + "change-encryption-key", + "--device", dev, + "--transition", } if err := runSnapFDEKeymgr(command, &buf); err != nil { diff --git a/secboot/encrypt_sb_test.go b/secboot/encrypt_sb_test.go index 36778ae571..d5b292a2ee 100644 --- a/secboot/encrypt_sb_test.go +++ b/secboot/encrypt_sb_test.go @@ -159,8 +159,8 @@ var ( key = keys.EncryptionKey{'e', 'n', 'c', 'r', 'y', 'p', 't', 1, 1, 1, 1} ) -func (s *keymgrSuite) TestChangeEncryptionKeyHappy(c *C) { - err := secboot.ChangeEncryptionKey("/dev/foo/bar", key) +func (s *keymgrSuite) TestStageEncryptionKeyHappy(c *C) { + err := secboot.StageEncryptionKeyChange("/dev/foo/bar", key) c.Assert(err, IsNil) c.Check(s.udevadmCmd.Calls(), DeepEquals, [][]string{ {"udevadm", "info", "--query", "property", "--name", "/dev/foo/bar"}, @@ -171,10 +171,11 @@ func (s *keymgrSuite) TestChangeEncryptionKeyHappy(c *C) { "--wait", "--pipe", "--collect", "--service-type=exec", "--quiet", "--property=KeyringMode=inherit", "--", s.keymgrCmd.Exe(), "change-encryption-key", "--device", "/dev/disk/by-partuuid/something", + "--stage", }, }) c.Check(s.keymgrCmd.Calls(), DeepEquals, [][]string{ - {"snap-fde-keymgr", "change-encryption-key", "--device", "/dev/disk/by-partuuid/something"}, + {"snap-fde-keymgr", "change-encryption-key", "--device", "/dev/disk/by-partuuid/something", "--stage"}, }) var b bytes.Buffer json.NewEncoder(&b).Encode(struct { @@ -185,12 +186,12 @@ func (s *keymgrSuite) TestChangeEncryptionKeyHappy(c *C) { c.Check(filepath.Join(s.d, "input"), testutil.FileEquals, b.String()) } -func (s *keymgrSuite) TestChangeEncryptionKeyBadUdev(c *C) { +func (s *keymgrSuite) TestStageEncryptionKeyBadUdev(c *C) { udevadmCmd := testutil.MockCommand(c, "udevadm", ` echo "unhappy udev" `) defer udevadmCmd.Restore() - err := secboot.ChangeEncryptionKey("/dev/foo/bar", key) + err := secboot.StageEncryptionKeyChange("/dev/foo/bar", key) c.Assert(err, ErrorMatches, "cannot get UUID of partition /dev/foo/bar: cannot get required udev partition UUID property") c.Check(udevadmCmd.Calls(), DeepEquals, [][]string{ {"udevadm", "info", "--query", "property", "--name", "/dev/foo/bar"}, @@ -199,7 +200,7 @@ func (s *keymgrSuite) TestChangeEncryptionKeyBadUdev(c *C) { c.Check(s.keymgrCmd.Calls(), HasLen, 0) } -func (s *keymgrSuite) TestChangeEncryptionKeyBadKeymgr(c *C) { +func (s *keymgrSuite) TestStageTransitionEncryptionKeyBadKeymgr(c *C) { keymgrCmd := testutil.MockCommand(c, "snap-fde-keymgr", `echo keymgr very unhappy; exit 1`) defer keymgrCmd.Restore() // update where /proc/self/exe resolves to @@ -208,26 +209,87 @@ func (s *keymgrSuite) TestChangeEncryptionKeyBadKeymgr(c *C) { }) defer restore() - err := secboot.ChangeEncryptionKey("/dev/foo/bar", key) + err := secboot.StageEncryptionKeyChange("/dev/foo/bar", key) c.Assert(err, ErrorMatches, "cannot run FDE key manager tool: cannot run .*: keymgr very unhappy") - c.Check(s.udevadmCmd.Calls(), DeepEquals, [][]string{ - {"udevadm", "info", "--query", "property", "--name", "/dev/foo/bar"}, - }) c.Check(s.systemdRunCmd.Calls(), DeepEquals, [][]string{ { "systemd-run", "--wait", "--pipe", "--collect", "--service-type=exec", "--quiet", "--property=KeyringMode=inherit", "--", keymgrCmd.Exe(), "change-encryption-key", "--device", "/dev/disk/by-partuuid/something", + "--stage", }, }) c.Check(keymgrCmd.Calls(), DeepEquals, [][]string{ - {"snap-fde-keymgr", "change-encryption-key", "--device", "/dev/disk/by-partuuid/something"}, + {"snap-fde-keymgr", "change-encryption-key", "--device", "/dev/disk/by-partuuid/something", "--stage"}, }) + + s.systemdRunCmd.ForgetCalls() + keymgrCmd.ForgetCalls() + + s.mocksForDeviceMounts(c) + err = secboot.TransitionEncryptionKeyChange("/foo", key) + c.Assert(err, ErrorMatches, "cannot run FDE key manager tool: cannot run .*: keymgr very unhappy") + + c.Check(s.systemdRunCmd.Calls(), DeepEquals, [][]string{ + { + "systemd-run", + "--wait", "--pipe", "--collect", "--service-type=exec", "--quiet", + "--property=KeyringMode=inherit", "--", + keymgrCmd.Exe(), "change-encryption-key", "--device", "/dev/disk/by-partuuid/foo-uuid", + "--transition", + }, + }) + c.Check(keymgrCmd.Calls(), DeepEquals, [][]string{ + {"snap-fde-keymgr", "change-encryption-key", "--device", "/dev/disk/by-partuuid/foo-uuid", "--transition"}, + }) +} + +func (s *keymgrSuite) TestTransitionEncryptionKeyNoMountDev(c *C) { + restore := osutil.MockMountInfo(` +27 27 600:3 / /foo rw,relatime shared:7 - vfat /dev/mapper/foo rw +`[1:]) + s.AddCleanup(restore) + + udevadmCmd := testutil.MockCommand(c, "udevadm", `echo nope; exit 1`) + defer udevadmCmd.Restore() + + err := secboot.TransitionEncryptionKeyChange("/foo", key) + c.Assert(err, ErrorMatches, "cannot find matching device: cannot partition for mount /foo: cannot process udev properties of /dev/mapper/foo: nope") +} + +func (s *keymgrSuite) TestTransitionEncryptionKeyHappy(c *C) { + udevadmCmd := s.mocksForDeviceMounts(c) + + err := secboot.TransitionEncryptionKeyChange("/foo", key) + c.Assert(err, IsNil) + c.Check(udevadmCmd.Calls(), DeepEquals, [][]string{ + {"udevadm", "info", "--query", "property", "--name", "/dev/mapper/foo"}, + {"udevadm", "info", "--query", "property", "--name", "/dev/disk/by-uuid/5a522809-c87e-4dfa-81a8-8dc5667d1304"}, + }) + c.Check(s.systemdRunCmd.Calls(), DeepEquals, [][]string{ + { + "systemd-run", + "--wait", "--pipe", "--collect", "--service-type=exec", "--quiet", + "--property=KeyringMode=inherit", "--", + s.keymgrCmd.Exe(), "change-encryption-key", "--device", "/dev/disk/by-partuuid/foo-uuid", + "--transition", + }, + }) + c.Check(s.keymgrCmd.Calls(), DeepEquals, [][]string{ + {"snap-fde-keymgr", "change-encryption-key", "--device", "/dev/disk/by-partuuid/foo-uuid", "--transition"}, + }) + var b bytes.Buffer + json.NewEncoder(&b).Encode(struct { + Key []byte `json:"key"` + }{ + Key: key, + }) + c.Check(filepath.Join(s.d, "input"), testutil.FileEquals, b.String()) } -func (s *keymgrSuite) mocksForRecoveryKeys(c *C) (udevadmCmd *testutil.MockCmd) { +func (s *keymgrSuite) mocksForDeviceMounts(c *C) (udevadmCmd *testutil.MockCmd) { restore := osutil.MockMountInfo(` 27 27 600:3 / /foo rw,relatime shared:7 - vfat /dev/mapper/foo rw 27 27 600:4 / /bar rw,relatime shared:7 - vfat /dev/mapper/bar rw @@ -279,7 +341,7 @@ done } func (s *keymgrSuite) TestEnsureRecoveryKey(c *C) { - udevadmCmd := s.mocksForRecoveryKeys(c) + udevadmCmd := s.mocksForDeviceMounts(c) rkey, err := secboot.EnsureRecoveryKey(filepath.Join(s.d, "recovery.key"), []secboot.RecoveryKeyDevice{ {Mountpoint: "/foo"}, @@ -317,7 +379,7 @@ func (s *keymgrSuite) TestEnsureRecoveryKey(c *C) { } func (s *keymgrSuite) TestRemoveRecoveryKey(c *C) { - udevadmCmd := s.mocksForRecoveryKeys(c) + udevadmCmd := s.mocksForDeviceMounts(c) snaptest.PopulateDir(s.d, [][]string{ {"recovery.key", "foobar"}, |
