| // Copyright 2020 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include <base/files/file_path.h> |
| #include <base/files/file_util.h> |
| #include <base/files/scoped_temp_dir.h> |
| #include <brillo/cryptohome.h> |
| #include <brillo/data_encoding.h> |
| #include <dbus/bus.h> |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| #include "arc/data-snapshotd/dbus_adaptor.h" |
| #include "arc/data-snapshotd/file_utils.h" |
| #include "bootlockbox-client/bootlockbox/boot_lockbox_client.h" |
| // Note that boot_lockbox_rpc.pb.h have to be included before |
| // dbus_adaptors/org.chromium.BootLockboxInterface.h because it is used in |
| // there. |
| #include "bootlockbox/proto_bindings/boot_lockbox_rpc.pb.h" |
| |
| #include "bootlockbox-client/bootlockbox/dbus-proxies.h" |
| |
| using testing::_; |
| using testing::Eq; |
| using testing::Invoke; |
| using testing::Return; |
| |
| namespace arc { |
| namespace data_snapshotd { |
| |
| namespace { |
| |
| constexpr char kRandomDir[] = "data"; |
| constexpr char kRandomFile[] = "random file"; |
| constexpr char kContent[] = "content"; |
| constexpr char kFakeLastSnapshotPublicKey[] = "fake_public_key"; |
| constexpr char kFakeAccountID[] = "fake_account_id"; |
| constexpr char kFakeAccountID2[] = "fake_aacount_id_2"; |
| |
| MATCHER_P(nEq, expected, "") { |
| return expected != arg; |
| } |
| |
| } // namespace |
| |
| class MockBootLockboxClient : public cryptohome::BootLockboxClient { |
| public: |
| explicit MockBootLockboxClient(scoped_refptr<dbus::Bus> bus) |
| : BootLockboxClient( |
| std::make_unique<org::chromium::BootLockboxInterfaceProxy>(bus), |
| bus) {} |
| ~MockBootLockboxClient() override = default; |
| |
| MOCK_METHOD(bool, |
| Store, |
| (const std::string&, const std::string&), |
| (override)); |
| MOCK_METHOD(bool, Read, (const std::string&, std::string*), (override)); |
| MOCK_METHOD(bool, Finalize, (), (override)); |
| }; |
| |
| class DBusAdaptorTest : public testing::Test { |
| public: |
| DBusAdaptorTest() : bus_(new dbus::Bus{dbus::Bus::Options{}}) { |
| brillo::cryptohome::home::SetSystemSalt(&salt_); |
| } |
| |
| void SetUp() override { |
| EXPECT_TRUE(root_tempdir_.CreateUniqueTempDir()); |
| user_directory_ = root_tempdir_.GetPath().Append(hash(kFakeAccountID)); |
| EXPECT_TRUE(base::CreateDirectory(user_directory_)); |
| auto boot_lockbox_client = |
| std::make_unique<testing::StrictMock<MockBootLockboxClient>>(bus_); |
| boot_lockbox_client_ = boot_lockbox_client.get(); |
| dbus_adaptor_ = DBusAdaptor::CreateForTesting( |
| root_tempdir_.GetPath(), root_tempdir_.GetPath(), |
| std::move(boot_lockbox_client)); |
| } |
| |
| void TearDown() override { |
| dbus_adaptor_.reset(); |
| EXPECT_TRUE(base::DeletePathRecursively(root_tempdir_.GetPath())); |
| } |
| |
| DBusAdaptor* dbus_adaptor() { return dbus_adaptor_.get(); } |
| const base::FilePath& last_snapshot_dir() const { |
| return dbus_adaptor_->get_last_snapshot_directory(); |
| } |
| const base::FilePath& previous_snapshot_dir() const { |
| return dbus_adaptor_->get_previous_snapshot_directory(); |
| } |
| base::FilePath android_data_dir() const { |
| return user_directory_.Append(kAndroidDataDirectory); |
| } |
| base::FilePath random_dir() const { |
| return root_tempdir_.GetPath().Append(kRandomDir); |
| } |
| std::string hash(const std::string& account_id) const { |
| return brillo::cryptohome::home::SanitizeUserName(account_id); |
| } |
| base::FilePath user_directory() const { return user_directory_; } |
| |
| // Creates |dir| and fills in with random content. |
| void CreateDir(const base::FilePath& dir) { |
| EXPECT_TRUE(base::CreateDirectory(dir)); |
| EXPECT_TRUE(base::CreateDirectory(dir.Append(kRandomDir))); |
| EXPECT_TRUE( |
| base::WriteFile(dir.Append(kRandomFile), kContent, strlen(kContent))); |
| } |
| |
| MockBootLockboxClient* boot_lockbox_client() { return boot_lockbox_client_; } |
| |
| private: |
| std::string salt_ = "salt"; |
| scoped_refptr<dbus::Bus> bus_; |
| MockBootLockboxClient* boot_lockbox_client_; |
| std::unique_ptr<DBusAdaptor> dbus_adaptor_; |
| base::ScopedTempDir root_tempdir_; |
| base::FilePath user_directory_; |
| }; |
| |
| TEST_F(DBusAdaptorTest, ClearSnapshotBasic) { |
| CreateDir(last_snapshot_dir()); |
| EXPECT_TRUE(base::DirectoryExists(last_snapshot_dir())); |
| |
| CreateDir(previous_snapshot_dir()); |
| EXPECT_TRUE(base::DirectoryExists(previous_snapshot_dir())); |
| |
| EXPECT_TRUE(dbus_adaptor()->ClearSnapshot(false /* last */)); |
| EXPECT_FALSE(base::DirectoryExists(previous_snapshot_dir())); |
| |
| EXPECT_TRUE(dbus_adaptor()->ClearSnapshot(false /* last */)); |
| |
| EXPECT_TRUE(dbus_adaptor()->ClearSnapshot(true /* last */)); |
| EXPECT_FALSE(base::DirectoryExists(last_snapshot_dir())); |
| |
| EXPECT_TRUE(dbus_adaptor()->ClearSnapshot(true /* last */)); |
| } |
| |
| // Test successful basic flow with no pre-existing snapshots. |
| TEST_F(DBusAdaptorTest, GenerateKeyPairBasic) { |
| EXPECT_CALL(*boot_lockbox_client(), Store(Eq(kLastSnapshotPublicKey), _)) |
| .WillOnce(Return(true)); |
| EXPECT_TRUE(dbus_adaptor()->GenerateKeyPair()); |
| } |
| |
| // Test successful basic flow with pre-existing snapshots. |
| TEST_F(DBusAdaptorTest, GenerateKeyPairExisting) { |
| CreateDir(last_snapshot_dir()); |
| CreateDir(previous_snapshot_dir()); |
| |
| SnapshotDirectory last_dir; |
| EXPECT_TRUE(ReadSnapshotDirectory(last_snapshot_dir(), &last_dir)); |
| std::vector<uint8_t> last_hash = |
| CalculateDirectoryCryptographicHash(last_dir); |
| EXPECT_FALSE(last_hash.empty()); |
| |
| { |
| SnapshotDirectory previous_dir; |
| EXPECT_TRUE(ReadSnapshotDirectory(previous_snapshot_dir(), &previous_dir)); |
| std::vector<uint8_t> previous_hash = |
| CalculateDirectoryCryptographicHash(previous_dir); |
| EXPECT_FALSE(previous_hash.empty()); |
| |
| EXPECT_NE(last_hash, previous_hash); |
| } |
| |
| // Last snapshot dir => previous snapshot dir. |
| EXPECT_CALL(*boot_lockbox_client(), Read(Eq(kLastSnapshotPublicKey), _)) |
| .WillOnce(Invoke([](const std::string& key, std::string* value) { |
| *value = kFakeLastSnapshotPublicKey; |
| return true; |
| })); |
| EXPECT_CALL(*boot_lockbox_client(), Store(Eq(kPreviousSnapshotPublicKey), |
| Eq(kFakeLastSnapshotPublicKey))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*boot_lockbox_client(), Store(Eq(kLastSnapshotPublicKey), Eq(""))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*boot_lockbox_client(), |
| Store(Eq(kLastSnapshotPublicKey), nEq(""))) |
| .WillOnce(Return(true)); |
| |
| EXPECT_TRUE(dbus_adaptor()->GenerateKeyPair()); |
| { |
| SnapshotDirectory previous_dir; |
| EXPECT_TRUE(ReadSnapshotDirectory(previous_snapshot_dir(), &previous_dir)); |
| std::vector<uint8_t> previous_hash = |
| CalculateDirectoryCryptographicHash(previous_dir); |
| EXPECT_FALSE(previous_hash.empty()); |
| // Check that the last snapshot has been moved to previous snapshot dir. |
| EXPECT_EQ(previous_hash, last_hash); |
| } |
| } |
| |
| // Test successful flow with last snapshot key reading failure. |
| TEST_F(DBusAdaptorTest, GenerateKeyPairReadFailure) { |
| CreateDir(last_snapshot_dir()); |
| |
| // Attempt failure: last snapshot dir => previous snapshot dir. |
| EXPECT_CALL(*boot_lockbox_client(), Read(Eq(kLastSnapshotPublicKey), _)) |
| .WillOnce(Return(false)); |
| EXPECT_CALL(*boot_lockbox_client(), |
| Store(Eq(kLastSnapshotPublicKey), nEq(""))) |
| .WillOnce(Return(true)); |
| |
| // Generating key pair should be still successful. |
| EXPECT_TRUE(dbus_adaptor()->GenerateKeyPair()); |
| } |
| |
| // Test successful flow with pre-existing last snapshot empty key. |
| TEST_F(DBusAdaptorTest, GenerateKeyPairReadEmpty) { |
| CreateDir(last_snapshot_dir()); |
| |
| // Attempt failure: last snapshot dir => previous snapshot dir. |
| EXPECT_CALL(*boot_lockbox_client(), Read(Eq(kLastSnapshotPublicKey), _)) |
| .WillOnce(Invoke([](const std::string& key, std::string* value) { |
| *value = ""; |
| return true; |
| })); |
| EXPECT_CALL(*boot_lockbox_client(), |
| Store(Eq(kLastSnapshotPublicKey), nEq(""))) |
| .WillOnce(Return(true)); |
| |
| // Generating key pair should be still successful. |
| EXPECT_TRUE(dbus_adaptor()->GenerateKeyPair()); |
| } |
| |
| // Test success flow with pre-existing snapshots and moving error. |
| TEST_F(DBusAdaptorTest, GenerateKeyPairMoveError) { |
| CreateDir(last_snapshot_dir()); |
| |
| // Last snapshot dir => previous snapshot dir. |
| EXPECT_CALL(*boot_lockbox_client(), Read(Eq(kLastSnapshotPublicKey), _)) |
| .WillOnce(Invoke([](const std::string& key, std::string* value) { |
| *value = kFakeLastSnapshotPublicKey; |
| return true; |
| })); |
| // Fail to move last snapshot public key to previous. |
| EXPECT_CALL(*boot_lockbox_client(), Store(Eq(kPreviousSnapshotPublicKey), |
| Eq(kFakeLastSnapshotPublicKey))) |
| .WillOnce(Return(false)); |
| EXPECT_CALL(*boot_lockbox_client(), |
| Store(Eq(kLastSnapshotPublicKey), nEq(""))) |
| .WillOnce(Return(true)); |
| |
| // Generating key pair should be still successful, because the last snapshot |
| // will be re-generated anyway. |
| EXPECT_TRUE(dbus_adaptor()->GenerateKeyPair()); |
| } |
| |
| // Test failure flow when storing freshly generated public key is failed. |
| TEST_F(DBusAdaptorTest, GenerateKeyPairStoreFailure) { |
| // Fail once freshly generated public key storage is attempted. |
| EXPECT_CALL(*boot_lockbox_client(), |
| Store(Eq(kLastSnapshotPublicKey), nEq(""))) |
| .WillOnce(Return(false)); |
| |
| EXPECT_FALSE(dbus_adaptor()->GenerateKeyPair()); |
| } |
| |
| // Test failure flow when the keys were not generated. |
| TEST_F(DBusAdaptorTest, TakeSnapshotNoPrivateKeyFailure) { |
| EXPECT_FALSE(dbus_adaptor()->TakeSnapshot(kFakeAccountID)); |
| } |
| |
| // Test failure flow when the last snapshot directory already exists. |
| TEST_F(DBusAdaptorTest, TakeSnapshotLastSnapshotExistFailure) { |
| EXPECT_CALL(*boot_lockbox_client(), Store(Eq(kLastSnapshotPublicKey), _)) |
| .WillOnce(Return(true)); |
| EXPECT_TRUE(dbus_adaptor()->GenerateKeyPair()); |
| |
| CreateDir(last_snapshot_dir()); |
| EXPECT_TRUE(base::DirectoryExists(last_snapshot_dir())); |
| EXPECT_FALSE(dbus_adaptor()->TakeSnapshot(kFakeAccountID)); |
| } |
| |
| // Test failure flow when android-data directory does not exist. |
| TEST_F(DBusAdaptorTest, TakeSnapshotAndroidDataDirNotExist) { |
| EXPECT_CALL(*boot_lockbox_client(), Store(Eq(kLastSnapshotPublicKey), _)) |
| .WillOnce(Return(true)); |
| EXPECT_TRUE(dbus_adaptor()->GenerateKeyPair()); |
| EXPECT_FALSE(base::DirectoryExists(android_data_dir())); |
| |
| EXPECT_FALSE(dbus_adaptor()->TakeSnapshot(kFakeAccountID)); |
| } |
| |
| // Test failure flow when android-data is file. |
| TEST_F(DBusAdaptorTest, TakeSnapshotAndroidDataNotDirFile) { |
| EXPECT_CALL(*boot_lockbox_client(), Store(Eq(kLastSnapshotPublicKey), _)) |
| .WillOnce(Return(true)); |
| EXPECT_TRUE(dbus_adaptor()->GenerateKeyPair()); |
| // Create a file instead of android-data directory. |
| EXPECT_TRUE(base::WriteFile(android_data_dir(), kContent, strlen(kContent))); |
| EXPECT_TRUE(base::PathExists(android_data_dir())); |
| EXPECT_FALSE(base::DirectoryExists(android_data_dir())); |
| |
| EXPECT_FALSE(dbus_adaptor()->TakeSnapshot(kFakeAccountID)); |
| } |
| |
| // Test failure flow when android-data is a sym link. |
| TEST_F(DBusAdaptorTest, TakeSnapshotAndroidDataSymLink) { |
| EXPECT_CALL(*boot_lockbox_client(), Store(Eq(kLastSnapshotPublicKey), _)) |
| .WillOnce(Return(true)); |
| EXPECT_TRUE(dbus_adaptor()->GenerateKeyPair()); |
| // Create a symlink. |
| CreateDir(random_dir()); |
| EXPECT_TRUE(base::CreateSymbolicLink(random_dir(), android_data_dir())); |
| EXPECT_TRUE(base::IsLink(android_data_dir())); |
| EXPECT_TRUE(base::DirectoryExists(android_data_dir())); |
| |
| EXPECT_FALSE(dbus_adaptor()->TakeSnapshot(kFakeAccountID)); |
| } |
| |
| // Test failure flow when android-data is a fifo. |
| TEST_F(DBusAdaptorTest, TakeSnapshotAndroidDataFiFo) { |
| EXPECT_CALL(*boot_lockbox_client(), Store(Eq(kLastSnapshotPublicKey), _)) |
| .WillOnce(Return(true)); |
| EXPECT_TRUE(dbus_adaptor()->GenerateKeyPair()); |
| // Create a fifo android-data. |
| mkfifo(android_data_dir().value().c_str(), 0666); |
| EXPECT_TRUE(base::PathExists(android_data_dir())); |
| EXPECT_FALSE(base::DirectoryExists(android_data_dir())); |
| |
| EXPECT_FALSE(dbus_adaptor()->TakeSnapshot(kFakeAccountID)); |
| } |
| |
| // TODO(crbug.com/1149744) Enable test once bug is fixed. |
| // Test basic TakeSnapshot success flow. |
| TEST_F(DBusAdaptorTest, DISABLED_TakeSnapshotSuccess) { |
| // In this test the copied snapshot directory is verified against the origin |
| // android data directory. Inodes verification must be disabled, because the |
| // inode values are changed after copying. |
| // In production, it is not the case, because the directorys' integrity is |
| // verified against itself and inode values should persist. |
| dbus_adaptor()->set_inode_verification_enabled_for_testing( |
| false /* enabled */); |
| std::string expected_public_key_digest; |
| EXPECT_CALL(*boot_lockbox_client(), Store(Eq(kLastSnapshotPublicKey), _)) |
| .WillOnce(Invoke([&expected_public_key_digest]( |
| const std::string& key, const std::string& digest) { |
| expected_public_key_digest = digest; |
| return true; |
| })); |
| EXPECT_TRUE(dbus_adaptor()->GenerateKeyPair()); |
| |
| CreateDir(android_data_dir()); |
| EXPECT_TRUE(base::DirectoryExists(android_data_dir())); |
| // Store userhash to ensure that userhash stays the same. |
| EXPECT_TRUE(StoreUserhash(android_data_dir(), hash(kFakeAccountID))); |
| SnapshotDirectory android_dir; |
| EXPECT_TRUE(ReadSnapshotDirectory(android_data_dir(), &android_dir, |
| false /* inode_verification_enabled */)); |
| std::vector<uint8_t> android_data_hash = |
| CalculateDirectoryCryptographicHash(android_dir); |
| EXPECT_FALSE(android_data_hash.empty()); |
| |
| EXPECT_TRUE(dbus_adaptor()->TakeSnapshot(kFakeAccountID)); |
| EXPECT_TRUE(base::DirectoryExists(last_snapshot_dir())); |
| SnapshotDirectory last_dir; |
| EXPECT_TRUE(ReadSnapshotDirectory(last_snapshot_dir(), &last_dir, |
| false /* inode_verification_enabled */)); |
| std::vector<uint8_t> last_snapshot_hash = |
| CalculateDirectoryCryptographicHash(last_dir); |
| EXPECT_FALSE(last_snapshot_hash.empty()); |
| EXPECT_EQ(android_data_hash, last_snapshot_hash); |
| |
| // Verification for another account ID should fail. |
| EXPECT_FALSE(VerifyHash(last_snapshot_dir(), hash(kFakeAccountID2), |
| expected_public_key_digest, |
| false /* inode_verification_enabled */)); |
| EXPECT_TRUE(VerifyHash(last_snapshot_dir(), hash(kFakeAccountID), |
| expected_public_key_digest, |
| false /* inode_verification_enabled */)); |
| dbus_adaptor()->set_inode_verification_enabled_for_testing( |
| true /* enabled */); |
| } |
| |
| // Test failure flow if TakeSnapshot is invoked twice. |
| TEST_F(DBusAdaptorTest, TakeSnapshotDouble) { |
| EXPECT_CALL(*boot_lockbox_client(), Store(Eq(kLastSnapshotPublicKey), _)) |
| .WillOnce(Return(true)); |
| EXPECT_TRUE(dbus_adaptor()->GenerateKeyPair()); |
| |
| CreateDir(android_data_dir()); |
| EXPECT_TRUE(dbus_adaptor()->TakeSnapshot(kFakeAccountID)); |
| |
| CreateDir(android_data_dir()); |
| EXPECT_FALSE(dbus_adaptor()->TakeSnapshot(kFakeAccountID)); |
| } |
| |
| // Test failure flow when user directory does not exist. |
| TEST_F(DBusAdaptorTest, LoadSnapshotNoAndroidDataDir) { |
| CreateDir(last_snapshot_dir()); |
| CreateDir(previous_snapshot_dir()); |
| EXPECT_TRUE(base::DeletePathRecursively(user_directory())); |
| EXPECT_FALSE(base::DirectoryExists(user_directory())); |
| |
| bool last, success; |
| dbus_adaptor()->LoadSnapshot(kFakeAccountID, &last, &success); |
| EXPECT_FALSE(success); |
| } |
| |
| // Test failure when snapshot directory does not exist. |
| TEST_F(DBusAdaptorTest, LoadSnapshotNoSnapshot) { |
| CreateDir(user_directory()); |
| EXPECT_TRUE(base::DirectoryExists(user_directory())); |
| EXPECT_FALSE(base::DirectoryExists(last_snapshot_dir())); |
| EXPECT_FALSE(base::DirectoryExists(previous_snapshot_dir())); |
| bool last, success; |
| dbus_adaptor()->LoadSnapshot(kFakeAccountID, &last, &success); |
| EXPECT_FALSE(success); |
| } |
| |
| // Test failure when public key is not stored in BootLockbox. |
| TEST_F(DBusAdaptorTest, LoadSnapshotNoPublicKey) { |
| CreateDir(user_directory()); |
| EXPECT_TRUE(base::DirectoryExists(user_directory())); |
| |
| CreateDir(last_snapshot_dir()); |
| EXPECT_TRUE(base::DirectoryExists(last_snapshot_dir())); |
| EXPECT_FALSE(base::DirectoryExists(previous_snapshot_dir())); |
| |
| EXPECT_CALL(*boot_lockbox_client(), Read(Eq(kLastSnapshotPublicKey), _)) |
| .WillOnce(Return(false)); |
| |
| bool last, success; |
| dbus_adaptor()->LoadSnapshot(kFakeAccountID, &last, &success); |
| EXPECT_FALSE(success); |
| } |
| |
| // Test failure when empty public key is stored in BootLockbox. |
| TEST_F(DBusAdaptorTest, LoadSnapshotEmptyPublicKey) { |
| CreateDir(user_directory()); |
| EXPECT_TRUE(base::DirectoryExists(user_directory())); |
| |
| CreateDir(last_snapshot_dir()); |
| EXPECT_TRUE(base::DirectoryExists(last_snapshot_dir())); |
| EXPECT_FALSE(base::DirectoryExists(previous_snapshot_dir())); |
| |
| EXPECT_CALL(*boot_lockbox_client(), Read(Eq(kLastSnapshotPublicKey), _)) |
| .WillOnce(Invoke([](const std::string& key, std::string* value) { |
| *value = ""; |
| return true; |
| })); |
| bool last, success; |
| dbus_adaptor()->LoadSnapshot(kFakeAccountID, &last, &success); |
| EXPECT_FALSE(success); |
| } |
| |
| // Test failure when snapshot verification fails. |
| TEST_F(DBusAdaptorTest, LoadSnapshotVerificationFailure) { |
| CreateDir(user_directory()); |
| EXPECT_TRUE(base::DirectoryExists(user_directory())); |
| |
| CreateDir(last_snapshot_dir()); |
| EXPECT_TRUE(base::DirectoryExists(last_snapshot_dir())); |
| EXPECT_FALSE(base::DirectoryExists(previous_snapshot_dir())); |
| |
| EXPECT_CALL(*boot_lockbox_client(), Read(Eq(kLastSnapshotPublicKey), _)) |
| .WillOnce(Invoke([](const std::string& key, std::string* value) { |
| *value = kFakeLastSnapshotPublicKey; |
| return true; |
| })); |
| bool last, success; |
| dbus_adaptor()->LoadSnapshot(kFakeAccountID, &last, &success); |
| EXPECT_FALSE(success); |
| } |
| |
| // Test failure when snapshot is loaded for unknown user. |
| TEST_F(DBusAdaptorTest, LoadSnapshotUnknownUser) { |
| // In this test the copied snapshot directory is verified against the origin |
| // snapshot directory. Inodes verification must be disabled, because the |
| // inode values are changed after copying. |
| // In production, it is not the case, because the directorys' integrity is |
| // verified against itself and inode values should persist. |
| dbus_adaptor()->set_inode_verification_enabled_for_testing( |
| false /* enabled */); |
| std::string expected_public_key_digest; |
| EXPECT_CALL(*boot_lockbox_client(), Store(Eq(kLastSnapshotPublicKey), _)) |
| .WillOnce(Invoke([&expected_public_key_digest]( |
| const std::string& key, const std::string& digest) { |
| expected_public_key_digest = digest; |
| return true; |
| })); |
| // Generate key pair. |
| EXPECT_TRUE(dbus_adaptor()->GenerateKeyPair()); |
| |
| // Create android-data directory. |
| CreateDir(android_data_dir()); |
| EXPECT_TRUE(base::DirectoryExists(android_data_dir())); |
| |
| // Take a snapshot. |
| EXPECT_TRUE(dbus_adaptor()->TakeSnapshot(kFakeAccountID)); |
| EXPECT_TRUE(base::DirectoryExists(last_snapshot_dir())); |
| // Verify taken snapshot with disabled inode verification. |
| EXPECT_TRUE(VerifyHash(last_snapshot_dir(), hash(kFakeAccountID), |
| expected_public_key_digest, |
| false /* inode_verification_enabled */)); |
| |
| // Load a snapshot directory to android-data for unknown user. |
| bool last, success; |
| dbus_adaptor()->LoadSnapshot(kFakeAccountID2, &last, &success); |
| EXPECT_FALSE(success); |
| |
| dbus_adaptor()->set_inode_verification_enabled_for_testing( |
| true /* enabled */); |
| } |
| |
| // Test basic success flow. |
| TEST_F(DBusAdaptorTest, LoadSnapshotSuccess) { |
| // In this test the copied snapshot directory is verified against the origin |
| // snapshot directory. Inodes verification must be disabled, because the |
| // inode values are changed after copying. |
| // In production, it is not the case, because the directorys' integrity is |
| // verified against itself and inode values should persist. |
| dbus_adaptor()->set_inode_verification_enabled_for_testing( |
| false /* enabled */); |
| std::string expected_public_key_digest; |
| EXPECT_CALL(*boot_lockbox_client(), Store(Eq(kLastSnapshotPublicKey), _)) |
| .WillOnce(Invoke([&expected_public_key_digest]( |
| const std::string& key, const std::string& digest) { |
| expected_public_key_digest = digest; |
| return true; |
| })); |
| // Generate key pair. |
| EXPECT_TRUE(dbus_adaptor()->GenerateKeyPair()); |
| |
| // Create android-data directory. |
| CreateDir(android_data_dir()); |
| EXPECT_TRUE(base::DirectoryExists(android_data_dir())); |
| |
| // Take a snapshot. |
| EXPECT_TRUE(dbus_adaptor()->TakeSnapshot(kFakeAccountID)); |
| EXPECT_TRUE(base::DirectoryExists(last_snapshot_dir())); |
| // Verify taken snapshot with disabled inode verification. |
| EXPECT_TRUE(VerifyHash(last_snapshot_dir(), hash(kFakeAccountID), |
| expected_public_key_digest, |
| false /* inode_verification_enabled */)); |
| |
| // Remove android-data directory to be able to load a snapshot. |
| EXPECT_TRUE(base::DeletePathRecursively(android_data_dir())); |
| EXPECT_FALSE(base::DirectoryExists(android_data_dir())); |
| |
| EXPECT_CALL(*boot_lockbox_client(), Read(Eq(kLastSnapshotPublicKey), _)) |
| .WillOnce(Invoke([expected_public_key_digest](const std::string& key, |
| std::string* value) { |
| *value = expected_public_key_digest; |
| return true; |
| })); |
| // Load a snapshot directory to android-data. |
| bool last, success; |
| dbus_adaptor()->LoadSnapshot(kFakeAccountID, &last, &success); |
| EXPECT_TRUE(success); |
| |
| // Verify the integrity of the last snapshot with disabld inode verification. |
| EXPECT_TRUE(last); |
| EXPECT_TRUE(VerifyHash(android_data_dir(), hash(kFakeAccountID), |
| expected_public_key_digest, |
| false /* inode_verification_enabled */)); |
| dbus_adaptor()->set_inode_verification_enabled_for_testing( |
| true /* enabled */); |
| } |
| |
| // Test success flow when loading of last snapshot fails, but loading of |
| // previous snapshot succeeds. |
| TEST_F(DBusAdaptorTest, LoadSnapshotPreviousSuccess) { |
| // In this test the copied snapshot directory is verified against the origin |
| // snapshot directory. Inodes verification must be disabled, because the |
| // inode values are changed after copying. |
| // In production, it is not the case, because the directorys' integrity is |
| // verified against itself and inode values should persist. |
| dbus_adaptor()->set_inode_verification_enabled_for_testing( |
| false /* enabled */); |
| std::string expected_public_key_digest = ""; |
| EXPECT_CALL(*boot_lockbox_client(), |
| Store(Eq(kLastSnapshotPublicKey), nEq(""))) |
| .Times(2) |
| .WillRepeatedly( |
| Invoke([&expected_public_key_digest](const std::string& key, |
| const std::string& digest) { |
| if (expected_public_key_digest.empty()) { |
| expected_public_key_digest = digest; |
| } |
| return true; |
| })); |
| // First time snapshot generating flow. |
| // Generate a key pair. |
| EXPECT_TRUE(dbus_adaptor()->GenerateKeyPair()); |
| |
| CreateDir(android_data_dir()); |
| EXPECT_TRUE(base::DirectoryExists(android_data_dir())); |
| |
| // Take android-data snapshot and name it as a last snapshot. |
| EXPECT_TRUE(dbus_adaptor()->TakeSnapshot(kFakeAccountID)); |
| EXPECT_TRUE(base::DirectoryExists(last_snapshot_dir())); |
| EXPECT_TRUE(VerifyHash(last_snapshot_dir(), hash(kFakeAccountID), |
| expected_public_key_digest, |
| false /* inode_verification_enabled */)); |
| |
| EXPECT_CALL(*boot_lockbox_client(), Read(Eq(kLastSnapshotPublicKey), _)) |
| .WillOnce(Invoke([expected_public_key_digest](const std::string& key, |
| std::string* value) { |
| *value = expected_public_key_digest; |
| return true; |
| })); |
| EXPECT_CALL(*boot_lockbox_client(), Store(Eq(kPreviousSnapshotPublicKey), _)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*boot_lockbox_client(), Store(Eq(kLastSnapshotPublicKey), Eq(""))) |
| .WillOnce(Return(true)); |
| // Second time snapshot generating flow. |
| // Previous snapshot has been generated during the first flow. |
| EXPECT_TRUE(dbus_adaptor()->GenerateKeyPair()); |
| EXPECT_TRUE(base::DirectoryExists(previous_snapshot_dir())); |
| |
| // Take a snapshot. |
| EXPECT_TRUE(base::DirectoryExists(android_data_dir())); |
| EXPECT_TRUE(dbus_adaptor()->TakeSnapshot(kFakeAccountID)); |
| |
| EXPECT_CALL(*boot_lockbox_client(), Read(Eq(kPreviousSnapshotPublicKey), _)) |
| .WillOnce(Invoke([expected_public_key_digest](const std::string& key, |
| std::string* value) { |
| *value = expected_public_key_digest; |
| return true; |
| })); |
| // Invalidate the last snapshot. |
| EXPECT_TRUE(base::DeletePathRecursively(last_snapshot_dir())); |
| // Remove android-data directory to be able to load a snapshot. |
| EXPECT_TRUE(base::DeletePathRecursively(android_data_dir())); |
| // Load the previous snapshot, because the last one is invalid. |
| bool last, success; |
| dbus_adaptor()->LoadSnapshot(kFakeAccountID, &last, &success); |
| EXPECT_TRUE(success); |
| EXPECT_FALSE(last); |
| EXPECT_TRUE(VerifyHash(android_data_dir(), hash(kFakeAccountID), |
| expected_public_key_digest, |
| false /* inode_verification_enabled */)); |
| dbus_adaptor()->set_inode_verification_enabled_for_testing( |
| true /* enabled */); |
| } |
| |
| } // namespace data_snapshotd |
| } // namespace arc |