OLD | NEW |
---|---|
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 <string> | 7 #include <string> |
8 #include <utility> | 8 #include <utility> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
11 #include "base/callback.h" | 11 #include "base/callback.h" |
12 #include "base/callback_helpers.h" | |
12 #include "base/json/json_reader.h" | 13 #include "base/json/json_reader.h" |
13 #include "base/json/json_writer.h" | 14 #include "base/json/json_writer.h" |
14 #include "base/strings/string_number_conversions.h" | 15 #include "base/strings/string_number_conversions.h" |
15 #include "base/strings/string_util.h" | 16 #include "base/strings/string_util.h" |
16 #include "base/strings/stringize_macros.h" | 17 #include "base/strings/stringize_macros.h" |
17 #include "base/threading/thread.h" | 18 #include "base/threading/thread.h" |
19 #include "base/time/time.h" | |
18 #include "base/values.h" | 20 #include "base/values.h" |
19 #include "build/build_config.h" | 21 #include "build/build_config.h" |
22 #include "components/policy/policy_constants.h" | |
20 #include "net/base/url_util.h" | 23 #include "net/base/url_util.h" |
21 #include "net/url_request/url_request_context_getter.h" | 24 #include "net/url_request/url_request_context_getter.h" |
22 #include "remoting/base/auto_thread_task_runner.h" | 25 #include "remoting/base/auto_thread_task_runner.h" |
23 #include "remoting/host/chromoting_host_context.h" | 26 #include "remoting/host/chromoting_host_context.h" |
24 #include "remoting/host/host_exit_codes.h" | 27 #include "remoting/host/host_exit_codes.h" |
28 #include "remoting/host/policy_watcher.h" | |
25 #include "remoting/host/service_urls.h" | 29 #include "remoting/host/service_urls.h" |
26 #include "remoting/protocol/name_value_map.h" | 30 #include "remoting/protocol/name_value_map.h" |
27 | 31 |
32 #if defined(OS_WIN) | |
33 #include "base/command_line.h" | |
34 #include "base/files/file_path.h" | |
35 | |
36 #include "remoting/host/win/elevated_native_messaging_host.h" | |
37 #endif // defined(OS_WIN) | |
38 | |
28 namespace remoting { | 39 namespace remoting { |
29 | 40 |
30 namespace { | 41 namespace { |
31 | 42 |
32 const remoting::protocol::NameMapElement<It2MeHostState> kIt2MeHostStates[] = { | 43 const remoting::protocol::NameMapElement<It2MeHostState> kIt2MeHostStates[] = { |
33 {kDisconnected, "DISCONNECTED"}, | 44 {kDisconnected, "DISCONNECTED"}, |
34 {kStarting, "STARTING"}, | 45 {kStarting, "STARTING"}, |
35 {kRequestedAccessCode, "REQUESTED_ACCESS_CODE"}, | 46 {kRequestedAccessCode, "REQUESTED_ACCESS_CODE"}, |
36 {kReceivedAccessCode, "RECEIVED_ACCESS_CODE"}, | 47 {kReceivedAccessCode, "RECEIVED_ACCESS_CODE"}, |
37 {kConnected, "CONNECTED"}, | 48 {kConnected, "CONNECTED"}, |
38 {kError, "ERROR"}, | 49 {kError, "ERROR"}, |
39 {kInvalidDomainError, "INVALID_DOMAIN_ERROR"}, | 50 {kInvalidDomainError, "INVALID_DOMAIN_ERROR"}, |
40 }; | 51 }; |
41 | 52 |
53 #if defined(OS_WIN) | |
54 const base::FilePath::CharType kBaseHostBinaryName[] = | |
55 FILE_PATH_LITERAL("remote_assistance_host.exe"); | |
56 const base::FilePath::CharType kElevatedHostBinaryName[] = | |
57 FILE_PATH_LITERAL("remote_assistance_host_uiaccess.exe"); | |
58 #endif // defined(OS_WIN) | |
59 | |
42 } // namespace | 60 } // namespace |
43 | 61 |
44 It2MeNativeMessagingHost::It2MeNativeMessagingHost( | 62 It2MeNativeMessagingHost::It2MeNativeMessagingHost( |
63 bool needs_elevation, | |
45 std::unique_ptr<ChromotingHostContext> context, | 64 std::unique_ptr<ChromotingHostContext> context, |
46 std::unique_ptr<It2MeHostFactory> factory) | 65 std::unique_ptr<It2MeHostFactory> factory) |
47 : client_(nullptr), | 66 : needs_elevation_(needs_elevation), |
48 host_context_(std::move(context)), | 67 host_context_(std::move(context)), |
49 factory_(std::move(factory)), | 68 factory_(std::move(factory)), |
69 policy_watcher_(PolicyWatcher::Create(factory_->get_policy_service(), | |
70 host_context_->file_task_runner())), | |
50 weak_factory_(this) { | 71 weak_factory_(this) { |
51 weak_ptr_ = weak_factory_.GetWeakPtr(); | 72 weak_ptr_ = weak_factory_.GetWeakPtr(); |
52 | 73 |
53 const ServiceUrls* service_urls = ServiceUrls::GetInstance(); | 74 const ServiceUrls* service_urls = ServiceUrls::GetInstance(); |
54 const bool xmpp_server_valid = | 75 const bool xmpp_server_valid = |
55 net::ParseHostAndPort(service_urls->xmpp_server_address(), | 76 net::ParseHostAndPort(service_urls->xmpp_server_address(), |
56 &xmpp_server_config_.host, | 77 &xmpp_server_config_.host, |
57 &xmpp_server_config_.port); | 78 &xmpp_server_config_.port); |
58 DCHECK(xmpp_server_valid); | 79 DCHECK(xmpp_server_valid); |
59 | 80 |
60 xmpp_server_config_.use_tls = service_urls->xmpp_server_use_tls(); | 81 xmpp_server_config_.use_tls = service_urls->xmpp_server_use_tls(); |
61 directory_bot_jid_ = service_urls->directory_bot_jid(); | 82 directory_bot_jid_ = service_urls->directory_bot_jid(); |
83 | |
84 policy_watcher_->StartWatching( | |
85 base::Bind(&It2MeNativeMessagingHost::OnPolicyUpdate, | |
86 base::Unretained(this)), | |
Sergey Ulanov
2016/08/31 23:00:17
I don't think it's safe to use Unretained() here b
joedow
2016/09/02 21:58:59
Done.
| |
87 base::Bind(&It2MeNativeMessagingHost::OnPolicyError, | |
88 base::Unretained(this))); | |
62 } | 89 } |
63 | 90 |
64 It2MeNativeMessagingHost::~It2MeNativeMessagingHost() { | 91 It2MeNativeMessagingHost::~It2MeNativeMessagingHost() { |
65 DCHECK(task_runner()->BelongsToCurrentThread()); | 92 DCHECK(task_runner()->BelongsToCurrentThread()); |
66 | 93 |
67 if (it2me_host_.get()) { | 94 if (it2me_host_.get()) { |
68 it2me_host_->Disconnect(); | 95 it2me_host_->Disconnect(); |
69 it2me_host_ = nullptr; | 96 it2me_host_ = nullptr; |
70 } | 97 } |
71 } | 98 } |
(...skipping 20 matching lines...) Expand all Loading... | |
92 | 119 |
93 std::string type; | 120 std::string type; |
94 if (!message_dict->GetString("type", &type)) { | 121 if (!message_dict->GetString("type", &type)) { |
95 SendErrorAndExit(std::move(response), "'type' not found in request."); | 122 SendErrorAndExit(std::move(response), "'type' not found in request."); |
96 return; | 123 return; |
97 } | 124 } |
98 | 125 |
99 response->SetString("type", type + "Response"); | 126 response->SetString("type", type + "Response"); |
100 | 127 |
101 if (type == "hello") { | 128 if (type == "hello") { |
102 ProcessHello(*message_dict, std::move(response)); | 129 ProcessHello(std::move(message_dict), std::move(response)); |
103 } else if (type == "connect") { | 130 } else if (type == "connect") { |
104 ProcessConnect(*message_dict, std::move(response)); | 131 ProcessConnect(std::move(message_dict), std::move(response)); |
105 } else if (type == "disconnect") { | 132 } else if (type == "disconnect") { |
106 ProcessDisconnect(*message_dict, std::move(response)); | 133 ProcessDisconnect(std::move(message_dict), std::move(response)); |
107 } else { | 134 } else { |
108 SendErrorAndExit(std::move(response), "Unsupported request type: " + type); | 135 SendErrorAndExit(std::move(response), "Unsupported request type: " + type); |
109 } | 136 } |
110 } | 137 } |
111 | 138 |
112 void It2MeNativeMessagingHost::Start(Client* client) { | 139 void It2MeNativeMessagingHost::Start(Client* client) { |
113 DCHECK(task_runner()->BelongsToCurrentThread()); | 140 DCHECK(task_runner()->BelongsToCurrentThread()); |
114 client_ = client; | 141 client_ = client; |
115 #if !defined(OS_CHROMEOS) | 142 #if !defined(OS_CHROMEOS) |
116 log_message_handler_.reset( | 143 log_message_handler_.reset( |
117 new LogMessageHandler( | 144 new LogMessageHandler( |
118 base::Bind(&It2MeNativeMessagingHost::SendMessageToClient, | 145 base::Bind(&It2MeNativeMessagingHost::SendMessageToClient, |
119 base::Unretained(this)))); | 146 base::Unretained(this)))); |
120 #endif // !defined(OS_CHROMEOS) | 147 #endif // !defined(OS_CHROMEOS) |
121 } | 148 } |
122 | 149 |
123 void It2MeNativeMessagingHost::SendMessageToClient( | 150 void It2MeNativeMessagingHost::SendMessageToClient( |
124 std::unique_ptr<base::Value> message) const { | 151 std::unique_ptr<base::Value> message) const { |
125 DCHECK(task_runner()->BelongsToCurrentThread()); | 152 DCHECK(task_runner()->BelongsToCurrentThread()); |
126 std::string message_json; | 153 std::string message_json; |
127 base::JSONWriter::Write(*message, &message_json); | 154 base::JSONWriter::Write(*message, &message_json); |
128 client_->PostMessageFromNativeHost(message_json); | 155 client_->PostMessageFromNativeHost(message_json); |
129 } | 156 } |
130 | 157 |
131 void It2MeNativeMessagingHost::ProcessHello( | 158 void It2MeNativeMessagingHost::ProcessHello( |
132 const base::DictionaryValue& message, | 159 std::unique_ptr<base::DictionaryValue> message, |
133 std::unique_ptr<base::DictionaryValue> response) const { | 160 std::unique_ptr<base::DictionaryValue> response) const { |
134 DCHECK(task_runner()->BelongsToCurrentThread()); | 161 DCHECK(task_runner()->BelongsToCurrentThread()); |
135 | 162 |
136 response->SetString("version", STRINGIZE(VERSION)); | 163 response->SetString("version", STRINGIZE(VERSION)); |
137 | 164 |
138 // This list will be populated when new features are added. | 165 // This list will be populated when new features are added. |
139 std::unique_ptr<base::ListValue> supported_features_list( | 166 std::unique_ptr<base::ListValue> supported_features_list( |
140 new base::ListValue()); | 167 new base::ListValue()); |
141 response->Set("supportedFeatures", supported_features_list.release()); | 168 response->Set("supportedFeatures", supported_features_list.release()); |
142 | 169 |
143 SendMessageToClient(std::move(response)); | 170 SendMessageToClient(std::move(response)); |
144 } | 171 } |
145 | 172 |
146 void It2MeNativeMessagingHost::ProcessConnect( | 173 void It2MeNativeMessagingHost::ProcessConnect( |
147 const base::DictionaryValue& message, | 174 std::unique_ptr<base::DictionaryValue> message, |
148 std::unique_ptr<base::DictionaryValue> response) { | 175 std::unique_ptr<base::DictionaryValue> response) { |
149 DCHECK(task_runner()->BelongsToCurrentThread()); | 176 DCHECK(task_runner()->BelongsToCurrentThread()); |
150 | 177 |
178 if (!policy_received_) { | |
179 DCHECK(pending_connect_.is_null()); | |
180 pending_connect_ = | |
181 base::Bind(&It2MeNativeMessagingHost::ProcessConnect, weak_ptr_, | |
182 base::Passed(&message), base::Passed(&response)); | |
183 return; | |
184 } | |
185 | |
186 if (needs_elevation_ && allow_elevated_host_) { | |
Sergey Ulanov
2016/08/31 23:00:16
Add a comment here to explain what happens here.
joedow
2016/09/02 21:58:59
Done.
| |
187 if (!DelegateToElevatedHost(std::move(message))) { | |
188 response->SetBoolean("result", false); | |
Sergey Ulanov
2016/08/31 23:00:16
Does the app support this field right now?
joedow
2016/09/02 21:58:59
I seem to remember testing this in a previous iter
| |
189 SendMessageToClient(std::move(response)); | |
190 } | |
191 return; | |
192 } | |
193 | |
151 if (it2me_host_.get()) { | 194 if (it2me_host_.get()) { |
152 SendErrorAndExit(std::move(response), | 195 SendErrorAndExit(std::move(response), |
153 "Connect can be called only when disconnected."); | 196 "Connect can be called only when disconnected."); |
154 return; | 197 return; |
155 } | 198 } |
156 | 199 |
157 XmppSignalStrategy::XmppServerConfig xmpp_config = xmpp_server_config_; | 200 XmppSignalStrategy::XmppServerConfig xmpp_config = xmpp_server_config_; |
158 | 201 |
159 if (!message.GetString("userName", &xmpp_config.username)) { | 202 if (!message->GetString("userName", &xmpp_config.username)) { |
160 SendErrorAndExit(std::move(response), "'userName' not found in request."); | 203 SendErrorAndExit(std::move(response), "'userName' not found in request."); |
161 return; | 204 return; |
162 } | 205 } |
163 | 206 |
164 std::string auth_service_with_token; | 207 std::string auth_service_with_token; |
165 if (!message.GetString("authServiceWithToken", &auth_service_with_token)) { | 208 if (!message->GetString("authServiceWithToken", &auth_service_with_token)) { |
166 SendErrorAndExit(std::move(response), | 209 SendErrorAndExit(std::move(response), |
167 "'authServiceWithToken' not found in request."); | 210 "'authServiceWithToken' not found in request."); |
168 return; | 211 return; |
169 } | 212 } |
170 | 213 |
171 // For backward compatibility the webapp still passes OAuth service as part of | 214 // For backward compatibility the webapp still passes OAuth service as part of |
172 // the authServiceWithToken field. But auth service part is always expected to | 215 // the authServiceWithToken field. But auth service part is always expected to |
173 // be set to oauth2. | 216 // be set to oauth2. |
174 const char kOAuth2ServicePrefix[] = "oauth2:"; | 217 const char kOAuth2ServicePrefix[] = "oauth2:"; |
175 if (!base::StartsWith(auth_service_with_token, kOAuth2ServicePrefix, | 218 if (!base::StartsWith(auth_service_with_token, kOAuth2ServicePrefix, |
176 base::CompareCase::SENSITIVE)) { | 219 base::CompareCase::SENSITIVE)) { |
177 SendErrorAndExit(std::move(response), "Invalid 'authServiceWithToken': " + | 220 SendErrorAndExit(std::move(response), "Invalid 'authServiceWithToken': " + |
178 auth_service_with_token); | 221 auth_service_with_token); |
179 return; | 222 return; |
180 } | 223 } |
181 | 224 |
182 xmpp_config.auth_token = | 225 xmpp_config.auth_token = |
183 auth_service_with_token.substr(strlen(kOAuth2ServicePrefix)); | 226 auth_service_with_token.substr(strlen(kOAuth2ServicePrefix)); |
184 | 227 |
185 #if !defined(NDEBUG) | 228 #if !defined(NDEBUG) |
186 std::string address; | 229 std::string address; |
187 if (!message.GetString("xmppServerAddress", &address)) { | 230 if (!message->GetString("xmppServerAddress", &address)) { |
188 SendErrorAndExit(std::move(response), | 231 SendErrorAndExit(std::move(response), |
189 "'xmppServerAddress' not found in request."); | 232 "'xmppServerAddress' not found in request."); |
190 return; | 233 return; |
191 } | 234 } |
192 | 235 |
193 if (!net::ParseHostAndPort(address, &xmpp_config.host, | 236 if (!net::ParseHostAndPort(address, &xmpp_config.host, |
194 &xmpp_config.port)) { | 237 &xmpp_config.port)) { |
195 SendErrorAndExit(std::move(response), | 238 SendErrorAndExit(std::move(response), |
196 "Invalid 'xmppServerAddress': " + address); | 239 "Invalid 'xmppServerAddress': " + address); |
197 return; | 240 return; |
198 } | 241 } |
199 | 242 |
200 if (!message.GetBoolean("xmppServerUseTls", &xmpp_config.use_tls)) { | 243 if (!message->GetBoolean("xmppServerUseTls", &xmpp_config.use_tls)) { |
201 SendErrorAndExit(std::move(response), | 244 SendErrorAndExit(std::move(response), |
202 "'xmppServerUseTls' not found in request."); | 245 "'xmppServerUseTls' not found in request."); |
203 return; | 246 return; |
204 } | 247 } |
205 | 248 |
206 if (!message.GetString("directoryBotJid", &directory_bot_jid_)) { | 249 if (!message->GetString("directoryBotJid", &directory_bot_jid_)) { |
207 SendErrorAndExit(std::move(response), | 250 SendErrorAndExit(std::move(response), |
208 "'directoryBotJid' not found in request."); | 251 "'directoryBotJid' not found in request."); |
209 return; | 252 return; |
210 } | 253 } |
211 #endif // !defined(NDEBUG) | 254 #endif // !defined(NDEBUG) |
212 | 255 |
213 // Create the It2Me host and start connecting. | 256 // Create the It2Me host and start connecting. |
214 it2me_host_ = factory_->CreateIt2MeHost(host_context_->Copy(), | 257 it2me_host_ = factory_->CreateIt2MeHost(host_context_->Copy(), |
215 weak_ptr_, | 258 weak_ptr_, |
216 xmpp_config, | 259 xmpp_config, |
217 directory_bot_jid_); | 260 directory_bot_jid_); |
218 it2me_host_->Connect(); | 261 it2me_host_->Connect(); |
219 | 262 |
220 SendMessageToClient(std::move(response)); | 263 SendMessageToClient(std::move(response)); |
221 } | 264 } |
222 | 265 |
223 void It2MeNativeMessagingHost::ProcessDisconnect( | 266 void It2MeNativeMessagingHost::ProcessDisconnect( |
224 const base::DictionaryValue& message, | 267 std::unique_ptr<base::DictionaryValue> message, |
225 std::unique_ptr<base::DictionaryValue> response) { | 268 std::unique_ptr<base::DictionaryValue> response) { |
226 DCHECK(task_runner()->BelongsToCurrentThread()); | 269 DCHECK(task_runner()->BelongsToCurrentThread()); |
270 DCHECK(policy_received_); | |
271 | |
272 if (needs_elevation_ && allow_elevated_host_) { | |
273 if (!DelegateToElevatedHost(std::move(message))) { | |
274 response->SetBoolean("result", false); | |
275 SendMessageToClient(std::move(response)); | |
276 } | |
277 return; | |
278 } | |
227 | 279 |
228 if (it2me_host_.get()) { | 280 if (it2me_host_.get()) { |
229 it2me_host_->Disconnect(); | 281 it2me_host_->Disconnect(); |
230 it2me_host_ = nullptr; | 282 it2me_host_ = nullptr; |
231 } | 283 } |
232 SendMessageToClient(std::move(response)); | 284 SendMessageToClient(std::move(response)); |
233 } | 285 } |
234 | 286 |
235 void It2MeNativeMessagingHost::SendErrorAndExit( | 287 void It2MeNativeMessagingHost::SendErrorAndExit( |
236 std::unique_ptr<base::DictionaryValue> response, | 288 std::unique_ptr<base::DictionaryValue> response, |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
321 It2MeNativeMessagingHost::task_runner() const { | 373 It2MeNativeMessagingHost::task_runner() const { |
322 return host_context_->ui_task_runner(); | 374 return host_context_->ui_task_runner(); |
323 } | 375 } |
324 | 376 |
325 /* static */ | 377 /* static */ |
326 std::string It2MeNativeMessagingHost::HostStateToString( | 378 std::string It2MeNativeMessagingHost::HostStateToString( |
327 It2MeHostState host_state) { | 379 It2MeHostState host_state) { |
328 return ValueToName(kIt2MeHostStates, host_state); | 380 return ValueToName(kIt2MeHostStates, host_state); |
329 } | 381 } |
330 | 382 |
383 void It2MeNativeMessagingHost::OnPolicyUpdate( | |
384 std::unique_ptr<base::DictionaryValue> policies) { | |
385 // The policy watcher runs on the |file_task_runner| but we want to run the | |
386 // update code on |task_runner|. | |
387 if (!task_runner()->BelongsToCurrentThread()) { | |
388 task_runner()->PostTask( | |
389 FROM_HERE, base::Bind(&It2MeNativeMessagingHost::OnPolicyUpdate, | |
390 weak_ptr_, base::Passed(&policies))); | |
391 return; | |
392 } | |
393 | |
394 if (policy_received_) { | |
395 // Don't dynamically change how the host operates since we don't have a good | |
396 // way to communicate changes to the user. | |
397 return; | |
398 } | |
399 | |
400 if (!policies->GetBoolean( | |
401 policy::key::kRemoteAccessHostAllowUiAccessForRemoteAssistance, | |
402 &allow_elevated_host_)) { | |
403 LOG(WARNING) << "Failed to retrieve elevated host policy value."; | |
404 } | |
405 | |
406 policy_received_ = true; | |
407 if (!pending_connect_.is_null()) { | |
408 base::ResetAndReturn(&pending_connect_).Run(); | |
409 } | |
410 } | |
411 | |
412 void It2MeNativeMessagingHost::OnPolicyError() { | |
413 // TODO(joedow): Report the policy error to the user. crbug.com/433009 | |
414 NOTIMPLEMENTED(); | |
415 } | |
416 | |
417 #if defined(OS_WIN) | |
418 | |
419 bool It2MeNativeMessagingHost::DelegateToElevatedHost( | |
420 std::unique_ptr<base::DictionaryValue> message) { | |
421 DCHECK(task_runner()->BelongsToCurrentThread()); | |
422 DCHECK(needs_elevation_); | |
423 DCHECK(allow_elevated_host_); | |
424 | |
425 if (!elevated_host_) { | |
426 base::FilePath binary_path = | |
427 base::CommandLine::ForCurrentProcess()->GetProgram(); | |
428 CHECK(binary_path.BaseName() == base::FilePath(kBaseHostBinaryName)); | |
429 | |
430 // The new process runs at an elevated level due to being granted uiAccess. | |
431 elevated_host_.reset(new ElevatedNativeMessagingHost( | |
432 binary_path.DirName().Append(kElevatedHostBinaryName), | |
433 /*parent_window_handle=*/0, | |
Sergey Ulanov
2016/08/31 23:00:17
Why is this not parameter passed to the elevated p
joedow
2016/09/02 21:58:59
Done.
| |
434 /*elevate_process=*/false, | |
435 /*host_timeout=*/base::TimeDelta(), client_)); | |
436 } | |
437 | |
438 if (elevated_host_->EnsureElevatedHostCreated()) { | |
439 elevated_host_->SendMessage(std::move(message)); | |
440 return true; | |
441 } | |
442 | |
443 return false; | |
444 } | |
445 | |
446 #else // !defined(OS_WIN) | |
447 | |
448 bool It2MeNativeMessagingHost::DelegateToElevatedHost( | |
449 std::unique_ptr<base::DictionaryValue> message) { | |
450 NOTREACHED(); | |
451 return false; | |
452 } | |
453 | |
454 #endif // !defined(OS_WIN) | |
455 | |
331 } // namespace remoting | 456 } // namespace remoting |
OLD | NEW |