| // Copyright 2020 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/dbus/tpm_manager/tpm_manager_client.h" |
| |
| #include <utility> |
| |
| #include <google/protobuf/message_lite.h> |
| |
| #include "base/check_op.h" |
| #include "base/command_line.h" |
| #include "base/functional/bind.h" |
| #include "base/location.h" |
| #include "base/logging.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/observer_list.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "base/time/time.h" |
| #include "chromeos/dbus/constants/dbus_switches.h" |
| #include "chromeos/dbus/tpm_manager/fake_tpm_manager_client.h" |
| #include "dbus/bus.h" |
| #include "dbus/message.h" |
| #include "dbus/object_path.h" |
| #include "dbus/object_proxy.h" |
| #include "third_party/cros_system_api/dbus/tpm_manager/dbus-constants.h" |
| |
| namespace chromeos { |
| namespace { |
| |
| // An arbitrary timeout for taking ownership. |
| constexpr base::TimeDelta kTakeOwnershipTimeout = base::Seconds(80); |
| |
| TpmManagerClient* g_instance = nullptr; |
| |
| // Tries to parse a proto message from |response| into |proto|. |
| // Returns false if |response| is nullptr or the message cannot be parsed. |
| bool ParseProto(dbus::Response* response, |
| google::protobuf::MessageLite* proto) { |
| if (!response) { |
| LOG(ERROR) << "Failed to call tpm_managerd"; |
| return false; |
| } |
| |
| dbus::MessageReader reader(response); |
| if (!reader.PopArrayOfBytesAsProto(proto)) { |
| LOG(ERROR) << "Failed to parse response message from tpm_managerd"; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void OnSignalConnected(const std::string& interface_name, |
| const std::string& signal_name, |
| bool success) { |
| DCHECK_EQ(interface_name, ::tpm_manager::kTpmManagerInterface); |
| LOG_IF(DFATAL, !success) << "Failed to connect to D-Bus signal; interface: " |
| << interface_name << "; signal: " << signal_name; |
| } |
| |
| // "Real" implementation of TpmManagerClient talking to the TpmManager daemon |
| // on the Chrome OS side. |
| class TpmManagerClientImpl : public TpmManagerClient { |
| public: |
| TpmManagerClientImpl() = default; |
| ~TpmManagerClientImpl() override = default; |
| |
| // Not copyable or movable. |
| TpmManagerClientImpl(const TpmManagerClientImpl&) = delete; |
| TpmManagerClientImpl& operator=(const TpmManagerClientImpl&) = delete; |
| TpmManagerClientImpl(TpmManagerClientImpl&&) = delete; |
| TpmManagerClientImpl& operator=(TpmManagerClientImpl&&) = delete; |
| |
| // TpmManagerClient overrides: |
| void GetTpmNonsensitiveStatus( |
| const ::tpm_manager::GetTpmNonsensitiveStatusRequest& request, |
| GetTpmNonsensitiveStatusCallback callback) override { |
| CallProtoMethod(::tpm_manager::kGetTpmNonsensitiveStatus, request, |
| std::move(callback)); |
| } |
| void GetVersionInfo(const ::tpm_manager::GetVersionInfoRequest& request, |
| GetVersionInfoCallback callback) override { |
| CallProtoMethod(::tpm_manager::kGetVersionInfo, request, |
| std::move(callback)); |
| } |
| void GetSupportedFeatures( |
| const ::tpm_manager::GetSupportedFeaturesRequest& request, |
| GetSupportedFeaturesCallback callback) override { |
| CallProtoMethod(::tpm_manager::kGetSupportedFeatures, request, |
| std::move(callback)); |
| } |
| void GetDictionaryAttackInfo( |
| const ::tpm_manager::GetDictionaryAttackInfoRequest& request, |
| GetDictionaryAttackInfoCallback callback) override { |
| CallProtoMethod(::tpm_manager::kGetDictionaryAttackInfo, request, |
| std::move(callback)); |
| } |
| void TakeOwnership(const ::tpm_manager::TakeOwnershipRequest& request, |
| TakeOwnershipCallback callback) override { |
| // Use a longer timeout for TPM ownership operation. |
| CallProtoMethodWithTimeout(::tpm_manager::kTakeOwnership, |
| kTakeOwnershipTimeout.InMilliseconds(), request, |
| std::move(callback)); |
| } |
| void ClearStoredOwnerPassword( |
| const ::tpm_manager::ClearStoredOwnerPasswordRequest& request, |
| ClearStoredOwnerPasswordCallback callback) override { |
| CallProtoMethod(::tpm_manager::kClearStoredOwnerPassword, request, |
| std::move(callback)); |
| } |
| void ClearTpm(const ::tpm_manager::ClearTpmRequest& request, |
| ClearTpmCallback callback) override { |
| CallProtoMethod(::tpm_manager::kClearTpm, request, std::move(callback)); |
| } |
| |
| void AddObserver(Observer* observer) override { |
| observer_list_.AddObserver(observer); |
| } |
| void RemoveObserver(Observer* observer) override { |
| observer_list_.RemoveObserver(observer); |
| } |
| |
| void Init(dbus::Bus* bus) { |
| proxy_ = bus->GetObjectProxy( |
| ::tpm_manager::kTpmManagerServiceName, |
| dbus::ObjectPath(::tpm_manager::kTpmManagerServicePath)); |
| ConnectToOwnershipTakenSignal(); |
| } |
| |
| private: |
| TestInterface* GetTestInterface() override { return nullptr; } |
| |
| // Calls tpm_managerd's |method_name| method, passing in |request| as input |
| // with |timeout_ms|. Once the (asynchronous) call finishes, |callback| is |
| // called with the response proto. |
| template <typename RequestType, typename ReplyType> |
| void CallProtoMethodWithTimeout( |
| const char* method_name, |
| int timeout_ms, |
| const RequestType& request, |
| base::OnceCallback<void(const ReplyType&)> callback) { |
| dbus::MethodCall method_call(::tpm_manager::kTpmManagerInterface, |
| method_name); |
| dbus::MessageWriter writer(&method_call); |
| if (!writer.AppendProtoAsArrayOfBytes(request)) { |
| ReplyType reply; |
| reply.set_status(::tpm_manager::STATUS_DBUS_ERROR); |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(callback), reply)); |
| return; |
| } |
| // Bind with the weak pointer of |this| so the response is not |
| // handled once |this| is already destroyed. |
| proxy_->CallMethod( |
| &method_call, timeout_ms, |
| base::BindOnce(&TpmManagerClientImpl::HandleResponse<ReplyType>, |
| weak_factory_.GetWeakPtr(), std::move(callback))); |
| } |
| |
| // Calls tpm_managerd's |method_name| method, passing in |request| as input |
| // with the default timeout. Once the (asynchronous) call finishes, |callback| |
| // is called with the response proto. |
| template <typename RequestType, typename ReplyType> |
| void CallProtoMethod(const char* method_name, |
| const RequestType& request, |
| base::OnceCallback<void(const ReplyType&)> callback) { |
| CallProtoMethodWithTimeout(method_name, |
| dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, request, |
| std::move(callback)); |
| } |
| |
| // Parses the response proto message from |response| and calls |callback| with |
| // the decoded message. Calls |callback| with an |STATUS_DBUS_ERROR| message |
| // on error, including timeout. |
| template <typename ReplyType> |
| void HandleResponse(base::OnceCallback<void(const ReplyType&)> callback, |
| dbus::Response* response) { |
| ReplyType reply_proto; |
| if (!ParseProto(response, &reply_proto)) |
| reply_proto.set_status(::tpm_manager::STATUS_DBUS_ERROR); |
| std::move(callback).Run(reply_proto); |
| } |
| |
| // Called when receiving ownership taken signal. |
| void OnOwnershipTakenSignal(dbus::Signal*) { |
| for (auto& observer : observer_list_) { |
| observer.OnOwnershipTaken(); |
| } |
| } |
| |
| // Connects to ownership taken signal. |
| void ConnectToOwnershipTakenSignal() { |
| proxy_->ConnectToSignal( |
| ::tpm_manager::kTpmManagerInterface, |
| ::tpm_manager::kOwnershipTakenSignal, |
| base::BindRepeating(&TpmManagerClientImpl::OnOwnershipTakenSignal, |
| weak_factory_.GetWeakPtr()), |
| base::BindOnce(&OnSignalConnected)); |
| } |
| |
| // D-Bus proxy for the TpmManager daemon, not owned. |
| raw_ptr<dbus::ObjectProxy, LeakedDanglingUntriaged> proxy_ = nullptr; |
| |
| // The observer list of ownership taken signal. |
| base::ObserverList<Observer> observer_list_; |
| |
| base::WeakPtrFactory<TpmManagerClientImpl> weak_factory_{this}; |
| }; |
| |
| } // namespace |
| |
| TpmManagerClient::TpmManagerClient() { |
| CHECK(!g_instance); |
| g_instance = this; |
| } |
| |
| TpmManagerClient::~TpmManagerClient() { |
| CHECK_EQ(this, g_instance); |
| g_instance = nullptr; |
| } |
| |
| // static |
| void TpmManagerClient::Initialize(dbus::Bus* bus) { |
| CHECK(bus); |
| (new TpmManagerClientImpl())->Init(bus); |
| } |
| |
| // static |
| void TpmManagerClient::InitializeFake() { |
| // Do not create a new instance if it was initialized early in a browser test |
| // (for early setup calls dependent on TpmManagerClient). |
| if (!FakeTpmManagerClient::Get()) |
| new FakeTpmManagerClient(); |
| } |
| |
| // static |
| void TpmManagerClient::Shutdown() { |
| CHECK(g_instance); |
| delete g_instance; |
| // The destructor resets |g_instance|. |
| DCHECK(!g_instance); |
| } |
| |
| // static |
| TpmManagerClient* TpmManagerClient::Get() { |
| return g_instance; |
| } |
| |
| } // namespace chromeos |