diff options
| author | Eric Biggers <ebiggers@google.com> | 2019-12-15 19:31:39 -0800 |
|---|---|---|
| committer | Eric Biggers <ebiggers@google.com> | 2020-01-05 10:02:13 -0800 |
| commit | 42e0dfe85ec7a75a2fa30c417d57eae60b5a881d (patch) | |
| tree | ffa9fb08a5bbe51ec23bf219ef206ddc81799453 /keyring/keyring_test.go | |
| parent | 2b25de6d445faefc28629603dd754aec9f744e60 (diff) | |
Keyring support for v2 encryption policies
Implement adding/removing v2 encryption policy keys to/from the kernel.
The kernel requires that the new ioctls FS_IOC_ADD_ENCRYPTION_KEY and
FS_IOC_REMOVE_ENCRYPTION_KEY be used for this. Root is not required.
However, non-root support brings an extra complication: the kernel keeps
track of which users have called FS_IOC_ADD_ENCRYPTION_KEY for the same
key. FS_IOC_REMOVE_ENCRYPTION_KEY only works as one of these users, and
it only removes the calling user's claim to the key; the key is only
truly removed when the last claim is removed.
Implement the following behavior:
- 'fscrypt unlock' and pam_fscrypt add the key for the user, even if
other user(s) have it added already. This behavior is needed so that
another user can't remove the key out from under the user.
- 'fscrypt lock' and pam_fscrypt remove the key for the user. However,
if the key wasn't truly removed because other users still have it
added, 'fscrypt lock' prints a warning.
- 'fscrypt status' shows whether the directory is unlocked for anyone.
Diffstat (limited to 'keyring/keyring_test.go')
| -rw-r--r-- | keyring/keyring_test.go | 163 |
1 files changed, 159 insertions, 4 deletions
diff --git a/keyring/keyring_test.go b/keyring/keyring_test.go index 9a4570b..a675a70 100644 --- a/keyring/keyring_test.go +++ b/keyring/keyring_test.go @@ -19,6 +19,8 @@ package keyring import ( + "os/user" + "strconv" "testing" "golang.org/x/sys/unix" @@ -50,6 +52,7 @@ var ( fakeValidPolicyKey, _ = makeKey(42, metadata.PolicyKeyLen) fakeInvalidPolicyKey, _ = makeKey(42, metadata.PolicyKeyLen-1) fakeV1Descriptor = "0123456789abcdef" + fakeV2Descriptor, _ = crypto.ComputeKeyDescriptor(fakeValidPolicyKey, 2) ) func assertKeyStatus(t *testing.T, descriptor string, options *Options, @@ -77,8 +80,8 @@ func getTestMount(t *testing.T) *filesystem.Mount { return mount } -// getTestMountV2 is like getTestMount, but it also checks that the filesystem -// keyring is supported. +// getTestMountV2 is like getTestMount, but it also checks that the +// filesystem keyring and v2 encryption policies are supported. func getTestMountV2(t *testing.T) *filesystem.Mount { mount := getTestMount(t) if !isFsKeyringSupported(mount) { @@ -93,9 +96,42 @@ func requireRoot(t *testing.T) { } } +// getNonRootUsers checks for root permission, then returns the users for uids +// 1000...1000+count-1. If this fails, the test is skipped. +func getNonRootUsers(t *testing.T, count int) []*user.User { + requireRoot(t) + users := make([]*user.User, count) + for i := 0; i < count; i++ { + uid := 1000 + i + user, err := user.LookupId(strconv.Itoa(uid)) + if err != nil { + t.Skip(err) + } + users[i] = user + } + return users +} + +func getOptionsForFsKeyringUsers(t *testing.T, numNonRootUsers int) (rootOptions *Options, userOptions []*Options) { + mount := getTestMountV2(t) + nonRootUsers := getNonRootUsers(t, numNonRootUsers) + rootOptions = &Options{ + Mount: mount, + User: testUser, + } + userOptions = make([]*Options, numNonRootUsers) + for i := 0; i < numNonRootUsers; i++ { + userOptions[i] = &Options{ + Mount: mount, + User: nonRootUsers[i], + } + } + return +} + // 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). +// run in multiple configurations (v1 policies with user keyring, v1 policies +// with fs keyring, and v2 policies with fs keyring). func testAddAndRemoveKey(t *testing.T, descriptor string, options *Options) { // Basic add, get status, and remove @@ -167,3 +203,122 @@ func TestFsKeyringV1PolicyKey(t *testing.T) { } testAddAndRemoveKey(t, fakeV1Descriptor, options) } + +func TestV2PolicyKey(t *testing.T) { + mount := getTestMountV2(t) + options := &Options{ + Mount: mount, + User: testUser, + } + testAddAndRemoveKey(t, fakeV2Descriptor, options) +} + +func TestV2PolicyKeyCannotBeRemovedByAnotherUser(t *testing.T) { + rootOptions, userOptions := getOptionsForFsKeyringUsers(t, 2) + user1Options := userOptions[0] + user2Options := userOptions[1] + + // Add key as non-root user. + if err := AddEncryptionKey(fakeValidPolicyKey, fakeV2Descriptor, user1Options); err != nil { + t.Error(err) + } + assertKeyStatus(t, fakeV2Descriptor, user1Options, KeyPresent) + assertKeyStatus(t, fakeV2Descriptor, user2Options, KeyPresentButOnlyOtherUsers) + assertKeyStatus(t, fakeV2Descriptor, rootOptions, KeyPresentButOnlyOtherUsers) + + // Key shouldn't be removable by another user, even root. + err := RemoveEncryptionKey(fakeV2Descriptor, user2Options) + if err != ErrKeyAddedByOtherUsers { + t.Error(err) + } + assertKeyStatus(t, fakeV2Descriptor, user1Options, KeyPresent) + assertKeyStatus(t, fakeV2Descriptor, user2Options, KeyPresentButOnlyOtherUsers) + assertKeyStatus(t, fakeV2Descriptor, rootOptions, KeyPresentButOnlyOtherUsers) + err = RemoveEncryptionKey(fakeV2Descriptor, rootOptions) + if err != ErrKeyAddedByOtherUsers { + t.Error(err) + } + assertKeyStatus(t, fakeV2Descriptor, user1Options, KeyPresent) + assertKeyStatus(t, fakeV2Descriptor, user2Options, KeyPresentButOnlyOtherUsers) + assertKeyStatus(t, fakeV2Descriptor, rootOptions, KeyPresentButOnlyOtherUsers) + + if err := RemoveEncryptionKey(fakeV2Descriptor, user1Options); err != nil { + t.Error(err) + } + assertKeyStatus(t, fakeV2Descriptor, user1Options, KeyAbsent) + assertKeyStatus(t, fakeV2Descriptor, user2Options, KeyAbsent) + assertKeyStatus(t, fakeV2Descriptor, rootOptions, KeyAbsent) +} + +func TestV2PolicyKeyMultipleUsers(t *testing.T) { + rootOptions, userOptions := getOptionsForFsKeyringUsers(t, 2) + user1Options := userOptions[0] + user2Options := userOptions[1] + + // Add key as two non-root users. + if err := AddEncryptionKey(fakeValidPolicyKey, fakeV2Descriptor, user1Options); err != nil { + t.Error(err) + } + if err := AddEncryptionKey(fakeValidPolicyKey, fakeV2Descriptor, user2Options); err != nil { + t.Error(err) + } + assertKeyStatus(t, fakeV2Descriptor, user1Options, KeyPresent) + assertKeyStatus(t, fakeV2Descriptor, user2Options, KeyPresent) + assertKeyStatus(t, fakeV2Descriptor, rootOptions, KeyPresentButOnlyOtherUsers) + + // Remove key as one user. + err := RemoveEncryptionKey(fakeV2Descriptor, user1Options) + if err != ErrKeyAddedByOtherUsers { + t.Error(err) + } + assertKeyStatus(t, fakeV2Descriptor, user1Options, KeyPresentButOnlyOtherUsers) + assertKeyStatus(t, fakeV2Descriptor, user2Options, KeyPresent) + assertKeyStatus(t, fakeV2Descriptor, rootOptions, KeyPresentButOnlyOtherUsers) + + // Remove key as the other user. + err = RemoveEncryptionKey(fakeV2Descriptor, user2Options) + if err != nil { + t.Error(err) + } + assertKeyStatus(t, fakeV2Descriptor, user1Options, KeyAbsent) + assertKeyStatus(t, fakeV2Descriptor, user2Options, KeyAbsent) + assertKeyStatus(t, fakeV2Descriptor, rootOptions, KeyAbsent) +} + +func TestV2PolicyKeyWrongDescriptor(t *testing.T) { + mount := getTestMountV2(t) + options := &Options{ + Mount: mount, + User: testUser, + } + // one wrong but valid hex, and one not valid hex + wrongV2Descriptors := []string{"abcdabcdabcdabcdabcdabcdabcdabcd", "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"} + + for _, desc := range wrongV2Descriptors { + if err := AddEncryptionKey(fakeValidPolicyKey, desc, options); err == nil { + RemoveEncryptionKey(desc, options) + t.Error("For v2 policy keys, AddEncryptionKey should fail if the descriptor is wrong") + } + } +} + +func TestV2PolicyKeyBadMount(t *testing.T) { + options := &Options{ + Mount: &filesystem.Mount{Path: "/NONEXISTENT_MOUNT"}, + User: testUser, + } + if err := AddEncryptionKey(fakeValidPolicyKey, fakeV2Descriptor, options); err == nil { + RemoveEncryptionKey(fakeV2Descriptor, options) + t.Error("AddEncryptionKey should have failed with bad mount!") + } + if err := RemoveEncryptionKey(fakeV2Descriptor, options); err == nil { + t.Error("RemoveEncryptionKey should have failed with bad mount!") + } + status, err := GetEncryptionKeyStatus(fakeV2Descriptor, options) + if err == nil { + t.Error("GetEncryptionKeyStatus should have failed with bad mount!") + } + if status != KeyStatusUnknown { + t.Error("GetEncryptionKeyStatus should have returned unknown status!") + } +} |