diff options
Diffstat (limited to 'keyring/keyring_test.go')
| -rw-r--r-- | keyring/keyring_test.go | 350 |
1 files changed, 350 insertions, 0 deletions
diff --git a/keyring/keyring_test.go b/keyring/keyring_test.go new file mode 100644 index 0000000..8912556 --- /dev/null +++ b/keyring/keyring_test.go @@ -0,0 +1,350 @@ +/* + * keyring_test.go - tests for the keyring package + * + * Copyright 2017 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package keyring + +import ( + "os/user" + "strconv" + "testing" + + "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" +) + +// Reader that always returns the same byte +type ConstReader byte + +func (r ConstReader) Read(b []byte) (n int, err error) { + for i := range b { + b[i] = byte(r) + } + return len(b), nil +} + +// Makes a key of the same repeating byte +func makeKey(b byte, n int) (*crypto.Key, error) { + return crypto.NewFixedLengthKeyFromReader(ConstReader(b), n) +} + +var ( + defaultService = unix.FSCRYPT_KEY_DESC_PREFIX + testUser, _ = util.EffectiveUser() + 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, + 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) + } +} + +// 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 and v2 encryption policies are 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.") + } +} + +// 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, 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 + if err := AddEncryptionKey(fakeValidPolicyKey, descriptor, options); err != nil { + t.Error(err) + } + assertKeyStatus(t, descriptor, options, KeyPresent) + if err := RemoveEncryptionKey(descriptor, options, false); err != nil { + t.Error(err) + } + assertKeyStatus(t, descriptor, options, KeyAbsent) + err := RemoveEncryptionKey(descriptor, options, false) + 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(descriptor, options, false) + assertKeyStatus(t, descriptor, options, KeyAbsent) + + // Adding a key with wrong length should fail + if err := AddEncryptionKey(fakeInvalidPolicyKey, descriptor, options); err == nil { + RemoveEncryptionKey(descriptor, options, false) + t.Error("AddEncryptionKey should fail with wrong-length key") + } + assertKeyStatus(t, descriptor, options, KeyAbsent) +} + +func TestUserKeyringDefaultService(t *testing.T) { + options := &Options{ + User: testUser, + Service: defaultService, + UseFsKeyringForV1Policies: false, + } + 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) +} + +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, false) + 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, false) + 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, false); 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, false) + 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, false) + 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, false) + 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, false) + t.Error("AddEncryptionKey should have failed with bad mount!") + } + if err := RemoveEncryptionKey(fakeV2Descriptor, options, false); 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!") + } +} + +func TestV2PolicyKeyRemoveForAllUsers(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 for all users as root. + err := RemoveEncryptionKey(fakeV2Descriptor, rootOptions, true) + if err != nil { + t.Error(err) + } + assertKeyStatus(t, fakeV2Descriptor, user1Options, KeyAbsent) + assertKeyStatus(t, fakeV2Descriptor, user2Options, KeyAbsent) + assertKeyStatus(t, fakeV2Descriptor, rootOptions, KeyAbsent) +} |