123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614 |
- // Copyright 2013 The Chromium 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 "remoting/host/setup/me2me_native_messaging_host.h"
- #include <cstdint>
- #include <memory>
- #include <string>
- #include <utility>
- #include "base/bind.h"
- #include "base/callback.h"
- #include "base/command_line.h"
- #include "base/json/json_reader.h"
- #include "base/json/json_writer.h"
- #include "base/logging.h"
- #include "base/strings/stringize_macros.h"
- #include "base/task/single_thread_task_runner.h"
- #include "base/time/time.h"
- #include "base/values.h"
- #include "build/build_config.h"
- #include "google_apis/gaia/gaia_oauth_client.h"
- #include "google_apis/google_api_keys.h"
- #include "net/base/network_interfaces.h"
- #include "remoting/base/auto_thread_task_runner.h"
- #include "remoting/base/oauth_client.h"
- #include "remoting/base/rsa_key_pair.h"
- #include "remoting/host/chromoting_host_context.h"
- #include "remoting/host/native_messaging/log_message_handler.h"
- #include "remoting/host/pin_hash.h"
- #include "remoting/protocol/pairing_registry.h"
- #include "third_party/abseil-cpp/absl/types/optional.h"
- #if BUILDFLAG(IS_WIN)
- #include "remoting/host/win/elevated_native_messaging_host.h"
- #endif // BUILDFLAG(IS_WIN)
- namespace {
- #if BUILDFLAG(IS_WIN)
- const int kElevatedHostTimeoutSeconds = 300;
- #endif // BUILDFLAG(IS_WIN)
- // Features supported in addition to the base protocol.
- const char* kSupportedFeatures[] = {
- "pairingRegistry",
- "oauthClient",
- "getRefreshTokenFromAuthCode",
- #if BUILDFLAG(IS_APPLE)
- "it2mePermissionCheck",
- #endif // BUILDFLAG(IS_APPLE)
- };
- // Helper to extract the "config" part of a message as a base::Value::Dict.
- // Returns nullptr on failure, and logs an error message.
- absl::optional<base::Value::Dict> ConfigDictionaryFromMessage(
- base::Value::Dict message) {
- if (base::Value::Dict* config_dict = message.FindDict("config")) {
- return std::move(*config_dict);
- }
- return absl::nullopt;
- }
- } // namespace
- namespace remoting {
- Me2MeNativeMessagingHost::Me2MeNativeMessagingHost(
- bool needs_elevation,
- intptr_t parent_window_handle,
- std::unique_ptr<ChromotingHostContext> host_context,
- scoped_refptr<DaemonController> daemon_controller,
- scoped_refptr<protocol::PairingRegistry> pairing_registry,
- std::unique_ptr<OAuthClient> oauth_client)
- : needs_elevation_(needs_elevation),
- #if BUILDFLAG(IS_WIN)
- parent_window_handle_(parent_window_handle),
- #endif
- host_context_(std::move(host_context)),
- daemon_controller_(daemon_controller),
- pairing_registry_(pairing_registry),
- oauth_client_(std::move(oauth_client)) {
- weak_ptr_ = weak_factory_.GetWeakPtr();
- }
- Me2MeNativeMessagingHost::~Me2MeNativeMessagingHost() {
- DCHECK(task_runner()->BelongsToCurrentThread());
- }
- void Me2MeNativeMessagingHost::OnMessage(const std::string& message) {
- DCHECK(task_runner()->BelongsToCurrentThread());
- base::Value::Dict response;
- absl::optional<base::Value> message_value = base::JSONReader::Read(message);
- if (!message_value || !message_value->is_dict()) {
- OnError("Received a message that's not a dictionary.");
- return;
- }
- base::Value::Dict& message_dict = message_value->GetDict();
- // If the client supplies an ID, it will expect it in the response. This
- // might be a string or a number, so cope with both.
- if (const base::Value* id = message_dict.Find("id"))
- response.Set("id", id->Clone());
- const std::string* type = message_dict.FindString("type");
- if (!type) {
- OnError("'type' not found");
- return;
- }
- response.Set("type", *type + "Response");
- if (*type == "hello") {
- ProcessHello(std::move(message_dict), std::move(response));
- } else if (*type == "clearPairedClients") {
- ProcessClearPairedClients(std::move(message_dict), std::move(response));
- } else if (*type == "deletePairedClient") {
- ProcessDeletePairedClient(std::move(message_dict), std::move(response));
- } else if (*type == "getHostName") {
- ProcessGetHostName(std::move(message_dict), std::move(response));
- } else if (*type == "getPinHash") {
- ProcessGetPinHash(std::move(message_dict), std::move(response));
- } else if (*type == "generateKeyPair") {
- ProcessGenerateKeyPair(std::move(message_dict), std::move(response));
- } else if (*type == "updateDaemonConfig") {
- ProcessUpdateDaemonConfig(std::move(message_dict), std::move(response));
- } else if (*type == "getDaemonConfig") {
- ProcessGetDaemonConfig(std::move(message_dict), std::move(response));
- } else if (*type == "getPairedClients") {
- ProcessGetPairedClients(std::move(message_dict), std::move(response));
- } else if (*type == "getUsageStatsConsent") {
- ProcessGetUsageStatsConsent(std::move(message_dict), std::move(response));
- } else if (*type == "startDaemon") {
- ProcessStartDaemon(std::move(message_dict), std::move(response));
- } else if (*type == "stopDaemon") {
- ProcessStopDaemon(std::move(message_dict), std::move(response));
- } else if (*type == "getDaemonState") {
- ProcessGetDaemonState(std::move(message_dict), std::move(response));
- } else if (*type == "getHostClientId") {
- ProcessGetHostClientId(std::move(message_dict), std::move(response));
- } else if (*type == "getCredentialsFromAuthCode") {
- ProcessGetCredentialsFromAuthCode(
- std::move(message_dict), std::move(response), true);
- } else if (*type == "getRefreshTokenFromAuthCode") {
- ProcessGetCredentialsFromAuthCode(
- std::move(message_dict), std::move(response), false);
- } else if (*type == "it2mePermissionCheck") {
- ProcessIt2mePermissionCheck(std::move(message_dict), std::move(response));
- } else {
- OnError("Unsupported request type: " + *type);
- }
- }
- void Me2MeNativeMessagingHost::Start(Client* client) {
- DCHECK(task_runner()->BelongsToCurrentThread());
- client_ = client;
- log_message_handler_ =
- std::make_unique<LogMessageHandler>(base::BindRepeating(
- &Me2MeNativeMessagingHost::SendMessageToClient, weak_ptr_));
- }
- scoped_refptr<base::SingleThreadTaskRunner>
- Me2MeNativeMessagingHost::task_runner() const {
- return host_context_->ui_task_runner();
- }
- void Me2MeNativeMessagingHost::ProcessHello(base::Value::Dict message,
- base::Value::Dict response) {
- DCHECK(task_runner()->BelongsToCurrentThread());
- response.Set("version", STRINGIZE(VERSION));
- base::Value::List supported_features_list;
- for (const char* feature : kSupportedFeatures) {
- supported_features_list.Append(feature);
- }
- response.Set("supportedFeatures", std::move(supported_features_list));
- SendMessageToClient(std::move(response));
- }
- void Me2MeNativeMessagingHost::ProcessClearPairedClients(
- base::Value::Dict message,
- base::Value::Dict response) {
- DCHECK(task_runner()->BelongsToCurrentThread());
- if (needs_elevation_) {
- if (DelegateToElevatedHost(std::move(message)) != DELEGATION_SUCCESS) {
- SendBooleanResult(std::move(response), false);
- }
- return;
- }
- if (pairing_registry_.get()) {
- pairing_registry_->ClearAllPairings(
- base::BindOnce(&Me2MeNativeMessagingHost::SendBooleanResult, weak_ptr_,
- std::move(response)));
- } else {
- SendBooleanResult(std::move(response), false);
- }
- }
- void Me2MeNativeMessagingHost::ProcessDeletePairedClient(
- base::Value::Dict message,
- base::Value::Dict response) {
- DCHECK(task_runner()->BelongsToCurrentThread());
- if (needs_elevation_) {
- if (DelegateToElevatedHost(std::move(message)) != DELEGATION_SUCCESS) {
- SendBooleanResult(std::move(response), false);
- }
- return;
- }
- std::string* client_id =
- message.FindString(protocol::PairingRegistry::kClientIdKey);
- if (!client_id) {
- OnError("'" + std::string(protocol::PairingRegistry::kClientIdKey) +
- "' string not found.");
- return;
- }
- if (pairing_registry_.get()) {
- pairing_registry_->DeletePairing(
- std::move(*client_id),
- base::BindOnce(&Me2MeNativeMessagingHost::SendBooleanResult, weak_ptr_,
- std::move(response)));
- } else {
- SendBooleanResult(std::move(response), false);
- }
- }
- void Me2MeNativeMessagingHost::ProcessGetHostName(base::Value::Dict message,
- base::Value::Dict response) {
- DCHECK(task_runner()->BelongsToCurrentThread());
- response.Set("hostname", net::GetHostName());
- SendMessageToClient(std::move(response));
- }
- void Me2MeNativeMessagingHost::ProcessGetPinHash(base::Value::Dict message,
- base::Value::Dict response) {
- DCHECK(task_runner()->BelongsToCurrentThread());
- std::string* host_id = message.FindString("hostId");
- if (!host_id) {
- std::string message_json;
- base::JSONWriter::Write(message, &message_json);
- OnError("'hostId' not found: " + message_json);
- return;
- }
- std::string* pin = message.FindString("pin");
- if (!pin) {
- std::string message_json;
- base::JSONWriter::Write(message, &message_json);
- OnError("'pin' not found: " + message_json);
- return;
- }
- response.Set("hash", MakeHostPinHash(std::move(*host_id), std::move(*pin)));
- SendMessageToClient(std::move(response));
- }
- void Me2MeNativeMessagingHost::ProcessGenerateKeyPair(
- base::Value::Dict message,
- base::Value::Dict response) {
- DCHECK(task_runner()->BelongsToCurrentThread());
- scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::Generate();
- response.Set("privateKey", key_pair->ToString());
- response.Set("publicKey", key_pair->GetPublicKey());
- SendMessageToClient(std::move(response));
- }
- void Me2MeNativeMessagingHost::ProcessUpdateDaemonConfig(
- base::Value::Dict message,
- base::Value::Dict response) {
- DCHECK(task_runner()->BelongsToCurrentThread());
- if (needs_elevation_) {
- DelegationResult result = DelegateToElevatedHost(std::move(message));
- switch (result) {
- case DELEGATION_SUCCESS:
- return; // Result will be returned by elevated host.
- case DELEGATION_CANCELLED:
- SendAsyncResult(std::move(response),
- DaemonController::RESULT_CANCELLED);
- return;
- default:
- SendAsyncResult(std::move(response), DaemonController::RESULT_FAILED);
- return;
- }
- }
- absl::optional<base::Value::Dict> config_dict =
- ConfigDictionaryFromMessage(std::move(message));
- if (!config_dict) {
- OnError("'config' dictionary not found");
- return;
- }
- daemon_controller_->UpdateConfig(
- std::move(*config_dict),
- base::BindOnce(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
- std::move(response)));
- }
- void Me2MeNativeMessagingHost::ProcessGetDaemonConfig(
- base::Value::Dict message,
- base::Value::Dict response) {
- DCHECK(task_runner()->BelongsToCurrentThread());
- daemon_controller_->GetConfig(
- base::BindOnce(&Me2MeNativeMessagingHost::SendConfigResponse, weak_ptr_,
- std::move(response)));
- }
- void Me2MeNativeMessagingHost::ProcessGetPairedClients(
- base::Value::Dict message,
- base::Value::Dict response) {
- DCHECK(task_runner()->BelongsToCurrentThread());
- if (pairing_registry_.get()) {
- pairing_registry_->GetAllPairings(
- base::BindOnce(&Me2MeNativeMessagingHost::SendPairedClientsResponse,
- weak_ptr_, std::move(response)));
- } else {
- SendPairedClientsResponse(std::move(response),
- /*pairings=*/base::Value::List());
- }
- }
- void Me2MeNativeMessagingHost::ProcessGetUsageStatsConsent(
- base::Value::Dict message,
- base::Value::Dict response) {
- DCHECK(task_runner()->BelongsToCurrentThread());
- daemon_controller_->GetUsageStatsConsent(
- base::BindOnce(&Me2MeNativeMessagingHost::SendUsageStatsConsentResponse,
- weak_ptr_, std::move(response)));
- }
- void Me2MeNativeMessagingHost::ProcessStartDaemon(base::Value::Dict message,
- base::Value::Dict response) {
- DCHECK(task_runner()->BelongsToCurrentThread());
- if (needs_elevation_) {
- DelegationResult result = DelegateToElevatedHost(std::move(message));
- switch (result) {
- case DELEGATION_SUCCESS:
- return; // Result will be returned by elevated host.
- case DELEGATION_CANCELLED:
- SendAsyncResult(std::move(response),
- DaemonController::RESULT_CANCELLED);
- return;
- default:
- SendAsyncResult(std::move(response), DaemonController::RESULT_FAILED);
- return;
- }
- }
- absl::optional<bool> consent = message.FindBool("consent");
- if (!consent) {
- OnError("'consent' not found.");
- return;
- }
- absl::optional<base::Value::Dict> config_dict =
- ConfigDictionaryFromMessage(std::move(message));
- if (!config_dict) {
- OnError("'config' dictionary not found");
- return;
- }
- daemon_controller_->SetConfigAndStart(
- std::move(*config_dict), *consent,
- base::BindOnce(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
- std::move(response)));
- }
- void Me2MeNativeMessagingHost::ProcessStopDaemon(base::Value::Dict message,
- base::Value::Dict response) {
- DCHECK(task_runner()->BelongsToCurrentThread());
- if (needs_elevation_) {
- DelegationResult result = DelegateToElevatedHost(std::move(message));
- switch (result) {
- case DELEGATION_SUCCESS:
- return; // Result will be returned by elevated host.
- case DELEGATION_CANCELLED:
- SendAsyncResult(std::move(response),
- DaemonController::RESULT_CANCELLED);
- return;
- default:
- SendAsyncResult(std::move(response), DaemonController::RESULT_FAILED);
- return;
- }
- }
- daemon_controller_->Stop(
- base::BindOnce(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
- std::move(response)));
- }
- void Me2MeNativeMessagingHost::ProcessGetDaemonState(
- base::Value::Dict message,
- base::Value::Dict response) {
- DCHECK(task_runner()->BelongsToCurrentThread());
- DaemonController::State state = daemon_controller_->GetState();
- switch (state) {
- case DaemonController::STATE_NOT_IMPLEMENTED:
- response.Set("state", "NOT_IMPLEMENTED");
- break;
- case DaemonController::STATE_STOPPED:
- response.Set("state", "STOPPED");
- break;
- case DaemonController::STATE_STARTING:
- response.Set("state", "STARTING");
- break;
- case DaemonController::STATE_STARTED:
- response.Set("state", "STARTED");
- break;
- case DaemonController::STATE_STOPPING:
- response.Set("state", "STOPPING");
- break;
- case DaemonController::STATE_UNKNOWN:
- response.Set("state", "UNKNOWN");
- break;
- }
- SendMessageToClient(std::move(response));
- }
- void Me2MeNativeMessagingHost::ProcessGetHostClientId(
- base::Value::Dict message,
- base::Value::Dict response) {
- DCHECK(task_runner()->BelongsToCurrentThread());
- response.Set("clientId", google_apis::GetOAuth2ClientID(
- google_apis::CLIENT_REMOTING_HOST));
- SendMessageToClient(std::move(response));
- }
- void Me2MeNativeMessagingHost::ProcessGetCredentialsFromAuthCode(
- base::Value::Dict message,
- base::Value::Dict response,
- bool need_user_email) {
- DCHECK(task_runner()->BelongsToCurrentThread());
- std::string* auth_code = message.FindString("authorizationCode");
- if (!auth_code) {
- OnError("'authorizationCode' string not found.");
- return;
- }
- // Pass an empty redirect_uri value since it's not needed when exchanging
- // robot auth codes. See b/231442487 for more details.
- std::string redirect_uri;
- gaia::OAuthClientInfo oauth_client_info = {
- google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING_HOST),
- google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_REMOTING_HOST),
- redirect_uri};
- oauth_client_->GetCredentialsFromAuthCode(
- oauth_client_info, std::move(*auth_code), need_user_email,
- base::BindOnce(&Me2MeNativeMessagingHost::SendCredentialsResponse,
- weak_ptr_, std::move(response)));
- }
- void Me2MeNativeMessagingHost::ProcessIt2mePermissionCheck(
- base::Value::Dict message,
- base::Value::Dict response) {
- DCHECK(task_runner()->BelongsToCurrentThread());
- daemon_controller_->CheckPermission(
- /* it2me */ true,
- base::BindOnce(&Me2MeNativeMessagingHost::SendBooleanResult, weak_ptr_,
- std::move(response)));
- }
- void Me2MeNativeMessagingHost::SendConfigResponse(
- base::Value::Dict response,
- absl::optional<base::Value::Dict> config) {
- DCHECK(task_runner()->BelongsToCurrentThread());
- if (config) {
- response.Set("config", std::move(*config));
- } else {
- response.Set("config", base::Value());
- }
- SendMessageToClient(std::move(response));
- }
- void Me2MeNativeMessagingHost::SendPairedClientsResponse(
- base::Value::Dict response,
- base::Value::List pairings) {
- DCHECK(task_runner()->BelongsToCurrentThread());
- response.Set("pairedClients", std::move(pairings));
- SendMessageToClient(std::move(response));
- }
- void Me2MeNativeMessagingHost::SendUsageStatsConsentResponse(
- base::Value::Dict response,
- const DaemonController::UsageStatsConsent& consent) {
- DCHECK(task_runner()->BelongsToCurrentThread());
- response.Set("supported", consent.supported);
- response.Set("allowed", consent.allowed);
- response.Set("setByPolicy", consent.set_by_policy);
- SendMessageToClient(std::move(response));
- }
- void Me2MeNativeMessagingHost::SendAsyncResult(
- base::Value::Dict response,
- DaemonController::AsyncResult result) {
- DCHECK(task_runner()->BelongsToCurrentThread());
- switch (result) {
- case DaemonController::RESULT_OK:
- response.Set("result", "OK");
- break;
- case DaemonController::RESULT_FAILED:
- response.Set("result", "FAILED");
- break;
- case DaemonController::RESULT_CANCELLED:
- response.Set("result", "CANCELLED");
- break;
- }
- SendMessageToClient(std::move(response));
- }
- void Me2MeNativeMessagingHost::SendBooleanResult(base::Value::Dict response,
- bool result) {
- DCHECK(task_runner()->BelongsToCurrentThread());
- response.Set("result", result);
- SendMessageToClient(std::move(response));
- }
- void Me2MeNativeMessagingHost::SendCredentialsResponse(
- base::Value::Dict response,
- const std::string& user_email,
- const std::string& refresh_token) {
- DCHECK(task_runner()->BelongsToCurrentThread());
- if (!user_email.empty()) {
- response.Set("userEmail", user_email);
- }
- response.Set("refreshToken", refresh_token);
- SendMessageToClient(std::move(response));
- }
- void Me2MeNativeMessagingHost::SendMessageToClient(
- base::Value::Dict message) const {
- DCHECK(task_runner()->BelongsToCurrentThread());
- std::string message_json;
- base::JSONWriter::Write(message, &message_json);
- client_->PostMessageFromNativeHost(message_json);
- }
- void Me2MeNativeMessagingHost::OnError(const std::string& error_message) {
- DCHECK(task_runner()->BelongsToCurrentThread());
- if (!error_message.empty()) {
- LOG(ERROR) << error_message;
- }
- // Trigger a host shutdown by sending an empty message.
- client_->CloseChannel(std::string());
- }
- #if BUILDFLAG(IS_WIN)
- Me2MeNativeMessagingHost::DelegationResult
- Me2MeNativeMessagingHost::DelegateToElevatedHost(base::Value::Dict message) {
- DCHECK(task_runner()->BelongsToCurrentThread());
- DCHECK(needs_elevation_);
- if (!elevated_host_) {
- elevated_host_ = std::make_unique<ElevatedNativeMessagingHost>(
- base::CommandLine::ForCurrentProcess()->GetProgram(),
- parent_window_handle_,
- /*elevate_process=*/true, base::Seconds(kElevatedHostTimeoutSeconds),
- client_);
- }
- ProcessLaunchResult result = elevated_host_->EnsureElevatedHostCreated();
- if (result == PROCESS_LAUNCH_RESULT_SUCCESS) {
- elevated_host_->SendMessage(
- base::Value::ToUniquePtrValue(base::Value(std::move(message))));
- }
- switch (result) {
- case PROCESS_LAUNCH_RESULT_SUCCESS:
- return DELEGATION_SUCCESS;
- case PROCESS_LAUNCH_RESULT_CANCELLED:
- return DELEGATION_CANCELLED;
- case PROCESS_LAUNCH_RESULT_FAILED:
- return DELEGATION_FAILED;
- }
- }
- #else // BUILDFLAG(IS_WIN)
- Me2MeNativeMessagingHost::DelegationResult
- Me2MeNativeMessagingHost::DelegateToElevatedHost(base::Value::Dict message) {
- NOTREACHED();
- return DELEGATION_FAILED;
- }
- #endif // !BUILDFLAG(IS_WIN)
- } // namespace remoting
|