| // Copyright 2020 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "cryptohome/storage/homedirs.h" |
| |
| #include <optional> |
| #include <set> |
| #include <string> |
| #include <vector> |
| |
| #include <base/files/file_path.h> |
| #include <base/logging.h> |
| #include <base/time/time.h> |
| #include <brillo/cryptohome.h> |
| // TODO(b/177929620): Cleanup once lvm utils are built unconditionally. |
| #if USE_LVM_STATEFUL_PARTITION |
| #include <brillo/blkdev_utils/mock_lvm.h> |
| #endif // USE_LVM_STATEFUL_PARTITION |
| #include <brillo/secure_blob.h> |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| #include <policy/mock_device_policy.h> |
| |
| #include "cryptohome/credentials.h" |
| #include "cryptohome/crypto.h" |
| #include "cryptohome/filesystem_layout.h" |
| #include "cryptohome/mock_keyset_management.h" |
| #include "cryptohome/mock_platform.h" |
| #include "cryptohome/storage/cryptohome_vault_factory.h" |
| #include "cryptohome/storage/encrypted_container/encrypted_container.h" |
| #include "cryptohome/storage/encrypted_container/encrypted_container_factory.h" |
| #include "cryptohome/storage/encrypted_container/fake_backing_device.h" |
| #include "cryptohome/storage/keyring/fake_keyring.h" |
| #include "cryptohome/storage/mount_constants.h" |
| |
| using ::testing::_; |
| using ::testing::DoAll; |
| using ::testing::Eq; |
| using ::testing::NiceMock; |
| using ::testing::Return; |
| |
| namespace cryptohome { |
| namespace { |
| |
| struct UserPassword { |
| const char* name; |
| const char* password; |
| }; |
| |
| constexpr char kUser0[] = "First User"; |
| constexpr char kUserPassword0[] = "user0_pass"; |
| constexpr char kUser1[] = "Second User"; |
| constexpr char kUserPassword1[] = "user1_pass"; |
| constexpr char kUser2[] = "Third User"; |
| constexpr char kUserPassword2[] = "user2_pass"; |
| constexpr char kOwner[] = "I am the device owner"; |
| constexpr char kOwnerPassword[] = "owner_pass"; |
| |
| constexpr int kOwnerIndex = 3; |
| |
| ACTION_P2(SetOwner, owner_known, owner) { |
| if (owner_known) |
| *arg0 = owner; |
| return owner_known; |
| } |
| |
| ACTION_P(SetEphemeralUsersEnabled, ephemeral_users_enabled) { |
| *arg0 = ephemeral_users_enabled; |
| return true; |
| } |
| |
| struct UserInfo { |
| std::string name; |
| std::string obfuscated; |
| brillo::SecureBlob passkey; |
| Credentials credentials; |
| base::FilePath homedir_path; |
| base::FilePath user_path; |
| }; |
| |
| struct test_homedir { |
| const char* obfuscated; |
| base::Time::Exploded time; |
| }; |
| |
| } // namespace |
| |
| class HomeDirsTest |
| : public ::testing::TestWithParam<bool /* should_test_ecryptfs */> { |
| public: |
| HomeDirsTest() |
| : crypto_(&platform_), |
| mock_device_policy_(new policy::MockDevicePolicy()) {} |
| ~HomeDirsTest() override {} |
| |
| // Not copyable or movable |
| HomeDirsTest(const HomeDirsTest&) = delete; |
| HomeDirsTest& operator=(const HomeDirsTest&) = delete; |
| HomeDirsTest(HomeDirsTest&&) = delete; |
| HomeDirsTest& operator=(HomeDirsTest&&) = delete; |
| |
| void SetUp() override { |
| PreparePolicy(true, kOwner, false, ""); |
| |
| std::unique_ptr<EncryptedContainerFactory> container_factory = |
| std::make_unique<EncryptedContainerFactory>( |
| &platform_, std::make_unique<FakeKeyring>(), |
| std::make_unique<FakeBackingDeviceFactory>(&platform_)); |
| HomeDirs::RemoveCallback remove_callback = |
| base::BindRepeating(&MockKeysetManagement::RemoveLECredentials, |
| base::Unretained(&keyset_management_)); |
| homedirs_ = std::make_unique<HomeDirs>( |
| &platform_, |
| std::make_unique<policy::PolicyProvider>( |
| std::unique_ptr<policy::MockDevicePolicy>(mock_device_policy_)), |
| remove_callback, |
| std::make_unique<CryptohomeVaultFactory>(&platform_, |
| std::move(container_factory))); |
| |
| AddUser(kUser0, kUserPassword0); |
| AddUser(kUser1, kUserPassword1); |
| AddUser(kUser2, kUserPassword2); |
| AddUser(kOwner, kOwnerPassword); |
| |
| ASSERT_EQ(kOwner, users_[kOwnerIndex].name); |
| |
| PrepareDirectoryStructure(); |
| } |
| |
| void AddUser(const char* name, const char* password) { |
| std::string obfuscated = brillo::cryptohome::home::SanitizeUserName(name); |
| brillo::SecureBlob passkey(password); |
| Credentials credentials(name, passkey); |
| |
| UserInfo info = {name, |
| obfuscated, |
| passkey, |
| credentials, |
| UserPath(obfuscated), |
| brillo::cryptohome::home::GetHashedUserPath(obfuscated)}; |
| users_.push_back(info); |
| } |
| |
| void PreparePolicy(bool owner_known, |
| const std::string& owner, |
| bool ephemeral_users_enabled, |
| const std::string& clean_up_strategy) { |
| EXPECT_CALL(*mock_device_policy_, LoadPolicy()) |
| .WillRepeatedly(Return(true)); |
| EXPECT_CALL(*mock_device_policy_, GetOwner(_)) |
| .WillRepeatedly(SetOwner(owner_known, owner)); |
| EXPECT_CALL(*mock_device_policy_, GetEphemeralUsersEnabled(_)) |
| .WillRepeatedly(SetEphemeralUsersEnabled(ephemeral_users_enabled)); |
| } |
| |
| // Returns true if the test is running for eCryptfs, false if for dircrypto. |
| bool ShouldTestEcryptfs() const { return GetParam(); } |
| |
| protected: |
| NiceMock<MockPlatform> platform_; |
| MockKeysetManagement keyset_management_; |
| Crypto crypto_; |
| policy::MockDevicePolicy* mock_device_policy_; // owned by homedirs_ |
| std::unique_ptr<HomeDirs> homedirs_; |
| |
| // Information about users' homedirs. The order of users is equal to kUsers. |
| std::vector<UserInfo> users_; |
| |
| static const uid_t kAndroidSystemRealUid = |
| HomeDirs::kAndroidSystemUid + kArcContainerShiftUid; |
| |
| void PrepareDirectoryStructure() { |
| ASSERT_TRUE(platform_.CreateDirectory( |
| brillo::cryptohome::home::GetUserPathPrefix())); |
| for (const auto& user : users_) { |
| ASSERT_TRUE(platform_.CreateDirectory(user.homedir_path)); |
| ASSERT_TRUE( |
| platform_.CreateDirectory(user.homedir_path.Append(kMountDir))); |
| if (ShouldTestEcryptfs()) { |
| ASSERT_TRUE(platform_.CreateDirectory( |
| user.homedir_path.Append(kEcryptfsVaultDir))); |
| } |
| ASSERT_TRUE(platform_.CreateDirectory(user.user_path)); |
| } |
| } |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P(WithEcryptfs, HomeDirsTest, ::testing::Values(true)); |
| INSTANTIATE_TEST_SUITE_P(WithDircrypto, HomeDirsTest, ::testing::Values(false)); |
| |
| TEST_P(HomeDirsTest, RemoveNonOwnerCryptohomes) { |
| EXPECT_TRUE(platform_.DirectoryExists(users_[0].homedir_path)); |
| EXPECT_TRUE(platform_.DirectoryExists(users_[1].homedir_path)); |
| EXPECT_TRUE(platform_.DirectoryExists(users_[2].homedir_path)); |
| EXPECT_TRUE(platform_.DirectoryExists(users_[kOwnerIndex].homedir_path)); |
| |
| EXPECT_CALL(platform_, IsDirectoryMounted(_)).WillRepeatedly(Return(false)); |
| EXPECT_CALL(keyset_management_, RemoveLECredentials(_)).Times(3); |
| |
| homedirs_->RemoveNonOwnerCryptohomes(); |
| |
| // Non-owners' vaults are removed |
| EXPECT_FALSE(platform_.DirectoryExists(users_[0].homedir_path)); |
| EXPECT_FALSE(platform_.DirectoryExists(users_[1].homedir_path)); |
| EXPECT_FALSE(platform_.DirectoryExists(users_[2].homedir_path)); |
| |
| // Owner's vault still exists |
| EXPECT_TRUE(platform_.DirectoryExists(users_[kOwnerIndex].homedir_path)); |
| } |
| |
| TEST_P(HomeDirsTest, CreateCryptohome) { |
| constexpr char kNewUserId[] = "some_new_user"; |
| const std::string kHashedNewUserId = |
| brillo::cryptohome::home::SanitizeUserName(kNewUserId); |
| const base::FilePath kNewUserPath = UserPath(kHashedNewUserId); |
| |
| EXPECT_TRUE(homedirs_->Create(kNewUserId)); |
| EXPECT_TRUE(platform_.DirectoryExists(kNewUserPath)); |
| } |
| |
| TEST_P(HomeDirsTest, RemoveCryptohome) { |
| constexpr char kNewUserId[] = "some_new_user"; |
| const std::string kHashedNewUserId = |
| brillo::cryptohome::home::SanitizeUserName(kNewUserId); |
| const base::FilePath kNewUserPath = UserPath(kHashedNewUserId); |
| |
| EXPECT_TRUE(homedirs_->Create(kNewUserId)); |
| EXPECT_TRUE(platform_.DirectoryExists(kNewUserPath)); |
| |
| EXPECT_CALL(platform_, IsDirectoryMounted(_)).WillOnce(Return(true)); |
| EXPECT_FALSE(homedirs_->Remove(kHashedNewUserId)); |
| EXPECT_TRUE(platform_.DirectoryExists(kNewUserPath)); |
| |
| EXPECT_CALL(platform_, IsDirectoryMounted(_)).WillRepeatedly(Return(false)); |
| EXPECT_TRUE(homedirs_->Remove(kHashedNewUserId)); |
| EXPECT_FALSE(platform_.DirectoryExists(kNewUserPath)); |
| } |
| |
| TEST_P(HomeDirsTest, ComputeDiskUsage) { |
| // /home/.shadow/$hash/mount in production code. |
| base::FilePath mount_dir = users_[0].homedir_path.Append(kMountDir); |
| // /home/.shadow/$hash/vault in production code. |
| base::FilePath vault_dir = users_[0].homedir_path.Append(kEcryptfsVaultDir); |
| // /home/user/$hash in production code and here in unit test. |
| base::FilePath user_dir = users_[0].user_path; |
| |
| constexpr int64_t mount_bytes = 123456789012345; |
| constexpr int64_t vault_bytes = 98765432154321; |
| |
| EXPECT_CALL(platform_, ComputeDirectoryDiskUsage(mount_dir)) |
| .WillRepeatedly(Return(mount_bytes)); |
| EXPECT_CALL(platform_, ComputeDirectoryDiskUsage(vault_dir)) |
| .WillRepeatedly(Return(vault_bytes)); |
| EXPECT_CALL(platform_, ComputeDirectoryDiskUsage(user_dir)).Times(0); |
| |
| const int64_t expected_bytes = |
| ShouldTestEcryptfs() ? vault_bytes : mount_bytes; |
| EXPECT_EQ(expected_bytes, homedirs_->ComputeDiskUsage(users_[0].name)); |
| } |
| |
| TEST_P(HomeDirsTest, ComputeDiskUsageEphemeral) { |
| // /home/.shadow/$hash/mount in production code. |
| base::FilePath mount_dir = users_[0].homedir_path.Append(kMountDir); |
| // /home/.shadow/$hash/vault in production code. |
| base::FilePath vault_dir = users_[0].homedir_path.Append(kEcryptfsVaultDir); |
| // /home/user/$hash in production code and here in unit test. |
| base::FilePath user_dir = users_[0].user_path; |
| |
| // Ephemeral users have no vault. |
| EXPECT_TRUE(platform_.DeletePathRecursively(users_[0].homedir_path)); |
| |
| constexpr int64_t userdir_bytes = 349857223479; |
| |
| EXPECT_CALL(platform_, ComputeDirectoryDiskUsage(mount_dir)).Times(0); |
| EXPECT_CALL(platform_, ComputeDirectoryDiskUsage(vault_dir)).Times(0); |
| EXPECT_CALL(platform_, ComputeDirectoryDiskUsage(user_dir)) |
| .WillRepeatedly(Return(userdir_bytes)); |
| |
| int64_t expected_bytes = userdir_bytes; |
| EXPECT_EQ(expected_bytes, homedirs_->ComputeDiskUsage(users_[0].name)); |
| } |
| |
| TEST_P(HomeDirsTest, ComputeDiskUsageWithNonexistentUser) { |
| // If the specified user doesn't exist, there is no directory for the user, so |
| // ComputeDiskUsage should return 0. |
| const char kNonExistentUserId[] = "non_existent_user"; |
| EXPECT_EQ(0, homedirs_->ComputeDiskUsage(kNonExistentUserId)); |
| } |
| |
| TEST_P(HomeDirsTest, GetTrackedDirectoryForDirCrypto) { |
| // /home/.shadow/$hash/mount in production code. |
| base::FilePath mount_dir = users_[0].homedir_path.Append(kMountDir); |
| // /home/.shadow/$hash/vault in production code. |
| base::FilePath vault_dir = users_[0].homedir_path.Append(kEcryptfsVaultDir); |
| |
| const char* const kDirectories[] = { |
| "aaa", |
| "bbb", |
| "bbb/ccc", |
| "bbb/ccc/ddd", |
| }; |
| // Prepare directories. |
| for (const auto& directory : kDirectories) { |
| const base::FilePath path = mount_dir.Append(base::FilePath(directory)); |
| ASSERT_TRUE(platform_.CreateDirectory(path)); |
| std::string name = path.BaseName().value(); |
| ASSERT_TRUE(platform_.SetExtendedFileAttribute( |
| path, kTrackedDirectoryNameAttribute, name.data(), name.length())); |
| } |
| |
| // Use GetTrackedDirectoryForDirCrypto() to get the path. |
| // When dircrypto is being used and we don't have the key, the returned path |
| // will be encrypted, but here we just get the same path. |
| for (const auto& directory : kDirectories) { |
| SCOPED_TRACE(directory); |
| base::FilePath result; |
| EXPECT_TRUE(homedirs_->GetTrackedDirectory( |
| users_[0].homedir_path, base::FilePath(directory), &result)); |
| if (ShouldTestEcryptfs()) { |
| EXPECT_EQ(vault_dir.Append(base::FilePath(directory)).value(), |
| result.value()); |
| } else { |
| EXPECT_EQ(mount_dir.Append(base::FilePath(directory)).value(), |
| result.value()); |
| } |
| } |
| |
| // TODO(chromium:1141301, dlunev): GetTrackedDirectory always returns true for |
| // ecryptfs. Figure out what should actually be the behaviour in the case. |
| if (!ShouldTestEcryptfs()) { |
| // Return false for unknown directories. |
| base::FilePath result; |
| EXPECT_FALSE(homedirs_->GetTrackedDirectory( |
| users_[0].homedir_path, base::FilePath("zzz"), &result)); |
| EXPECT_FALSE(homedirs_->GetTrackedDirectory( |
| users_[0].homedir_path, base::FilePath("aaa/zzz"), &result)); |
| } |
| } |
| |
| TEST_P(HomeDirsTest, GetUnmountedAndroidDataCount) { |
| if (ShouldTestEcryptfs()) { |
| // We don't support Ecryptfs. |
| EXPECT_EQ(0, homedirs_->GetUnmountedAndroidDataCount()); |
| return; |
| } |
| |
| for (const auto& user : users_) { |
| // Set up a root hierarchy for the encrypted version of homedir_path |
| // without android-data (added a suffix _encrypted in the code to mark them |
| // encrypted). |
| // root |
| // |-session_manager |
| // |-policy |
| base::FilePath root = |
| user.homedir_path.Append(kMountDir).Append(kRootHomeSuffix); |
| base::FilePath session_manager = root.Append("session_manager_encrypted"); |
| ASSERT_TRUE(platform_.CreateDirectory(session_manager)); |
| base::FilePath policy = session_manager.Append("policy_encrypted"); |
| ASSERT_TRUE(platform_.CreateDirectory(policy)); |
| } |
| |
| // Add android data for the first user. |
| // |-android-data |
| // |-cache |
| // |-data |
| base::FilePath root = |
| users_[0].homedir_path.Append(kMountDir).Append(kRootHomeSuffix); |
| ASSERT_TRUE(platform_.CreateDirectory(root)); |
| std::string name = root.BaseName().value(); |
| ASSERT_TRUE(platform_.SetExtendedFileAttribute( |
| root, kTrackedDirectoryNameAttribute, name.data(), name.length())); |
| |
| base::FilePath android_data = root.Append("android-data_encrypted"); |
| ASSERT_TRUE(platform_.CreateDirectory(android_data)); |
| base::FilePath data = android_data.Append("data_encrypted"); |
| base::FilePath cache = android_data.Append("cache_encrypted"); |
| ASSERT_TRUE(platform_.CreateDirectory(data)); |
| ASSERT_TRUE(platform_.CreateDirectory(cache)); |
| ASSERT_TRUE(platform_.SetOwnership(cache, kAndroidSystemRealUid, |
| kAndroidSystemRealUid, false)); |
| |
| // Expect 1 home directory with android-data: homedir_paths_[0]. |
| EXPECT_EQ(1, homedirs_->GetUnmountedAndroidDataCount()); |
| } |
| |
| TEST_P(HomeDirsTest, GetHomedirsAllMounted) { |
| std::vector<bool> all_mounted(users_.size(), true); |
| std::set<std::string> hashes, got_hashes; |
| |
| for (int i = 0; i < users_.size(); i++) { |
| hashes.insert(users_[i].obfuscated); |
| } |
| |
| EXPECT_CALL(platform_, AreDirectoriesMounted(_)) |
| .WillOnce(Return(all_mounted)); |
| auto dirs = homedirs_->GetHomeDirs(); |
| |
| for (const auto& dir : dirs) { |
| EXPECT_TRUE(dir.is_mounted); |
| got_hashes.insert(dir.obfuscated); |
| } |
| EXPECT_EQ(hashes, got_hashes); |
| } |
| |
| TEST_P(HomeDirsTest, GetHomedirsSomeMounted) { |
| std::vector<bool> some_mounted(users_.size()); |
| std::set<std::string> hashes, got_hashes; |
| |
| for (int i = 0; i < users_.size(); i++) { |
| hashes.insert(users_[i].obfuscated); |
| some_mounted[i] = i % 2; |
| } |
| |
| EXPECT_CALL(platform_, AreDirectoriesMounted(_)) |
| .WillOnce(Return(some_mounted)); |
| auto dirs = homedirs_->GetHomeDirs(); |
| for (int i = 0; i < users_.size(); i++) { |
| EXPECT_EQ(dirs[i].is_mounted, some_mounted[i]); |
| got_hashes.insert(dirs[i].obfuscated); |
| } |
| EXPECT_EQ(hashes, got_hashes); |
| } |
| |
| class HomeDirsVaultTest : public ::testing::Test { |
| public: |
| HomeDirsVaultTest() |
| : user_({.obfuscated = "foo", |
| .homedir_path = base::FilePath(UserPath("foo"))}), |
| key_reference_({.fek_sig = brillo::SecureBlob("random keyref")}) {} |
| ~HomeDirsVaultTest() override = default; |
| |
| // TODO(b/177929620): Cleanup once lvm utils are built unconditionally. |
| #if USE_LVM_STATEFUL_PARTITION |
| void ExpectLogicalVolumeStatefulPartition( |
| MockPlatform* platform, |
| HomeDirs* homedirs, |
| const std::string& obfuscated_username, |
| bool existing_cryptohome) { |
| brillo::PhysicalVolume pv(base::FilePath("/dev/mmcblk0p1"), nullptr); |
| brillo::VolumeGroup vg("stateful", nullptr); |
| brillo::Thinpool thinpool("thinpool", "stateful", nullptr); |
| brillo::LogicalVolume lv(LogicalVolumePrefix(obfuscated_username) |
| .append(kDmcryptDataContainerSuffix), |
| "stateful", nullptr); |
| std::unique_ptr<brillo::MockLogicalVolumeManager> lvm( |
| new brillo::MockLogicalVolumeManager()); |
| |
| EXPECT_CALL(*platform, GetStatefulDevice()) |
| .WillRepeatedly(Return(base::FilePath("/dev/mmcblk0"))); |
| EXPECT_CALL(*platform, GetBlkSize(_, _)) |
| .WillRepeatedly( |
| DoAll(SetArgPointee<1>(1024 * 1024 * 1024), Return(true))); |
| EXPECT_CALL(*lvm.get(), GetPhysicalVolume(_)).WillRepeatedly(Return(pv)); |
| EXPECT_CALL(*lvm.get(), GetVolumeGroup(_)).WillRepeatedly(Return(vg)); |
| EXPECT_CALL(*lvm.get(), GetThinpool(_, _)).WillRepeatedly(Return(thinpool)); |
| if (existing_cryptohome) { |
| EXPECT_CALL(*lvm.get(), GetLogicalVolume(_, _)) |
| .WillRepeatedly(Return(lv)); |
| } else { |
| EXPECT_CALL(*lvm.get(), GetLogicalVolume(_, _)) |
| .WillRepeatedly(Return(std::nullopt)); |
| } |
| |
| homedirs->SetLogicalVolumeManagerForTesting(std::move(lvm)); |
| } |
| #endif // USE_LVM_STATEFUL_PARTITION |
| |
| protected: |
| struct HomedirsTestCase { |
| std::string name; |
| bool lvm_supported; |
| bool fscrypt_supported; |
| EncryptedContainerType existing_container_type; |
| CryptohomeVault::Options options; |
| |
| EncryptedContainerType expected_type; |
| MountError expected_error; |
| }; |
| |
| const UserInfo user_; |
| const FileSystemKeyReference key_reference_; |
| |
| void PrepareTestCase(const HomedirsTestCase& test_case, |
| MockPlatform* platform, |
| HomeDirs* homedirs) { |
| #if USE_LVM_STATEFUL_PARTITION |
| if (test_case.lvm_supported) { |
| auto type = test_case.existing_container_type; |
| ExpectLogicalVolumeStatefulPartition( |
| platform, homedirs, user_.obfuscated, |
| type == EncryptedContainerType::kDmcrypt); |
| homedirs->set_lvm_migration_enabled(true); |
| } |
| #endif // USE_LVM_STATEFUL_PARTITION |
| |
| dircrypto::KeyState root_keystate = |
| test_case.fscrypt_supported ? dircrypto::KeyState::NO_KEY |
| : dircrypto::KeyState::NOT_SUPPORTED; |
| ON_CALL(*platform, GetDirCryptoKeyState(ShadowRoot())) |
| .WillByDefault(Return(root_keystate)); |
| |
| switch (test_case.existing_container_type) { |
| case EncryptedContainerType::kEcryptfs: |
| ASSERT_TRUE(platform->CreateDirectory( |
| GetEcryptfsUserVaultPath(user_.obfuscated))); |
| ASSERT_TRUE( |
| platform->CreateDirectory(GetUserMountDirectory(user_.obfuscated))); |
| break; |
| case EncryptedContainerType::kFscrypt: |
| ASSERT_TRUE( |
| platform->CreateDirectory(GetUserMountDirectory(user_.obfuscated))); |
| ON_CALL(*platform, |
| GetDirCryptoKeyState(GetUserMountDirectory(user_.obfuscated))) |
| .WillByDefault(Return(dircrypto::KeyState::ENCRYPTED)); |
| break; |
| case EncryptedContainerType::kEcryptfsToFscrypt: |
| ASSERT_TRUE(platform->CreateDirectory( |
| GetEcryptfsUserVaultPath(user_.obfuscated))); |
| ASSERT_TRUE( |
| platform->CreateDirectory(GetUserMountDirectory(user_.obfuscated))); |
| ON_CALL(*platform, |
| GetDirCryptoKeyState(GetUserMountDirectory(user_.obfuscated))) |
| .WillByDefault(Return(dircrypto::KeyState::ENCRYPTED)); |
| break; |
| default: |
| // kDmcrypt is handled above. |
| // kEphemeral doesn't need special preparations. |
| break; |
| } |
| } |
| }; |
| |
| namespace { |
| TEST_F(HomeDirsVaultTest, PickVaultType) { |
| const std::vector<HomeDirsVaultTest::HomedirsTestCase> test_cases = { |
| { |
| .name = "new_ecryptfs_allowed", |
| .lvm_supported = false, |
| .fscrypt_supported = false, |
| .existing_container_type = EncryptedContainerType::kUnknown, |
| .options = {}, |
| .expected_type = EncryptedContainerType::kEcryptfs, |
| .expected_error = MOUNT_ERROR_NONE, |
| }, |
| { |
| .name = "new_ecryptfs_block_no_effect", |
| .lvm_supported = false, |
| .fscrypt_supported = false, |
| .existing_container_type = EncryptedContainerType::kUnknown, |
| .options = {.block_ecryptfs = true}, |
| .expected_type = EncryptedContainerType::kEcryptfs, |
| .expected_error = MOUNT_ERROR_NONE, |
| }, |
| { |
| .name = "new_ecryptfs_cant_migrate", |
| .lvm_supported = false, |
| .fscrypt_supported = false, |
| .existing_container_type = EncryptedContainerType::kUnknown, |
| .options = {.migrate = true}, |
| .expected_type = EncryptedContainerType::kUnknown, |
| .expected_error = MOUNT_ERROR_UNEXPECTED_MOUNT_TYPE, |
| }, |
| { |
| .name = "new_ecryptfs_forced", |
| .lvm_supported = false, |
| .fscrypt_supported = true, |
| .existing_container_type = EncryptedContainerType::kUnknown, |
| .options = {.force_type = EncryptedContainerType::kEcryptfs}, |
| .expected_type = EncryptedContainerType::kEcryptfs, |
| .expected_error = MOUNT_ERROR_NONE, |
| }, |
| { |
| .name = "new_fscrypt", |
| .lvm_supported = false, |
| .fscrypt_supported = true, |
| .existing_container_type = EncryptedContainerType::kUnknown, |
| .options = {}, |
| .expected_type = EncryptedContainerType::kFscrypt, |
| .expected_error = MOUNT_ERROR_NONE, |
| }, |
| { |
| .name = "existing_ecryptfs_allowed", |
| .lvm_supported = false, |
| .fscrypt_supported = true, |
| .existing_container_type = EncryptedContainerType::kEcryptfs, |
| .options = {}, |
| .expected_type = EncryptedContainerType::kEcryptfs, |
| .expected_error = MOUNT_ERROR_NONE, |
| }, |
| { |
| .name = "existing_ecryptfs_not_allowed", |
| .lvm_supported = false, |
| .fscrypt_supported = true, |
| .existing_container_type = EncryptedContainerType::kEcryptfs, |
| .options = {.block_ecryptfs = true}, |
| .expected_type = EncryptedContainerType::kUnknown, |
| .expected_error = MOUNT_ERROR_OLD_ENCRYPTION, |
| }, |
| { |
| .name = "existing_ecryptfs_migrate_to_fscrypt", |
| .lvm_supported = false, |
| .fscrypt_supported = true, |
| .existing_container_type = EncryptedContainerType::kEcryptfs, |
| .options = {.migrate = true}, |
| .expected_type = EncryptedContainerType::kEcryptfsToFscrypt, |
| .expected_error = MOUNT_ERROR_NONE, |
| }, |
| { |
| .name = "existing_fscrypt", |
| .lvm_supported = false, |
| .fscrypt_supported = true, |
| .existing_container_type = EncryptedContainerType::kFscrypt, |
| .options = {}, |
| .expected_type = EncryptedContainerType::kFscrypt, |
| .expected_error = MOUNT_ERROR_NONE, |
| }, |
| { |
| .name = "existing_fscrypt_force_ignored", |
| .lvm_supported = false, |
| .fscrypt_supported = true, |
| .existing_container_type = EncryptedContainerType::kFscrypt, |
| .options = {.force_type = EncryptedContainerType::kEcryptfs}, |
| .expected_type = EncryptedContainerType::kFscrypt, |
| .expected_error = MOUNT_ERROR_NONE, |
| }, |
| { |
| .name = "existing_migration", |
| .lvm_supported = false, |
| .fscrypt_supported = true, |
| .existing_container_type = EncryptedContainerType::kEcryptfsToFscrypt, |
| .options = {.migrate = true}, |
| .expected_type = EncryptedContainerType::kEcryptfsToFscrypt, |
| .expected_error = MOUNT_ERROR_NONE, |
| }, |
| { |
| .name = "existing_migration_without_flag", |
| .lvm_supported = false, |
| .fscrypt_supported = true, |
| .existing_container_type = EncryptedContainerType::kEcryptfsToFscrypt, |
| .options = {}, |
| .expected_type = EncryptedContainerType::kUnknown, |
| .expected_error = MOUNT_ERROR_PREVIOUS_MIGRATION_INCOMPLETE, |
| }, |
| #if USE_LVM_STATEFUL_PARTITION |
| { |
| .name = "existing_fscrypt_migrate", |
| .lvm_supported = true, |
| .fscrypt_supported = true, |
| .existing_container_type = EncryptedContainerType::kFscrypt, |
| .options = {.migrate = true}, |
| .expected_type = EncryptedContainerType::kFscryptToDmcrypt, |
| .expected_error = MOUNT_ERROR_NONE, |
| }, |
| { |
| .name = "existing_ecryptfs_migrate_to_dmcrypt", |
| .lvm_supported = true, |
| .fscrypt_supported = true, |
| .existing_container_type = EncryptedContainerType::kEcryptfs, |
| .options = {.migrate = true}, |
| .expected_type = EncryptedContainerType::kEcryptfsToDmcrypt, |
| .expected_error = MOUNT_ERROR_NONE, |
| }, |
| { |
| .name = "new_lvm", |
| .lvm_supported = true, |
| .fscrypt_supported = true, |
| .existing_container_type = EncryptedContainerType::kUnknown, |
| .options = {}, |
| .expected_type = EncryptedContainerType::kDmcrypt, |
| .expected_error = MOUNT_ERROR_NONE, |
| }, |
| { |
| .name = "existing_lvm", |
| .lvm_supported = true, |
| .fscrypt_supported = true, |
| .existing_container_type = EncryptedContainerType::kDmcrypt, |
| .options = {}, |
| .expected_type = EncryptedContainerType::kDmcrypt, |
| .expected_error = MOUNT_ERROR_NONE, |
| }, |
| #endif // USE_LVM_STATEFUL_PARTITION |
| }; |
| |
| for (const auto& test_case : test_cases) { |
| NiceMock<MockPlatform> platform; |
| Crypto crypto(&platform); |
| HomeDirs homedirs(&platform, |
| std::make_unique<policy::PolicyProvider>( |
| std::make_unique<policy::MockDevicePolicy>()), |
| HomeDirs::RemoveCallback()); |
| |
| PrepareTestCase(test_case, &platform, &homedirs); |
| MountError error = MOUNT_ERROR_NONE; |
| auto vault_type = |
| homedirs.PickVaultType(user_.obfuscated, test_case.options, &error); |
| EXPECT_THAT(vault_type, Eq(test_case.expected_type)) |
| << "TestCase: " << test_case.name; |
| ASSERT_THAT(error, Eq(test_case.expected_error)) |
| << "TestCase: " << test_case.name; |
| } |
| } |
| } // namespace |
| |
| } // namespace cryptohome |