summaryrefslogtreecommitdiff
path: root/kernel
diff options
authorIan Johnson <ian.johnson@canonical.com>2022-03-11 11:21:00 -0600
committerIan Johnson <ian.johnson@canonical.com>2022-03-11 11:21:00 -0600
commitac539fa26903ad06a3c6060c6cb9a0805e7f497a (patch)
tree0953e8032904896d1abecf5a411ada6b0e5f1ae9 /kernel
parent314af6e91282fbecc86da3d0fb8ce78f49070c68 (diff)
parentc1255a113b76c59c1ac2eada93968e021c283082 (diff)
Merge branch 'master' into ice/fde-device-unlock
Signed-off-by: Ian Johnson <ian.johnson@canonical.com>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/fde/fde.go64
-rw-r--r--kernel/fde/fde_test.go70
-rw-r--r--kernel/fde/mapper.go52
3 files changed, 184 insertions, 2 deletions
diff --git a/kernel/fde/fde.go b/kernel/fde/fde.go
index d65bd873bb..de7b32438d 100644
--- a/kernel/fde/fde.go
+++ b/kernel/fde/fde.go
@@ -29,6 +29,8 @@ import (
"encoding/json"
"fmt"
"os/exec"
+
+ "github.com/snapcore/snapd/osutil"
)
// HasRevealKey return true if the current system has a "fde-reveal-key"
@@ -80,16 +82,25 @@ func unmarshalInitialSetupResult(hookOutput []byte) (*InitialSetupResult, error)
return &res, nil
}
+// TODO: unexport this because how the hook is driven is an implemenation
+// detail. It creates quite a bit of churn unfortunately, see
+// https://github.com/snapcore/snapd/compare/master...mvo5:ice/refactor-fde?expand=1
+//
// SetupRequest carries the operation and parameters for the fde-setup hooks
// made available to them via the snapctl fde-setup-request command.
type SetupRequest struct {
- // XXX: make "op" a type: "features", "initial-setup", "update" ?
Op string `json:"op"`
// This needs to be a []byte so that Go's standard library will base64
// encode it automatically for us
- Key []byte `json:"key,omitempty"`
+ Key []byte `json:"key,omitempty"`
+
+ // Only used when called with "initial-setup"
KeyName string `json:"key-name,omitempty"`
+
+ // The part of the device kernel path for a "setup-device" call.
+ // Only used when called with "device-setup"
+ Device string `json:"device,omitempty"`
}
// A RunSetupHookFunc implements running the fde-setup kernel hook.
@@ -126,3 +137,52 @@ func InitialSetup(runSetupHook RunSetupHookFunc, params *InitialSetupParams) (*I
}
return res, nil
}
+
+// CheckFeatures returns the features of fde-setup hook.
+func CheckFeatures(runSetupHook RunSetupHookFunc) ([]string, error) {
+ req := &SetupRequest{
+ Op: "features",
+ }
+ output, err := runSetupHook(req)
+ if err != nil {
+ return nil, err
+ }
+ var res struct {
+ Features []string `json:"features"`
+ Error string `json:"error"`
+ }
+ if err := json.Unmarshal(output, &res); err != nil {
+ return nil, fmt.Errorf("cannot parse hook output %q: %v", output, err)
+ }
+ if res.Features == nil && res.Error == "" {
+ return nil, fmt.Errorf(`cannot use hook: neither "features" nor "error" returned`)
+ }
+ if res.Error != "" {
+ return nil, fmt.Errorf("cannot use hook: it returned error: %v", res.Error)
+ }
+ return res.Features, nil
+}
+
+// DeviceSetupParams contains the inputs for the fde-setup hook.
+// The encryption key and the device (partition) are passed in.
+type DeviceSetupParams struct {
+ Key []byte
+ Device string
+}
+
+// DeviceSetup invokes the "device-setup" op running the fde-setup
+// hook via runSetupHook. This can be used to e.g. initialize
+// inline crypto hardware.
+func DeviceSetup(runSetupHook RunSetupHookFunc, params *DeviceSetupParams) error {
+ req := &SetupRequest{
+ Op: "device-setup",
+ Key: params.Key,
+ Device: params.Device,
+ }
+ hookOutput, err := runSetupHook(req)
+ if err != nil {
+ return fmt.Errorf("device setup failed with: %v", osutil.OutputErr(hookOutput, err))
+ }
+
+ return nil
+}
diff --git a/kernel/fde/fde_test.go b/kernel/fde/fde_test.go
index 3b53743fa7..ca2819a5e9 100644
--- a/kernel/fde/fde_test.go
+++ b/kernel/fde/fde_test.go
@@ -528,6 +528,52 @@ func (s *fdeSuite) TestRevealErr(c *C) {
c.Check(osutil.FileExists(filepath.Join(dirs.GlobalRootDir, "/run/fde-reveal-key")), Equals, false)
}
+func (s *fdeSuite) TestDeviceSetupHappy(c *C) {
+ mockKey := []byte{1, 2, 3, 4}
+ mockDevice := "/dev/sda2"
+
+ runSetupHook := func(req *fde.SetupRequest) ([]byte, error) {
+ c.Check(req, DeepEquals, &fde.SetupRequest{
+ Op: "device-setup",
+ Key: mockKey,
+ Device: mockDevice,
+ })
+ // empty reply: no error
+ mockJSON := `{}`
+ return []byte(mockJSON), nil
+ }
+
+ params := &fde.DeviceSetupParams{
+ Key: mockKey,
+ Device: mockDevice,
+ }
+ err := fde.DeviceSetup(runSetupHook, params)
+ c.Assert(err, IsNil)
+}
+
+func (s *fdeSuite) TestDeviceSetupError(c *C) {
+ mockKey := []byte{1, 2, 3, 4}
+ mockDevice := "/dev/sda2"
+
+ runSetupHook := func(req *fde.SetupRequest) ([]byte, error) {
+ c.Check(req, DeepEquals, &fde.SetupRequest{
+ Op: "device-setup",
+ Key: mockKey,
+ Device: mockDevice,
+ })
+ // empty reply: no error
+ mockJSON := `something failed badly`
+ return []byte(mockJSON), fmt.Errorf("exit status 1")
+ }
+
+ params := &fde.DeviceSetupParams{
+ Key: mockKey,
+ Device: mockDevice,
+ }
+ err := fde.DeviceSetup(runSetupHook, params)
+ c.Check(err, ErrorMatches, "device setup failed with: something failed badly")
+}
+
func (s *fdeSuite) TestDeviceUnlock(c *C) {
checkSystemdRunOrSkip(c)
@@ -607,3 +653,27 @@ func (s *fdeSuite) TestHasDeviceUnlock(c *C) {
c.Check(fde.HasDeviceUnlock(), Equals, true)
}
+
+func (s *fdeSuite) TestIsEncryptedDeviceMapperName(c *C) {
+ // matches
+ for _, t := range []string{
+ "something-device-locked",
+ "foo23-device-locked",
+ "device-device-locked",
+ "WE-DON'T-CARE-WHAT-THE-PREFIX-IS-AND-YOU-CAN'T-MAKE-US-device-locked",
+ } {
+ c.Assert(fde.IsHardwareEncryptedDeviceMapperName(t), Equals, true)
+ }
+
+ // doesn't match
+ for _, t := range []string{
+ "",
+ "-device-locked",
+ "device-locked",
+ "-device-locked-foo",
+ "some-device",
+ "CRYPT-LUKS2-5a522809c87e4dfa81a88dc5667d1304-ubuntu-data-3776bab4-8bcc-46b7-9da2-6a84ce7f93b4",
+ } {
+ c.Assert(fde.IsHardwareEncryptedDeviceMapperName(t), Equals, false)
+ }
+}
diff --git a/kernel/fde/mapper.go b/kernel/fde/mapper.go
new file mode 100644
index 0000000000..a1928a697b
--- /dev/null
+++ b/kernel/fde/mapper.go
@@ -0,0 +1,52 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2021 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package fde
+
+import (
+ "path/filepath"
+ "strings"
+
+ "github.com/snapcore/snapd/osutil/disks"
+)
+
+// IsEncryptedDevice returns true when the provided device mapper name indicates
+// that it is encrypted using FDE hooks.
+func IsHardwareEncryptedDeviceMapperName(dmName string) bool {
+ // TODO: is there anything more we can use to limit the prefix of the
+ // dmName?
+ return dmName != "-device-locked" && strings.HasSuffix(dmName, "-device-locked")
+}
+
+// DeviceUnlockKernelHookDeviceMapperBackResolver is a back resolver to be used
+// with disks.RegisterDeviceMapperBackResolver for devices that implement full
+// disk encryption via hardware devices with kernel snap hooks.
+func DeviceUnlockKernelHookDeviceMapperBackResolver(dmUUID, dmName []byte) (dev string, ok bool) {
+ if !IsHardwareEncryptedDeviceMapperName(string(dmName)) {
+ return "", false
+ }
+ // this is a device encrypted using FDE hooks
+
+ // the uuid of the mapper device is the same as the partuuid
+ return filepath.Join("/dev/disk/by-partuuid", string(dmUUID)), true
+}
+
+func init() {
+ disks.RegisterDeviceMapperBackResolver("device-unlock-kernel-fde", DeviceUnlockKernelHookDeviceMapperBackResolver)
+}