From 6ffc9457945a9484d2757cc4b01de35426502d0a Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sun, 15 Dec 2019 19:31:39 -0800 Subject: keyring: support filesystem keyring with v1 encryption policies Linux v5.4 and later allows fscrypt keys to be added/removed directly to/from the filesystem via the new ioctls FS_IOC_ADD_ENCRYPTION_KEY and FS_IOC_REMOVE_ENCRYPTION_KEY. Among other benefits, these fix the key visibility problems that many users have been running into, where system services and containers can't access encrypted files. Allow the user to opt-in to using these new ioctls for their existing encrypted directories by setting in their /etc/fscrypt.conf: "use_fs_keyring_for_v1_policies": true Note that it can't really be on by default, since for v1 policies the ioctls require root, whereas user keyrings don't. I.e., setting this to true means that users will need to use 'sudo fscrypt unlock', not 'fscrypt unlock'. v2 policies won't have this restriction. --- keyring/keyring_test.go | 132 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 103 insertions(+), 29 deletions(-) (limited to 'keyring/keyring_test.go') diff --git a/keyring/keyring_test.go b/keyring/keyring_test.go index 10ff874..9a4570b 100644 --- a/keyring/keyring_test.go +++ b/keyring/keyring_test.go @@ -24,6 +24,7 @@ import ( "golang.org/x/sys/unix" "github.com/google/fscrypt/crypto" + "github.com/google/fscrypt/filesystem" "github.com/google/fscrypt/metadata" "github.com/google/fscrypt/util" ) @@ -44,52 +45,125 @@ func makeKey(b byte, n int) (*crypto.Key, error) { } var ( - fakeValidDescriptor = "0123456789abcdef" defaultService = unix.FSCRYPT_KEY_DESC_PREFIX testUser, _ = util.EffectiveUser() fakeValidPolicyKey, _ = makeKey(42, metadata.PolicyKeyLen) fakeInvalidPolicyKey, _ = makeKey(42, metadata.PolicyKeyLen-1) + fakeV1Descriptor = "0123456789abcdef" ) -// Adds and removes a key with various services. -func TestAddRemoveKeys(t *testing.T) { - for _, service := range []string{defaultService, "ext4:", "f2fs:"} { - options := &Options{ - User: testUser, - Service: service, - } - if err := AddEncryptionKey(fakeValidPolicyKey, fakeValidDescriptor, options); err != nil { - t.Error(err) - } - if err := RemoveEncryptionKey(fakeValidDescriptor, options); err != nil { - t.Error(err) - } +func assertKeyStatus(t *testing.T, descriptor string, options *Options, + expectedStatus KeyStatus) { + status, err := GetEncryptionKeyStatus(descriptor, options) + if err != nil { + t.Error(err) + } + if status != expectedStatus { + t.Errorf("Expected key status %v but got key status %v", expectedStatus, status) } } -// Adds a key twice (both should succeed) -func TestAddTwice(t *testing.T) { - options := &Options{ - User: testUser, - Service: defaultService, +// getTestMount retrieves the Mount for a test filesystem, or skips the test if +// no test filesystem is available. +func getTestMount(t *testing.T) *filesystem.Mount { + root, err := util.TestRoot() + if err != nil { + t.Skip(err) + } + mount, err := filesystem.GetMount(root) + if err != nil { + t.Skip(err) + } + return mount +} + +// getTestMountV2 is like getTestMount, but it also checks that the filesystem +// keyring is supported. +func getTestMountV2(t *testing.T) *filesystem.Mount { + mount := getTestMount(t) + if !isFsKeyringSupported(mount) { + t.Skip("No support for fs keyring, skipping test.") + } + return mount +} + +func requireRoot(t *testing.T) { + if !util.IsUserRoot() { + t.Skip("Not root, skipping test.") } - if err := AddEncryptionKey(fakeValidPolicyKey, fakeValidDescriptor, options); err != nil { +} + +// testAddAndRemoveKey does the common tests for adding+removing keys that are +// run in multiple configurations (v1 policies with user keyring and v1 policies +// with fs keyring). +func testAddAndRemoveKey(t *testing.T, descriptor string, options *Options) { + + // Basic add, get status, and remove + if err := AddEncryptionKey(fakeValidPolicyKey, descriptor, options); err != nil { t.Error(err) } - if err := AddEncryptionKey(fakeValidPolicyKey, fakeValidDescriptor, options); err != nil { + assertKeyStatus(t, descriptor, options, KeyPresent) + if err := RemoveEncryptionKey(descriptor, options); err != nil { + t.Error(err) + } + assertKeyStatus(t, descriptor, options, KeyAbsent) + err := RemoveEncryptionKey(descriptor, options) + if err != ErrKeyNotPresent { + t.Error(err) + } + + // Adding a key twice should succeed + if err := AddEncryptionKey(fakeValidPolicyKey, descriptor, options); err != nil { + t.Error(err) + } + if err := AddEncryptionKey(fakeValidPolicyKey, descriptor, options); err != nil { t.Error("AddEncryptionKey should not fail if key already exists") } - RemoveEncryptionKey(fakeValidDescriptor, options) + RemoveEncryptionKey(descriptor, options) + assertKeyStatus(t, descriptor, options, KeyAbsent) + + // Adding a key with wrong length should fail + if err := AddEncryptionKey(fakeInvalidPolicyKey, descriptor, options); err == nil { + RemoveEncryptionKey(descriptor, options) + t.Error("AddEncryptionKey should fail with wrong-length key") + } + assertKeyStatus(t, descriptor, options, KeyAbsent) } -// Makes sure trying to add a key of the wrong length fails -func TestAddWrongLengthKey(t *testing.T) { +func TestUserKeyringDefaultService(t *testing.T) { options := &Options{ - User: testUser, - Service: defaultService, + User: testUser, + Service: defaultService, + UseFsKeyringForV1Policies: false, } - if err := AddEncryptionKey(fakeInvalidPolicyKey, fakeValidDescriptor, options); err == nil { - RemoveEncryptionKey(fakeValidDescriptor, options) - t.Error("AddEncryptionKey should fail with wrong-length key") + testAddAndRemoveKey(t, fakeV1Descriptor, options) +} + +func TestUserKeyringExt4Service(t *testing.T) { + options := &Options{ + User: testUser, + Service: "ext4:", + UseFsKeyringForV1Policies: false, + } + testAddAndRemoveKey(t, fakeV1Descriptor, options) +} + +func TestUserKeyringF2fsService(t *testing.T) { + options := &Options{ + User: testUser, + Service: "f2fs:", + UseFsKeyringForV1Policies: false, + } + testAddAndRemoveKey(t, fakeV1Descriptor, options) +} + +func TestFsKeyringV1PolicyKey(t *testing.T) { + requireRoot(t) + mount := getTestMountV2(t) + options := &Options{ + Mount: mount, + User: testUser, + UseFsKeyringForV1Policies: true, } + testAddAndRemoveKey(t, fakeV1Descriptor, options) } -- cgit v1.2.3