blob: 8cc9d69cfe81dbd4bf43d76626b246d9016db5ae [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.h"
#include <base/files/file_util.h>
#include <gtest/gtest.h>
#include <memory>
#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;
// MockSRHandlers is a class that contains the 3 functions required to create
// the StatefulRecovery object. This mock object is created to simplify testing.
class MockSRHandlers {
public:
MOCK_METHOD(bool,
Mount,
(const std::string& username,
const std::string& passkey,
base::FilePath* out_home_path),
());
MOCK_METHOD(bool, Unmount, (), ());
MOCK_METHOD(bool, IsOwner, (const std::string& username), ());
};
// 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());
handlers_.reset(new MockSRHandlers());
}
void Initialize() {
auto mount =
base::Bind(&MockSRHandlers::Mount, base::Unretained(handlers_.get()));
auto unmount =
base::Bind(&MockSRHandlers::Unmount, base::Unretained(handlers_.get()));
auto is_owner =
base::Bind(&MockSRHandlers::IsOwner, base::Unretained(handlers_.get()));
recovery_.reset(
new StatefulRecovery(platform_.get(), mount, unmount, is_owner));
}
protected:
// Handlers for Mount, Unmount and IsOwner.
std::unique_ptr<MockSRHandlers> handlers_;
// Mock platform object.
std::unique_ptr<MockPlatform> platform_;
// The Stateful Recovery that we want to test.
std::unique_ptr<StatefulRecovery> recovery_;
};
TEST_F(StatefulRecoveryTest, ValidRequestV1) {
std::string flag_content = "1";
EXPECT_CALL(*platform_,
ReadFileToString(FilePath(StatefulRecovery::kFlagFile), _))
.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(StatefulRecovery::kFlagFile), _))
.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;
FilePath mount_path("/home/.shadow/hashhashash/mount");
EXPECT_CALL(*platform_,
ReadFileToString(FilePath(StatefulRecovery::kFlagFile), _))
.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
EXPECT_CALL(*handlers_, Mount(StrEq(user), StrEq(passkey), _))
.WillOnce(DoAll(SetArgPointee<2>(mount_path), Return(true)));
EXPECT_CALL(*platform_,
Copy(mount_path, FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
EXPECT_CALL(*handlers_, Unmount()).WillOnce(Return(true));
EXPECT_CALL(*handlers_, IsOwner(_)).WillOnce(Return(true));
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;
FilePath mount_path("/home/.shadow/hashhashash/mount");
EXPECT_CALL(*platform_,
ReadFileToString(FilePath(StatefulRecovery::kFlagFile), _))
.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
EXPECT_CALL(*handlers_, Mount(StrEq(user), StrEq(passkey), _))
.WillOnce(DoAll(SetArgPointee<2>(mount_path), Return(true)));
EXPECT_CALL(*platform_,
Copy(mount_path, FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
EXPECT_CALL(*handlers_, Unmount()).WillOnce(Return(true));
EXPECT_CALL(*handlers_, IsOwner(_)).WillOnce(Return(false));
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(StatefulRecovery::kFlagFile), _))
.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
EXPECT_CALL(*handlers_, Mount(StrEq(user), StrEq(passkey), _))
.WillOnce(Return(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;
FilePath mount_path("/home/.shadow/hashhashash/mount");
EXPECT_CALL(*platform_,
ReadFileToString(FilePath(StatefulRecovery::kFlagFile), _))
.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
EXPECT_CALL(*handlers_, Mount(StrEq(user), StrEq(passkey), _))
.WillOnce(Return(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;
FilePath mount_path("/home/.shadow/hashhashash/mount");
EXPECT_CALL(*platform_,
ReadFileToString(FilePath(StatefulRecovery::kFlagFile), _))
.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
EXPECT_CALL(*handlers_, Mount(StrEq(user), StrEq(passkey), _))
.WillOnce(DoAll(SetArgPointee<2>(mount_path), Return(true)));
EXPECT_CALL(*platform_,
Copy(mount_path, FilePath(StatefulRecovery::kRecoverDestination)))
.WillOnce(Return(true));
EXPECT_CALL(*handlers_, Unmount()).WillOnce(Return(true));
EXPECT_CALL(*handlers_, IsOwner(_)).WillOnce(Return(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, InvalidFlagFileContents) {
std::string flag_content = "0 hello";
EXPECT_CALL(*platform_,
ReadFileToString(FilePath(StatefulRecovery::kFlagFile), _))
.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(StatefulRecovery::kFlagFile), _))
.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(StatefulRecovery::kFlagFile), _))
.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(StatefulRecovery::kFlagFile), _))
.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(StatefulRecovery::kFlagFile), _))
.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(StatefulRecovery::kFlagFile), _))
.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, MountsParseOk) {
Platform platform;
FilePath mount_info;
FILE* fp;
std::string device_in = "/dev/pan", device_out, mount_info_contents;
mount_info_contents.append("84 24 0:29 / ");
FilePath filesystem = FilePath("/second/star/to/the/right");
mount_info_contents.append(filesystem.value());
mount_info_contents.append(" rw,nosuid,nodev,noexec,relatime - fairyfs ");
mount_info_contents.append(device_in);
mount_info_contents.append(" rw,ecryp...");
fp = base::CreateAndOpenTemporaryStream(&mount_info).release();
ASSERT_TRUE(fp != NULL);
EXPECT_EQ(
fwrite(mount_info_contents.c_str(), mount_info_contents.length(), 1, fp),
1);
EXPECT_EQ(fclose(fp), 0);
platform.set_mount_info_path(mount_info);
/* Fails if item is missing. */
EXPECT_FALSE(platform.FindFilesystemDevice(FilePath("monkey"), &device_out));
/* Works normally. */
device_out.clear();
EXPECT_TRUE(platform.FindFilesystemDevice(filesystem, &device_out));
EXPECT_TRUE(device_out == device_in);
/* Clean up. */
EXPECT_TRUE(base::DeleteFile(mount_info));
}
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(StatefulRecovery::kFlagFile), _))
.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