| // Copyright (c) 2012 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/homedirs.h" |
| |
| #include <memory> |
| #include <vector> |
| |
| #include <base/files/file_path.h> |
| #include <base/files/scoped_temp_dir.h> |
| #include <base/stl_util.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <base/strings/stringprintf.h> |
| #include <brillo/cryptohome.h> |
| #include <brillo/data_encoding.h> |
| #include <brillo/secure_blob.h> |
| #include <chromeos/constants/cryptohome.h> |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| #include <policy/mock_device_policy.h> |
| |
| #include "cryptohome/credentials.h" |
| #include "cryptohome/crypto_error.h" |
| #include "cryptohome/cryptolib.h" |
| #include "cryptohome/make_tests.h" |
| #include "cryptohome/mock_crypto.h" |
| #include "cryptohome/mock_platform.h" |
| #include "cryptohome/mock_tpm.h" |
| #include "cryptohome/mock_user_oldest_activity_timestamp_cache.h" |
| #include "cryptohome/mock_vault_keyset.h" |
| #include "cryptohome/mock_vault_keyset_factory.h" |
| #include "cryptohome/mount.h" |
| #include "cryptohome/signed_secret.pb.h" |
| |
| using base::FilePath; |
| using base::StringPrintf; |
| using brillo::SecureBlob; |
| using ::testing::_; |
| using ::testing::DoAll; |
| using ::testing::EndsWith; |
| using ::testing::HasSubstr; |
| using ::testing::InSequence; |
| using ::testing::Invoke; |
| using ::testing::InvokeWithoutArgs; |
| using ::testing::MatchesRegex; |
| using ::testing::NiceMock; |
| using ::testing::Return; |
| using ::testing::ReturnRef; |
| using ::testing::SetArgPointee; |
| using ::testing::SetArrayArgument; |
| using ::testing::StartsWith; |
| using ::testing::StrEq; |
| |
| namespace cryptohome { |
| |
| extern const char kKeyFile[]; |
| extern const int kKeyFileMax; |
| extern const char kKeyLegacyPrefix[]; |
| |
| 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; |
| } |
| |
| ACTION_P(SetCleanUpStrategy, clean_up_strategy) { |
| if (!clean_up_strategy.empty()) { |
| *arg0 = clean_up_strategy; |
| return true; |
| } |
| return false; |
| } |
| |
| namespace { |
| const FilePath kTestRoot("alt_test_home_dir"); |
| |
| struct homedir { |
| const char* name; |
| base::Time::Exploded time; |
| }; |
| |
| const char* kOwner = "<<OWNER>>"; |
| // Note, the order is important. These should be oldest to newest. |
| const struct homedir kHomedirs[] = { |
| {"d5510a8dda6d743c46dadd979a61ae5603529742", {2011, 1, 6, 1}}, |
| {"8f995cdee8f0711fd32e1cf6246424002c483d47", {2011, 2, 2, 1}}, |
| {"973b9640e86f6073c6b6e2759ff3cf3084515e61", {2011, 3, 2, 1}}, |
| {kOwner, {2011, 4, 5, 1}}}; |
| |
| NiceMock<MockFileEnumerator>* CreateMockFileEnumerator() { |
| return new NiceMock<MockFileEnumerator>; |
| } |
| } // namespace |
| |
| class HomeDirsTest |
| : public ::testing::TestWithParam<bool /* should_test_ecryptfs */> { |
| public: |
| HomeDirsTest() : crypto_(&platform_) {} |
| virtual ~HomeDirsTest() {} |
| |
| void SetUp() { |
| test_helper_.SetUpSystemSalt(); |
| // TODO(wad) Only generate the user data we need. This is time consuming. |
| test_helper_.InitTestData(kTestRoot, kDefaultUsers, kDefaultUserCount, |
| ShouldTestEcryptfs()); |
| homedirs_.set_shadow_root(kTestRoot); |
| test_helper_.InjectSystemSalt(&platform_, kTestRoot.Append("salt")); |
| set_policy(true, kOwner, false, ""); |
| |
| homedirs_.Init(&platform_, &crypto_, ×tamp_cache_); |
| FilePath fp = FilePath(kTestRoot); |
| for (const auto& hd : kHomedirs) { |
| FilePath path = fp.Append(hd.name); |
| std::string user; |
| if (hd.name == std::string(kOwner)) |
| homedirs_.GetOwner(&user); |
| else |
| user = hd.name; |
| obfuscated_users_.push_back(user); |
| homedir_paths_.push_back(fp.Append(user)); |
| user_paths_.push_back(brillo::cryptohome::home::GetHashedUserPath(user)); |
| base::Time t; |
| CHECK(base::Time::FromUTCExploded(hd.time, &t)); |
| homedir_times_.push_back(t); |
| } |
| EXPECT_CALL(platform_, HasExtendedFileAttribute(_, kRemovableFileAttribute)) |
| .WillRepeatedly(Return(false)); |
| } |
| |
| void TearDown() { test_helper_.TearDownSystemSalt(); } |
| |
| void set_policy(bool owner_known, |
| const std::string& owner, |
| bool ephemeral_users_enabled, |
| const std::string& clean_up_strategy) { |
| policy::MockDevicePolicy* device_policy = new policy::MockDevicePolicy(); |
| EXPECT_CALL(*device_policy, LoadPolicy()).WillRepeatedly(Return(true)); |
| EXPECT_CALL(*device_policy, GetOwner(_)) |
| .WillRepeatedly(SetOwner(owner_known, owner)); |
| EXPECT_CALL(*device_policy, GetEphemeralUsersEnabled(_)) |
| .WillRepeatedly(SetEphemeralUsersEnabled(ephemeral_users_enabled)); |
| homedirs_.own_policy_provider(new policy::PolicyProvider( |
| std::unique_ptr<policy::MockDevicePolicy>(device_policy))); |
| } |
| |
| // Create an enumerator that will enumerate the given child_directories. |
| NiceMock<MockFileEnumerator>* CreateFileEnumerator( |
| const std::vector<FilePath>& child_directories) { |
| auto* mock = new NiceMock<MockFileEnumerator>; |
| for (const auto& child : child_directories) { |
| #if BASE_VER < 780000 |
| struct stat stat = {}; |
| #else |
| base::stat_wrapper_t stat = {}; |
| #endif |
| mock->entries_.push_back(FileEnumerator::FileInfo(child, stat)); |
| } |
| return mock; |
| } |
| |
| // Sets up expectations for the given tracked directories which belong to the |
| // same parent directory. |
| void ExpectTrackedDirectoryEnumeration( |
| const std::vector<FilePath>& child_directories) { |
| DCHECK(!child_directories.empty()); |
| FilePath parent_directory = child_directories[0].DirName(); |
| // xattr is used to track directories. |
| for (const auto& child : child_directories) { |
| DCHECK_EQ(parent_directory.value(), child.DirName().value()); |
| EXPECT_CALL(platform_, GetExtendedFileAttributeAsString( |
| child, kTrackedDirectoryNameAttribute, _)) |
| .WillRepeatedly( |
| DoAll(SetArgPointee<2>(child.BaseName().value()), Return(true))); |
| EXPECT_CALL(platform_, HasExtendedFileAttribute( |
| child, kTrackedDirectoryNameAttribute)) |
| .WillRepeatedly(Return(true)); |
| } |
| // |child_directories| should be enumerated as the parent's children. |
| auto create_file_enumerator_function = [child_directories]() { |
| auto* mock = new NiceMock<MockFileEnumerator>; |
| for (const auto& child : child_directories) { |
| #if BASE_VER < 780000 |
| struct stat stat = {}; |
| #else |
| base::stat_wrapper_t stat = {}; |
| #endif |
| mock->entries_.push_back(FileEnumerator::FileInfo(child, stat)); |
| } |
| return mock; |
| }; |
| EXPECT_CALL(platform_, GetFileEnumerator(parent_directory, false, |
| base::FileEnumerator::DIRECTORIES)) |
| .WillRepeatedly(InvokeWithoutArgs(create_file_enumerator_function)); |
| } |
| |
| // Returns true if the test is running for eCryptfs, false if for dircrypto. |
| bool ShouldTestEcryptfs() const { return GetParam(); } |
| |
| protected: |
| MakeTests test_helper_; |
| NiceMock<MockPlatform> platform_; |
| Crypto crypto_; |
| std::vector<FilePath> homedir_paths_; |
| std::vector<FilePath> user_paths_; |
| std::vector<std::string> obfuscated_users_; |
| MockUserOldestActivityTimestampCache timestamp_cache_; |
| std::vector<base::Time> homedir_times_; |
| MockVaultKeysetFactory vault_keyset_factory_; |
| HomeDirs homedirs_; |
| |
| static const uid_t kAndroidSystemRealUid = |
| HomeDirs::kAndroidSystemUid + kArcContainerShiftUid; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(HomeDirsTest); |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P(WithEcryptfs, HomeDirsTest, ::testing::Values(true)); |
| INSTANTIATE_TEST_SUITE_P(WithDircrypto, HomeDirsTest, ::testing::Values(false)); |
| |
| TEST_P(HomeDirsTest, RemoveNonOwnerCryptohomes) { |
| // Ensure that RemoveNonOwnerCryptohomes does. |
| EXPECT_CALL(platform_, EnumerateDirectoryEntries(kTestRoot, false, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(homedir_paths_), Return(true))); |
| FilePath user_prefix = brillo::cryptohome::home::GetUserPathPrefix(); |
| FilePath root_prefix = brillo::cryptohome::home::GetRootPathPrefix(); |
| EXPECT_CALL(platform_, EnumerateDirectoryEntries(user_prefix, _, _)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(platform_, EnumerateDirectoryEntries(root_prefix, _, _)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(platform_, DirectoryExists(_)).WillRepeatedly(Return(true)); |
| EXPECT_CALL(platform_, DirectoryExists(Property(&FilePath::value, |
| EndsWith(kEcryptfsVaultDir)))) |
| .WillRepeatedly(Return(ShouldTestEcryptfs())); |
| EXPECT_CALL(platform_, IsDirectoryMounted(_)).WillRepeatedly(Return(false)); |
| EXPECT_CALL(platform_, DeleteFile(homedir_paths_[0], true)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(platform_, DeleteFile(homedir_paths_[1], true)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(platform_, DeleteFile(homedir_paths_[2], true)) |
| .WillOnce(Return(true)); |
| |
| homedirs_.RemoveNonOwnerCryptohomes(); |
| } |
| |
| TEST_P(HomeDirsTest, RenameCryptohome) { |
| ASSERT_TRUE(base::CreateDirectory(FilePath(test_helper_.users[0].base_path))); |
| ASSERT_TRUE(base::CreateDirectory(FilePath(test_helper_.users[1].base_path))); |
| ASSERT_TRUE(base::CreateDirectory(FilePath(test_helper_.users[2].base_path))); |
| |
| const char kNewUserId[] = "some_new_user"; |
| EXPECT_TRUE(homedirs_.Rename(kDefaultUsers[0].username, kNewUserId)); |
| |
| // If source directory doesn't exist, assume renamed. |
| EXPECT_TRUE(homedirs_.Rename(kDefaultUsers[0].username, kNewUserId)); |
| |
| // This should fail as target directory already exists. |
| EXPECT_FALSE( |
| homedirs_.Rename(kDefaultUsers[1].username, kDefaultUsers[2].username)); |
| |
| // Rename back. |
| EXPECT_TRUE(homedirs_.Rename(kNewUserId, kDefaultUsers[0].username)); |
| } |
| |
| TEST_P(HomeDirsTest, ComputeDiskUsageDircrypto) { |
| FilePath base_path(test_helper_.users[0].base_path); |
| // /home/.shadow in production code. |
| FilePath shadow_home = |
| homedirs_.shadow_root().Append(base_path.BaseName().value()); |
| // /home/.shadow/$hash/mount in production code. |
| FilePath mount_dir = shadow_home.Append(kMountDir); |
| // /home/.shadow/$hash/vault in production code. |
| FilePath vault_dir = shadow_home.Append(kEcryptfsVaultDir); |
| // /home/user/$hash in production code and here in unit test. |
| FilePath user_dir = brillo::cryptohome::home::GetUserPathPrefix().Append( |
| test_helper_.users[0].obfuscated_username); |
| |
| // If anyone asks, shadow_home, mount_dir and user_dir exists but not |
| // vault_dir. |
| ON_CALL(platform_, DirectoryExists(shadow_home)).WillByDefault(Return(true)); |
| ON_CALL(platform_, DirectoryExists(mount_dir)).WillByDefault(Return(true)); |
| ON_CALL(platform_, DirectoryExists(vault_dir)).WillByDefault(Return(false)); |
| ON_CALL(platform_, DirectoryExists(user_dir)).WillByDefault(Return(true)); |
| |
| constexpr int64_t expected_bytes = 123456789012345; |
| constexpr int64_t unexpected_bytes = 98765432154321; |
| EXPECT_CALL(platform_, ComputeDirectoryDiskUsage(mount_dir)) |
| .WillOnce(Return(expected_bytes)); |
| ON_CALL(platform_, ComputeDirectoryDiskUsage(vault_dir)) |
| .WillByDefault(Return(unexpected_bytes)); |
| |
| EXPECT_EQ(expected_bytes, |
| homedirs_.ComputeDiskUsage(kDefaultUsers[0].username)); |
| } |
| |
| TEST_P(HomeDirsTest, ComputeDiskUsageEcryptfs) { |
| FilePath base_path(test_helper_.users[0].base_path); |
| FilePath shadow_home = |
| homedirs_.shadow_root().Append(base_path.BaseName().value()); |
| FilePath mount_dir = shadow_home.Append(kMountDir); |
| FilePath vault_dir = shadow_home.Append(kEcryptfsVaultDir); |
| FilePath user_dir = brillo::cryptohome::home::GetUserPathPrefix().Append( |
| test_helper_.users[0].obfuscated_username); |
| |
| // If anyone asks, shadow_home, mount_dir, vault_dir and user_dir all exists. |
| ON_CALL(platform_, DirectoryExists(shadow_home)).WillByDefault(Return(true)); |
| ON_CALL(platform_, DirectoryExists(mount_dir)).WillByDefault(Return(true)); |
| ON_CALL(platform_, DirectoryExists(vault_dir)).WillByDefault(Return(true)); |
| ON_CALL(platform_, DirectoryExists(user_dir)).WillByDefault(Return(true)); |
| |
| constexpr int64_t expected_bytes = 123456789012345; |
| constexpr int64_t unexpected_bytes = 98765432154321; |
| EXPECT_CALL(platform_, ComputeDirectoryDiskUsage(vault_dir)) |
| .WillOnce(Return(expected_bytes)); |
| ON_CALL(platform_, ComputeDirectoryDiskUsage(mount_dir)) |
| .WillByDefault(Return(unexpected_bytes)); |
| |
| EXPECT_EQ(expected_bytes, |
| homedirs_.ComputeDiskUsage(kDefaultUsers[0].username)); |
| } |
| |
| TEST_P(HomeDirsTest, ComputeDiskUsageEphemeral) { |
| FilePath base_path(test_helper_.users[0].base_path); |
| FilePath shadow_home = |
| homedirs_.shadow_root().Append(base_path.BaseName().value()); |
| FilePath mount_dir = shadow_home.Append(kMountDir); |
| FilePath vault_dir = shadow_home.Append(kEcryptfsVaultDir); |
| FilePath user_dir = brillo::cryptohome::home::GetUserPathPrefix().Append( |
| test_helper_.users[0].obfuscated_username); |
| |
| // If anyone asks, shadow_home, mount_dir and vault_dir doesn't exist, but |
| // user_dir exists. |
| ON_CALL(platform_, DirectoryExists(shadow_home)).WillByDefault(Return(false)); |
| ON_CALL(platform_, DirectoryExists(mount_dir)).WillByDefault(Return(false)); |
| ON_CALL(platform_, DirectoryExists(vault_dir)).WillByDefault(Return(false)); |
| ON_CALL(platform_, DirectoryExists(user_dir)).WillByDefault(Return(true)); |
| |
| constexpr int64_t expected_bytes = 123456789012345; |
| constexpr int64_t unexpected_bytes = 98765432154321; |
| EXPECT_CALL(platform_, ComputeDirectoryDiskUsage(user_dir)) |
| .WillOnce(Return(expected_bytes)); |
| ON_CALL(platform_, ComputeDirectoryDiskUsage(mount_dir)) |
| .WillByDefault(Return(unexpected_bytes)); |
| ON_CALL(platform_, ComputeDirectoryDiskUsage(vault_dir)) |
| .WillByDefault(Return(unexpected_bytes)); |
| ON_CALL(platform_, ComputeDirectoryDiskUsage(shadow_home)) |
| .WillByDefault(Return(unexpected_bytes)); |
| |
| EXPECT_EQ(expected_bytes, |
| homedirs_.ComputeDiskUsage(kDefaultUsers[0].username)); |
| } |
| |
| 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) { |
| Platform real_platform; |
| // Use real PathExists. |
| EXPECT_CALL(platform_, FileExists(_)) |
| .WillRepeatedly(Invoke(&real_platform, &Platform::FileExists)); |
| // Use real FileEnumerator. |
| EXPECT_CALL(platform_, GetFileEnumerator(_, _, _)) |
| .WillRepeatedly(Invoke(&real_platform, &Platform::GetFileEnumerator)); |
| // Use real HasExtendedFileAttribute. |
| EXPECT_CALL(platform_, HasExtendedFileAttribute(_, _)) |
| .WillRepeatedly( |
| Invoke(&real_platform, &Platform::HasExtendedFileAttribute)); |
| // Use real GetExtendedFileAttributeAsString. |
| EXPECT_CALL(platform_, GetExtendedFileAttributeAsString(_, _, _)) |
| .WillRepeatedly( |
| Invoke(&real_platform, &Platform::GetExtendedFileAttributeAsString)); |
| |
| base::ScopedTempDir temp_dir; |
| ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| const FilePath mount_dir = temp_dir.GetPath().Append(FilePath(kMountDir)); |
| ASSERT_TRUE(base::CreateDirectory(mount_dir)); |
| |
| const char* const kDirectories[] = { |
| "aaa", |
| "bbb", |
| "bbb/ccc", |
| "bbb/ccc/ddd", |
| }; |
| // Prepare directories. |
| for (const auto& directory : kDirectories) { |
| const FilePath path = mount_dir.Append(FilePath(directory)); |
| ASSERT_TRUE(base::CreateDirectory(path)); |
| std::string name = path.BaseName().value(); |
| ASSERT_TRUE(real_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); |
| FilePath result; |
| EXPECT_TRUE(homedirs_.GetTrackedDirectory(temp_dir.GetPath(), |
| FilePath(directory), &result)); |
| EXPECT_EQ(mount_dir.Append(FilePath(directory)).value(), result.value()); |
| } |
| // Return false for unknown directories. |
| FilePath result; |
| EXPECT_FALSE(homedirs_.GetTrackedDirectory(temp_dir.GetPath(), |
| FilePath("zzz"), &result)); |
| EXPECT_FALSE(homedirs_.GetTrackedDirectory(temp_dir.GetPath(), |
| FilePath("aaa/zzz"), &result)); |
| } |
| |
| TEST_P(HomeDirsTest, GetUnmountedAndroidDataCount) { |
| EXPECT_CALL(platform_, EnumerateDirectoryEntries(kTestRoot, false, _)) |
| .WillOnce(DoAll(SetArgPointee<2>(homedir_paths_), Return(true))); |
| |
| for (const auto& path : homedir_paths_) { |
| auto homedir_path = FilePath("/home/user/").Append(path.BaseName().value()); |
| |
| EXPECT_CALL(platform_, DirectoryExists(homedir_path)) |
| .WillRepeatedly(Return(true)); |
| } |
| |
| if (ShouldTestEcryptfs()) { |
| // We don't support Ecryptfs. |
| for (int i = 0; i < homedir_paths_.size(); i++) { |
| FilePath vault_path = homedir_paths_[i].Append(kEcryptfsVaultDir); |
| EXPECT_CALL(platform_, DirectoryExists(vault_path)) |
| .WillRepeatedly(Return(true)); |
| } |
| EXPECT_EQ(0, homedirs_.GetUnmountedAndroidDataCount()); |
| return; |
| } |
| |
| // Basic setup. |
| for (size_t i = 0; i < homedir_paths_.size(); i++) { |
| // Set up tracked root directory under DirCrypto's home. |
| FilePath vault_path = homedir_paths_[i].Append(kEcryptfsVaultDir); |
| EXPECT_CALL(platform_, DirectoryExists(vault_path)) |
| .WillRepeatedly(Return(false)); |
| FilePath mount = homedir_paths_[i].Append(kMountDir); |
| FilePath root = mount.Append(kRootHomeSuffix); |
| |
| ExpectTrackedDirectoryEnumeration({root}); |
| } |
| |
| // Set up a root hierarchy for the encrypted version of homedir_paths_[0] |
| // (added a suffix _encrypted in the code to mark them encrypted). |
| // root |
| // |-android-data |
| // | |-cache |
| // | |-data |
| // |-session_manager |
| FilePath root = homedir_paths_[0].Append(kMountDir).Append(kRootHomeSuffix); |
| FilePath android_data = root.Append("android-data_encrypted"); |
| FilePath session_manager = root.Append("session_manager_encrypted"); |
| EXPECT_CALL(platform_, |
| GetFileEnumerator(root, false, base::FileEnumerator::DIRECTORIES)) |
| .WillOnce(Return(CreateFileEnumerator({android_data, session_manager}))); |
| FilePath data = android_data.Append("data_encrypted"); |
| FilePath cache = android_data.Append("cache_encrypted"); |
| EXPECT_CALL(platform_, GetFileEnumerator(android_data, false, |
| base::FileEnumerator::DIRECTORIES)) |
| .WillOnce(Return(CreateFileEnumerator({cache, data}))); |
| |
| // This marks dir2 directory under homedir_paths_[0] as android-data by |
| // assigning System UID as the uid owner of dir4 (dir2's children). |
| EXPECT_CALL(platform_, GetOwnership(cache, _, _, false)) |
| .WillOnce(DoAll(SetArgPointee<1>(kAndroidSystemRealUid), Return(true))); |
| |
| // Other homedir_paths_ shouldn't have android-data. |
| for (size_t i = 1; i < homedir_paths_.size(); i++) { |
| // Set up a root hierarchy for the encrypted version of homedir_paths |
| // without android-data (added a suffix _encrypted in the code to mark them |
| // encrypted). |
| // root |
| // |-session_manager |
| // |-policy |
| FilePath root = homedir_paths_[i].Append(kMountDir).Append(kRootHomeSuffix); |
| FilePath session_manager = root.Append("session_manager_encrypted"); |
| EXPECT_CALL(platform_, GetFileEnumerator(root, false, |
| base::FileEnumerator::DIRECTORIES)) |
| .WillOnce(Return(CreateFileEnumerator({session_manager}))); |
| FilePath policy = session_manager.Append("policy_encrypted"); |
| EXPECT_CALL(platform_, GetFileEnumerator(session_manager, false, |
| base::FileEnumerator::DIRECTORIES)) |
| .WillOnce(Return(CreateFileEnumerator({policy}))); |
| EXPECT_CALL(platform_, GetOwnership(policy, _, _, false)) |
| .WillOnce(Return(false)); |
| } |
| |
| // Expect 1 home directory with android-data: homedir_paths_[0]. |
| EXPECT_EQ(1, homedirs_.GetUnmountedAndroidDataCount()); |
| } |
| |
| TEST_P(HomeDirsTest, AddUserTimestampToCacheEmpty) { |
| std::string obfuscatedUser = obfuscated_users_[0]; |
| base::FilePath userPath = homedir_paths_[0]; |
| |
| // Skip vault keyset loading to cause "Notime". |
| EXPECT_CALL(platform_, FileExists(Property(&FilePath::value, |
| StartsWith(userPath.value())))) |
| .WillRepeatedly(Return(true)); |
| |
| auto vk = new MockVaultKeyset(); |
| EXPECT_CALL(vault_keyset_factory_, New(_, _)).WillOnce(Return(vk)); |
| EXPECT_CALL(*vk, Load(_)).WillRepeatedly(Return(false)); |
| homedirs_.set_vault_keyset_factory(&vault_keyset_factory_); |
| |
| // No user is added. |
| EXPECT_CALL(timestamp_cache_, AddExistingUser(_, _)).Times(0); |
| |
| homedirs_.AddUserTimestampToCache(obfuscatedUser); |
| } |
| |
| TEST_P(HomeDirsTest, AddUserTimestampToCache) { |
| std::string obfuscatedUser = obfuscated_users_[0]; |
| base::FilePath userPath = homedir_paths_[0]; |
| base::Time userTime = homedir_times_[0]; |
| |
| auto vk = new MockVaultKeyset(); |
| EXPECT_CALL(vault_keyset_factory_, New(_, _)).WillOnce(Return(vk)); |
| EXPECT_CALL(*vk, Load(_)).WillRepeatedly(Return(true)); |
| |
| NiceMock<MockFileEnumerator>* master0; |
| EXPECT_CALL(platform_, GetFileEnumerator(userPath, false, _)) |
| .WillOnce(Return(master0 = new NiceMock<MockFileEnumerator>)); |
| EXPECT_CALL(*master0, Next()) |
| .WillOnce(Return(userPath.Append(kKeyFile).AddExtension("0"))) |
| .WillRepeatedly(Return(FilePath())); |
| |
| EXPECT_CALL(*vk, Load(_)).WillOnce(Return(true)); |
| SerializedVaultKeyset serialized; |
| serialized.set_last_activity_timestamp(userTime.ToInternalValue()); |
| EXPECT_CALL(*vk, serialized()).Times(2).WillRepeatedly(ReturnRef(serialized)); |
| homedirs_.set_vault_keyset_factory(&vault_keyset_factory_); |
| |
| // User is added. |
| EXPECT_CALL(timestamp_cache_, AddExistingUser(_, _)).Times(0); |
| EXPECT_CALL(timestamp_cache_, AddExistingUser(obfuscatedUser, userTime)) |
| .WillOnce(Return()); |
| |
| homedirs_.AddUserTimestampToCache(obfuscatedUser); |
| } |
| |
| TEST_P(HomeDirsTest, GetHomedirs) { |
| EXPECT_CALL(platform_, EnumerateDirectoryEntries(kTestRoot, false, _)) |
| .WillRepeatedly(DoAll(SetArgPointee<2>(homedir_paths_), Return(true))); |
| |
| std::vector<base::FilePath> home_paths(homedir_paths_.size()); |
| std::vector<bool> some_mounted(homedir_paths_.size()); |
| std::vector<bool> all_mounted(homedir_paths_.size(), true); |
| |
| for (int i = 0; i < homedir_paths_.size(); i++) { |
| home_paths[i] = |
| FilePath("/home/user/").Append(homedir_paths_[i].BaseName().value()); |
| |
| EXPECT_CALL(platform_, DirectoryExists(home_paths[i])) |
| .WillRepeatedly(Return(true)); |
| |
| some_mounted[i] = i % 2; |
| } |
| |
| EXPECT_CALL(platform_, AreDirectoriesMounted(home_paths)) |
| .WillOnce(Return(all_mounted)); |
| auto dirs = homedirs_.GetHomeDirs(); |
| for (int i = 0; i < homedir_paths_.size(); i++) { |
| EXPECT_TRUE(dirs[i].is_mounted); |
| EXPECT_EQ(dirs[i].obfuscated, obfuscated_users_[i]); |
| } |
| |
| EXPECT_CALL(platform_, AreDirectoriesMounted(home_paths)) |
| .WillOnce(Return(some_mounted)); |
| dirs = homedirs_.GetHomeDirs(); |
| for (int i = 0; i < homedir_paths_.size(); i++) { |
| EXPECT_EQ(dirs[i].is_mounted, some_mounted[i]); |
| EXPECT_EQ(dirs[i].obfuscated, obfuscated_users_[i]); |
| } |
| } |
| |
| TEST_P(HomeDirsTest, RemoveLECredentials) { |
| std::string obfuscatedUser = obfuscated_users_[0]; |
| base::FilePath userPath = homedir_paths_[0]; |
| |
| EXPECT_CALL(platform_, GetFileEnumerator(userPath, false, _)) |
| .WillOnce(InvokeWithoutArgs(CreateMockFileEnumerator)); |
| |
| homedirs_.RemoveLECredentials(obfuscatedUser); |
| } |
| |
| TEST_P(HomeDirsTest, GoodDecryptTest) { |
| // create a HomeDirs instance that points to a good shadow root, test that it |
| // properly authenticates against the first key. |
| SecureBlob system_salt; |
| NiceMock<MockTpm> tpm; |
| homedirs_.crypto()->set_tpm(&tpm); |
| homedirs_.crypto()->set_use_tpm(false); |
| ASSERT_TRUE(homedirs_.GetSystemSalt(&system_salt)); |
| set_policy(false, "", false, ""); |
| |
| test_helper_.users[1].InjectKeyset(&platform_); |
| EXPECT_CALL(platform_, FileExists(base::FilePath(kLockedToSingleUserFile))) |
| .WillRepeatedly(Return(false)); |
| SecureBlob passkey; |
| cryptohome::Crypto::PasswordToPasskey(test_helper_.users[1].password, |
| system_salt, &passkey); |
| Credentials credentials(test_helper_.users[1].username, passkey); |
| |
| ASSERT_TRUE(homedirs_.AreCredentialsValid(credentials)); |
| } |
| |
| TEST_P(HomeDirsTest, BadDecryptTest) { |
| // create a HomeDirs instance that points to a good shadow root, test that it |
| // properly denies access with a bad passkey |
| SecureBlob system_salt; |
| NiceMock<MockTpm> tpm; |
| homedirs_.crypto()->set_tpm(&tpm); |
| homedirs_.crypto()->set_use_tpm(false); |
| set_policy(false, "", false, ""); |
| |
| test_helper_.users[4].InjectKeyset(&platform_); |
| EXPECT_CALL(platform_, FileExists(base::FilePath(kLockedToSingleUserFile))) |
| .WillRepeatedly(Return(false)); |
| SecureBlob passkey; |
| cryptohome::Crypto::PasswordToPasskey("bogus", system_salt, &passkey); |
| Credentials credentials(test_helper_.users[4].username, passkey); |
| |
| ASSERT_FALSE(homedirs_.AreCredentialsValid(credentials)); |
| } |
| |
| #define MAX_VKS 5 |
| class KeysetManagementTest : public HomeDirsTest { |
| public: |
| KeysetManagementTest() {} |
| virtual ~KeysetManagementTest() {} |
| |
| void SetUp() { |
| HomeDirsTest::SetUp(); |
| last_vk_ = -1; |
| active_vk_ = NULL; |
| memset(active_vks_, 0, sizeof(active_vks_)); |
| } |
| |
| void TearDown() { |
| HomeDirsTest::TearDown(); |
| last_vk_++; |
| for (; last_vk_ < MAX_VKS; ++last_vk_) { |
| if (active_vks_[last_vk_]) |
| delete active_vks_[last_vk_]; |
| active_vks_[last_vk_] = NULL; |
| } |
| last_vk_ = -1; |
| active_vk_ = NULL; |
| } |
| |
| virtual bool VkDecrypt0(const brillo::SecureBlob& key, |
| bool locked_to_single_user, |
| CryptoError* crypto_error) { |
| return memcmp(key.data(), keys_[0].data(), key.size()) == 0; |
| } |
| |
| virtual const SerializedVaultKeyset& FakeSerialized() const { |
| return serialized_; |
| } |
| |
| virtual SerializedVaultKeyset* FakeMutableSerialized() { |
| return &serialized_; |
| } |
| |
| virtual MockFileEnumerator* NewKeysetFileEnumerator() { |
| MockFileEnumerator* files = new MockFileEnumerator(); |
| { |
| InSequence s; |
| // Single key. |
| EXPECT_CALL(*files, Next()).WillOnce(Return(keyset_paths_[0])); |
| EXPECT_CALL(*files, Next()).WillOnce(Return(FilePath())); |
| } |
| return files; |
| } |
| |
| virtual MockVaultKeyset* NewActiveVaultKeyset() { |
| last_vk_++; |
| CHECK(last_vk_ < MAX_VKS); |
| active_vk_ = active_vks_[last_vk_]; |
| EXPECT_CALL(*active_vk_, Decrypt(_, _, _)) |
| .WillRepeatedly(Invoke(this, &KeysetManagementTest::VkDecrypt0)); |
| |
| EXPECT_CALL(*active_vk_, serialized()) |
| .WillRepeatedly(Invoke(this, &KeysetManagementTest::FakeSerialized)); |
| EXPECT_CALL(*active_vk_, mutable_serialized()) |
| .WillRepeatedly( |
| Invoke(this, &KeysetManagementTest::FakeMutableSerialized)); |
| return active_vk_; |
| } |
| |
| virtual void KeysetSetUp() { |
| serialized_.Clear(); |
| NiceMock<MockTpm> tpm; |
| homedirs_.crypto()->set_tpm(&tpm); |
| homedirs_.crypto()->set_use_tpm(false); |
| ASSERT_TRUE(homedirs_.GetSystemSalt(&system_salt_)); |
| set_policy(false, "", false, ""); |
| |
| // Setup the base keyset files for users[1] |
| keyset_paths_.push_back(test_helper_.users[1].keyset_path); |
| keys_.push_back(test_helper_.users[1].passkey); |
| |
| EXPECT_CALL(platform_, FileExists(base::FilePath(kLockedToSingleUserFile))) |
| .WillRepeatedly(Return(false)); |
| EXPECT_CALL(platform_, |
| GetFileEnumerator(test_helper_.users[1].base_path, false, _)) |
| .WillRepeatedly(InvokeWithoutArgs( |
| this, &KeysetManagementTest::NewKeysetFileEnumerator)); |
| |
| homedirs_.set_vault_keyset_factory(&vault_keyset_factory_); |
| // Pre-allocate VKs so that each call can advance |
| // but expectations can be set. |
| for (int i = 0; i < MAX_VKS; ++i) { |
| active_vks_[i] = new MockVaultKeyset(); |
| // Move this particular expectation setting here instead of |
| // NewActiveVaultKeyset, since this allows us to make some modifications |
| // to the expectation in the test itself, if necessary. |
| // Also change the cardinality to be WillRepeatedly, since this makes it |
| // more forgiving even if we don't make an invocation for a VaultKeyset |
| // which isn't used in a test. |
| EXPECT_CALL(*active_vks_[i], Load(keyset_paths_[0])) |
| .WillRepeatedly(Return(true)); |
| } |
| active_vk_ = active_vks_[0]; |
| |
| EXPECT_CALL(vault_keyset_factory_, New(_, _)) |
| .WillRepeatedly(InvokeWithoutArgs( |
| this, &KeysetManagementTest::NewActiveVaultKeyset)); |
| SecureBlob passkey; |
| cryptohome::Crypto::PasswordToPasskey(test_helper_.users[1].password, |
| system_salt_, &passkey); |
| credentials_.reset( |
| new Credentials(test_helper_.users[1].username, passkey)); |
| |
| // Since most of the tests were written without reset_seed in mind, |
| // it is tedious to add expectations to every test, for the situation |
| // where a wrapped_reset_seed is not present. |
| // So, we instead set the wrapped_reset_seed by default, |
| // and have a separate test case where it is not set. |
| std::string dummy_reset_seed("DEADBEEF"); |
| serialized_.set_wrapped_reset_seed(dummy_reset_seed); |
| } |
| |
| void ClearFakeSerializedResetSeed() { |
| serialized_.clear_wrapped_reset_seed(); |
| } |
| |
| int last_vk_; |
| MockVaultKeyset* active_vk_; |
| MockVaultKeyset* active_vks_[MAX_VKS]; |
| std::vector<FilePath> keyset_paths_; |
| std::vector<brillo::SecureBlob> keys_; |
| std::unique_ptr<Credentials> credentials_; |
| SecureBlob system_salt_; |
| SerializedVaultKeyset serialized_; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P(WithEcryptfs, |
| KeysetManagementTest, |
| ::testing::Values(true)); |
| INSTANTIATE_TEST_SUITE_P(WithDircrypto, |
| KeysetManagementTest, |
| ::testing::Values(false)); |
| |
| TEST_P(KeysetManagementTest, AddKeysetSuccess) { |
| KeysetSetUp(); |
| |
| SecureBlob newkey; |
| cryptohome::Crypto::PasswordToPasskey("why not", system_salt_, &newkey); |
| int index = -1; |
| // The injected keyset in the fixture handles the |credentials_| validation. |
| EXPECT_CALL( |
| platform_, |
| OpenFile(Property(&FilePath::value, EndsWith("master.0")), StrEq("wx"))) |
| .WillOnce(Return(reinterpret_cast<FILE*>(NULL))); |
| EXPECT_CALL( |
| platform_, |
| OpenFile(Property(&FilePath::value, EndsWith("master.1")), StrEq("wx"))) |
| .WillOnce(Return(reinterpret_cast<FILE*>(0xbeefbeef))); |
| EXPECT_CALL(*active_vk_, Encrypt(newkey, _)).WillOnce(Return(true)); |
| EXPECT_CALL(*active_vk_, |
| Save(Property(&FilePath::value, EndsWith("master.1")))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(platform_, DeleteFile(_, _)).Times(0); |
| |
| EXPECT_EQ(CRYPTOHOME_ERROR_NOT_SET, |
| homedirs_.AddKeyset(*credentials_, newkey, NULL, false, &index)); |
| EXPECT_EQ(index, 1); |
| } |
| |
| TEST_P(KeysetManagementTest, AddKeysetClobber) { |
| KeysetSetUp(); |
| |
| SecureBlob newkey; |
| cryptohome::Crypto::PasswordToPasskey("why not", system_salt_, &newkey); |
| serialized_.mutable_key_data()->set_label("current label"); |
| KeyData key_data; |
| key_data.set_label("current label"); |
| FilePath vk_path("/some/path/master.0"); |
| // Show that 0 is taken. |
| EXPECT_CALL( |
| platform_, |
| OpenFile(Property(&FilePath::value, EndsWith("master.0")), StrEq("wx"))) |
| .WillOnce(Return(reinterpret_cast<FILE*>(NULL))); |
| // Let it claim 1 until it searches the labels. |
| EXPECT_CALL( |
| platform_, |
| OpenFile(Property(&FilePath::value, EndsWith("master.1")), StrEq("wx"))) |
| .WillOnce(Return(reinterpret_cast<FILE*>(0xbeefbeef))); |
| EXPECT_CALL(*active_vk_, Encrypt(newkey, _)).WillOnce(Return(true)); |
| EXPECT_CALL(*active_vks_[1], set_legacy_index(_)); |
| EXPECT_CALL(*active_vks_[1], legacy_index()).WillOnce(Return(0)); |
| EXPECT_CALL(*active_vks_[1], source_file()).WillOnce(ReturnRef(vk_path)); |
| EXPECT_CALL(*active_vk_, Save(vk_path)).WillOnce(Return(true)); |
| EXPECT_CALL(platform_, |
| DeleteFile(Property(&FilePath::value, EndsWith("master.1")), _)) |
| .Times(1); |
| |
| int index = -1; |
| EXPECT_EQ( |
| CRYPTOHOME_ERROR_NOT_SET, |
| homedirs_.AddKeyset(*credentials_, newkey, &key_data, true, &index)); |
| EXPECT_EQ(index, 0); |
| } |
| |
| TEST_P(KeysetManagementTest, AddKeysetNoClobber) { |
| KeysetSetUp(); |
| |
| SecureBlob newkey; |
| cryptohome::Crypto::PasswordToPasskey("why not", system_salt_, &newkey); |
| int index = -1; |
| serialized_.mutable_key_data()->set_label("current label"); |
| KeyData key_data; |
| key_data.set_label("current label"); |
| // The injected keyset in the fixture handles the |credentials_| validation. |
| EXPECT_CALL( |
| platform_, |
| OpenFile(Property(&FilePath::value, EndsWith("master.0")), StrEq("wx"))) |
| .WillOnce(Return(reinterpret_cast<FILE*>(NULL))); |
| EXPECT_CALL( |
| platform_, |
| OpenFile(Property(&FilePath::value, EndsWith("master.1")), StrEq("wx"))) |
| .WillOnce(Return(reinterpret_cast<FILE*>(0xbeefbeef))); |
| |
| EXPECT_EQ( |
| CRYPTOHOME_ERROR_KEY_LABEL_EXISTS, |
| homedirs_.AddKeyset(*credentials_, newkey, &key_data, false, &index)); |
| EXPECT_EQ(index, -1); |
| } |
| |
| TEST_P(KeysetManagementTest, UpdateKeysetSuccess) { |
| KeysetSetUp(); |
| |
| // No need to do PasswordToPasskey as that is the |
| // external callers job. |
| SecureBlob new_secret("why not"); |
| Key new_key; |
| new_key.set_secret("why not"); |
| new_key.mutable_data()->set_label("new label"); |
| // The injected keyset in the fixture handles the |credentials_| validation. |
| serialized_.mutable_key_data()->set_label("current label"); |
| FilePath vk_path("/some/path/master.0"); |
| EXPECT_CALL(*active_vk_, source_file()).WillOnce(ReturnRef(vk_path)); |
| EXPECT_CALL(*active_vk_, Encrypt(new_secret, _)).WillOnce(Return(true)); |
| EXPECT_CALL(*active_vk_, Save(vk_path)).WillOnce(Return(true)); |
| |
| EXPECT_EQ(CRYPTOHOME_ERROR_NOT_SET, |
| homedirs_.UpdateKeyset(*credentials_, |
| const_cast<const Key*>(&new_key), "")); |
| EXPECT_EQ(serialized_.key_data().label(), new_key.data().label()); |
| } |
| |
| TEST_P(KeysetManagementTest, UpdateKeysetAuthorizedNoSignature) { |
| KeysetSetUp(); |
| |
| // No need to do PasswordToPasskey as that is the |
| // external callers job. |
| Key new_key; |
| new_key.set_secret("why not"); |
| new_key.mutable_data()->set_label("new label"); |
| new_key.mutable_data()->set_revision(1); |
| // The injected keyset in the fixture handles the |credentials_| validation. |
| KeyData* key_data = serialized_.mutable_key_data(); |
| key_data->set_label("current label"); |
| // Allow the default override on the revision. |
| key_data->mutable_privileges()->set_update(false); |
| key_data->mutable_privileges()->set_authorized_update(true); |
| KeyAuthorizationData* auth_data = key_data->add_authorization_data(); |
| auth_data->set_type(KeyAuthorizationData::KEY_AUTHORIZATION_TYPE_HMACSHA256); |
| KeyAuthorizationSecret* auth_secret = auth_data->add_secrets(); |
| auth_secret->mutable_usage()->set_sign(true); |
| const std::string kSomeHMACKey("abc123"); |
| auth_secret->set_symmetric_key(kSomeHMACKey); |
| |
| EXPECT_EQ(CRYPTOHOME_ERROR_UPDATE_SIGNATURE_INVALID, |
| homedirs_.UpdateKeyset(*credentials_, |
| const_cast<const Key*>(&new_key), "")); |
| EXPECT_NE(serialized_.key_data().label(), new_key.data().label()); |
| } |
| |
| TEST_P(KeysetManagementTest, UpdateKeysetAuthorizedSuccess) { |
| KeysetSetUp(); |
| |
| // No need to do PasswordToPasskey as that is the |
| // external callers job. |
| SecureBlob new_pass("why not"); |
| Key new_key; |
| new_key.set_secret("why not"); |
| new_key.mutable_data()->set_label("new label"); |
| // Allow updating over an undefined revision. |
| new_key.mutable_data()->set_revision(0); |
| // The injected keyset in the fixture handles the |credentials_| validation. |
| KeyData* key_data = serialized_.mutable_key_data(); |
| key_data->set_label("current label"); |
| key_data->mutable_privileges()->set_update(false); |
| key_data->mutable_privileges()->set_authorized_update(true); |
| KeyAuthorizationData* auth_data = key_data->add_authorization_data(); |
| auth_data->set_type(KeyAuthorizationData::KEY_AUTHORIZATION_TYPE_HMACSHA256); |
| KeyAuthorizationSecret* auth_secret = auth_data->add_secrets(); |
| auth_secret->mutable_usage()->set_sign(true); |
| const std::string kSomeHMACKey("abc123"); |
| auth_secret->set_symmetric_key(kSomeHMACKey); |
| |
| FilePath vk_path("/some/path/master.0"); |
| EXPECT_CALL(*active_vk_, source_file()).WillOnce(ReturnRef(vk_path)); |
| EXPECT_CALL(*active_vk_, Encrypt(new_pass, _)).WillOnce(Return(true)); |
| EXPECT_CALL(*active_vk_, Save(vk_path)).WillOnce(Return(true)); |
| |
| std::string changes_str; |
| ac::chrome::managedaccounts::account::Secret new_secret; |
| new_secret.set_revision(new_key.data().revision()); |
| new_secret.set_secret(new_key.secret()); |
| ASSERT_TRUE(new_secret.SerializeToString(&changes_str)); |
| |
| brillo::SecureBlob hmac_key(auth_secret->symmetric_key()); |
| brillo::SecureBlob hmac_data(changes_str.begin(), changes_str.end()); |
| SecureBlob hmac = CryptoLib::HmacSha256(hmac_key, hmac_data); |
| EXPECT_EQ( |
| CRYPTOHOME_ERROR_NOT_SET, |
| homedirs_.UpdateKeyset(*credentials_, const_cast<const Key*>(&new_key), |
| hmac.to_string())); |
| EXPECT_EQ(serialized_.key_data().revision(), new_key.data().revision()); |
| } |
| |
| // Ensure signing matches the test vectors in Chrome. |
| TEST_P(KeysetManagementTest, UpdateKeysetAuthorizedCompatVector) { |
| KeysetSetUp(); |
| |
| // The salted password passed in from Chrome. |
| const char kPassword[] = "OSL3HZZSfK+mDQTYUh3lXhgAzJNWhYz52ax0Bleny7Q="; |
| // A no-op encryption key. |
| const char kB64CipherKey[] = "QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUE=\n"; |
| // The signing key pre-installed. |
| const char kB64SigningKey[] = |
| "p5TR/34XX0R7IMuffH14BiL1vcdSD8EajPzdIg09z9M=\n"; |
| // The HMAC-256 signature over kPassword using kSigningKey. |
| const char kB64Signature[] = "KOPQmmJcMr9iMkr36N1cX+G9gDdBBu7zutAxNayPMN4=\n"; |
| |
| // No need to do PasswordToPasskey as that is the |
| // external callers job. |
| SecureBlob new_pass(kPassword); |
| Key new_key; |
| new_key.set_secret(std::string(kPassword, sizeof(kPassword) - 1)); |
| new_key.mutable_data()->set_label("new label"); |
| // The compat revision to test is '1'. |
| new_key.mutable_data()->set_revision(1); |
| // The injected keyset in the fixture handles the |credentials_| validation. |
| KeyData* key_data = serialized_.mutable_key_data(); |
| key_data->set_label("current label"); |
| key_data->set_revision(0); |
| key_data->mutable_privileges()->set_update(false); |
| key_data->mutable_privileges()->set_authorized_update(true); |
| KeyAuthorizationData* auth_data = key_data->add_authorization_data(); |
| auth_data->set_type(KeyAuthorizationData::KEY_AUTHORIZATION_TYPE_HMACSHA256); |
| KeyAuthorizationSecret* auth_secret = auth_data->add_secrets(); |
| // Add an encryption secret to ensure later upgrades are viable. |
| auth_secret->mutable_usage()->set_encrypt(true); |
| std::string cipher_key; |
| ASSERT_TRUE(brillo::data_encoding::Base64Decode(kB64CipherKey, &cipher_key)); |
| auth_secret->set_symmetric_key(cipher_key); |
| // Add the signing key |
| auth_secret = auth_data->add_secrets(); |
| auth_secret->mutable_usage()->set_sign(true); |
| std::string signing_key; |
| ASSERT_TRUE( |
| brillo::data_encoding::Base64Decode(kB64SigningKey, &signing_key)); |
| auth_secret->set_symmetric_key(signing_key); |
| |
| FilePath vk_path("/some/path/master.0"); |
| EXPECT_CALL(*active_vk_, source_file()).WillOnce(ReturnRef(vk_path)); |
| EXPECT_CALL(*active_vk_, Encrypt(new_pass, _)).WillOnce(Return(true)); |
| EXPECT_CALL(*active_vk_, Save(vk_path)).WillOnce(Return(true)); |
| |
| std::string signature; |
| ASSERT_TRUE(brillo::data_encoding::Base64Decode(kB64Signature, &signature)); |
| EXPECT_EQ(CRYPTOHOME_ERROR_NOT_SET, |
| homedirs_.UpdateKeyset( |
| *credentials_, const_cast<const Key*>(&new_key), signature)); |
| EXPECT_EQ(new_key.data().revision(), serialized_.key_data().revision()); |
| } |
| |
| TEST_P(KeysetManagementTest, UpdateKeysetAuthorizedNoEqualReplay) { |
| KeysetSetUp(); |
| |
| // No need to do PasswordToPasskey as that is the |
| // external callers job. |
| Key new_key; |
| new_key.set_secret("why not"); |
| new_key.mutable_data()->set_label("new label"); |
| new_key.mutable_data()->set_revision(100); |
| // The injected keyset in the fixture handles the |credentials_| validation. |
| KeyData* key_data = serialized_.mutable_key_data(); |
| key_data->set_revision(100); |
| key_data->set_label("current label"); |
| key_data->mutable_privileges()->set_update(false); |
| key_data->mutable_privileges()->set_authorized_update(true); |
| KeyAuthorizationData* auth_data = key_data->add_authorization_data(); |
| auth_data->set_type(KeyAuthorizationData::KEY_AUTHORIZATION_TYPE_HMACSHA256); |
| KeyAuthorizationSecret* auth_secret = auth_data->add_secrets(); |
| auth_secret->mutable_usage()->set_sign(true); |
| const std::string kSomeHMACKey("abc123"); |
| auth_secret->set_symmetric_key(kSomeHMACKey); |
| |
| std::string changes_str; |
| ac::chrome::managedaccounts::account::Secret new_secret; |
| new_secret.set_revision(new_key.data().revision()); |
| new_secret.set_secret(new_key.secret()); |
| ASSERT_TRUE(new_secret.SerializeToString(&changes_str)); |
| brillo::SecureBlob hmac_key(auth_secret->symmetric_key()); |
| brillo::SecureBlob hmac_data(changes_str.begin(), changes_str.end()); |
| SecureBlob hmac = CryptoLib::HmacSha256(hmac_key, hmac_data); |
| EXPECT_EQ( |
| CRYPTOHOME_ERROR_UPDATE_SIGNATURE_INVALID, |
| homedirs_.UpdateKeyset(*credentials_, const_cast<const Key*>(&new_key), |
| hmac.to_string())); |
| EXPECT_NE(serialized_.key_data().label(), new_key.data().label()); |
| } |
| |
| TEST_P(KeysetManagementTest, UpdateKeysetAuthorizedNoLessReplay) { |
| KeysetSetUp(); |
| |
| // No need to do PasswordToPasskey as that is the |
| // external callers job. |
| Key new_key; |
| new_key.set_secret("why not"); |
| new_key.mutable_data()->set_label("new label"); |
| new_key.mutable_data()->set_revision(0); |
| // The injected keyset in the fixture handles the |credentials_| validation. |
| KeyData* key_data = serialized_.mutable_key_data(); |
| key_data->set_revision(1); |
| key_data->set_label("current label"); |
| key_data->mutable_privileges()->set_update(false); |
| key_data->mutable_privileges()->set_authorized_update(true); |
| KeyAuthorizationData* auth_data = key_data->add_authorization_data(); |
| auth_data->set_type(KeyAuthorizationData::KEY_AUTHORIZATION_TYPE_HMACSHA256); |
| KeyAuthorizationSecret* auth_secret = auth_data->add_secrets(); |
| auth_secret->mutable_usage()->set_sign(true); |
| const std::string kSomeHMACKey("abc123"); |
| auth_secret->set_symmetric_key(kSomeHMACKey); |
| |
| std::string changes_str; |
| ac::chrome::managedaccounts::account::Secret new_secret; |
| new_secret.set_revision(new_key.data().revision()); |
| new_secret.set_secret(new_key.secret()); |
| ASSERT_TRUE(new_secret.SerializeToString(&changes_str)); |
| |
| brillo::SecureBlob hmac_key(auth_secret->symmetric_key()); |
| brillo::SecureBlob hmac_data(changes_str.begin(), changes_str.end()); |
| SecureBlob hmac = CryptoLib::HmacSha256(hmac_key, hmac_data); |
| EXPECT_EQ( |
| CRYPTOHOME_ERROR_UPDATE_SIGNATURE_INVALID, |
| homedirs_.UpdateKeyset(*credentials_, const_cast<const Key*>(&new_key), |
| hmac.to_string())); |
| EXPECT_NE(serialized_.key_data().label(), new_key.data().label()); |
| } |
| |
| TEST_P(KeysetManagementTest, UpdateKeysetAuthorizedBadSignature) { |
| KeysetSetUp(); |
| |
| // No need to do PasswordToPasskey as that is the |
| // external callers job. |
| Key new_key; |
| new_key.set_secret("why not"); |
| new_key.mutable_data()->set_label("new label"); |
| new_key.mutable_data()->set_revision(0); |
| // The injected keyset in the fixture handles the |credentials_| validation. |
| KeyData* key_data = serialized_.mutable_key_data(); |
| key_data->set_label("current label"); |
| key_data->mutable_privileges()->set_update(false); |
| key_data->mutable_privileges()->set_authorized_update(true); |
| KeyAuthorizationData* auth_data = key_data->add_authorization_data(); |
| auth_data->set_type(KeyAuthorizationData::KEY_AUTHORIZATION_TYPE_HMACSHA256); |
| KeyAuthorizationSecret* auth_secret = auth_data->add_secrets(); |
| auth_secret->mutable_usage()->set_sign(true); |
| const std::string kSomeHMACKey("abc123"); |
| auth_secret->set_symmetric_key(kSomeHMACKey); |
| |
| std::string changes_str; |
| ac::chrome::managedaccounts::account::Secret bad_secret; |
| bad_secret.set_revision(new_key.data().revision()); |
| bad_secret.set_secret("something else"); |
| ASSERT_TRUE(bad_secret.SerializeToString(&changes_str)); |
| |
| brillo::SecureBlob hmac_key(auth_secret->symmetric_key()); |
| brillo::SecureBlob hmac_data(changes_str.begin(), changes_str.end()); |
| SecureBlob hmac = CryptoLib::HmacSha256(hmac_key, hmac_data); |
| EXPECT_EQ( |
| CRYPTOHOME_ERROR_UPDATE_SIGNATURE_INVALID, |
| homedirs_.UpdateKeyset(*credentials_, const_cast<const Key*>(&new_key), |
| hmac.to_string())); |
| EXPECT_NE(serialized_.key_data().label(), new_key.data().label()); |
| } |
| |
| TEST_P(KeysetManagementTest, UpdateKeysetBadSecret) { |
| KeysetSetUp(); |
| |
| // No need to do PasswordToPasskey as that is the |
| // external callers job. |
| SecureBlob new_secret("why not"); |
| Key new_key; |
| new_key.set_secret("why not"); |
| new_key.mutable_data()->set_label("new label"); |
| // The injected keyset in the fixture handles the |credentials_| validation. |
| serialized_.mutable_key_data()->set_label("current label"); |
| |
| SecureBlob bad_pass("not it"); |
| credentials_.reset(new Credentials(test_helper_.users[1].username, bad_pass)); |
| EXPECT_EQ(CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED, |
| homedirs_.UpdateKeyset(*credentials_, |
| const_cast<const Key*>(&new_key), "")); |
| EXPECT_NE(serialized_.key_data().label(), new_key.data().label()); |
| } |
| |
| TEST_P(KeysetManagementTest, UpdateKeysetNotFoundWithLabel) { |
| KeysetSetUp(); |
| |
| KeyData some_label; |
| some_label.set_label("key that doesn't exist"); |
| credentials_->set_key_data(some_label); |
| const Key new_key; |
| EXPECT_EQ(CRYPTOHOME_ERROR_AUTHORIZATION_KEY_NOT_FOUND, |
| homedirs_.UpdateKeyset(*credentials_, &new_key, "")); |
| } |
| |
| TEST_P(KeysetManagementTest, RemoveKeysetSuccess) { |
| KeysetSetUp(); |
| |
| Key remove_key; |
| remove_key.mutable_data()->set_label("remove me"); |
| |
| // Expect the 0 slot since it'll match all the fake keys. |
| EXPECT_CALL(*active_vks_[0], set_legacy_index(0)); |
| // Return a different slot to make sure the code is using the right object. |
| EXPECT_CALL(*active_vks_[0], legacy_index()).WillOnce(Return(1)); |
| // The VaultKeyset which will be removed will get index 2. |
| EXPECT_CALL(*active_vks_[2], |
| Load(keyset_paths_[0].ReplaceExtension(std::to_string(1)))) |
| .WillOnce(Return(true)); |
| |
| serialized_.mutable_key_data()->mutable_privileges()->set_remove(true); |
| serialized_.mutable_key_data()->set_label("remove me"); |
| EXPECT_EQ(CRYPTOHOME_ERROR_NOT_SET, |
| homedirs_.RemoveKeyset(*credentials_, remove_key.data())); |
| } |
| |
| TEST_P(KeysetManagementTest, RemoveKeysetNotFound) { |
| KeysetSetUp(); |
| |
| Key remove_key; |
| remove_key.mutable_data()->set_label("remove me please"); |
| |
| serialized_.mutable_key_data()->mutable_privileges()->set_remove(true); |
| serialized_.mutable_key_data()->set_label("the only key in town"); |
| EXPECT_EQ(CRYPTOHOME_ERROR_KEY_NOT_FOUND, |
| homedirs_.RemoveKeyset(*credentials_, remove_key.data())); |
| } |
| |
| TEST_P(KeysetManagementTest, GetVaultKeysetLabelsOneLabeled) { |
| KeysetSetUp(); |
| |
| serialized_.mutable_key_data()->set_label("a labeled key"); |
| std::vector<std::string> labels; |
| EXPECT_TRUE(homedirs_.GetVaultKeysetLabels( |
| credentials_->GetObfuscatedUsername(system_salt_), &labels)); |
| ASSERT_NE(0, labels.size()); |
| EXPECT_EQ(serialized_.key_data().label(), labels[0]); |
| } |
| |
| TEST_P(KeysetManagementTest, GetVaultKeysetLabelsOneLegacyLabeled) { |
| KeysetSetUp(); |
| |
| serialized_.clear_key_data(); |
| std::vector<std::string> labels; |
| EXPECT_TRUE(homedirs_.GetVaultKeysetLabels( |
| credentials_->GetObfuscatedUsername(system_salt_), &labels)); |
| ASSERT_NE(0, labels.size()); |
| EXPECT_EQ(StringPrintf("%s%d", kKeyLegacyPrefix, 0), labels[0]); |
| } |
| |
| TEST_P(KeysetManagementTest, AddKeysetInvalidCreds) { |
| KeysetSetUp(); |
| |
| SecureBlob newkey; |
| cryptohome::Crypto::PasswordToPasskey("why not", system_salt_, &newkey); |
| int index = -1; |
| |
| EXPECT_CALL(platform_, DeleteFile(_, _)).Times(0); |
| // Try to authenticate with an unknown key. |
| Credentials bad_credentials(test_helper_.users[1].username, newkey); |
| ASSERT_EQ(CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED, |
| homedirs_.AddKeyset(bad_credentials, newkey, NULL, false, &index)); |
| EXPECT_EQ(index, -1); |
| } |
| |
| TEST_P(KeysetManagementTest, AddKeysetInvalidPrivileges) { |
| // Check for key use that lacks valid add privileges |
| KeysetSetUp(); |
| |
| // The injected keyset in the fixture handles the |credentials_| validation. |
| SecureBlob newkey; |
| cryptohome::Crypto::PasswordToPasskey("why not", system_salt_, &newkey); |
| |
| serialized_.mutable_key_data()->mutable_privileges()->set_add(false); |
| int index = -1; |
| // Tery to authenticate with a key that cannot add keys. |
| ASSERT_EQ(CRYPTOHOME_ERROR_AUTHORIZATION_KEY_DENIED, |
| homedirs_.AddKeyset(*credentials_, newkey, NULL, false, &index)); |
| EXPECT_EQ(index, -1); |
| } |
| |
| TEST_P(KeysetManagementTest, AddKeyset0Available) { |
| // While this doesn't affect the hole-finding logic, it's good to cover the |
| // full logical behavior by changing which key auths too. |
| // master.0 -> master.1 |
| FilePath new_keyset = test_helper_.users[1].keyset_path.ReplaceExtension("1"); |
| test_helper_.users[1].keyset_path = new_keyset; |
| KeysetSetUp(); |
| |
| // The injected keyset in the fixture handles the |credentials_| validation. |
| SecureBlob newkey; |
| cryptohome::Crypto::PasswordToPasskey("why not", system_salt_, &newkey); |
| |
| EXPECT_CALL( |
| platform_, |
| OpenFile(Property(&FilePath::value, EndsWith("master.0")), StrEq("wx"))) |
| .WillOnce(Return(reinterpret_cast<FILE*>(0xbeefbeef))); |
| EXPECT_CALL(*active_vk_, Encrypt(newkey, _)).WillOnce(Return(true)); |
| EXPECT_CALL(*active_vk_, |
| Save(Property(&FilePath::value, EndsWith("master.0")))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(platform_, DeleteFile(_, _)).Times(0); |
| |
| int index = -1; |
| // Try to authenticate with an unknown key. |
| ASSERT_EQ(CRYPTOHOME_ERROR_NOT_SET, |
| homedirs_.AddKeyset(*credentials_, newkey, NULL, false, &index)); |
| EXPECT_EQ(index, 0); |
| } |
| |
| TEST_P(KeysetManagementTest, AddKeyset10Available) { |
| KeysetSetUp(); |
| |
| // The injected keyset in the fixture handles the |credentials_| validation. |
| SecureBlob newkey; |
| cryptohome::Crypto::PasswordToPasskey("why not", system_salt_, &newkey); |
| |
| EXPECT_CALL(platform_, OpenFile(Property(&FilePath::value, |
| MatchesRegex(".*/master\\..$")), |
| StrEq("wx"))) |
| .Times(10) |
| .WillRepeatedly(Return(reinterpret_cast<FILE*>(NULL))); |
| EXPECT_CALL( |
| platform_, |
| OpenFile(Property(&FilePath::value, EndsWith("master.10")), StrEq("wx"))) |
| .WillOnce(Return(reinterpret_cast<FILE*>(0xbeefbeef))); |
| EXPECT_CALL(platform_, DeleteFile(_, _)).Times(0); |
| EXPECT_CALL(*active_vk_, Encrypt(newkey, _)).WillOnce(Return(true)); |
| EXPECT_CALL(*active_vk_, |
| Save(Property(&FilePath::value, EndsWith("master.10")))) |
| .WillOnce(Return(true)); |
| |
| int index = -1; |
| ASSERT_EQ(CRYPTOHOME_ERROR_NOT_SET, |
| homedirs_.AddKeyset(*credentials_, newkey, NULL, false, &index)); |
| EXPECT_EQ(index, 10); |
| } |
| |
| TEST_P(KeysetManagementTest, AddKeysetNoFreeIndices) { |
| KeysetSetUp(); |
| |
| // The injected keyset in the fixture handles the |credentials_| validation. |
| SecureBlob newkey; |
| cryptohome::Crypto::PasswordToPasskey("why not", system_salt_, &newkey); |
| |
| EXPECT_CALL(platform_, OpenFile(Property(&FilePath::value, |
| MatchesRegex(".*/master\\..*$")), |
| StrEq("wx"))) |
| .Times(kKeyFileMax) |
| .WillRepeatedly(Return(reinterpret_cast<FILE*>(NULL))); |
| EXPECT_CALL(platform_, DeleteFile(_, _)).Times(0); |
| |
| int index = -1; |
| ASSERT_EQ(CRYPTOHOME_ERROR_KEY_QUOTA_EXCEEDED, |
| homedirs_.AddKeyset(*credentials_, newkey, NULL, false, &index)); |
| EXPECT_EQ(index, -1); |
| } |
| |
| TEST_P(KeysetManagementTest, AddKeysetEncryptFail) { |
| KeysetSetUp(); |
| |
| SecureBlob newkey; |
| cryptohome::Crypto::PasswordToPasskey("why not", system_salt_, &newkey); |
| int index = -1; |
| // The injected keyset in the fixture handles the |credentials_| validation. |
| EXPECT_CALL( |
| platform_, |
| OpenFile(Property(&FilePath::value, EndsWith("master.0")), StrEq("wx"))) |
| .WillOnce(Return(reinterpret_cast<FILE*>(0xbeefbeef))); |
| EXPECT_CALL(*active_vk_, Encrypt(newkey, _)).WillOnce(Return(false)); |
| EXPECT_CALL(platform_, CloseFile(reinterpret_cast<FILE*>(0xbeefbeef))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL( |
| platform_, |
| DeleteFile(Property(&FilePath::value, EndsWith("master.0")), false)) |
| .WillOnce(Return(true)); |
| ASSERT_EQ(CRYPTOHOME_ERROR_BACKING_STORE_FAILURE, |
| homedirs_.AddKeyset(*credentials_, newkey, NULL, false, &index)); |
| EXPECT_EQ(index, -1); |
| } |
| |
| TEST_P(KeysetManagementTest, AddKeysetSaveFail) { |
| KeysetSetUp(); |
| |
| SecureBlob newkey; |
| cryptohome::Crypto::PasswordToPasskey("why not", system_salt_, &newkey); |
| int index = -1; |
| // The injected keyset in the fixture handles the |credentials_| validation. |
| EXPECT_CALL( |
| platform_, |
| OpenFile(Property(&FilePath::value, EndsWith("master.0")), StrEq("wx"))) |
| .WillOnce(Return(reinterpret_cast<FILE*>(0xbeefbeef))); |
| EXPECT_CALL(*active_vk_, Encrypt(newkey, _)).WillOnce(Return(true)); |
| EXPECT_CALL(*active_vk_, |
| Save(Property(&FilePath::value, EndsWith("master.0")))) |
| .WillOnce(Return(false)); |
| EXPECT_CALL(platform_, CloseFile(reinterpret_cast<FILE*>(0xbeefbeef))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL( |
| platform_, |
| DeleteFile(Property(&FilePath::value, EndsWith("master.0")), false)) |
| .WillOnce(Return(true)); |
| ASSERT_EQ(CRYPTOHOME_ERROR_BACKING_STORE_FAILURE, |
| homedirs_.AddKeyset(*credentials_, newkey, NULL, false, &index)); |
| EXPECT_EQ(index, -1); |
| } |
| |
| TEST_P(KeysetManagementTest, AddKeysetNoResetSeedSuccess) { |
| KeysetSetUp(); |
| ClearFakeSerializedResetSeed(); |
| |
| std::string old_file_name("master.0"); |
| |
| const SecureBlob oldkey = credentials_->passkey(); |
| SecureBlob newkey; |
| cryptohome::Crypto::PasswordToPasskey("why not", system_salt_, &newkey); |
| int index = -1; |
| |
| // Expectations for calls used to generate the reset_seed |
| base::FilePath orig_file(old_file_name); |
| EXPECT_CALL(*active_vk_, Encrypt(oldkey, _)).WillOnce(Return(true)); |
| EXPECT_CALL(*active_vk_, |
| Save(Property(&FilePath::value, EndsWith(old_file_name)))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*active_vk_, source_file()).WillOnce(ReturnRef(orig_file)); |
| |
| // The injected keyset in the fixture handles the |credentials_| validation. |
| EXPECT_CALL(platform_, |
| OpenFile(Property(&FilePath::value, EndsWith(old_file_name)), |
| StrEq("wx"))) |
| .WillOnce(Return(reinterpret_cast<FILE*>(NULL))); |
| EXPECT_CALL( |
| platform_, |
| OpenFile(Property(&FilePath::value, EndsWith("master.1")), StrEq("wx"))) |
| .WillOnce(Return(reinterpret_cast<FILE*>(0xbeefbeef))); |
| EXPECT_CALL(*active_vk_, Encrypt(newkey, _)).WillOnce(Return(true)); |
| EXPECT_CALL(*active_vk_, |
| Save(Property(&FilePath::value, EndsWith("master.1")))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(platform_, DeleteFile(_, _)).Times(0); |
| |
| EXPECT_EQ(CRYPTOHOME_ERROR_NOT_SET, |
| homedirs_.AddKeyset(*credentials_, newkey, NULL, false, &index)); |
| EXPECT_EQ(index, 1); |
| } |
| |
| TEST_P(KeysetManagementTest, ForceRemoveKeysetSuccess) { |
| KeysetSetUp(); |
| EXPECT_CALL( |
| platform_, |
| DeleteFile(Property(&FilePath::value, EndsWith("master.0")), false)) |
| .WillOnce(Return(true)); |
| // There is only one call to VaultKeyset, so it gets the MockVaultKeyset |
| // with index 0. |
| EXPECT_CALL(*active_vks_[0], Load(_)).WillOnce(Return(true)); |
| ASSERT_TRUE(homedirs_.ForceRemoveKeyset("a0b0c0", 0)); |
| } |
| |
| TEST_P(KeysetManagementTest, ForceRemoveKeysetMissingKeyset) { |
| KeysetSetUp(); |
| // There is only one call to VaultKeyset, so it gets the MockVaultKeyset |
| // with index 0. |
| // Set it to false, since there is no valid VaultKeyset. |
| EXPECT_CALL(*active_vks_[0], Load(_)).WillOnce(Return(false)); |
| ASSERT_TRUE(homedirs_.ForceRemoveKeyset("a0b0c0", 0)); |
| } |
| |
| TEST_P(KeysetManagementTest, ForceRemoveKeysetNegativeIndex) { |
| ASSERT_FALSE(homedirs_.ForceRemoveKeyset("a0b0c0", -1)); |
| } |
| |
| TEST_P(KeysetManagementTest, ForceRemoveKeysetOverMaxIndex) { |
| ASSERT_FALSE(homedirs_.ForceRemoveKeyset("a0b0c0", kKeyFileMax)); |
| } |
| |
| TEST_P(KeysetManagementTest, ForceRemoveKeysetFailedDelete) { |
| KeysetSetUp(); |
| EXPECT_CALL( |
| platform_, |
| DeleteFile(Property(&FilePath::value, EndsWith("master.0")), false)) |
| .WillOnce(Return(false)); |
| // There is only one call to VaultKeyset, so it gets the MockVaultKeyset |
| // with index 0. |
| EXPECT_CALL(*active_vks_[0], Load(_)).WillOnce(Return(true)); |
| ASSERT_FALSE(homedirs_.ForceRemoveKeyset("a0b0c0", 0)); |
| } |
| |
| TEST_P(KeysetManagementTest, MoveKeysetSuccess_0_to_1) { |
| const std::string obfuscated = "a0b0c0"; |
| EXPECT_CALL(platform_, |
| FileExists(Property(&FilePath::value, EndsWith("master.0")))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(platform_, |
| FileExists(Property(&FilePath::value, EndsWith("master.1")))) |
| .WillOnce(Return(false)); |
| EXPECT_CALL( |
| platform_, |
| OpenFile(Property(&FilePath::value, EndsWith("master.1")), StrEq("wx"))) |
| .WillOnce(Return(reinterpret_cast<FILE*>(0xbeefbeef))); |
| EXPECT_CALL(platform_, |
| Rename(Property(&FilePath::value, EndsWith("master.0")), |
| Property(&FilePath::value, EndsWith("master.1")))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(platform_, CloseFile(reinterpret_cast<FILE*>(0xbeefbeef))) |
| .WillOnce(Return(true)); |
| ASSERT_TRUE(homedirs_.MoveKeyset(obfuscated, 0, 1)); |
| } |
| |
| TEST_P(KeysetManagementTest, MoveKeysetSuccess_1_to_99) { |
| const std::string obfuscated = "a0b0c0"; |
| EXPECT_CALL(platform_, |
| FileExists(Property(&FilePath::value, EndsWith("master.1")))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(platform_, |
| FileExists(Property(&FilePath::value, EndsWith("master.99")))) |
| .WillOnce(Return(false)); |
| EXPECT_CALL( |
| platform_, |
| OpenFile(Property(&FilePath::value, EndsWith("master.99")), StrEq("wx"))) |
| .WillOnce(Return(reinterpret_cast<FILE*>(0xbeefbeef))); |
| EXPECT_CALL(platform_, |
| Rename(Property(&FilePath::value, EndsWith("master.1")), |
| Property(&FilePath::value, EndsWith("master.99")))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(platform_, CloseFile(reinterpret_cast<FILE*>(0xbeefbeef))) |
| .WillOnce(Return(true)); |
| ASSERT_TRUE(homedirs_.MoveKeyset(obfuscated, 1, 99)); |
| } |
| |
| TEST_P(KeysetManagementTest, MoveKeysetNegativeSource) { |
| const std::string obfuscated = "a0b0c0"; |
| ASSERT_FALSE(homedirs_.MoveKeyset(obfuscated, -1, 1)); |
| } |
| |
| TEST_P(KeysetManagementTest, MoveKeysetNegativeDestination) { |
| const std::string obfuscated = "a0b0c0"; |
| ASSERT_FALSE(homedirs_.MoveKeyset(obfuscated, 1, -1)); |
| } |
| |
| TEST_P(KeysetManagementTest, MoveKeysetTooLargeDestination) { |
| const std::string obfuscated = "a0b0c0"; |
| ASSERT_FALSE(homedirs_.MoveKeyset(obfuscated, 1, kKeyFileMax)); |
| } |
| |
| TEST_P(KeysetManagementTest, MoveKeysetTooLargeSource) { |
| const std::string obfuscated = "a0b0c0"; |
| ASSERT_FALSE(homedirs_.MoveKeyset(obfuscated, kKeyFileMax, 0)); |
| } |
| |
| TEST_P(KeysetManagementTest, MoveKeysetMissingSource) { |
| const std::string obfuscated = "a0b0c0"; |
| EXPECT_CALL(platform_, |
| FileExists(Property(&FilePath::value, EndsWith("master.0")))) |
| .WillOnce(Return(false)); |
| ASSERT_FALSE(homedirs_.MoveKeyset(obfuscated, 0, 1)); |
| } |
| |
| TEST_P(KeysetManagementTest, MoveKeysetDestinationExists) { |
| const std::string obfuscated = "a0b0c0"; |
| EXPECT_CALL(platform_, |
| FileExists(Property(&FilePath::value, EndsWith("master.0")))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(platform_, |
| FileExists(Property(&FilePath::value, EndsWith("master.1")))) |
| .WillOnce(Return(true)); |
| ASSERT_FALSE(homedirs_.MoveKeyset(obfuscated, 0, 1)); |
| } |
| |
| TEST_P(KeysetManagementTest, MoveKeysetExclusiveOpenFailed) { |
| const std::string obfuscated = "a0b0c0"; |
| EXPECT_CALL(platform_, |
| FileExists(Property(&FilePath::value, EndsWith("master.0")))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(platform_, |
| FileExists(Property(&FilePath::value, EndsWith("master.1")))) |
| .WillOnce(Return(false)); |
| EXPECT_CALL( |
| platform_, |
| OpenFile(Property(&FilePath::value, EndsWith("master.1")), StrEq("wx"))) |
| .WillOnce(Return(reinterpret_cast<FILE*>(NULL))); |
| ASSERT_FALSE(homedirs_.MoveKeyset(obfuscated, 0, 1)); |
| } |
| |
| TEST_P(KeysetManagementTest, MoveKeysetRenameFailed) { |
| const std::string obfuscated = "a0b0c0"; |
| EXPECT_CALL(platform_, |
| FileExists(Property(&FilePath::value, EndsWith("master.0")))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(platform_, |
| FileExists(Property(&FilePath::value, EndsWith("master.1")))) |
| .WillOnce(Return(false)); |
| EXPECT_CALL( |
| platform_, |
| OpenFile(Property(&FilePath::value, EndsWith("master.1")), StrEq("wx"))) |
| .WillOnce(Return(reinterpret_cast<FILE*>(0xbeefbeef))); |
| EXPECT_CALL(platform_, |
| Rename(Property(&FilePath::value, EndsWith("master.0")), |
| Property(&FilePath::value, EndsWith("master.1")))) |
| .WillOnce(Return(false)); |
| EXPECT_CALL(platform_, CloseFile(reinterpret_cast<FILE*>(0xbeefbeef))) |
| .WillOnce(Return(true)); |
| ASSERT_FALSE(homedirs_.MoveKeyset(obfuscated, 0, 1)); |
| } |
| |
| } // namespace cryptohome |