blob: 7f389c8c879e735f0c3310e7b570b613e5c51ee5 [file] [log] [blame]
// 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 "homedirs.h"
#include <base/stringprintf.h>
#include <chromeos/cryptohome.h>
#include <chromeos/secure_blob.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <policy/mock_device_policy.h>
#include "make_tests.h"
#include "mock_platform.h"
#include "mock_tpm.h"
#include "mock_user_oldest_activity_timestamp_cache.h"
#include "mock_vault_keyset.h"
#include "mock_vault_keyset_factory.h"
#include "username_passkey.h"
namespace cryptohome {
using chromeos::SecureBlob;
using std::string;
using ::testing::DoAll;
using ::testing::EndsWith;
using ::testing::HasSubstr;
using ::testing::InSequence;
using ::testing::MatchesRegex;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::ReturnRef;
using ::testing::SaveArg;
using ::testing::SetArgumentPointee;
using ::testing::StartsWith;
using ::testing::_;
extern const char kKeyFile[];
extern const int kKeyFileMax;
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;
}
namespace {
const char *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 } }
};
const base::Time::Exploded jan1st2011_exploded = { 2011, 1, 6, 1 };
const base::Time time_jan1 = base::Time::FromUTCExploded(jan1st2011_exploded);
const base::Time::Exploded feb1st2011_exploded = { 2011, 2, 2, 1 };
const base::Time time_feb1 = base::Time::FromUTCExploded(feb1st2011_exploded);
const base::Time::Exploded mar1st2011_exploded = { 2011, 3, 2, 1 };
const base::Time time_mar1 = base::Time::FromUTCExploded(mar1st2011_exploded);
const base::Time::Exploded apr5th2011_exploded = { 2011, 4, 5, 1 };
const base::Time time_apr5 = base::Time::FromUTCExploded(apr5th2011_exploded);
} // namespace
class HomeDirsTest : public ::testing::Test {
public:
HomeDirsTest() { }
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);
homedirs_.set_platform(&platform_);
homedirs_.crypto()->set_platform(&platform_);
homedirs_.set_shadow_root(kTestRoot);
test_helper_.InjectSystemSalt(&platform_,
StringPrintf("%s/salt", kTestRoot));
set_policy(true, kOwner, false);
// Mount() normally sets this.
homedirs_.set_old_user_last_activity_time(kOldUserLastActivityTime);
homedirs_.set_timestamp_cache(&timestamp_cache_);
homedirs_.Init();
FilePath fp = FilePath(kTestRoot);
for (unsigned int i = 0; i < arraysize(kHomedirs); i++) {
const struct homedir *hd = &kHomedirs[i];
FilePath path;
std::string owner;
homedirs_.GetOwner(&owner);
path = fp.Append(hd->name);
if (!strcmp(hd->name, kOwner))
path = fp.Append(owner);
homedir_paths_.push_back(path.value());
base::Time t = base::Time::FromUTCExploded(hd->time);
homedir_times_.push_back(t);
}
}
void TearDown() {
test_helper_.TearDownSystemSalt();
}
void set_policy(bool owner_known,
const string& owner,
bool ephemeral_users_enabled) {
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(device_policy));
}
protected:
HomeDirs homedirs_;
NiceMock<MockPlatform> platform_;
std::vector<std::string> homedir_paths_;
MakeTests test_helper_;
MockUserOldestActivityTimestampCache timestamp_cache_;
std::vector<base::Time> homedir_times_;
MockVaultKeysetFactory vault_keyset_factory_;
private:
DISALLOW_COPY_AND_ASSIGN(HomeDirsTest);
};
TEST_F(HomeDirsTest, RemoveNonOwnerCryptohomes) {
// Ensure that RemoveNonOwnerCryptohomes does.
EXPECT_CALL(platform_, EnumerateDirectoryEntries(kTestRoot, false, _))
.WillOnce(
DoAll(SetArgumentPointee<2>(homedir_paths_),
Return(true)));
FilePath user_prefix = chromeos::cryptohome::home::GetUserPathPrefix();
FilePath root_prefix = chromeos::cryptohome::home::GetRootPathPrefix();
EXPECT_CALL(platform_, EnumerateDirectoryEntries(user_prefix.value(), _, _))
.WillOnce(Return(true));
EXPECT_CALL(platform_, EnumerateDirectoryEntries(root_prefix.value(), _, _))
.WillOnce(Return(true));
EXPECT_CALL(platform_, DirectoryExists(_))
.WillRepeatedly(Return(true));
EXPECT_CALL(platform_, IsDirectoryMountedWith(_, _))
.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();
}
class FreeDiskSpaceTest : public HomeDirsTest {
public:
FreeDiskSpaceTest() { }
virtual ~FreeDiskSpaceTest() { }
};
TEST_F(FreeDiskSpaceTest, InitializeTimeCacheWithNoTime) {
// To get to the init logic, we need to fail three kEnoughFreeSpace checks.
EXPECT_CALL(platform_, AmountOfFreeDiskSpace(kTestRoot))
.WillOnce(Return(0))
.WillOnce(Return(0))
.WillOnce(Return(kEnoughFreeSpace - 1));
// Expect cache clean ups.
EXPECT_CALL(platform_, EnumerateDirectoryEntries(kTestRoot, false, _))
.WillRepeatedly(
DoAll(SetArgumentPointee<2>(homedir_paths_),
Return(true)));
EXPECT_CALL(platform_, DirectoryExists(_))
.WillRepeatedly(Return(true));
EXPECT_CALL(platform_, IsDirectoryMountedWith(_, _))
.WillRepeatedly(Return(false));
// The master.* enumerators (wildcard matcher first)
EXPECT_CALL(platform_, GetFileEnumerator(_, false, _))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>));
// Empty enumerators per-user per-cache dirs plus
// enumerators for empty vaults.
EXPECT_CALL(platform_, GetFileEnumerator(HasSubstr("user/Cache"), false, _))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>));
EXPECT_CALL(platform_, GetFileEnumerator(HasSubstr("user/GCache"), false, _))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>));
// Now cover the actual initialization piece
EXPECT_CALL(timestamp_cache_, initialized())
.WillOnce(Return(false));
EXPECT_CALL(timestamp_cache_, Initialize())
.Times(1);
// It then walks the user vault to populate.
MockVaultKeyset* vk[arraysize(kHomedirs)];
EXPECT_CALL(vault_keyset_factory_, New(_, _))
.WillOnce(Return(vk[0] = new MockVaultKeyset()))
.WillOnce(Return(vk[1] = new MockVaultKeyset()))
.WillOnce(Return(vk[2] = new MockVaultKeyset()))
.WillOnce(Return(vk[3] = new MockVaultKeyset()));
for (size_t i = 0; i < arraysize(vk); ++i) {
EXPECT_CALL(*vk[i], Load(_))
.WillRepeatedly(Return(false));
}
homedirs_.set_vault_keyset_factory(&vault_keyset_factory_);
// Expect an addition for all users with no time.
EXPECT_CALL(timestamp_cache_, AddExistingUserNotime(_))
.Times(4);
// Now skip the deletion steps by not having a legit owner.
set_policy(false, "", false);
EXPECT_TRUE(homedirs_.FreeDiskSpace());
}
TEST_F(FreeDiskSpaceTest, InitializeTimeCacheWithOneTime) {
// To get to the init logic, we need to fail three kEnoughFreeSpace checks.
EXPECT_CALL(platform_, AmountOfFreeDiskSpace(kTestRoot))
.WillOnce(Return(0))
.WillOnce(Return(0))
.WillOnce(Return(kEnoughFreeSpace - 1));
// Expect cache clean ups.
EXPECT_CALL(platform_, EnumerateDirectoryEntries(kTestRoot, false, _))
.WillRepeatedly(
DoAll(SetArgumentPointee<2>(homedir_paths_),
Return(true)));
EXPECT_CALL(platform_, DirectoryExists(_))
.WillRepeatedly(Return(true));
// The master.* enumerators (wildcard matcher first)
EXPECT_CALL(platform_, GetFileEnumerator(_, false, _))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>));
// Empty enumerators per-user per-cache dirs plus
// enumerators for empty vaults.
EXPECT_CALL(platform_, GetFileEnumerator(HasSubstr("user/Cache"), false, _))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>));
EXPECT_CALL(platform_, GetFileEnumerator(HasSubstr("user/GCache"), false, _))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>));
// Now cover the actual initialization piece
EXPECT_CALL(timestamp_cache_, initialized())
.WillOnce(Return(false));
EXPECT_CALL(timestamp_cache_, Initialize())
.Times(1);
// Skip vault keyset loading to cause "Notime".
EXPECT_CALL(platform_, FileExists(StartsWith(homedir_paths_[0])))
.WillRepeatedly(Return(true));
MockVaultKeyset* vk[arraysize(kHomedirs)];
EXPECT_CALL(vault_keyset_factory_, New(_, _))
.WillOnce(Return(vk[0] = new MockVaultKeyset()))
.WillOnce(Return(vk[1] = new MockVaultKeyset()))
.WillOnce(Return(vk[2] = new MockVaultKeyset()))
.WillOnce(Return(vk[3] = new MockVaultKeyset()));
// The first three will have no time.
size_t i;
for (i = 0; i < arraysize(vk) - 1; ++i) {
EXPECT_CALL(*vk[i], Load(_))
.WillRepeatedly(Return(false));
}
// Owner will have a master.0
NiceMock<MockFileEnumerator> *master0;
EXPECT_CALL(platform_, GetFileEnumerator(homedir_paths_[3], false, _))
.WillOnce(Return(master0 = new NiceMock<MockFileEnumerator>));
EXPECT_CALL(*master0, Next())
.WillOnce(Return(StringPrintf("%s/%s0",
homedir_paths_[3].c_str(), kKeyFile)))
.WillRepeatedly(Return(""));
// The owner will have a time.
EXPECT_CALL(*vk[i], Load(_))
.WillOnce(Return(true));
SerializedVaultKeyset serialized;
serialized.set_last_activity_timestamp(homedir_times_[3].ToInternalValue());
EXPECT_CALL(*vk[i], serialized())
.Times(2)
.WillRepeatedly(ReturnRef(serialized));
homedirs_.set_vault_keyset_factory(&vault_keyset_factory_);
// Expect an addition for all users with no time.
EXPECT_CALL(timestamp_cache_, AddExistingUserNotime(_))
.Times(3);
// Adding the owner
EXPECT_CALL(timestamp_cache_,
AddExistingUser(FilePath(homedir_paths_[3]), homedir_times_[3]))
.Times(1);
// Now skip the deletion steps by not having a legit owner.
set_policy(false, "", false);
EXPECT_TRUE(homedirs_.FreeDiskSpace());
}
TEST_F(FreeDiskSpaceTest, NoCacheCleanup) {
// Pretend we have lots of free space
EXPECT_CALL(platform_, AmountOfFreeDiskSpace(kTestRoot))
.WillOnce(Return(kMinFreeSpace + 1));
EXPECT_FALSE(homedirs_.FreeDiskSpace());
}
TEST_F(FreeDiskSpaceTest, OnlyCacheCleanup) {
// Only clean up the Cache data. Not GCache, etc.
EXPECT_CALL(platform_, EnumerateDirectoryEntries(kTestRoot, false, _))
.WillRepeatedly(
DoAll(SetArgumentPointee<2>(homedir_paths_),
Return(true)));
EXPECT_CALL(platform_, AmountOfFreeDiskSpace(kTestRoot))
.WillOnce(Return(0))
.WillOnce(Return(kEnoughFreeSpace + 1));
EXPECT_CALL(platform_, DirectoryExists(_))
.WillRepeatedly(Return(true));
// Empty enumerators per-user per-cache dirs
NiceMock<MockFileEnumerator>* fe[arraysize(kHomedirs)];
EXPECT_CALL(platform_, GetFileEnumerator(_, false, _))
.WillOnce(Return(fe[0] = new NiceMock<MockFileEnumerator>))
.WillOnce(Return(fe[1] = new NiceMock<MockFileEnumerator>))
.WillOnce(Return(fe[2] = new NiceMock<MockFileEnumerator>))
.WillOnce(Return(fe[3] = new NiceMock<MockFileEnumerator>));
// Exercise the delete file path.
for (size_t f = 0; f < arraysize(fe); ++f) {
EXPECT_CALL(*fe[f], Next())
.WillOnce(Return(StringPrintf("%s/%s", homedir_paths_[f].c_str(),
"Cache/foo")))
.WillRepeatedly(Return(""));
}
EXPECT_CALL(platform_, DeleteFile(EndsWith("/Cache/foo"), true))
.Times(4)
.WillRepeatedly(Return(true));
EXPECT_TRUE(homedirs_.FreeDiskSpace());
}
TEST_F(FreeDiskSpaceTest, CacheAndGCacheCleanup) {
EXPECT_CALL(platform_, EnumerateDirectoryEntries(kTestRoot, false, _))
.WillRepeatedly(
DoAll(SetArgumentPointee<2>(homedir_paths_),
Return(true)));
EXPECT_CALL(platform_, AmountOfFreeDiskSpace(kTestRoot))
.WillOnce(Return(0))
.WillOnce(Return(0))
.WillOnce(Return(kEnoughFreeSpace + 1));
EXPECT_CALL(platform_, DirectoryExists(_))
.WillRepeatedly(Return(true));
// Empty enumerators per-user per-cache dirs
NiceMock<MockFileEnumerator>* fe[arraysize(kHomedirs)];
EXPECT_CALL(platform_, GetFileEnumerator(EndsWith("/Cache"), false, _))
.WillOnce(Return(fe[0] = new NiceMock<MockFileEnumerator>))
.WillOnce(Return(fe[1] = new NiceMock<MockFileEnumerator>))
.WillOnce(Return(fe[2] = new NiceMock<MockFileEnumerator>))
.WillOnce(Return(fe[3] = new NiceMock<MockFileEnumerator>));
// Exercise the delete file path.
for (size_t f = 0; f < arraysize(fe); ++f) {
EXPECT_CALL(*fe[f], Next())
.WillOnce(Return(StringPrintf("%s/%s", homedir_paths_[f].c_str(),
"Cache/foo")))
.WillRepeatedly(Return(""));
}
EXPECT_CALL(platform_, DeleteFile(EndsWith("/Cache/foo"), true))
.Times(4)
.WillRepeatedly(Return(true));
// Repeat for GCache
EXPECT_CALL(platform_, GetFileEnumerator(EndsWith("GCache/v1/tmp"), false, _))
.WillOnce(Return(fe[0] = new NiceMock<MockFileEnumerator>))
.WillOnce(Return(fe[1] = new NiceMock<MockFileEnumerator>))
.WillOnce(Return(fe[2] = new NiceMock<MockFileEnumerator>))
.WillOnce(Return(fe[3] = new NiceMock<MockFileEnumerator>));
// Exercise the delete file path.
for (size_t f = 0; f < arraysize(fe); ++f) {
EXPECT_CALL(*fe[f], Next())
.WillOnce(Return(StringPrintf("%s/%s", homedir_paths_[f].c_str(),
"GCache/v1/tmp/foo")))
.WillRepeatedly(Return(""));
}
EXPECT_CALL(platform_, DeleteFile(EndsWith("/GCache/v1/tmp/foo"), true))
.Times(4)
.WillRepeatedly(Return(true));
EXPECT_TRUE(homedirs_.FreeDiskSpace());
}
TEST_F(FreeDiskSpaceTest, OldUsersCleanupNoTimestamp) {
// Removes old (except owner and the current one, if any) even if
// users had no oldest activity timestamp.
//
// This requires at least one user have a valid timestamp that is older than
// the delta. If not, all the NoTimestamp users will stick around. I believe
// the supposition is that if there are no timestamps, the vault keysets are
// either invalid or predate timestamping. If _any_ user has a timestamp
// that is old enough, then all unmarked ones must also be old enough. We
// may want to consider adding Stat() support to stop relying on this
// assumption.
EXPECT_CALL(platform_, EnumerateDirectoryEntries(kTestRoot, false, _))
.WillRepeatedly(
DoAll(SetArgumentPointee<2>(homedir_paths_),
Return(true)));
EXPECT_CALL(platform_, AmountOfFreeDiskSpace(kTestRoot))
.WillOnce(Return(0))
.WillRepeatedly(Return(kEnoughFreeSpace - 1));
EXPECT_CALL(platform_, DirectoryExists(_))
.WillRepeatedly(Return(true));
// Don't bother with files to delete.
EXPECT_CALL(platform_, GetFileEnumerator(EndsWith("/Cache"), false, _))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>));
EXPECT_CALL(platform_, GetFileEnumerator(EndsWith("GCache/v1/tmp"), false, _))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>));
// Nothing is mounted.
EXPECT_CALL(platform_, IsDirectoryMountedWith(_, _))
.WillRepeatedly(Return(false));
EXPECT_CALL(timestamp_cache_, initialized())
.WillRepeatedly(Return(true));
// Now pretend that one user had a time, so that we have a non-0
// oldest time.
EXPECT_CALL(timestamp_cache_, oldest_known_timestamp())
.WillOnce(Return(base::Time::Now() - kOldUserLastActivityTime))
.WillOnce(Return(base::Time::Now() - kOldUserLastActivityTime))
.WillOnce(Return(base::Time::Now() - kOldUserLastActivityTime))
.WillOnce(Return(base::Time::Now() - kOldUserLastActivityTime))
.WillOnce(Return(base::Time::Now() - kOldUserLastActivityTime))
.WillOnce(Return(base::Time::Now() - kOldUserLastActivityTime))
.WillOnce(Return(base::Time::Now() - kOldUserLastActivityTime))
.WillOnce(Return(base::Time::Now() - kOldUserLastActivityTime))
.WillOnce(Return(base::Time())); // end with is_null()
EXPECT_CALL(timestamp_cache_, RemoveOldestUser())
.WillOnce(Return(FilePath(homedir_paths_[0])))
.WillOnce(Return(FilePath(homedir_paths_[1])))
.WillOnce(Return(FilePath(homedir_paths_[2])))
.WillOnce(Return(FilePath(homedir_paths_[3])));
EXPECT_CALL(platform_, DeleteFile(_, true))
.Times(arraysize(kHomedirs)-1) // All but the owner
.WillRepeatedly(Return(true));
EXPECT_CALL(platform_, DeleteFile(homedir_paths_[3], true))
.Times(0);
EXPECT_TRUE(homedirs_.FreeDiskSpace());
}
TEST_F(FreeDiskSpaceTest, CleanUpOneOldUser) {
// Ensure that the oldest user directory deleted, but not any
// others.
EXPECT_CALL(timestamp_cache_, initialized())
.WillRepeatedly(Return(true));
// Move this loop to a helper just for these expects.
EXPECT_CALL(timestamp_cache_, RemoveOldestUser())
.WillOnce(Return(FilePath(homedir_paths_[0])));
// All timestamps are walked but the last one fails the time
// test so no null time is needed.
EXPECT_CALL(timestamp_cache_, oldest_known_timestamp())
.WillOnce(Return(homedir_times_[0]))
.WillOnce(Return(homedir_times_[0])); // Loop ends early.
EXPECT_CALL(platform_, EnumerateDirectoryEntries(kTestRoot, false, _))
.WillRepeatedly(
DoAll(SetArgumentPointee<2>(homedir_paths_),
Return(true)));
EXPECT_CALL(platform_, AmountOfFreeDiskSpace(kTestRoot))
.WillOnce(Return(0))
.WillOnce(Return(0))
.WillOnce(Return(kMinFreeSpace + 1))
.WillOnce(Return(kEnoughFreeSpace + 1));
EXPECT_CALL(platform_, DirectoryExists(_))
.WillRepeatedly(Return(true));
// Empty enumerators per-user per-cache, per-gcache dirs
EXPECT_CALL(platform_, GetFileEnumerator(_, false, _))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>));
EXPECT_CALL(platform_, DeleteFile(homedir_paths_[0], true))
.WillOnce(Return(true));
EXPECT_TRUE(homedirs_.FreeDiskSpace());
}
TEST_F(FreeDiskSpaceTest, CleanUpMultipleOldUsers) {
// Ensure that the two oldest user directories are deleted, but not any
// others.
EXPECT_CALL(timestamp_cache_, initialized())
.WillRepeatedly(Return(true));
// Move this loop to a helper just for these expects.
EXPECT_CALL(timestamp_cache_, RemoveOldestUser())
.WillOnce(Return(FilePath(homedir_paths_[0])))
.WillOnce(Return(FilePath(homedir_paths_[1])));
// All timestamps are walked but the last one fails the time
// test so no null time is needed.
EXPECT_CALL(timestamp_cache_, oldest_known_timestamp())
.WillOnce(Return(homedir_times_[0]))
.WillOnce(Return(homedir_times_[0]))
.WillOnce(Return(homedir_times_[1]))
.WillOnce(Return(homedir_times_[1]));
EXPECT_CALL(platform_, EnumerateDirectoryEntries(kTestRoot, false, _))
.WillRepeatedly(
DoAll(SetArgumentPointee<2>(homedir_paths_),
Return(true)));
EXPECT_CALL(platform_, AmountOfFreeDiskSpace(kTestRoot))
.WillOnce(Return(0))
.WillOnce(Return(0))
.WillOnce(Return(kMinFreeSpace + 1))
.WillOnce(Return(kEnoughFreeSpace - 1))
.WillOnce(Return(kEnoughFreeSpace + 1));
EXPECT_CALL(platform_, DirectoryExists(_))
.WillRepeatedly(Return(true));
// Empty enumerators per-user per-cache dirs
EXPECT_CALL(platform_, GetFileEnumerator(_, false, _))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>));
EXPECT_CALL(platform_, DeleteFile(homedir_paths_[0], true))
.WillOnce(Return(true));
EXPECT_CALL(platform_, DeleteFile(homedir_paths_[1], true))
.WillOnce(Return(true));
EXPECT_TRUE(homedirs_.FreeDiskSpace());
}
TEST_F(FreeDiskSpaceTest, EnterpriseCleanUpAllUsers) {
set_policy(true, "", false);
homedirs_.set_enterprise_owned(true);
// Ensure that the two oldest user directories are deleted, but not any
// others.
EXPECT_CALL(timestamp_cache_, initialized())
.WillRepeatedly(Return(true));
// Move this loop to a helper just for these expects.
EXPECT_CALL(timestamp_cache_, RemoveOldestUser())
.WillOnce(Return(FilePath(homedir_paths_[1])))
.WillOnce(Return(FilePath(homedir_paths_[2])))
.WillOnce(Return(FilePath(homedir_paths_[3])))
.WillOnce(Return(FilePath(homedir_paths_[0])));
// Pretend only user 0 has a timestamp. All the others
// will be deleted first.
EXPECT_CALL(timestamp_cache_, oldest_known_timestamp())
.WillOnce(Return(homedir_times_[0]))
.WillOnce(Return(homedir_times_[0]))
.WillOnce(Return(homedir_times_[0]))
.WillOnce(Return(homedir_times_[0]))
.WillOnce(Return(homedir_times_[0]))
.WillOnce(Return(homedir_times_[0]))
.WillOnce(Return(homedir_times_[0]))
.WillOnce(Return(homedir_times_[0]))
.WillOnce(Return(base::Time())); // No more users.
EXPECT_CALL(platform_, EnumerateDirectoryEntries(kTestRoot, false, _))
.WillRepeatedly(
DoAll(SetArgumentPointee<2>(homedir_paths_),
Return(true)));
EXPECT_CALL(platform_, AmountOfFreeDiskSpace(kTestRoot))
.WillRepeatedly(Return(0));
EXPECT_CALL(platform_, DirectoryExists(_))
.WillRepeatedly(Return(true));
// Empty enumerators per-user per-cache dirs
EXPECT_CALL(platform_, GetFileEnumerator(_, false, _))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>));
EXPECT_CALL(platform_, DeleteFile(homedir_paths_[1], true))
.WillOnce(Return(true));
EXPECT_CALL(platform_, DeleteFile(homedir_paths_[2], true))
.WillOnce(Return(true));
EXPECT_CALL(platform_, DeleteFile(homedir_paths_[3], true))
.WillOnce(Return(true));
EXPECT_CALL(platform_, DeleteFile(homedir_paths_[0], true))
.WillOnce(Return(true));
EXPECT_TRUE(homedirs_.FreeDiskSpace());
}
TEST_F(FreeDiskSpaceTest, CleanUpMultipleNonadjacentOldUsers) {
// Ensure that the two oldest user directories are deleted, but not any
// others. The owner is inserted in the middle.
EXPECT_CALL(timestamp_cache_, initialized())
.WillRepeatedly(Return(true));
// Move this loop to a helper just for these expects.
EXPECT_CALL(timestamp_cache_, RemoveOldestUser())
.WillOnce(Return(FilePath(homedir_paths_[0])))
.WillOnce(Return(FilePath(homedir_paths_[3])))
.WillOnce(Return(FilePath(homedir_paths_[1])));
// All timestamps are walked but the last one fails the time
// test so no null time is needed.
EXPECT_CALL(timestamp_cache_, oldest_known_timestamp())
.WillOnce(Return(homedir_times_[0]))
.WillOnce(Return(homedir_times_[0]))
.WillOnce(Return(homedir_times_[1])) // Treat the owner as having the
.WillOnce(Return(homedir_times_[1])) // same time.
.WillOnce(Return(homedir_times_[1]))
.WillOnce(Return(homedir_times_[1]))
.WillOnce(Return(base::Time::Now()))
.WillOnce(Return(base::Time::Now())); // Stop looping with current time
EXPECT_CALL(platform_, EnumerateDirectoryEntries(kTestRoot, false, _))
.WillRepeatedly(
DoAll(SetArgumentPointee<2>(homedir_paths_),
Return(true)));
EXPECT_CALL(platform_, AmountOfFreeDiskSpace(kTestRoot))
.WillOnce(Return(0))
.WillOnce(Return(0))
.WillOnce(Return(kMinFreeSpace + 1))
.WillRepeatedly(Return(kEnoughFreeSpace - 1));
EXPECT_CALL(platform_, DirectoryExists(_))
.WillRepeatedly(Return(true));
// Empty enumerators per-user per-cache dirs
EXPECT_CALL(platform_, GetFileEnumerator(_, false, _))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>));
EXPECT_CALL(platform_, DeleteFile(homedir_paths_[0], true))
.WillOnce(Return(true));
EXPECT_CALL(platform_, DeleteFile(homedir_paths_[1], true))
.WillOnce(Return(true));
// Ensure the owner isn't deleted!
EXPECT_CALL(platform_, DeleteFile(homedir_paths_[3], true))
.Times(0);
EXPECT_TRUE(homedirs_.FreeDiskSpace());
}
TEST_F(FreeDiskSpaceTest, NoOwnerNoEnterpriseNoCleanup) {
// Ensure that no users are deleted with no owner/enterprise-owner.
EXPECT_CALL(platform_, AmountOfFreeDiskSpace(kTestRoot))
.WillRepeatedly(Return(0));
// Expect cache clean ups.
EXPECT_CALL(platform_, EnumerateDirectoryEntries(kTestRoot, false, _))
.WillRepeatedly(
DoAll(SetArgumentPointee<2>(homedir_paths_),
Return(true)));
EXPECT_CALL(platform_, DirectoryExists(_))
.WillRepeatedly(Return(true));
// Empty enumerators per-user per-cache dirs.
// That means no Delete calls for (G)Cache.
EXPECT_CALL(platform_, GetFileEnumerator(_, false, _))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>));
// Skip re-init
EXPECT_CALL(timestamp_cache_, initialized())
.WillOnce(Return(true));
// No user deletions!
EXPECT_CALL(platform_, DeleteFile(homedir_paths_[0], true))
.Times(0);
EXPECT_CALL(platform_, DeleteFile(homedir_paths_[1], true))
.Times(0);
EXPECT_CALL(platform_, DeleteFile(homedir_paths_[2], true))
.Times(0);
EXPECT_CALL(platform_, DeleteFile(homedir_paths_[3], true))
.Times(0);
// Now skip the deletion steps by not having a legit owner.
set_policy(false, "", false);
EXPECT_TRUE(homedirs_.FreeDiskSpace());
}
TEST_F(FreeDiskSpaceTest, ConsumerEphemeralUsers) {
// When ephemeral users are enabled, no cryptohomes are kept except the owner.
set_policy(true, kOwner, true);
// homedirs_.set_enterprise_owned(true);
EXPECT_CALL(platform_, EnumerateDirectoryEntries(kTestRoot, false, _))
.WillRepeatedly(
DoAll(SetArgumentPointee<2>(homedir_paths_),
Return(true)));
EXPECT_CALL(platform_,
EnumerateDirectoryEntries(
chromeos::cryptohome::home::GetUserPathPrefix().value(),
false, _))
.WillRepeatedly(
DoAll(SetArgumentPointee<2>(homedir_paths_),
Return(true)));
EXPECT_CALL(platform_,
EnumerateDirectoryEntries(
chromeos::cryptohome::home::GetRootPathPrefix().value(),
false, _))
.WillRepeatedly(
DoAll(SetArgumentPointee<2>(homedir_paths_),
Return(true)));
EXPECT_CALL(platform_, AmountOfFreeDiskSpace(kTestRoot))
.WillOnce(Return(kMinFreeSpace - 1));
EXPECT_CALL(platform_, DirectoryExists(_))
.WillRepeatedly(Return(true));
EXPECT_CALL(platform_, DeleteFile(homedir_paths_[0], true))
.WillOnce(Return(true)) // vault
.WillOnce(Return(true)) // user
.WillOnce(Return(true)); // root
EXPECT_CALL(platform_, DeleteFile(homedir_paths_[1], true))
.WillOnce(Return(true))
.WillOnce(Return(true))
.WillOnce(Return(true));
EXPECT_CALL(platform_, DeleteFile(homedir_paths_[2], true))
.WillOnce(Return(true))
.WillOnce(Return(true))
.WillOnce(Return(true));
// Ensure the owner isn't deleted!
EXPECT_CALL(platform_, DeleteFile(homedir_paths_[3], true))
.Times(0);
EXPECT_TRUE(homedirs_.FreeDiskSpace());
}
TEST_F(FreeDiskSpaceTest, EnterpriseEphemeralUsers) {
// When ephemeral users are enabled, no cryptohomes are kept except the owner.
set_policy(true, "", true);
homedirs_.set_enterprise_owned(true);
EXPECT_CALL(platform_, EnumerateDirectoryEntries(kTestRoot, false, _))
.WillRepeatedly(
DoAll(SetArgumentPointee<2>(homedir_paths_),
Return(true)));
EXPECT_CALL(platform_,
EnumerateDirectoryEntries(
chromeos::cryptohome::home::GetUserPathPrefix().value(),
false, _))
.WillRepeatedly(
DoAll(SetArgumentPointee<2>(homedir_paths_),
Return(true)));
EXPECT_CALL(platform_,
EnumerateDirectoryEntries(
chromeos::cryptohome::home::GetRootPathPrefix().value(),
false, _))
.WillRepeatedly(
DoAll(SetArgumentPointee<2>(homedir_paths_),
Return(true)));
EXPECT_CALL(platform_, AmountOfFreeDiskSpace(kTestRoot))
.WillOnce(Return(kMinFreeSpace - 1));
EXPECT_CALL(platform_, DirectoryExists(_))
.WillRepeatedly(Return(true));
EXPECT_CALL(platform_, DeleteFile(homedir_paths_[0], true))
.WillOnce(Return(true)) // vault
.WillOnce(Return(true)) // user
.WillOnce(Return(true)); // root
EXPECT_CALL(platform_, DeleteFile(homedir_paths_[1], true))
.WillOnce(Return(true))
.WillOnce(Return(true))
.WillOnce(Return(true));
EXPECT_CALL(platform_, DeleteFile(homedir_paths_[2], true))
.WillOnce(Return(true))
.WillOnce(Return(true))
.WillOnce(Return(true));
EXPECT_CALL(platform_, DeleteFile(homedir_paths_[3], true))
.WillOnce(Return(true))
.WillOnce(Return(true))
.WillOnce(Return(true));
EXPECT_TRUE(homedirs_.FreeDiskSpace());
}
TEST_F(FreeDiskSpaceTest, DontCleanUpOneOldMountedUser) {
// Ensure that the oldest user isn't deleted because
// it appears to be mounted.
EXPECT_CALL(timestamp_cache_, initialized())
.WillRepeatedly(Return(true));
// This will only be called once (see time below).
EXPECT_CALL(timestamp_cache_, RemoveOldestUser())
.WillOnce(Return(FilePath(homedir_paths_[0])));
// Only mark user 0 as old.
EXPECT_CALL(timestamp_cache_, oldest_known_timestamp())
.WillOnce(Return(homedir_times_[0]))
.WillOnce(Return(homedir_times_[0]))
.WillOnce(Return(base::Time::Now()))
.WillOnce(Return(base::Time::Now()));
EXPECT_CALL(platform_, EnumerateDirectoryEntries(kTestRoot, false, _))
.WillRepeatedly(
DoAll(SetArgumentPointee<2>(homedir_paths_),
Return(true)));
EXPECT_CALL(platform_, AmountOfFreeDiskSpace(kTestRoot))
.WillRepeatedly(Return(0));
EXPECT_CALL(platform_, DirectoryExists(_))
.WillRepeatedly(Return(true));
// Ensure the mounted user never has (G)Cache traversed!
EXPECT_CALL(platform_, GetFileEnumerator(
StartsWith(homedir_paths_[0]), false, _))
.Times(0);
// Empty enumerators per-user per-cache dirs
EXPECT_CALL(platform_, GetFileEnumerator(_, false, _))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>))
.WillOnce(Return(new NiceMock<MockFileEnumerator>));
EXPECT_CALL(platform_,
IsDirectoryMountedWith(StartsWith(homedir_paths_[0]), _))
.Times(3) // Cache, GCache, user removal
.WillRepeatedly(Return(true));
for (size_t i = 1; i < arraysize(kHomedirs); ++i) {
EXPECT_CALL(platform_,
IsDirectoryMountedWith(StartsWith(homedir_paths_[i]), _))
.Times(2) // Cache, GCache
.WillRepeatedly(Return(false));
}
EXPECT_TRUE(homedirs_.FreeDiskSpace());
}
TEST_F(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_);
cryptohome::SecureBlob passkey;
cryptohome::Crypto::PasswordToPasskey(test_helper_.users[1].password,
system_salt, &passkey);
UsernamePasskey up(test_helper_.users[1].username, passkey);
ASSERT_TRUE(homedirs_.AreCredentialsValid(up));
}
TEST_F(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_);
cryptohome::SecureBlob passkey;
cryptohome::Crypto::PasswordToPasskey("bogus", system_salt, &passkey);
UsernamePasskey up(test_helper_.users[4].username, passkey);
ASSERT_FALSE(homedirs_.AreCredentialsValid(up));
}
class KeysetManagementTest : public HomeDirsTest {
public:
KeysetManagementTest() { }
virtual ~KeysetManagementTest() { }
virtual bool VkDecrypt0(const chromeos::SecureBlob& key) {
return memcmp(key.const_data(), keys_[0].const_data(),
key.size()) == 0;
}
virtual void KeysetSetUp() {
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);
MockFileEnumerator* files = new MockFileEnumerator();
EXPECT_CALL(platform_, GetFileEnumerator(
test_helper_.users[1].base_path, false, _))
.WillOnce(Return(files));
{
InSequence s;
// Single key.
EXPECT_CALL(*files, Next())
.WillOnce(Return(keyset_paths_[0]));
EXPECT_CALL(*files, Next())
.WillOnce(Return(""));
}
homedirs_.set_vault_keyset_factory(&vault_keyset_factory_);
active_vk_ = new MockVaultKeyset();
EXPECT_CALL(vault_keyset_factory_, New(_, _))
.WillOnce(Return(active_vk_));
EXPECT_CALL(*active_vk_, Load(keyset_paths_[0]))
.WillOnce(Return(true));
EXPECT_CALL(*active_vk_, Decrypt(_))
.WillOnce(Invoke(this, &KeysetManagementTest::VkDecrypt0));
cryptohome::SecureBlob passkey;
cryptohome::Crypto::PasswordToPasskey(test_helper_.users[1].password,
system_salt_, &passkey);
up_.reset(new UsernamePasskey(test_helper_.users[1].username, passkey));
}
MockVaultKeyset* active_vk_;
std::vector<std::string> keyset_paths_;
std::vector<chromeos::SecureBlob> keys_;
scoped_ptr<UsernamePasskey> up_;
SecureBlob system_salt_;
};
TEST_F(KeysetManagementTest, AddKeysetSuccess) {
KeysetSetUp();
cryptohome::SecureBlob newkey;
cryptohome::Crypto::PasswordToPasskey("why not", system_salt_, &newkey);
int index = -1;
// The injected keyset in the fixture handles the up_ validation.
EXPECT_CALL(platform_, OpenFile(EndsWith("master.0"), "wx"))
.WillOnce(Return(reinterpret_cast<FILE*>(NULL)));
EXPECT_CALL(platform_, OpenFile(EndsWith("master.1"), "wx"))
.WillOnce(Return(reinterpret_cast<FILE*>(0xbeefbeef)));
EXPECT_CALL(*active_vk_, Encrypt(newkey))
.WillOnce(Return(true));
EXPECT_CALL(*active_vk_, Save(EndsWith("master.1")))
.WillOnce(Return(true));
EXPECT_CALL(platform_, DeleteFile(_, _))
.Times(0);
ASSERT_TRUE(homedirs_.AddKeyset(*up_, newkey, &index));
EXPECT_EQ(index, 1);
}
TEST_F(KeysetManagementTest, AddKeysetInvalidCreds) {
KeysetSetUp();
cryptohome::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.
UsernamePasskey bad_p(test_helper_.users[1].username, newkey);
ASSERT_FALSE(homedirs_.AddKeyset(bad_p, newkey, &index));
EXPECT_EQ(index, -1);
}
TEST_F(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
test_helper_.users[1].keyset_path.erase(
test_helper_.users[1].keyset_path.end() - 1);
test_helper_.users[1].keyset_path.append("1");
KeysetSetUp();
// The injected keyset in the fixture handles the up_ validation.
cryptohome::SecureBlob newkey;
cryptohome::Crypto::PasswordToPasskey("why not", system_salt_, &newkey);
EXPECT_CALL(platform_, OpenFile(EndsWith("master.0"), "wx"))
.WillOnce(Return(reinterpret_cast<FILE*>(0xbeefbeef)));
EXPECT_CALL(*active_vk_, Encrypt(newkey))
.WillOnce(Return(true));
EXPECT_CALL(*active_vk_, Save(EndsWith("master.0")))
.WillOnce(Return(true));
EXPECT_CALL(platform_, DeleteFile(_, _))
.Times(0);
int index = -1;
// Try to authenticate with an unknown key.
ASSERT_TRUE(homedirs_.AddKeyset(*up_, newkey, &index));
EXPECT_EQ(index, 0);
}
TEST_F(KeysetManagementTest, AddKeyset10Available) {
KeysetSetUp();
// The injected keyset in the fixture handles the up_ validation.
cryptohome::SecureBlob newkey;
cryptohome::Crypto::PasswordToPasskey("why not", system_salt_, &newkey);
EXPECT_CALL(platform_, OpenFile(MatchesRegex(".*/master\\..$"), "wx"))
.Times(10)
.WillRepeatedly(Return(reinterpret_cast<FILE*>(NULL)));
EXPECT_CALL(platform_, OpenFile(EndsWith("master.10"), "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(EndsWith("master.10")))
.WillOnce(Return(true));
int index = -1;
// Try to authenticate with an unknown key.
ASSERT_TRUE(homedirs_.AddKeyset(*up_, newkey, &index));
EXPECT_EQ(index, 10);
}
TEST_F(KeysetManagementTest, AddKeysetNoFreeIndices) {
KeysetSetUp();
// The injected keyset in the fixture handles the up_ validation.
cryptohome::SecureBlob newkey;
cryptohome::Crypto::PasswordToPasskey("why not", system_salt_, &newkey);
EXPECT_CALL(platform_, OpenFile(MatchesRegex(".*/master\\..*$"), "wx"))
.Times(kKeyFileMax)
.WillRepeatedly(Return(reinterpret_cast<FILE*>(NULL)));
EXPECT_CALL(platform_, DeleteFile(_, _))
.Times(0);
int index = -1;
// Try to authenticate with an unknown key.
ASSERT_FALSE(homedirs_.AddKeyset(*up_, newkey, &index));
EXPECT_EQ(index, -1);
}
TEST_F(KeysetManagementTest, AddKeysetEncryptFail) {
KeysetSetUp();
cryptohome::SecureBlob newkey;
cryptohome::Crypto::PasswordToPasskey("why not", system_salt_, &newkey);
int index = -1;
// The injected keyset in the fixture handles the up_ validation.
EXPECT_CALL(platform_, OpenFile(EndsWith("master.0"), "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(EndsWith("master.0"), false))
.WillOnce(Return(true));
ASSERT_FALSE(homedirs_.AddKeyset(*up_, newkey, &index));
EXPECT_EQ(index, -1);
}
TEST_F(KeysetManagementTest, AddKeysetSaveFail) {
KeysetSetUp();
cryptohome::SecureBlob newkey;
cryptohome::Crypto::PasswordToPasskey("why not", system_salt_, &newkey);
int index = -1;
// The injected keyset in the fixture handles the up_ validation.
EXPECT_CALL(platform_, OpenFile(EndsWith("master.0"), "wx"))
.WillOnce(Return(reinterpret_cast<FILE*>(0xbeefbeef)));
EXPECT_CALL(*active_vk_, Encrypt(newkey))
.WillOnce(Return(true));
EXPECT_CALL(*active_vk_, Save(EndsWith("master.0")))
.WillOnce(Return(false));
EXPECT_CALL(platform_, CloseFile(reinterpret_cast<FILE*>(0xbeefbeef)))
.WillOnce(Return(true));
EXPECT_CALL(platform_, DeleteFile(EndsWith("master.0"), false))
.WillOnce(Return(true));
ASSERT_FALSE(homedirs_.AddKeyset(*up_, newkey, &index));
EXPECT_EQ(index, -1);
}
TEST_F(KeysetManagementTest, ForceRemoveKeysetSuccess) {
EXPECT_CALL(platform_, FileExists(EndsWith("master.0")))
.WillOnce(Return(true));
EXPECT_CALL(platform_, DeleteFile(EndsWith("master.0"), false))
.WillOnce(Return(true));
ASSERT_TRUE(homedirs_.ForceRemoveKeyset("a0b0c0", 0));
}
TEST_F(KeysetManagementTest, ForceRemoveKeysetMissingKeyset) {
EXPECT_CALL(platform_, FileExists(EndsWith("master.0")))
.WillOnce(Return(false));
ASSERT_TRUE(homedirs_.ForceRemoveKeyset("a0b0c0", 0));
}
TEST_F(KeysetManagementTest, ForceRemoveKeysetNegativeIndex) {
ASSERT_FALSE(homedirs_.ForceRemoveKeyset("a0b0c0", -1));
}
TEST_F(KeysetManagementTest, ForceRemoveKeysetOverMaxIndex) {
ASSERT_FALSE(homedirs_.ForceRemoveKeyset("a0b0c0", kKeyFileMax));
}
TEST_F(KeysetManagementTest, ForceRemoveKeysetFailedDelete) {
EXPECT_CALL(platform_, FileExists(EndsWith("master.0")))
.WillOnce(Return(true));
EXPECT_CALL(platform_, DeleteFile(EndsWith("master.0"), false))
.WillOnce(Return(false));
ASSERT_FALSE(homedirs_.ForceRemoveKeyset("a0b0c0", 0));
}
TEST_F(KeysetManagementTest, MoveKeysetSuccess_0_to_1) {
const std::string obfuscated = "a0b0c0";
EXPECT_CALL(platform_, FileExists(EndsWith("master.0")))
.WillOnce(Return(true));
EXPECT_CALL(platform_, FileExists(EndsWith("master.1")))
.WillOnce(Return(false));
EXPECT_CALL(platform_, OpenFile(EndsWith("master.1"), "wx"))
.WillOnce(Return(reinterpret_cast<FILE*>(0xbeefbeef)));
EXPECT_CALL(platform_, Rename(EndsWith("master.0"), 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_F(KeysetManagementTest, MoveKeysetSuccess_1_to_99) {
const std::string obfuscated = "a0b0c0";
EXPECT_CALL(platform_, FileExists(EndsWith("master.1")))
.WillOnce(Return(true));
EXPECT_CALL(platform_, FileExists(EndsWith("master.99")))
.WillOnce(Return(false));
EXPECT_CALL(platform_, OpenFile(EndsWith("master.99"), "wx"))
.WillOnce(Return(reinterpret_cast<FILE*>(0xbeefbeef)));
EXPECT_CALL(platform_, Rename(EndsWith("master.1"), 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_F(KeysetManagementTest, MoveKeysetNegativeSource) {
const std::string obfuscated = "a0b0c0";
ASSERT_FALSE(homedirs_.MoveKeyset(obfuscated, -1, 1));
}
TEST_F(KeysetManagementTest, MoveKeysetNegativeDestination) {
const std::string obfuscated = "a0b0c0";
ASSERT_FALSE(homedirs_.MoveKeyset(obfuscated, 1, -1));
}
TEST_F(KeysetManagementTest, MoveKeysetTooLargeDestination) {
const std::string obfuscated = "a0b0c0";
ASSERT_FALSE(homedirs_.MoveKeyset(obfuscated, 1, kKeyFileMax));
}
TEST_F(KeysetManagementTest, MoveKeysetTooLargeSource) {
const std::string obfuscated = "a0b0c0";
ASSERT_FALSE(homedirs_.MoveKeyset(obfuscated, kKeyFileMax, 0));
}
TEST_F(KeysetManagementTest, MoveKeysetMissingSource) {
const std::string obfuscated = "a0b0c0";
EXPECT_CALL(platform_, FileExists(EndsWith("master.0")))
.WillOnce(Return(false));
ASSERT_FALSE(homedirs_.MoveKeyset(obfuscated, 0, 1));
}
TEST_F(KeysetManagementTest, MoveKeysetDestinationExists) {
const std::string obfuscated = "a0b0c0";
EXPECT_CALL(platform_, FileExists(EndsWith("master.0")))
.WillOnce(Return(true));
EXPECT_CALL(platform_, FileExists(EndsWith("master.1")))
.WillOnce(Return(true));
ASSERT_FALSE(homedirs_.MoveKeyset(obfuscated, 0, 1));
}
TEST_F(KeysetManagementTest, MoveKeysetExclusiveOpenFailed) {
const std::string obfuscated = "a0b0c0";
EXPECT_CALL(platform_, FileExists(EndsWith("master.0")))
.WillOnce(Return(true));
EXPECT_CALL(platform_, FileExists(EndsWith("master.1")))
.WillOnce(Return(false));
EXPECT_CALL(platform_, OpenFile(EndsWith("master.1"), "wx"))
.WillOnce(Return(reinterpret_cast<FILE*>(NULL)));
ASSERT_FALSE(homedirs_.MoveKeyset(obfuscated, 0, 1));
}
TEST_F(KeysetManagementTest, MoveKeysetRenameFailed) {
const std::string obfuscated = "a0b0c0";
EXPECT_CALL(platform_, FileExists(EndsWith("master.0")))
.WillOnce(Return(true));
EXPECT_CALL(platform_, FileExists(EndsWith("master.1")))
.WillOnce(Return(false));
EXPECT_CALL(platform_, OpenFile(EndsWith("master.1"), "wx"))
.WillOnce(Return(reinterpret_cast<FILE*>(0xbeefbeef)));
EXPECT_CALL(platform_, Rename(EndsWith("master.0"), 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