| // 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 "login_manager/device_local_account_policy_service.h" |
| |
| #include <algorithm> |
| #include <memory> |
| |
| #include <base/compiler_specific.h> |
| #include <base/files/file_util.h> |
| #include <base/files/scoped_temp_dir.h> |
| #include <base/run_loop.h> |
| #include <brillo/cryptohome.h> |
| #include <brillo/message_loops/fake_message_loop.h> |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| #include "bindings/chrome_device_policy.pb.h" |
| #include "login_manager/mock_policy_key.h" |
| #include "login_manager/mock_policy_service.h" |
| #include "login_manager/mock_policy_store.h" |
| |
| namespace em = enterprise_management; |
| |
| using testing::Return; |
| using testing::StrictMock; |
| using testing::_; |
| |
| namespace login_manager { |
| |
| class DeviceLocalAccountPolicyServiceTest : public ::testing::Test { |
| public: |
| DeviceLocalAccountPolicyServiceTest() |
| : fake_account_("account@example.com"), salt_("salt") {} |
| |
| void SetUp() override { |
| fake_loop_.SetAsCurrent(); |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| brillo::cryptohome::home::SetSystemSalt(&salt_); |
| |
| fake_account_policy_path_ = |
| temp_dir_.path() |
| .Append(brillo::cryptohome::home::SanitizeUserName(fake_account_)) |
| .Append(DeviceLocalAccountPolicyService::kPolicyDir) |
| .Append(DeviceLocalAccountPolicyService::kPolicyFileName); |
| |
| em::PolicyFetchResponse policy_proto; |
| policy_proto.set_policy_data("policy-data"); |
| policy_proto.set_policy_data_signature("policy-data-signature"); |
| ASSERT_TRUE(policy_proto.SerializeToString(&policy_blob_)); |
| |
| service_.reset( |
| new DeviceLocalAccountPolicyService(temp_dir_.path(), &key_)); |
| } |
| |
| void SetupAccount() { |
| em::ChromeDeviceSettingsProto device_settings; |
| em::DeviceLocalAccountInfoProto* account = |
| device_settings.mutable_device_local_accounts()->add_account(); |
| account->set_type( |
| em::DeviceLocalAccountInfoProto::ACCOUNT_TYPE_PUBLIC_SESSION); |
| account->set_account_id(fake_account_); |
| service_->UpdateDeviceSettings(device_settings); |
| } |
| |
| void SetupKey() { |
| EXPECT_CALL(key_, PopulateFromDiskIfPossible()).Times(0); |
| EXPECT_CALL(key_, IsPopulated()).WillRepeatedly(Return(true)); |
| EXPECT_CALL(key_, Verify(_, _, _, _)).WillRepeatedly(Return(true)); |
| } |
| |
| protected: |
| const std::string fake_account_; |
| base::FilePath fake_account_policy_path_; |
| |
| std::string salt_; |
| std::string policy_blob_; |
| |
| brillo::FakeMessageLoop fake_loop_{nullptr}; |
| base::ScopedTempDir temp_dir_; |
| |
| MockPolicyKey key_; |
| |
| std::unique_ptr<DeviceLocalAccountPolicyService> service_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(DeviceLocalAccountPolicyServiceTest); |
| }; |
| |
| TEST_F(DeviceLocalAccountPolicyServiceTest, StoreInvalidAccount) { |
| EXPECT_FALSE( |
| service_->Store(fake_account_, |
| reinterpret_cast<const uint8_t*>(policy_blob_.c_str()), |
| policy_blob_.size(), |
| MockPolicyService::CreateExpectFailureCallback())); |
| fake_loop_.Run(); |
| EXPECT_FALSE(base::PathExists(fake_account_policy_path_)); |
| } |
| |
| TEST_F(DeviceLocalAccountPolicyServiceTest, StoreSuccess) { |
| SetupAccount(); |
| SetupKey(); |
| |
| EXPECT_TRUE( |
| service_->Store(fake_account_, |
| reinterpret_cast<const uint8_t*>(policy_blob_.c_str()), |
| policy_blob_.size(), |
| MockPolicyService::CreateExpectSuccessCallback())); |
| fake_loop_.Run(); |
| EXPECT_TRUE(base::PathExists(fake_account_policy_path_)); |
| } |
| |
| TEST_F(DeviceLocalAccountPolicyServiceTest, StoreBadPolicy) { |
| SetupAccount(); |
| SetupKey(); |
| |
| policy_blob_ = "bad!"; |
| |
| EXPECT_FALSE( |
| service_->Store(fake_account_, |
| reinterpret_cast<const uint8_t*>(policy_blob_.c_str()), |
| policy_blob_.size(), |
| MockPolicyService::CreateExpectFailureCallback())); |
| fake_loop_.Run(); |
| EXPECT_FALSE(base::PathExists(fake_account_policy_path_)); |
| } |
| |
| TEST_F(DeviceLocalAccountPolicyServiceTest, StoreBadSignature) { |
| SetupAccount(); |
| SetupKey(); |
| EXPECT_CALL(key_, Verify(_, _, _, _)).WillRepeatedly(Return(false)); |
| |
| EXPECT_FALSE( |
| service_->Store(fake_account_, |
| reinterpret_cast<const uint8_t*>(policy_blob_.c_str()), |
| policy_blob_.size(), |
| MockPolicyService::CreateExpectFailureCallback())); |
| fake_loop_.Run(); |
| EXPECT_FALSE(base::PathExists(fake_account_policy_path_)); |
| } |
| |
| TEST_F(DeviceLocalAccountPolicyServiceTest, StoreNoRotation) { |
| em::PolicyFetchResponse policy_proto; |
| policy_proto.set_policy_data("policy-data"); |
| policy_proto.set_policy_data_signature("policy-data-signature"); |
| policy_proto.set_new_public_key("new-public-key"); |
| policy_proto.set_new_public_key_signature("new-public-key-signature"); |
| ASSERT_TRUE(policy_proto.SerializeToString(&policy_blob_)); |
| |
| SetupAccount(); |
| SetupKey(); |
| |
| // No key modifications. |
| EXPECT_CALL(key_, Equals(_)).WillRepeatedly(Return(false)); |
| EXPECT_CALL(key_, PopulateFromBuffer(_)).Times(0); |
| EXPECT_CALL(key_, PopulateFromKeypair(_)).Times(0); |
| EXPECT_CALL(key_, Rotate(_, _)).Times(0); |
| EXPECT_CALL(key_, ClobberCompromisedKey(_)).Times(0); |
| |
| EXPECT_FALSE( |
| service_->Store(fake_account_, |
| reinterpret_cast<const uint8_t*>(policy_blob_.c_str()), |
| policy_blob_.size(), |
| MockPolicyService::CreateExpectFailureCallback())); |
| fake_loop_.Run(); |
| EXPECT_FALSE(base::PathExists(fake_account_policy_path_)); |
| } |
| |
| TEST_F(DeviceLocalAccountPolicyServiceTest, RetrieveInvalidAccount) { |
| SetupKey(); |
| |
| std::vector<uint8_t> policy_data; |
| EXPECT_FALSE(service_->Retrieve(fake_account_, &policy_data)); |
| EXPECT_TRUE(policy_data.empty()); |
| } |
| |
| TEST_F(DeviceLocalAccountPolicyServiceTest, RetrieveNoPolicy) { |
| SetupAccount(); |
| SetupKey(); |
| |
| std::vector<uint8_t> policy_data; |
| EXPECT_TRUE(service_->Retrieve(fake_account_, &policy_data)); |
| EXPECT_TRUE(policy_data.empty()); |
| } |
| |
| TEST_F(DeviceLocalAccountPolicyServiceTest, RetrieveSuccess) { |
| SetupAccount(); |
| SetupKey(); |
| |
| ASSERT_TRUE(base::CreateDirectory(fake_account_policy_path_.DirName())); |
| ASSERT_EQ(policy_blob_.size(), |
| base::WriteFile(fake_account_policy_path_, |
| policy_blob_.c_str(), |
| policy_blob_.size())); |
| |
| std::vector<uint8_t> policy_data; |
| EXPECT_TRUE(service_->Retrieve(fake_account_, &policy_data)); |
| EXPECT_FALSE(policy_data.empty()); |
| } |
| |
| TEST_F(DeviceLocalAccountPolicyServiceTest, PurgeStaleAccounts) { |
| SetupKey(); |
| |
| ASSERT_TRUE(base::WriteFile( |
| fake_account_policy_path_, policy_blob_.c_str(), policy_blob_.size())); |
| |
| em::ChromeDeviceSettingsProto device_settings; |
| service_->UpdateDeviceSettings(device_settings); |
| EXPECT_FALSE(base::PathExists(fake_account_policy_path_)); |
| } |
| |
| TEST_F(DeviceLocalAccountPolicyServiceTest, MigrateUppercaseDirs) { |
| const char* kDir1 = "356a192b7913b04c54574d18c28d46e6395428ab"; |
| const char* kDir2 = "DA4B9237BACCCDF19C0760CAB7AEC4A8359010B0"; |
| const char* kDir2Lower = "da4b9237bacccdf19c0760cab7aec4a8359010b0"; |
| const char* kUnrelated = "foobar"; |
| |
| base::FilePath fp1(temp_dir_.path().Append(kDir1)); |
| base::FilePath fp2(temp_dir_.path().Append(kDir2)); |
| base::FilePath fp2lower(temp_dir_.path().Append(kDir2Lower)); |
| base::FilePath fpunrel(temp_dir_.path().Append(kUnrelated)); |
| |
| EXPECT_TRUE(base::CreateDirectory(fp1)); |
| EXPECT_TRUE(base::CreateDirectory(fp2)); |
| EXPECT_TRUE(base::CreateDirectory(fpunrel)); |
| |
| EXPECT_TRUE(service_->MigrateUppercaseDirs()); |
| |
| EXPECT_TRUE(base::DirectoryExists(fp1)); |
| EXPECT_FALSE(base::DirectoryExists(fp2)); |
| EXPECT_TRUE(base::DirectoryExists(fp2lower)); |
| EXPECT_TRUE(base::DirectoryExists(fpunrel)); |
| } |
| |
| TEST_F(DeviceLocalAccountPolicyServiceTest, LegacyPublicSessionIdFallback) { |
| // Check that a legacy public session ID continues to work as long as the |
| // account_id / type fields are not present. |
| em::ChromeDeviceSettingsProto device_settings; |
| em::DeviceLocalAccountInfoProto* account = |
| device_settings.mutable_device_local_accounts()->add_account(); |
| account->set_deprecated_public_session_id(fake_account_); |
| service_->UpdateDeviceSettings(device_settings); |
| SetupKey(); |
| |
| EXPECT_TRUE( |
| service_->Store(fake_account_, |
| reinterpret_cast<const uint8_t*>(policy_blob_.c_str()), |
| policy_blob_.size(), |
| MockPolicyService::CreateExpectSuccessCallback())); |
| fake_loop_.Run(); |
| EXPECT_TRUE(base::PathExists(fake_account_policy_path_)); |
| |
| std::vector<uint8_t> policy_data; |
| EXPECT_TRUE(service_->Retrieve(fake_account_, &policy_data)); |
| |
| ASSERT_EQ(policy_blob_.size(), policy_data.size()); |
| EXPECT_TRUE(std::equal( |
| policy_blob_.begin(), policy_blob_.end(), policy_data.begin())); |
| } |
| |
| TEST_F(DeviceLocalAccountPolicyServiceTest, LegacyPublicSessionIdIgnored) { |
| // If there's a legacy public session ID and an account id / type pair, the |
| // former should get ignored. |
| const char kDeprecatedId[] = "deprecated"; |
| em::ChromeDeviceSettingsProto device_settings; |
| em::DeviceLocalAccountInfoProto* account = |
| device_settings.mutable_device_local_accounts()->add_account(); |
| account->set_deprecated_public_session_id(kDeprecatedId); |
| account->set_type( |
| em::DeviceLocalAccountInfoProto::ACCOUNT_TYPE_PUBLIC_SESSION); |
| account->set_account_id(fake_account_); |
| service_->UpdateDeviceSettings(device_settings); |
| SetupKey(); |
| |
| EXPECT_FALSE( |
| service_->Store(kDeprecatedId, |
| reinterpret_cast<const uint8_t*>(policy_blob_.c_str()), |
| policy_blob_.size(), |
| MockPolicyService::CreateExpectFailureCallback())); |
| fake_loop_.Run(); |
| EXPECT_FALSE(base::PathExists(fake_account_policy_path_)); |
| } |
| |
| } // namespace login_manager |