Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(109)

Side by Side Diff: remoting/host/it2me/it2me_native_messaging_host.cc

Issue 2179353004: Update Windows It2Me to allow remote users to interact with elevated windows (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@it2me_uiaccess
Patch Set: Removing the CHECK assertion and replacing it with LOG(ERROR) instead. Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "remoting/host/it2me/it2me_native_messaging_host.h" 5 #include "remoting/host/it2me/it2me_native_messaging_host.h"
6 6
7 #include <memory>
7 #include <string> 8 #include <string>
8 #include <utility> 9 #include <utility>
9 10
10 #include "base/bind.h" 11 #include "base/bind.h"
11 #include "base/callback.h" 12 #include "base/callback.h"
13 #include "base/callback_helpers.h"
12 #include "base/json/json_reader.h" 14 #include "base/json/json_reader.h"
13 #include "base/json/json_writer.h" 15 #include "base/json/json_writer.h"
14 #include "base/strings/string_number_conversions.h" 16 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_util.h" 17 #include "base/strings/string_util.h"
16 #include "base/strings/stringize_macros.h" 18 #include "base/strings/stringize_macros.h"
17 #include "base/threading/thread.h" 19 #include "base/threading/thread.h"
20 #include "base/time/time.h"
18 #include "base/values.h" 21 #include "base/values.h"
19 #include "build/build_config.h" 22 #include "build/build_config.h"
23 #include "components/policy/policy_constants.h"
20 #include "net/base/url_util.h" 24 #include "net/base/url_util.h"
21 #include "net/url_request/url_request_context_getter.h" 25 #include "net/url_request/url_request_context_getter.h"
22 #include "remoting/base/auto_thread_task_runner.h" 26 #include "remoting/base/auto_thread_task_runner.h"
23 #include "remoting/host/chromoting_host_context.h" 27 #include "remoting/host/chromoting_host_context.h"
24 #include "remoting/host/host_exit_codes.h" 28 #include "remoting/host/host_exit_codes.h"
29 #include "remoting/host/policy_watcher.h"
25 #include "remoting/host/service_urls.h" 30 #include "remoting/host/service_urls.h"
26 #include "remoting/protocol/name_value_map.h" 31 #include "remoting/protocol/name_value_map.h"
27 32
33 #if defined(OS_WIN)
34 #include "base/command_line.h"
35 #include "base/files/file_path.h"
36
37 #include "remoting/host/win/elevated_native_messaging_host.h"
38 #endif // defined(OS_WIN)
39
28 namespace remoting { 40 namespace remoting {
29 41
30 namespace { 42 namespace {
31 43
32 const remoting::protocol::NameMapElement<It2MeHostState> kIt2MeHostStates[] = { 44 const remoting::protocol::NameMapElement<It2MeHostState> kIt2MeHostStates[] = {
33 {kDisconnected, "DISCONNECTED"}, 45 {kDisconnected, "DISCONNECTED"},
34 {kStarting, "STARTING"}, 46 {kStarting, "STARTING"},
35 {kRequestedAccessCode, "REQUESTED_ACCESS_CODE"}, 47 {kRequestedAccessCode, "REQUESTED_ACCESS_CODE"},
36 {kReceivedAccessCode, "RECEIVED_ACCESS_CODE"}, 48 {kReceivedAccessCode, "RECEIVED_ACCESS_CODE"},
37 {kConnected, "CONNECTED"}, 49 {kConnected, "CONNECTED"},
38 {kError, "ERROR"}, 50 {kError, "ERROR"},
39 {kInvalidDomainError, "INVALID_DOMAIN_ERROR"}, 51 {kInvalidDomainError, "INVALID_DOMAIN_ERROR"},
40 }; 52 };
41 53
54 #if defined(OS_WIN)
55 const base::FilePath::CharType kBaseHostBinaryName[] =
56 FILE_PATH_LITERAL("remote_assistance_host.exe");
57 const base::FilePath::CharType kElevatedHostBinaryName[] =
58 FILE_PATH_LITERAL("remote_assistance_host_uiaccess.exe");
59 #endif // defined(OS_WIN)
60
61 // Helper function to run |callback| on the correct thread using |task_runner|.
62 void PolicyUpdateCallback(
63 scoped_refptr<base::SingleThreadTaskRunner> task_runner,
64 remoting::PolicyWatcher::PolicyUpdatedCallback callback,
65 std::unique_ptr<base::DictionaryValue> policies) {
66 DCHECK(!callback.is_null());
67
68 // Always post the task so the execution is consistent (always asynchronous).
69 task_runner->PostTask(FROM_HERE,
70 base::Bind(callback, base::Passed(&policies)));
71 }
72
73 // Called when malformed policies are detected.
74 void OnPolicyError() {
75 // TODO(joedow): Report the policy error to the user. crbug.com/433009
76 NOTIMPLEMENTED();
77 }
78
42 } // namespace 79 } // namespace
43 80
44 It2MeNativeMessagingHost::It2MeNativeMessagingHost( 81 It2MeNativeMessagingHost::It2MeNativeMessagingHost(
82 bool needs_elevation,
83 policy::PolicyService* policy_service,
45 std::unique_ptr<ChromotingHostContext> context, 84 std::unique_ptr<ChromotingHostContext> context,
46 std::unique_ptr<It2MeHostFactory> factory) 85 std::unique_ptr<It2MeHostFactory> factory)
47 : client_(nullptr), 86 : needs_elevation_(needs_elevation),
48 host_context_(std::move(context)), 87 host_context_(std::move(context)),
49 factory_(std::move(factory)), 88 factory_(std::move(factory)),
89 policy_service_(policy_service),
90 policy_watcher_(PolicyWatcher::Create(policy_service_,
91 host_context_->file_task_runner())),
50 weak_factory_(this) { 92 weak_factory_(this) {
51 weak_ptr_ = weak_factory_.GetWeakPtr(); 93 weak_ptr_ = weak_factory_.GetWeakPtr();
52 94
53 const ServiceUrls* service_urls = ServiceUrls::GetInstance(); 95 const ServiceUrls* service_urls = ServiceUrls::GetInstance();
54 const bool xmpp_server_valid = 96 const bool xmpp_server_valid =
55 net::ParseHostAndPort(service_urls->xmpp_server_address(), 97 net::ParseHostAndPort(service_urls->xmpp_server_address(),
56 &xmpp_server_config_.host, 98 &xmpp_server_config_.host,
57 &xmpp_server_config_.port); 99 &xmpp_server_config_.port);
58 DCHECK(xmpp_server_valid); 100 DCHECK(xmpp_server_valid);
59 101
60 xmpp_server_config_.use_tls = service_urls->xmpp_server_use_tls(); 102 xmpp_server_config_.use_tls = service_urls->xmpp_server_use_tls();
61 directory_bot_jid_ = service_urls->directory_bot_jid(); 103 directory_bot_jid_ = service_urls->directory_bot_jid();
104
105 // The policy watcher runs on the |file_task_runner| but we want to run the
106 // update code on |task_runner| so we use a shim to post the callback to the
107 // preferred task runner.
108 PolicyWatcher::PolicyUpdatedCallback update_callback =
109 base::Bind(&It2MeNativeMessagingHost::OnPolicyUpdate, weak_ptr_);
110 policy_watcher_->StartWatching(
111 base::Bind(&PolicyUpdateCallback, task_runner(), update_callback),
112 base::Bind(&OnPolicyError));
62 } 113 }
63 114
64 It2MeNativeMessagingHost::~It2MeNativeMessagingHost() { 115 It2MeNativeMessagingHost::~It2MeNativeMessagingHost() {
65 DCHECK(task_runner()->BelongsToCurrentThread()); 116 DCHECK(task_runner()->BelongsToCurrentThread());
66 117
67 if (it2me_host_.get()) { 118 if (it2me_host_.get()) {
68 it2me_host_->Disconnect(); 119 it2me_host_->Disconnect();
69 it2me_host_ = nullptr; 120 it2me_host_ = nullptr;
70 } 121 }
71 } 122 }
(...skipping 20 matching lines...) Expand all
92 143
93 std::string type; 144 std::string type;
94 if (!message_dict->GetString("type", &type)) { 145 if (!message_dict->GetString("type", &type)) {
95 SendErrorAndExit(std::move(response), "'type' not found in request."); 146 SendErrorAndExit(std::move(response), "'type' not found in request.");
96 return; 147 return;
97 } 148 }
98 149
99 response->SetString("type", type + "Response"); 150 response->SetString("type", type + "Response");
100 151
101 if (type == "hello") { 152 if (type == "hello") {
102 ProcessHello(*message_dict, std::move(response)); 153 ProcessHello(std::move(message_dict), std::move(response));
103 } else if (type == "connect") { 154 } else if (type == "connect") {
104 ProcessConnect(*message_dict, std::move(response)); 155 ProcessConnect(std::move(message_dict), std::move(response));
105 } else if (type == "disconnect") { 156 } else if (type == "disconnect") {
106 ProcessDisconnect(*message_dict, std::move(response)); 157 ProcessDisconnect(std::move(message_dict), std::move(response));
107 } else { 158 } else {
108 SendErrorAndExit(std::move(response), "Unsupported request type: " + type); 159 SendErrorAndExit(std::move(response), "Unsupported request type: " + type);
109 } 160 }
110 } 161 }
111 162
112 void It2MeNativeMessagingHost::Start(Client* client) { 163 void It2MeNativeMessagingHost::Start(Client* client) {
113 DCHECK(task_runner()->BelongsToCurrentThread()); 164 DCHECK(task_runner()->BelongsToCurrentThread());
114 client_ = client; 165 client_ = client;
115 #if !defined(OS_CHROMEOS) 166 #if !defined(OS_CHROMEOS)
116 log_message_handler_.reset( 167 log_message_handler_.reset(
117 new LogMessageHandler( 168 new LogMessageHandler(
118 base::Bind(&It2MeNativeMessagingHost::SendMessageToClient, 169 base::Bind(&It2MeNativeMessagingHost::SendMessageToClient,
119 base::Unretained(this)))); 170 base::Unretained(this))));
120 #endif // !defined(OS_CHROMEOS) 171 #endif // !defined(OS_CHROMEOS)
121 } 172 }
122 173
123 void It2MeNativeMessagingHost::SendMessageToClient( 174 void It2MeNativeMessagingHost::SendMessageToClient(
124 std::unique_ptr<base::Value> message) const { 175 std::unique_ptr<base::Value> message) const {
125 DCHECK(task_runner()->BelongsToCurrentThread()); 176 DCHECK(task_runner()->BelongsToCurrentThread());
126 std::string message_json; 177 std::string message_json;
127 base::JSONWriter::Write(*message, &message_json); 178 base::JSONWriter::Write(*message, &message_json);
128 client_->PostMessageFromNativeHost(message_json); 179 client_->PostMessageFromNativeHost(message_json);
129 } 180 }
130 181
131 void It2MeNativeMessagingHost::ProcessHello( 182 void It2MeNativeMessagingHost::ProcessHello(
132 const base::DictionaryValue& message, 183 std::unique_ptr<base::DictionaryValue> message,
133 std::unique_ptr<base::DictionaryValue> response) const { 184 std::unique_ptr<base::DictionaryValue> response) const {
134 DCHECK(task_runner()->BelongsToCurrentThread()); 185 DCHECK(task_runner()->BelongsToCurrentThread());
135 186
136 response->SetString("version", STRINGIZE(VERSION)); 187 response->SetString("version", STRINGIZE(VERSION));
137 188
138 // This list will be populated when new features are added. 189 // This list will be populated when new features are added.
139 std::unique_ptr<base::ListValue> supported_features_list( 190 std::unique_ptr<base::ListValue> supported_features_list(
140 new base::ListValue()); 191 new base::ListValue());
141 response->Set("supportedFeatures", supported_features_list.release()); 192 response->Set("supportedFeatures", supported_features_list.release());
142 193
143 SendMessageToClient(std::move(response)); 194 SendMessageToClient(std::move(response));
144 } 195 }
145 196
146 void It2MeNativeMessagingHost::ProcessConnect( 197 void It2MeNativeMessagingHost::ProcessConnect(
147 const base::DictionaryValue& message, 198 std::unique_ptr<base::DictionaryValue> message,
148 std::unique_ptr<base::DictionaryValue> response) { 199 std::unique_ptr<base::DictionaryValue> response) {
149 DCHECK(task_runner()->BelongsToCurrentThread()); 200 DCHECK(task_runner()->BelongsToCurrentThread());
150 201
202 if (!policy_received_) {
203 DCHECK(pending_connect_.is_null());
204 pending_connect_ =
205 base::Bind(&It2MeNativeMessagingHost::ProcessConnect, weak_ptr_,
206 base::Passed(&message), base::Passed(&response));
207 return;
208 }
209
210 if (needs_elevation_) {
211 // Attempt to pass the current message to the elevated process. This method
212 // will spin up the elevated process if it is not already running. On
213 // success, the elevated process will process the message and respond.
214 // If the process cannot be started or message passing fails, then return an
215 // error to the message sender.
216 if (!DelegateToElevatedHost(std::move(message))) {
217 SendErrorAndExit(std::move(response),
218 "Failed to send message to elevated host.");
219 }
220 return;
221 }
222
151 if (it2me_host_.get()) { 223 if (it2me_host_.get()) {
152 SendErrorAndExit(std::move(response), 224 SendErrorAndExit(std::move(response),
153 "Connect can be called only when disconnected."); 225 "Connect can be called only when disconnected.");
154 return; 226 return;
155 } 227 }
156 228
157 XmppSignalStrategy::XmppServerConfig xmpp_config = xmpp_server_config_; 229 XmppSignalStrategy::XmppServerConfig xmpp_config = xmpp_server_config_;
158 230
159 if (!message.GetString("userName", &xmpp_config.username)) { 231 if (!message->GetString("userName", &xmpp_config.username)) {
160 SendErrorAndExit(std::move(response), "'userName' not found in request."); 232 SendErrorAndExit(std::move(response), "'userName' not found in request.");
161 return; 233 return;
162 } 234 }
163 235
164 std::string auth_service_with_token; 236 std::string auth_service_with_token;
165 if (!message.GetString("authServiceWithToken", &auth_service_with_token)) { 237 if (!message->GetString("authServiceWithToken", &auth_service_with_token)) {
166 SendErrorAndExit(std::move(response), 238 SendErrorAndExit(std::move(response),
167 "'authServiceWithToken' not found in request."); 239 "'authServiceWithToken' not found in request.");
168 return; 240 return;
169 } 241 }
170 242
171 // For backward compatibility the webapp still passes OAuth service as part of 243 // For backward compatibility the webapp still passes OAuth service as part of
172 // the authServiceWithToken field. But auth service part is always expected to 244 // the authServiceWithToken field. But auth service part is always expected to
173 // be set to oauth2. 245 // be set to oauth2.
174 const char kOAuth2ServicePrefix[] = "oauth2:"; 246 const char kOAuth2ServicePrefix[] = "oauth2:";
175 if (!base::StartsWith(auth_service_with_token, kOAuth2ServicePrefix, 247 if (!base::StartsWith(auth_service_with_token, kOAuth2ServicePrefix,
176 base::CompareCase::SENSITIVE)) { 248 base::CompareCase::SENSITIVE)) {
177 SendErrorAndExit(std::move(response), "Invalid 'authServiceWithToken': " + 249 SendErrorAndExit(std::move(response), "Invalid 'authServiceWithToken': " +
178 auth_service_with_token); 250 auth_service_with_token);
179 return; 251 return;
180 } 252 }
181 253
182 xmpp_config.auth_token = 254 xmpp_config.auth_token =
183 auth_service_with_token.substr(strlen(kOAuth2ServicePrefix)); 255 auth_service_with_token.substr(strlen(kOAuth2ServicePrefix));
184 256
185 #if !defined(NDEBUG) 257 #if !defined(NDEBUG)
186 std::string address; 258 std::string address;
187 if (!message.GetString("xmppServerAddress", &address)) { 259 if (!message->GetString("xmppServerAddress", &address)) {
188 SendErrorAndExit(std::move(response), 260 SendErrorAndExit(std::move(response),
189 "'xmppServerAddress' not found in request."); 261 "'xmppServerAddress' not found in request.");
190 return; 262 return;
191 } 263 }
192 264
193 if (!net::ParseHostAndPort(address, &xmpp_config.host, 265 if (!net::ParseHostAndPort(address, &xmpp_config.host,
194 &xmpp_config.port)) { 266 &xmpp_config.port)) {
195 SendErrorAndExit(std::move(response), 267 SendErrorAndExit(std::move(response),
196 "Invalid 'xmppServerAddress': " + address); 268 "Invalid 'xmppServerAddress': " + address);
197 return; 269 return;
198 } 270 }
199 271
200 if (!message.GetBoolean("xmppServerUseTls", &xmpp_config.use_tls)) { 272 if (!message->GetBoolean("xmppServerUseTls", &xmpp_config.use_tls)) {
201 SendErrorAndExit(std::move(response), 273 SendErrorAndExit(std::move(response),
202 "'xmppServerUseTls' not found in request."); 274 "'xmppServerUseTls' not found in request.");
203 return; 275 return;
204 } 276 }
205 277
206 if (!message.GetString("directoryBotJid", &directory_bot_jid_)) { 278 if (!message->GetString("directoryBotJid", &directory_bot_jid_)) {
207 SendErrorAndExit(std::move(response), 279 SendErrorAndExit(std::move(response),
208 "'directoryBotJid' not found in request."); 280 "'directoryBotJid' not found in request.");
209 return; 281 return;
210 } 282 }
211 #endif // !defined(NDEBUG) 283 #endif // !defined(NDEBUG)
212 284
213 // Create the It2Me host and start connecting. 285 // Create the It2Me host and start connecting.
214 it2me_host_ = factory_->CreateIt2MeHost(host_context_->Copy(), 286 it2me_host_ =
215 weak_ptr_, 287 factory_->CreateIt2MeHost(host_context_->Copy(), policy_service_,
216 xmpp_config, 288 weak_ptr_, xmpp_config, directory_bot_jid_);
217 directory_bot_jid_);
218 it2me_host_->Connect(); 289 it2me_host_->Connect();
219 290
220 SendMessageToClient(std::move(response)); 291 SendMessageToClient(std::move(response));
221 } 292 }
222 293
223 void It2MeNativeMessagingHost::ProcessDisconnect( 294 void It2MeNativeMessagingHost::ProcessDisconnect(
224 const base::DictionaryValue& message, 295 std::unique_ptr<base::DictionaryValue> message,
225 std::unique_ptr<base::DictionaryValue> response) { 296 std::unique_ptr<base::DictionaryValue> response) {
226 DCHECK(task_runner()->BelongsToCurrentThread()); 297 DCHECK(task_runner()->BelongsToCurrentThread());
298 DCHECK(policy_received_);
299
300 if (needs_elevation_) {
301 // Attempt to pass the current message to the elevated process. This method
302 // will spin up the elevated process if it is not already running. On
303 // success, the elevated process will process the message and respond.
304 // If the process cannot be started or message passing fails, then return an
305 // error to the message sender.
306 if (!DelegateToElevatedHost(std::move(message))) {
307 SendErrorAndExit(std::move(response),
308 "Failed to send message to elevated host.");
309 }
310 return;
311 }
227 312
228 if (it2me_host_.get()) { 313 if (it2me_host_.get()) {
229 it2me_host_->Disconnect(); 314 it2me_host_->Disconnect();
230 it2me_host_ = nullptr; 315 it2me_host_ = nullptr;
231 } 316 }
232 SendMessageToClient(std::move(response)); 317 SendMessageToClient(std::move(response));
233 } 318 }
234 319
235 void It2MeNativeMessagingHost::SendErrorAndExit( 320 void It2MeNativeMessagingHost::SendErrorAndExit(
236 std::unique_ptr<base::DictionaryValue> response, 321 std::unique_ptr<base::DictionaryValue> response,
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
321 It2MeNativeMessagingHost::task_runner() const { 406 It2MeNativeMessagingHost::task_runner() const {
322 return host_context_->ui_task_runner(); 407 return host_context_->ui_task_runner();
323 } 408 }
324 409
325 /* static */ 410 /* static */
326 std::string It2MeNativeMessagingHost::HostStateToString( 411 std::string It2MeNativeMessagingHost::HostStateToString(
327 It2MeHostState host_state) { 412 It2MeHostState host_state) {
328 return ValueToName(kIt2MeHostStates, host_state); 413 return ValueToName(kIt2MeHostStates, host_state);
329 } 414 }
330 415
416 void It2MeNativeMessagingHost::OnPolicyUpdate(
417 std::unique_ptr<base::DictionaryValue> policies) {
418 if (policy_received_) {
419 // Don't dynamically change how the host operates since we don't have a good
420 // way to communicate changes to the user.
421 return;
422 }
423
424 bool allow_elevated_host = false;
425 if (!policies->GetBoolean(
426 policy::key::kRemoteAccessHostAllowUiAccessForRemoteAssistance,
427 &allow_elevated_host)) {
428 LOG(WARNING) << "Failed to retrieve elevated host policy value.";
429 }
430 #if defined(OS_WIN)
431 LOG(INFO) << "Allow UiAccess for Remote Assistance: " << allow_elevated_host;
432 #endif // defined(OS_WIN)
433
434 policy_received_ = true;
435
436 // If |allow_elevated_host| is false, then we will fall back to using a host
437 // running in the current context regardless of the elevation request. This
438 // may not be ideal, but is still functional.
439 needs_elevation_ = needs_elevation_ && allow_elevated_host;
440 if (!pending_connect_.is_null()) {
441 base::ResetAndReturn(&pending_connect_).Run();
442 }
443 }
444
445 #if defined(OS_WIN)
446
447 bool It2MeNativeMessagingHost::DelegateToElevatedHost(
448 std::unique_ptr<base::DictionaryValue> message) {
449 DCHECK(task_runner()->BelongsToCurrentThread());
450 DCHECK(needs_elevation_);
451
452 if (!elevated_host_) {
453 base::FilePath binary_path =
454 base::CommandLine::ForCurrentProcess()->GetProgram();
455 CHECK(binary_path.BaseName() == base::FilePath(kBaseHostBinaryName));
456
457 // The new process runs at an elevated level due to being granted uiAccess.
458 // |parent_window_handle| can be used to position dialog windows but is not
459 // currently used.
460 elevated_host_.reset(new ElevatedNativeMessagingHost(
461 binary_path.DirName().Append(kElevatedHostBinaryName),
462 /*parent_window_handle=*/0,
463 /*elevate_process=*/false,
464 /*host_timeout=*/base::TimeDelta(), client_));
465 }
466
467 if (elevated_host_->EnsureElevatedHostCreated()) {
468 elevated_host_->SendMessage(std::move(message));
469 return true;
470 }
471
472 return false;
473 }
474
475 #else // !defined(OS_WIN)
476
477 bool It2MeNativeMessagingHost::DelegateToElevatedHost(
478 std::unique_ptr<base::DictionaryValue> message) {
479 NOTREACHED();
480 return false;
481 }
482
483 #endif // !defined(OS_WIN)
484
331 } // namespace remoting 485 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/host/it2me/it2me_native_messaging_host.h ('k') | remoting/host/it2me/it2me_native_messaging_host_main.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698