| // Copyright 2016 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "remoting/host/security_key/security_key_ipc_client.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "base/functional/bind.h" |
| #include "base/functional/callback.h" |
| #include "base/logging.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "build/build_config.h" |
| #include "remoting/base/logging.h" |
| #include "remoting/host/chromoting_host_services_client.h" |
| #include "remoting/host/security_key/security_key_ipc_constants.h" |
| |
| #if BUILDFLAG(IS_WIN) |
| #include <Windows.h> |
| #endif |
| |
| namespace remoting { |
| |
| SecurityKeyIpcClient::SecurityKeyIpcClient() |
| : SecurityKeyIpcClient(std::make_unique<ChromotingHostServicesClient>()) {} |
| |
| SecurityKeyIpcClient::SecurityKeyIpcClient( |
| std::unique_ptr<ChromotingHostServicesProvider> service_provider) |
| : service_provider_(std::move(service_provider)) {} |
| |
| SecurityKeyIpcClient::~SecurityKeyIpcClient() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| } |
| |
| bool SecurityKeyIpcClient::CheckForSecurityKeyIpcServerChannel() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| return service_provider_->GetSessionServices() != nullptr; |
| } |
| |
| void SecurityKeyIpcClient::EstablishIpcConnection( |
| ConnectedCallback connected_callback, |
| base::OnceClosure connection_error_callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(connected_callback); |
| DCHECK(connection_error_callback); |
| DCHECK(!security_key_forwarder_.is_bound()); |
| |
| connected_callback_ = std::move(connected_callback); |
| connection_error_callback_ = std::move(connection_error_callback); |
| |
| ConnectToIpcChannel(); |
| } |
| |
| bool SecurityKeyIpcClient::SendSecurityKeyRequest( |
| const std::string& request_payload, |
| ResponseCallback response_callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!request_payload.empty()); |
| DCHECK(response_callback); |
| |
| if (!security_key_forwarder_.is_bound() || |
| !security_key_forwarder_.is_connected()) { |
| LOG(ERROR) << "Request made before IPC connection was established."; |
| return false; |
| } |
| |
| if (response_callback_) { |
| LOG(ERROR) |
| << "Request made while waiting for a response to a previous request."; |
| return false; |
| } |
| |
| response_callback_ = std::move(response_callback); |
| security_key_forwarder_->OnSecurityKeyRequest( |
| request_payload, |
| base::BindOnce(&SecurityKeyIpcClient::OnSecurityKeyResponse, |
| base::Unretained(this))); |
| |
| return true; |
| } |
| |
| void SecurityKeyIpcClient::CloseIpcConnection() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| HOST_LOG << "IPC connection closed."; |
| security_key_forwarder_.reset(); |
| } |
| |
| void SecurityKeyIpcClient::OnQueryVersionResult(uint32_t unused_version) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| HOST_LOG << "IPC channel connected."; |
| std::move(connected_callback_).Run(); |
| } |
| |
| void SecurityKeyIpcClient::OnChannelError() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| LOG(ERROR) << "IPC channel error."; |
| security_key_forwarder_.reset(); |
| if (connection_error_callback_) { |
| std::move(connection_error_callback_).Run(); |
| } |
| } |
| |
| void SecurityKeyIpcClient::OnSecurityKeyResponse( |
| const std::string& response_data) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| if (!response_data.empty()) { |
| std::move(response_callback_).Run(response_data); |
| } else { |
| LOG(ERROR) << "Invalid response received"; |
| if (connection_error_callback_) { |
| std::move(connection_error_callback_).Run(); |
| } |
| } |
| } |
| |
| void SecurityKeyIpcClient::ConnectToIpcChannel() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| // Verify that any existing IPC connection has been closed. |
| CloseIpcConnection(); |
| |
| if (!CheckForSecurityKeyIpcServerChannel()) { |
| LOG(ERROR) << "Invalid channel handle."; |
| OnChannelError(); |
| return; |
| } |
| auto disconnect_handler = base::BindRepeating( |
| &SecurityKeyIpcClient::OnChannelError, base::Unretained(this)); |
| // There is a bug in Mojo, such that if the host rejects binding of session |
| // services, there is a chance that binding of SecurityKeyForwarder appears to |
| // be successful and the disconnect handler of `remote_` is never called, so |
| // `remote_` will remain invalid forever. |
| // The disconnect handler of session services is still called, so we set a |
| // disconnect handler on it. |
| // See https://crbug.com/425759818#comment8 for more context. |
| service_provider_->set_disconnect_handler(disconnect_handler); |
| service_provider_->GetSessionServices()->BindSecurityKeyForwarder( |
| security_key_forwarder_.BindNewPipeAndPassReceiver()); |
| security_key_forwarder_.set_disconnect_handler(disconnect_handler); |
| // This is to determine if the peer binding is successful. If the connection |
| // is disconnected before OnQueryVersionResult() is called, it means the |
| // server has rejected the binding request. |
| security_key_forwarder_.QueryVersion(base::BindOnce( |
| &SecurityKeyIpcClient::OnQueryVersionResult, base::Unretained(this))); |
| } |
| |
| } // namespace remoting |