Index: remoting/host/it2me/it2me_native_messaging_host.cc |
diff --git a/remoting/host/it2me/it2me_native_messaging_host.cc b/remoting/host/it2me/it2me_native_messaging_host.cc |
index 1ef878bc79515006763a7c3e283a9bc548157acf..e3d0251d8a9648b2eca19c7b8e9d5e935905a60f 100644 |
--- a/remoting/host/it2me/it2me_native_messaging_host.cc |
+++ b/remoting/host/it2me/it2me_native_messaging_host.cc |
@@ -4,27 +4,39 @@ |
#include "remoting/host/it2me/it2me_native_messaging_host.h" |
+#include <memory> |
#include <string> |
#include <utility> |
#include "base/bind.h" |
#include "base/callback.h" |
+#include "base/callback_helpers.h" |
#include "base/json/json_reader.h" |
#include "base/json/json_writer.h" |
#include "base/strings/string_number_conversions.h" |
#include "base/strings/string_util.h" |
#include "base/strings/stringize_macros.h" |
#include "base/threading/thread.h" |
+#include "base/time/time.h" |
#include "base/values.h" |
#include "build/build_config.h" |
+#include "components/policy/policy_constants.h" |
#include "net/base/url_util.h" |
#include "net/url_request/url_request_context_getter.h" |
#include "remoting/base/auto_thread_task_runner.h" |
#include "remoting/host/chromoting_host_context.h" |
#include "remoting/host/host_exit_codes.h" |
+#include "remoting/host/policy_watcher.h" |
#include "remoting/host/service_urls.h" |
#include "remoting/protocol/name_value_map.h" |
+#if defined(OS_WIN) |
+#include "base/command_line.h" |
+#include "base/files/file_path.h" |
+ |
+#include "remoting/host/win/elevated_native_messaging_host.h" |
+#endif // defined(OS_WIN) |
+ |
namespace remoting { |
namespace { |
@@ -39,14 +51,44 @@ const remoting::protocol::NameMapElement<It2MeHostState> kIt2MeHostStates[] = { |
{kInvalidDomainError, "INVALID_DOMAIN_ERROR"}, |
}; |
+#if defined(OS_WIN) |
+const base::FilePath::CharType kBaseHostBinaryName[] = |
+ FILE_PATH_LITERAL("remote_assistance_host.exe"); |
+const base::FilePath::CharType kElevatedHostBinaryName[] = |
+ FILE_PATH_LITERAL("remote_assistance_host_uiaccess.exe"); |
+#endif // defined(OS_WIN) |
+ |
+// Helper function to run |callback| on the correct thread using |task_runner|. |
+void PolicyUpdateCallback( |
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner, |
+ remoting::PolicyWatcher::PolicyUpdatedCallback callback, |
+ std::unique_ptr<base::DictionaryValue> policies) { |
+ DCHECK(!callback.is_null()); |
+ |
+ // Always post the task so the execution is consistent (always asynchronous). |
+ task_runner->PostTask(FROM_HERE, |
+ base::Bind(callback, base::Passed(&policies))); |
+} |
+ |
+// Called when malformed policies are detected. |
+void OnPolicyError() { |
+ // TODO(joedow): Report the policy error to the user. crbug.com/433009 |
+ NOTIMPLEMENTED(); |
+} |
+ |
} // namespace |
It2MeNativeMessagingHost::It2MeNativeMessagingHost( |
+ bool needs_elevation, |
+ policy::PolicyService* policy_service, |
std::unique_ptr<ChromotingHostContext> context, |
std::unique_ptr<It2MeHostFactory> factory) |
- : client_(nullptr), |
+ : needs_elevation_(needs_elevation), |
host_context_(std::move(context)), |
factory_(std::move(factory)), |
+ policy_service_(policy_service), |
+ policy_watcher_(PolicyWatcher::Create(policy_service_, |
+ host_context_->file_task_runner())), |
weak_factory_(this) { |
weak_ptr_ = weak_factory_.GetWeakPtr(); |
@@ -59,6 +101,15 @@ It2MeNativeMessagingHost::It2MeNativeMessagingHost( |
xmpp_server_config_.use_tls = service_urls->xmpp_server_use_tls(); |
directory_bot_jid_ = service_urls->directory_bot_jid(); |
+ |
+ // The policy watcher runs on the |file_task_runner| but we want to run the |
+ // update code on |task_runner| so we use a shim to post the callback to the |
+ // preferred task runner. |
+ PolicyWatcher::PolicyUpdatedCallback update_callback = |
+ base::Bind(&It2MeNativeMessagingHost::OnPolicyUpdate, weak_ptr_); |
+ policy_watcher_->StartWatching( |
+ base::Bind(&PolicyUpdateCallback, task_runner(), update_callback), |
+ base::Bind(&OnPolicyError)); |
} |
It2MeNativeMessagingHost::~It2MeNativeMessagingHost() { |
@@ -99,11 +150,11 @@ void It2MeNativeMessagingHost::OnMessage(const std::string& message) { |
response->SetString("type", type + "Response"); |
if (type == "hello") { |
- ProcessHello(*message_dict, std::move(response)); |
+ ProcessHello(std::move(message_dict), std::move(response)); |
} else if (type == "connect") { |
- ProcessConnect(*message_dict, std::move(response)); |
+ ProcessConnect(std::move(message_dict), std::move(response)); |
} else if (type == "disconnect") { |
- ProcessDisconnect(*message_dict, std::move(response)); |
+ ProcessDisconnect(std::move(message_dict), std::move(response)); |
} else { |
SendErrorAndExit(std::move(response), "Unsupported request type: " + type); |
} |
@@ -129,7 +180,7 @@ void It2MeNativeMessagingHost::SendMessageToClient( |
} |
void It2MeNativeMessagingHost::ProcessHello( |
- const base::DictionaryValue& message, |
+ std::unique_ptr<base::DictionaryValue> message, |
std::unique_ptr<base::DictionaryValue> response) const { |
DCHECK(task_runner()->BelongsToCurrentThread()); |
@@ -144,10 +195,31 @@ void It2MeNativeMessagingHost::ProcessHello( |
} |
void It2MeNativeMessagingHost::ProcessConnect( |
- const base::DictionaryValue& message, |
+ std::unique_ptr<base::DictionaryValue> message, |
std::unique_ptr<base::DictionaryValue> response) { |
DCHECK(task_runner()->BelongsToCurrentThread()); |
+ if (!policy_received_) { |
+ DCHECK(pending_connect_.is_null()); |
+ pending_connect_ = |
+ base::Bind(&It2MeNativeMessagingHost::ProcessConnect, weak_ptr_, |
+ base::Passed(&message), base::Passed(&response)); |
+ return; |
+ } |
+ |
+ if (needs_elevation_) { |
+ // Attempt to pass the current message to the elevated process. This method |
+ // will spin up the elevated process if it is not already running. On |
+ // success, the elevated process will process the message and respond. |
+ // If the process cannot be started or message passing fails, then return an |
+ // error to the message sender. |
+ if (!DelegateToElevatedHost(std::move(message))) { |
+ SendErrorAndExit(std::move(response), |
+ "Failed to send message to elevated host."); |
+ } |
+ return; |
+ } |
+ |
if (it2me_host_.get()) { |
SendErrorAndExit(std::move(response), |
"Connect can be called only when disconnected."); |
@@ -156,13 +228,13 @@ void It2MeNativeMessagingHost::ProcessConnect( |
XmppSignalStrategy::XmppServerConfig xmpp_config = xmpp_server_config_; |
- if (!message.GetString("userName", &xmpp_config.username)) { |
+ if (!message->GetString("userName", &xmpp_config.username)) { |
SendErrorAndExit(std::move(response), "'userName' not found in request."); |
return; |
} |
std::string auth_service_with_token; |
- if (!message.GetString("authServiceWithToken", &auth_service_with_token)) { |
+ if (!message->GetString("authServiceWithToken", &auth_service_with_token)) { |
SendErrorAndExit(std::move(response), |
"'authServiceWithToken' not found in request."); |
return; |
@@ -184,7 +256,7 @@ void It2MeNativeMessagingHost::ProcessConnect( |
#if !defined(NDEBUG) |
std::string address; |
- if (!message.GetString("xmppServerAddress", &address)) { |
+ if (!message->GetString("xmppServerAddress", &address)) { |
SendErrorAndExit(std::move(response), |
"'xmppServerAddress' not found in request."); |
return; |
@@ -197,13 +269,13 @@ void It2MeNativeMessagingHost::ProcessConnect( |
return; |
} |
- if (!message.GetBoolean("xmppServerUseTls", &xmpp_config.use_tls)) { |
+ if (!message->GetBoolean("xmppServerUseTls", &xmpp_config.use_tls)) { |
SendErrorAndExit(std::move(response), |
"'xmppServerUseTls' not found in request."); |
return; |
} |
- if (!message.GetString("directoryBotJid", &directory_bot_jid_)) { |
+ if (!message->GetString("directoryBotJid", &directory_bot_jid_)) { |
SendErrorAndExit(std::move(response), |
"'directoryBotJid' not found in request."); |
return; |
@@ -211,19 +283,32 @@ void It2MeNativeMessagingHost::ProcessConnect( |
#endif // !defined(NDEBUG) |
// Create the It2Me host and start connecting. |
- it2me_host_ = factory_->CreateIt2MeHost(host_context_->Copy(), |
- weak_ptr_, |
- xmpp_config, |
- directory_bot_jid_); |
+ it2me_host_ = |
+ factory_->CreateIt2MeHost(host_context_->Copy(), policy_service_, |
+ weak_ptr_, xmpp_config, directory_bot_jid_); |
it2me_host_->Connect(); |
SendMessageToClient(std::move(response)); |
} |
void It2MeNativeMessagingHost::ProcessDisconnect( |
- const base::DictionaryValue& message, |
+ std::unique_ptr<base::DictionaryValue> message, |
std::unique_ptr<base::DictionaryValue> response) { |
DCHECK(task_runner()->BelongsToCurrentThread()); |
+ DCHECK(policy_received_); |
+ |
+ if (needs_elevation_) { |
+ // Attempt to pass the current message to the elevated process. This method |
+ // will spin up the elevated process if it is not already running. On |
+ // success, the elevated process will process the message and respond. |
+ // If the process cannot be started or message passing fails, then return an |
+ // error to the message sender. |
+ if (!DelegateToElevatedHost(std::move(message))) { |
+ SendErrorAndExit(std::move(response), |
+ "Failed to send message to elevated host."); |
+ } |
+ return; |
+ } |
if (it2me_host_.get()) { |
it2me_host_->Disconnect(); |
@@ -328,4 +413,73 @@ std::string It2MeNativeMessagingHost::HostStateToString( |
return ValueToName(kIt2MeHostStates, host_state); |
} |
+void It2MeNativeMessagingHost::OnPolicyUpdate( |
+ std::unique_ptr<base::DictionaryValue> policies) { |
+ if (policy_received_) { |
+ // Don't dynamically change how the host operates since we don't have a good |
+ // way to communicate changes to the user. |
+ return; |
+ } |
+ |
+ bool allow_elevated_host = false; |
+ if (!policies->GetBoolean( |
+ policy::key::kRemoteAccessHostAllowUiAccessForRemoteAssistance, |
+ &allow_elevated_host)) { |
+ LOG(WARNING) << "Failed to retrieve elevated host policy value."; |
+ } |
+#if defined(OS_WIN) |
+ LOG(INFO) << "Allow UiAccess for Remote Assistance: " << allow_elevated_host; |
+#endif // defined(OS_WIN) |
+ |
+ policy_received_ = true; |
+ |
+ // If |allow_elevated_host| is false, then we will fall back to using a host |
+ // running in the current context regardless of the elevation request. This |
+ // may not be ideal, but is still functional. |
+ needs_elevation_ = needs_elevation_ && allow_elevated_host; |
+ if (!pending_connect_.is_null()) { |
+ base::ResetAndReturn(&pending_connect_).Run(); |
+ } |
+} |
+ |
+#if defined(OS_WIN) |
+ |
+bool It2MeNativeMessagingHost::DelegateToElevatedHost( |
+ std::unique_ptr<base::DictionaryValue> message) { |
+ DCHECK(task_runner()->BelongsToCurrentThread()); |
+ DCHECK(needs_elevation_); |
+ |
+ if (!elevated_host_) { |
+ base::FilePath binary_path = |
+ base::CommandLine::ForCurrentProcess()->GetProgram(); |
+ CHECK(binary_path.BaseName() == base::FilePath(kBaseHostBinaryName)); |
+ |
+ // The new process runs at an elevated level due to being granted uiAccess. |
+ // |parent_window_handle| can be used to position dialog windows but is not |
+ // currently used. |
+ elevated_host_.reset(new ElevatedNativeMessagingHost( |
+ binary_path.DirName().Append(kElevatedHostBinaryName), |
+ /*parent_window_handle=*/0, |
+ /*elevate_process=*/false, |
+ /*host_timeout=*/base::TimeDelta(), client_)); |
+ } |
+ |
+ if (elevated_host_->EnsureElevatedHostCreated()) { |
+ elevated_host_->SendMessage(std::move(message)); |
+ return true; |
+ } |
+ |
+ return false; |
+} |
+ |
+#else // !defined(OS_WIN) |
+ |
+bool It2MeNativeMessagingHost::DelegateToElevatedHost( |
+ std::unique_ptr<base::DictionaryValue> message) { |
+ NOTREACHED(); |
+ return false; |
+} |
+ |
+#endif // !defined(OS_WIN) |
+ |
} // namespace remoting |