blob: a4e384503378952ca56545ec2ab3459b6c7d875d [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 "cryptohome/stateful_recovery/stateful_recovery.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <memory>
#include <string>
#include <base/files/file_util.h>
#include <brillo/cryptohome.h>
#include <user_data_auth-client-test/user_data_auth/dbus-proxy-mocks.h>
#include <policy/mock_device_policy.h>
#include <policy/mock_libpolicy.h>
#include "cryptohome/mock_platform.h"
namespace cryptohome {
using base::FilePath;
using std::ostringstream;
using ::testing::_;
using ::testing::DoAll;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::SetArgPointee;
using ::testing::StrEq;
// StatefulRecoveryTest is a test fixture for all Stateful Recovery unit tests.
class StatefulRecoveryTest : public ::testing::Test {
public:
StatefulRecoveryTest() {}
~StatefulRecoveryTest() override = default;
void SetUp() override {
platform_.reset(new MockPlatform());
userdataauth_proxy_.reset(new testing::StrictMock<
org::chromium::UserDataAuthInterfaceProxyMock>());
policy_provider_.reset(
new testing::StrictMock<policy::MockPolicyProvider>());
}
void Initialize() {
recovery_.reset(new StatefulRecovery(platform_.get(),
userdataauth_proxy_.get(),
policy_provider_.get(), flag_file_));
}
protected:
// Mock platform object.
std::unique_ptr<MockPlatform> platform_;
// Mock UserData Authentication interface.
std::unique_ptr<org::chromium::UserDataAuthInterfaceProxyMock>
userdataauth_proxy_;
std::unique_ptr<testing::StrictMock<policy::MockPolicyProvider>>
policy_provider_;
testing::StrictMock<policy::MockDevicePolicy> device_policy_;
// The Stateful Recovery that we want to test.
std::unique_ptr<StatefulRecovery> recovery_;
// Location of the flag_file
const std::string flag_file_ = "mylocalflagfile";
void PrepareMountRequest(bool success) {
// Mount
{
EXPECT_CALL(*userdataauth_proxy_, Mount)
.WillOnce([success](auto in_request, auto out_reply,
brillo::ErrorPtr* error, int timeout_ms) {
if (success)
out_reply->set_error(user_data_auth::CryptohomeErrorCode::
CRYPTOHOME_ERROR_NOT_SET);
else
out_reply->set_error(user_data_auth::CryptohomeErrorCode::
CRYPTOHOME_ERROR_MOUNT_FATAL);
return true;
});
}
// UnMount
if (success) {
EXPECT_CALL(*userdataauth_proxy_, Unmount)
.WillRepeatedly([](auto in_request, auto out_reply,
brillo::ErrorPtr* error, int timeout_ms) {
out_reply->set_error(
user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_NOT_SET);
return true;
});
}
}
void ExpectGetOwner(std::string owner) {
EXPECT_CALL(*policy_provider_, Reload()).WillOnce(Return(true));
EXPECT_CALL(*policy_provider_, device_policy_is_loaded())
.WillOnce(Return(true));
EXPECT_CALL(*policy_provider_, GetDevicePolicy())
.WillOnce(ReturnRef(device_policy_));
EXPECT_CALL(device_policy_, GetOwner(_))
.WillOnce(DoAll(SetArgPointee<0>(owner), Return(true)));
}
};
TEST_F(StatefulRecoveryTest, ValidRequestV1) {
std::string flag_content = "1";
EXPECT_CALL(*platform_, ReadFileToString(FilePath(flag_file_), _))
.WillOnce(DoAll(SetArgPointee<1>(flag_content), Return(true)));
EXPECT_CALL(*platform_, DeletePathRecursively(
FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
EXPECT_CALL(*platform_,
CreateDirectory(FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
EXPECT_CALL(*platform_, FirmwareWriteProtected()).WillOnce(Return(false));
EXPECT_CALL(*platform_,
StatVFS(FilePath(StatefulRecovery::kRecoverSource), _))
.WillOnce(Return(true));
EXPECT_CALL(
*platform_,
WriteStringToFile(FilePath(StatefulRecovery::kRecoverBlockUsage), _))
.WillOnce(Return(true));
EXPECT_CALL(*platform_,
ReportFilesystemDetails(
FilePath(StatefulRecovery::kRecoverSource),
FilePath(StatefulRecovery::kRecoverFilesystemDetails)))
.WillOnce(Return(true));
EXPECT_CALL(*platform_, Copy(FilePath(StatefulRecovery::kRecoverSource),
FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
Initialize();
EXPECT_TRUE(recovery_->Requested());
EXPECT_TRUE(recovery_->Recover());
}
TEST_F(StatefulRecoveryTest, ValidRequestV1WriteProtected) {
std::string flag_content = "1";
EXPECT_CALL(*platform_, ReadFileToString(FilePath(flag_file_), _))
.WillOnce(DoAll(SetArgPointee<1>(flag_content), Return(true)));
EXPECT_CALL(*platform_, DeletePathRecursively(
FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
EXPECT_CALL(*platform_,
CreateDirectory(FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
EXPECT_CALL(*platform_, FirmwareWriteProtected()).WillOnce(Return(true));
Initialize();
EXPECT_TRUE(recovery_->Requested());
EXPECT_FALSE(recovery_->Recover());
}
TEST_F(StatefulRecoveryTest, ValidRequestV2) {
std::string user = "user@example.com";
std::string passkey = "abcd1234";
std::string flag_content = "2\n" + user + "\n" + passkey;
std::string obfuscated_user =
brillo::cryptohome::home::SanitizeUserName(user);
FilePath mount_path =
FilePath("/home/.shadow/").Append(obfuscated_user).Append("mount");
EXPECT_CALL(*platform_, ReadFileToString(FilePath(flag_file_), _))
.WillOnce(DoAll(SetArgPointee<1>(flag_content), Return(true)));
EXPECT_CALL(*platform_, DeletePathRecursively(
FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
EXPECT_CALL(*platform_,
CreateDirectory(FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
// CopyUserContents
auto mock_mount_response = std::make_unique<user_data_auth::MountReply>();
EXPECT_CALL(*platform_,
Copy(mount_path, FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
PrepareMountRequest(true);
ExpectGetOwner(user);
EXPECT_CALL(*platform_, FirmwareWriteProtected()).WillOnce(Return(true));
// CopyPartitionInfo
EXPECT_CALL(*platform_,
StatVFS(FilePath(StatefulRecovery::kRecoverSource), _))
.WillOnce(Return(true));
EXPECT_CALL(
*platform_,
WriteStringToFile(FilePath(StatefulRecovery::kRecoverBlockUsage), _))
.WillOnce(Return(true));
EXPECT_CALL(*platform_,
ReportFilesystemDetails(
FilePath(StatefulRecovery::kRecoverSource),
FilePath(StatefulRecovery::kRecoverFilesystemDetails)))
.WillOnce(Return(true));
// CopyPartitionContents
EXPECT_CALL(*platform_, Copy(FilePath(StatefulRecovery::kRecoverSource),
FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
Initialize();
EXPECT_TRUE(recovery_->Requested());
EXPECT_TRUE(recovery_->Recover());
}
TEST_F(StatefulRecoveryTest, ValidRequestV2NotOwner) {
std::string user = "user@example.com";
std::string passkey = "abcd1234";
std::string flag_content = "2\n" + user + "\n" + passkey;
std::string obfuscated_user =
brillo::cryptohome::home::SanitizeUserName(user);
FilePath mount_path =
FilePath("/home/.shadow/").Append(obfuscated_user).Append("mount");
EXPECT_CALL(*platform_, ReadFileToString(FilePath(flag_file_), _))
.WillOnce(DoAll(SetArgPointee<1>(flag_content), Return(true)));
EXPECT_CALL(*platform_, DeletePathRecursively(
FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
EXPECT_CALL(*platform_,
CreateDirectory(FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
EXPECT_CALL(*platform_,
Copy(mount_path, FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
PrepareMountRequest(true);
ExpectGetOwner("notuser");
EXPECT_CALL(*platform_, FirmwareWriteProtected()).WillOnce(Return(true));
Initialize();
EXPECT_TRUE(recovery_->Requested());
EXPECT_TRUE(recovery_->Recover());
}
TEST_F(StatefulRecoveryTest, ValidRequestV2BadUser) {
std::string user = "user@example.com";
std::string passkey = "abcd1234";
std::string flag_content = "2\n" + user + "\n" + passkey;
EXPECT_CALL(*platform_, ReadFileToString(FilePath(flag_file_), _))
.WillOnce(DoAll(SetArgPointee<1>(flag_content), Return(true)));
EXPECT_CALL(*platform_, DeletePathRecursively(
FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
EXPECT_CALL(*platform_,
CreateDirectory(FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
PrepareMountRequest(false);
EXPECT_CALL(*platform_, FirmwareWriteProtected()).WillOnce(Return(true));
Initialize();
EXPECT_TRUE(recovery_->Requested());
EXPECT_FALSE(recovery_->Recover());
}
TEST_F(StatefulRecoveryTest, ValidRequestV2BadUserNotWriteProtected) {
std::string user = "user@example.com";
std::string passkey = "abcd1234";
std::string flag_content = "2\n" + user + "\n" + passkey;
EXPECT_CALL(*platform_, ReadFileToString(FilePath(flag_file_), _))
.WillOnce(DoAll(SetArgPointee<1>(flag_content), Return(true)));
EXPECT_CALL(*platform_, DeletePathRecursively(
FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
EXPECT_CALL(*platform_,
CreateDirectory(FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
PrepareMountRequest(false);
EXPECT_CALL(*platform_, FirmwareWriteProtected()).WillOnce(Return(false));
// CopyPartitionInfo
EXPECT_CALL(*platform_,
StatVFS(FilePath(StatefulRecovery::kRecoverSource), _))
.WillOnce(Return(true));
EXPECT_CALL(
*platform_,
WriteStringToFile(FilePath(StatefulRecovery::kRecoverBlockUsage), _))
.WillOnce(Return(true));
EXPECT_CALL(*platform_,
ReportFilesystemDetails(
FilePath(StatefulRecovery::kRecoverSource),
FilePath(StatefulRecovery::kRecoverFilesystemDetails)))
.WillOnce(Return(true));
// CopyPartitionContents
EXPECT_CALL(*platform_, Copy(FilePath(StatefulRecovery::kRecoverSource),
FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
Initialize();
EXPECT_TRUE(recovery_->Requested());
EXPECT_TRUE(recovery_->Recover());
}
TEST_F(StatefulRecoveryTest, ValidRequestV2NotOwnerNotWriteProtected) {
std::string user = "user@example.com";
std::string passkey = "abcd1234";
std::string flag_content = "2\n" + user + "\n" + passkey;
std::string obfuscated_user =
brillo::cryptohome::home::SanitizeUserName(user);
FilePath mount_path =
FilePath("/home/.shadow/").Append(obfuscated_user).Append("mount");
EXPECT_CALL(*platform_, ReadFileToString(FilePath(flag_file_), _))
.WillOnce(DoAll(SetArgPointee<1>(flag_content), Return(true)));
EXPECT_CALL(*platform_, DeletePathRecursively(
FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
EXPECT_CALL(*platform_,
CreateDirectory(FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
// CopyUserContents
PrepareMountRequest(true);
EXPECT_CALL(*platform_,
Copy(mount_path, FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
ExpectGetOwner("notowner");
EXPECT_CALL(*platform_, FirmwareWriteProtected()).WillOnce(Return(false));
// CopyPartitionInfo
EXPECT_CALL(*platform_,
StatVFS(FilePath(StatefulRecovery::kRecoverSource), _))
.WillOnce(Return(true));
EXPECT_CALL(
*platform_,
WriteStringToFile(FilePath(StatefulRecovery::kRecoverBlockUsage), _))
.WillOnce(Return(true));
EXPECT_CALL(*platform_,
ReportFilesystemDetails(
FilePath(StatefulRecovery::kRecoverSource),
FilePath(StatefulRecovery::kRecoverFilesystemDetails)))
.WillOnce(Return(true));
// CopyPartitionContents
EXPECT_CALL(*platform_, Copy(FilePath(StatefulRecovery::kRecoverSource),
FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
Initialize();
EXPECT_TRUE(recovery_->Requested());
EXPECT_TRUE(recovery_->Recover());
}
TEST_F(StatefulRecoveryTest, InvalidFlagFileContents) {
std::string flag_content = "0 hello";
EXPECT_CALL(*platform_, ReadFileToString(FilePath(flag_file_), _))
.WillOnce(DoAll(SetArgPointee<1>(flag_content), Return(true)));
Initialize();
EXPECT_FALSE(recovery_->Requested());
EXPECT_FALSE(recovery_->Recover());
}
TEST_F(StatefulRecoveryTest, UnreadableFlagFile) {
EXPECT_CALL(*platform_, ReadFileToString(FilePath(flag_file_), _))
.WillOnce(Return(false));
Initialize();
EXPECT_FALSE(recovery_->Requested());
EXPECT_FALSE(recovery_->Recover());
}
TEST_F(StatefulRecoveryTest, UncopyableData) {
std::string flag_content = "1";
EXPECT_CALL(*platform_, ReadFileToString(FilePath(flag_file_), _))
.WillOnce(DoAll(SetArgPointee<1>(flag_content), Return(true)));
EXPECT_CALL(*platform_, DeletePathRecursively(
FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
EXPECT_CALL(*platform_,
CreateDirectory(FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
EXPECT_CALL(*platform_, FirmwareWriteProtected()).WillOnce(Return(false));
EXPECT_CALL(*platform_, Copy(FilePath(StatefulRecovery::kRecoverSource),
FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(false));
Initialize();
EXPECT_TRUE(recovery_->Requested());
EXPECT_FALSE(recovery_->Recover());
}
TEST_F(StatefulRecoveryTest, DirectoryCreationFailure) {
std::string flag_content = "1";
EXPECT_CALL(*platform_, ReadFileToString(FilePath(flag_file_), _))
.WillOnce(DoAll(SetArgPointee<1>(flag_content), Return(true)));
EXPECT_CALL(*platform_, DeletePathRecursively(
FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
EXPECT_CALL(*platform_,
CreateDirectory(FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(false));
Initialize();
EXPECT_TRUE(recovery_->Requested());
EXPECT_FALSE(recovery_->Recover());
}
TEST_F(StatefulRecoveryTest, StatVFSFailure) {
std::string flag_content = "1";
EXPECT_CALL(*platform_, ReadFileToString(FilePath(flag_file_), _))
.WillOnce(DoAll(SetArgPointee<1>(flag_content), Return(true)));
EXPECT_CALL(*platform_, DeletePathRecursively(
FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
EXPECT_CALL(*platform_,
CreateDirectory(FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
EXPECT_CALL(*platform_, FirmwareWriteProtected()).WillOnce(Return(false));
EXPECT_CALL(*platform_, Copy(FilePath(StatefulRecovery::kRecoverSource),
FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
EXPECT_CALL(*platform_,
StatVFS(FilePath(StatefulRecovery::kRecoverSource), _))
.WillOnce(Return(false));
Initialize();
EXPECT_TRUE(recovery_->Requested());
EXPECT_FALSE(recovery_->Recover());
}
TEST_F(StatefulRecoveryTest, FilesystemDetailsFailure) {
std::string flag_content = "1";
EXPECT_CALL(*platform_, ReadFileToString(FilePath(flag_file_), _))
.WillOnce(DoAll(SetArgPointee<1>(flag_content), Return(true)));
EXPECT_CALL(*platform_, DeletePathRecursively(
FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
EXPECT_CALL(*platform_,
CreateDirectory(FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
EXPECT_CALL(*platform_, FirmwareWriteProtected()).WillOnce(Return(false));
EXPECT_CALL(*platform_, Copy(FilePath(StatefulRecovery::kRecoverSource),
FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
EXPECT_CALL(*platform_,
StatVFS(FilePath(StatefulRecovery::kRecoverSource), _))
.WillOnce(Return(true));
EXPECT_CALL(
*platform_,
WriteStringToFile(FilePath(StatefulRecovery::kRecoverBlockUsage), _))
.WillOnce(Return(true));
EXPECT_CALL(*platform_,
ReportFilesystemDetails(
FilePath(StatefulRecovery::kRecoverSource),
FilePath(StatefulRecovery::kRecoverFilesystemDetails)))
.WillOnce(Return(false));
Initialize();
EXPECT_TRUE(recovery_->Requested());
EXPECT_FALSE(recovery_->Recover());
}
TEST_F(StatefulRecoveryTest, UsageReportOk) {
Platform platform;
struct statvfs vfs;
/* Reporting on a valid location produces output. */
EXPECT_TRUE(platform_->StatVFS(FilePath("/"), &vfs));
EXPECT_NE(vfs.f_blocks, 0);
/* Reporting on an invalid location fails. */
EXPECT_FALSE(platform_->StatVFS(FilePath("/this/is/very/wrong"), &vfs));
/* TODO(keescook): mockable tune2fs, since it's not installed in chroot. */
}
TEST_F(StatefulRecoveryTest, DestinationRecreateFailure) {
std::string flag_content = "1";
EXPECT_CALL(*platform_, ReadFileToString(FilePath(flag_file_), _))
.WillOnce(DoAll(SetArgPointee<1>(flag_content), Return(true)));
EXPECT_CALL(*platform_, DeletePathRecursively(
FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
EXPECT_CALL(*platform_,
CreateDirectory(FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(false));
EXPECT_CALL(*platform_,
Copy(_, FilePath(StatefulRecovery::kRecoverDestination)))
.Times(0);
Initialize();
EXPECT_TRUE(recovery_->Requested());
EXPECT_FALSE(recovery_->Recover());
}
} // namespace cryptohome