| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chromeos/ash/components/dbus/userdataauth/install_attributes_client.h" |
| |
| #include <string> |
| #include <utility> |
| |
| #include "base/logging.h" |
| #include "base/test/task_environment.h" |
| #include "chromeos/ash/components/dbus/cryptohome/UserDataAuth.pb.h" |
| #include "dbus/mock_bus.h" |
| #include "dbus/mock_object_proxy.h" |
| #include "dbus/object_path.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/cros_system_api/dbus/cryptohome/dbus-constants.h" |
| |
| using ::testing::_; |
| using ::testing::Invoke; |
| using ::testing::Return; |
| using ::testing::SaveArg; |
| |
| namespace ash { |
| |
| namespace { |
| |
| // Runs |callback| with |response|. Needed due to ResponseCallback expecting a |
| // bare pointer rather than an std::unique_ptr. |
| void RunResponseCallback(dbus::ObjectProxy::ResponseCallback callback, |
| std::unique_ptr<dbus::Response> response) { |
| std::move(callback).Run(response.get()); |
| } |
| |
| bool ProtobufEquals(const google::protobuf::MessageLite& a, |
| const google::protobuf::MessageLite& b) { |
| std::string a_serialized, b_serialized; |
| a.SerializeToString(&a_serialized); |
| b.SerializeToString(&b_serialized); |
| return a_serialized == b_serialized; |
| } |
| |
| // FakeTaskRunner will run all tasks posted to it immediately in the PostTask() |
| // call. This class is a helper to ensure that BlockingMethodCaller would work |
| // correctly in the unit test. Note that Mock is not used because |
| // SingleThreadTaskRunner is refcounted and it doesn't play well with Mock. |
| class FakeTaskRunner : public base::SingleThreadTaskRunner { |
| public: |
| // Yes, this task runner runs everything in sequence. |
| bool RunsTasksInCurrentSequence() const override { return true; } |
| |
| // Run all tasks immediately, no delay is allowed. |
| bool PostDelayedTask(const base::Location& location, |
| base::OnceClosure closure, |
| base::TimeDelta delta) override { |
| // Since we are running it now, we can't accept any delay. |
| CHECK(delta.is_zero()); |
| std::move(closure).Run(); |
| return true; |
| } |
| |
| // Non nestable task not supported. |
| bool PostNonNestableDelayedTask(const base::Location& location, |
| base::OnceClosure closure, |
| base::TimeDelta delta) override { |
| // Can't run non-nested stuff. |
| NOTIMPLEMENTED(); |
| return false; |
| } |
| |
| private: |
| // For reference counting. |
| ~FakeTaskRunner() override {} |
| }; |
| |
| // Create a callback that would copy the input argument passed to it into |out|. |
| // This is used mostly to create a callback that would catch the reply from |
| // dbus. |
| template <typename T> |
| base::OnceCallback<void(T)> CreateCopyCallback(T* out) { |
| return base::BindOnce([](T* out, T result) { *out = result; }, out); |
| } |
| |
| } // namespace |
| |
| class InstallAttributesClientTest : public testing::Test { |
| public: |
| InstallAttributesClientTest() = default; |
| ~InstallAttributesClientTest() override = default; |
| |
| void SetUp() override { |
| dbus::Bus::Options options; |
| options.bus_type = dbus::Bus::SYSTEM; |
| bus_ = new dbus::MockBus(options); |
| |
| dbus::ObjectPath userdataauth_object_path = |
| dbus::ObjectPath(::user_data_auth::kUserDataAuthServicePath); |
| proxy_ = new dbus::MockObjectProxy( |
| bus_.get(), ::user_data_auth::kUserDataAuthServiceName, |
| userdataauth_object_path); |
| |
| // Makes sure `GetObjectProxy()` is called with the correct service name and |
| // path. |
| EXPECT_CALL(*bus_.get(), |
| GetObjectProxy(::user_data_auth::kUserDataAuthServiceName, |
| userdataauth_object_path)) |
| .WillRepeatedly(Return(proxy_.get())); |
| |
| EXPECT_CALL(*proxy_.get(), DoCallMethod(_, _, _)) |
| .WillRepeatedly( |
| Invoke(this, &InstallAttributesClientTest::OnCallMethod)); |
| EXPECT_CALL(*proxy_.get(), CallMethodAndBlockWithErrorDetails(_, _, _)) |
| .WillRepeatedly( |
| Invoke(this, &InstallAttributesClientTest::OnBlockingCallMethod)); |
| |
| InstallAttributesClient::Initialize(bus_.get()); |
| |
| // Execute callbacks posted by `client_->Init()`. |
| base::RunLoop().RunUntilIdle(); |
| |
| client_ = InstallAttributesClient::Get(); |
| } |
| |
| void TearDown() override { InstallAttributesClient::Shutdown(); } |
| |
| protected: |
| base::test::SingleThreadTaskEnvironment task_environment_; |
| |
| // Mock bus and proxy for simulating calls. |
| scoped_refptr<dbus::MockBus> bus_; |
| scoped_refptr<dbus::MockObjectProxy> proxy_; |
| |
| // Convenience pointer to the global instance. |
| InstallAttributesClient* client_; |
| |
| // The expected replies to the respective D-Bus calls. |
| ::user_data_auth::InstallAttributesGetReply |
| expected_install_attributes_get_reply_; |
| ::user_data_auth::InstallAttributesSetReply |
| expected_install_attributes_set_reply_; |
| ::user_data_auth::InstallAttributesFinalizeReply |
| expected_install_attributes_finalize_reply_; |
| ::user_data_auth::InstallAttributesGetStatusReply |
| expected_install_attributes_get_status_reply_; |
| ::user_data_auth::RemoveFirmwareManagementParametersReply |
| expected_remove_firmware_management_parameters_reply_; |
| ::user_data_auth::SetFirmwareManagementParametersReply |
| expected_set_firmware_management_parameters_reply_; |
| ::user_data_auth::GetFirmwareManagementParametersReply |
| expected_get_firmware_management_parameters_reply_; |
| |
| // The expected replies to the respective blocking D-Bus calls. |
| ::user_data_auth::InstallAttributesGetReply |
| expected_blocking_install_attributes_get_reply_; |
| ::user_data_auth::InstallAttributesSetReply |
| expected_blocking_install_attributes_set_reply_; |
| ::user_data_auth::InstallAttributesFinalizeReply |
| expected_blocking_install_attributes_finalize_reply_; |
| ::user_data_auth::InstallAttributesGetStatusReply |
| expected_blocking_install_attributes_get_status_reply_; |
| |
| // When it is set `true`, an invalid array of bytes that cannot be parsed will |
| // be the response. |
| bool shall_message_parsing_fail_ = false; |
| |
| private: |
| // Handles calls to |proxy_|'s `CallMethod()`. |
| void OnCallMethod(dbus::MethodCall* method_call, |
| int timeout_ms, |
| dbus::ObjectProxy::ResponseCallback* callback) { |
| std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty()); |
| dbus::MessageWriter writer(response.get()); |
| if (shall_message_parsing_fail_) { |
| // 0x02 => Field 0, Type String |
| // (0xFF)*6 => Varint, the size of the string, it is not terminated and is |
| // a very large value so the parsing will fail. |
| constexpr uint8_t invalid_protobuf[] = {0x02, 0xFF, 0xFF, 0xFF, |
| 0xFF, 0xFF, 0xFF}; |
| writer.AppendArrayOfBytes(invalid_protobuf, sizeof(invalid_protobuf)); |
| } else if (method_call->GetMember() == |
| ::user_data_auth::kInstallAttributesGet) { |
| writer.AppendProtoAsArrayOfBytes(expected_install_attributes_get_reply_); |
| } else if (method_call->GetMember() == |
| ::user_data_auth::kInstallAttributesFinalize) { |
| writer.AppendProtoAsArrayOfBytes( |
| expected_install_attributes_finalize_reply_); |
| } else if (method_call->GetMember() == |
| ::user_data_auth::kInstallAttributesGetStatus) { |
| writer.AppendProtoAsArrayOfBytes( |
| expected_install_attributes_get_status_reply_); |
| } else if (method_call->GetMember() == |
| ::user_data_auth::kRemoveFirmwareManagementParameters) { |
| writer.AppendProtoAsArrayOfBytes( |
| expected_remove_firmware_management_parameters_reply_); |
| } else if (method_call->GetMember() == |
| ::user_data_auth::kSetFirmwareManagementParameters) { |
| writer.AppendProtoAsArrayOfBytes( |
| expected_set_firmware_management_parameters_reply_); |
| } else if (method_call->GetMember() == |
| ::user_data_auth::kGetFirmwareManagementParameters) { |
| writer.AppendProtoAsArrayOfBytes( |
| expected_get_firmware_management_parameters_reply_); |
| } else { |
| ASSERT_FALSE(true) << "Unrecognized member: " << method_call->GetMember(); |
| } |
| task_environment_.GetMainThreadTaskRunner()->PostTask( |
| FROM_HERE, base::BindOnce(RunResponseCallback, std::move(*callback), |
| std::move(response))); |
| } |
| |
| // Handles blocking call to |proxy_|'s `CallMethodAndBlockWithErrorDetails`. |
| std::unique_ptr<dbus::Response> OnBlockingCallMethod( |
| dbus::MethodCall* method_call, |
| int timeout_ms, |
| dbus::ScopedDBusError* error) { |
| std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty()); |
| dbus::MessageWriter writer(response.get()); |
| if (shall_message_parsing_fail_) { |
| // 0x02 => Field 0, Type String |
| // (0xFF)*6 => Varint, the size of the string, it is not terminated and is |
| // a very large value so the parsing will fail. |
| constexpr uint8_t invalid_protobuf[] = {0x02, 0xFF, 0xFF, 0xFF, |
| 0xFF, 0xFF, 0xFF}; |
| writer.AppendArrayOfBytes(invalid_protobuf, sizeof(invalid_protobuf)); |
| } else if (method_call->GetMember() == |
| ::user_data_auth::kInstallAttributesGet) { |
| writer.AppendProtoAsArrayOfBytes( |
| expected_blocking_install_attributes_get_reply_); |
| } else if (method_call->GetMember() == |
| ::user_data_auth::kInstallAttributesSet) { |
| writer.AppendProtoAsArrayOfBytes( |
| expected_blocking_install_attributes_set_reply_); |
| } else if (method_call->GetMember() == |
| ::user_data_auth::kInstallAttributesFinalize) { |
| writer.AppendProtoAsArrayOfBytes( |
| expected_blocking_install_attributes_finalize_reply_); |
| } else if (method_call->GetMember() == |
| ::user_data_auth::kInstallAttributesGetStatus) { |
| writer.AppendProtoAsArrayOfBytes( |
| expected_blocking_install_attributes_get_status_reply_); |
| } else { |
| LOG(FATAL) << "Unrecognized member: " << method_call->GetMember(); |
| return nullptr; |
| } |
| return response; |
| } |
| }; |
| |
| TEST_F(InstallAttributesClientTest, InstallAttributesGet) { |
| expected_install_attributes_get_reply_.set_error( |
| user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_TPM_DEFEND_LOCK); |
| absl::optional<::user_data_auth::InstallAttributesGetReply> result_reply; |
| |
| client_->InstallAttributesGet(::user_data_auth::InstallAttributesGetRequest(), |
| CreateCopyCallback(&result_reply)); |
| base::RunLoop().RunUntilIdle(); |
| ASSERT_NE(result_reply, absl::nullopt); |
| EXPECT_TRUE(ProtobufEquals(result_reply.value(), |
| expected_install_attributes_get_reply_)); |
| } |
| |
| TEST_F(InstallAttributesClientTest, InstallAttributesGetInvalidProtobuf) { |
| shall_message_parsing_fail_ = true; |
| absl::optional<::user_data_auth::InstallAttributesGetReply> result_reply = |
| ::user_data_auth::InstallAttributesGetReply(); |
| |
| client_->InstallAttributesGet(::user_data_auth::InstallAttributesGetRequest(), |
| CreateCopyCallback(&result_reply)); |
| base::RunLoop().RunUntilIdle(); |
| ASSERT_EQ(result_reply, absl::nullopt); |
| } |
| |
| TEST_F(InstallAttributesClientTest, InstallAttributesFinalize) { |
| expected_install_attributes_finalize_reply_.set_error( |
| user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_TPM_DEFEND_LOCK); |
| absl::optional<::user_data_auth::InstallAttributesFinalizeReply> result_reply; |
| |
| client_->InstallAttributesFinalize( |
| ::user_data_auth::InstallAttributesFinalizeRequest(), |
| CreateCopyCallback(&result_reply)); |
| base::RunLoop().RunUntilIdle(); |
| ASSERT_NE(result_reply, absl::nullopt); |
| EXPECT_TRUE(ProtobufEquals(result_reply.value(), |
| expected_install_attributes_finalize_reply_)); |
| } |
| |
| TEST_F(InstallAttributesClientTest, InstallAttributesGetStatus) { |
| expected_install_attributes_get_status_reply_.set_error( |
| user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_TPM_DEFEND_LOCK); |
| absl::optional<::user_data_auth::InstallAttributesGetStatusReply> |
| result_reply; |
| |
| client_->InstallAttributesGetStatus( |
| ::user_data_auth::InstallAttributesGetStatusRequest(), |
| CreateCopyCallback(&result_reply)); |
| base::RunLoop().RunUntilIdle(); |
| ASSERT_NE(result_reply, absl::nullopt); |
| EXPECT_TRUE(ProtobufEquals(result_reply.value(), |
| expected_install_attributes_get_status_reply_)); |
| } |
| |
| TEST_F(InstallAttributesClientTest, RemoveFirmwareManagementParameters) { |
| expected_remove_firmware_management_parameters_reply_.set_error( |
| user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_TPM_DEFEND_LOCK); |
| absl::optional<::user_data_auth::RemoveFirmwareManagementParametersReply> |
| result_reply; |
| |
| client_->RemoveFirmwareManagementParameters( |
| ::user_data_auth::RemoveFirmwareManagementParametersRequest(), |
| CreateCopyCallback(&result_reply)); |
| base::RunLoop().RunUntilIdle(); |
| ASSERT_NE(result_reply, absl::nullopt); |
| EXPECT_TRUE( |
| ProtobufEquals(result_reply.value(), |
| expected_remove_firmware_management_parameters_reply_)); |
| } |
| |
| TEST_F(InstallAttributesClientTest, SetFirmwareManagementParameters) { |
| expected_set_firmware_management_parameters_reply_.set_error( |
| user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_TPM_DEFEND_LOCK); |
| absl::optional<::user_data_auth::SetFirmwareManagementParametersReply> |
| result_reply; |
| |
| client_->SetFirmwareManagementParameters( |
| ::user_data_auth::SetFirmwareManagementParametersRequest(), |
| CreateCopyCallback(&result_reply)); |
| base::RunLoop().RunUntilIdle(); |
| ASSERT_NE(result_reply, absl::nullopt); |
| EXPECT_TRUE( |
| ProtobufEquals(result_reply.value(), |
| expected_set_firmware_management_parameters_reply_)); |
| } |
| |
| TEST_F(InstallAttributesClientTest, GetFirmwareManagementParameters) { |
| expected_set_firmware_management_parameters_reply_.set_error( |
| user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_TPM_DEFEND_LOCK); |
| absl::optional<::user_data_auth::GetFirmwareManagementParametersReply> |
| result_reply; |
| |
| client_->GetFirmwareManagementParameters( |
| ::user_data_auth::GetFirmwareManagementParametersRequest(), |
| CreateCopyCallback(&result_reply)); |
| base::RunLoop().RunUntilIdle(); |
| ASSERT_NE(result_reply, absl::nullopt); |
| EXPECT_TRUE( |
| ProtobufEquals(result_reply.value(), |
| expected_get_firmware_management_parameters_reply_)); |
| } |
| |
| TEST_F(InstallAttributesClientTest, BlockingInstallAttributesGet) { |
| expected_blocking_install_attributes_get_reply_.set_error( |
| user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_TPM_DEFEND_LOCK); |
| absl::optional<::user_data_auth::InstallAttributesGetReply> result_reply; |
| |
| scoped_refptr<FakeTaskRunner> runner = new FakeTaskRunner; |
| EXPECT_CALL(*bus_.get(), GetDBusTaskRunner()) |
| .WillRepeatedly(Return(runner.get())); |
| |
| result_reply = client_->BlockingInstallAttributesGet( |
| ::user_data_auth::InstallAttributesGetRequest()); |
| |
| ASSERT_NE(result_reply, absl::nullopt); |
| EXPECT_TRUE(ProtobufEquals(result_reply.value(), |
| expected_blocking_install_attributes_get_reply_)); |
| } |
| |
| TEST_F(InstallAttributesClientTest, |
| BlockingInstallAttributesGetInvalidProtobuf) { |
| shall_message_parsing_fail_ = true; |
| absl::optional<::user_data_auth::InstallAttributesGetReply> result_reply = |
| ::user_data_auth::InstallAttributesGetReply(); |
| |
| scoped_refptr<FakeTaskRunner> runner = new FakeTaskRunner; |
| EXPECT_CALL(*bus_.get(), GetDBusTaskRunner()) |
| .WillRepeatedly(Return(runner.get())); |
| |
| result_reply = client_->BlockingInstallAttributesGet( |
| ::user_data_auth::InstallAttributesGetRequest()); |
| |
| EXPECT_EQ(result_reply, absl::nullopt); |
| } |
| |
| TEST_F(InstallAttributesClientTest, BlockingInstallAttributesSet) { |
| expected_blocking_install_attributes_set_reply_.set_error( |
| user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_TPM_DEFEND_LOCK); |
| absl::optional<::user_data_auth::InstallAttributesSetReply> result_reply; |
| |
| scoped_refptr<FakeTaskRunner> runner = new FakeTaskRunner; |
| EXPECT_CALL(*bus_.get(), GetDBusTaskRunner()) |
| .WillRepeatedly(Return(runner.get())); |
| |
| result_reply = client_->BlockingInstallAttributesSet( |
| ::user_data_auth::InstallAttributesSetRequest()); |
| |
| ASSERT_NE(result_reply, absl::nullopt); |
| EXPECT_TRUE(ProtobufEquals(result_reply.value(), |
| expected_blocking_install_attributes_set_reply_)); |
| } |
| |
| TEST_F(InstallAttributesClientTest, BlockingInstallAttributesFinalize) { |
| expected_blocking_install_attributes_finalize_reply_.set_error( |
| user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_TPM_DEFEND_LOCK); |
| absl::optional<::user_data_auth::InstallAttributesFinalizeReply> result_reply; |
| |
| scoped_refptr<FakeTaskRunner> runner = new FakeTaskRunner; |
| EXPECT_CALL(*bus_.get(), GetDBusTaskRunner()) |
| .WillRepeatedly(Return(runner.get())); |
| |
| result_reply = client_->BlockingInstallAttributesFinalize( |
| ::user_data_auth::InstallAttributesFinalizeRequest()); |
| |
| ASSERT_NE(result_reply, absl::nullopt); |
| EXPECT_TRUE( |
| ProtobufEquals(result_reply.value(), |
| expected_blocking_install_attributes_finalize_reply_)); |
| } |
| |
| TEST_F(InstallAttributesClientTest, BlockingInstallAttributesGetStatus) { |
| expected_blocking_install_attributes_get_status_reply_.set_error( |
| user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_TPM_DEFEND_LOCK); |
| absl::optional<::user_data_auth::InstallAttributesGetStatusReply> |
| result_reply; |
| |
| scoped_refptr<FakeTaskRunner> runner = new FakeTaskRunner; |
| EXPECT_CALL(*bus_.get(), GetDBusTaskRunner()) |
| .WillRepeatedly(Return(runner.get())); |
| |
| result_reply = client_->BlockingInstallAttributesGetStatus( |
| ::user_data_auth::InstallAttributesGetStatusRequest()); |
| |
| ASSERT_NE(result_reply, absl::nullopt); |
| EXPECT_TRUE( |
| ProtobufEquals(result_reply.value(), |
| expected_blocking_install_attributes_get_status_reply_)); |
| } |
| |
| } // namespace ash |