OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "remoting/host/setup/native_messaging_host.h" | |
6 | |
7 #include <string> | |
8 | |
9 #include "base/basictypes.h" | |
10 #include "base/bind.h" | |
11 #include "base/callback.h" | |
12 #include "base/command_line.h" | |
13 #include "base/logging.h" | |
14 #include "base/message_loop/message_loop.h" | |
15 #include "base/run_loop.h" | |
16 #include "base/strings/string_number_conversions.h" | |
17 #include "base/strings/stringize_macros.h" | |
18 #include "base/threading/thread.h" | |
19 #include "base/values.h" | |
20 #include "google_apis/gaia/gaia_oauth_client.h" | |
21 #include "google_apis/google_api_keys.h" | |
22 #include "net/base/net_util.h" | |
23 #include "net/url_request/url_fetcher.h" | |
24 #include "remoting/base/rsa_key_pair.h" | |
25 #include "remoting/base/url_request_context.h" | |
26 #include "remoting/host/host_exit_codes.h" | |
27 #include "remoting/host/pairing_registry_delegate.h" | |
28 #include "remoting/host/pin_hash.h" | |
29 #include "remoting/host/setup/oauth_client.h" | |
30 #include "remoting/protocol/pairing_registry.h" | |
31 | |
32 namespace { | |
33 | |
34 const char kParentWindowSwitchName[] = "parent-window"; | |
35 | |
36 // redirect_uri to use when authenticating service accounts (service account | |
37 // codes are obtained "out-of-band", i.e., not through an OAuth redirect). | |
38 const char* kServiceAccountRedirectUri = "oob"; | |
39 | |
40 // Features supported in addition to the base protocol. | |
41 const char* kSupportedFeatures[] = { | |
42 "pairingRegistry", | |
43 "oauthClient" | |
44 }; | |
45 | |
46 // Helper to extract the "config" part of a message as a DictionaryValue. | |
47 // Returns NULL on failure, and logs an error message. | |
48 scoped_ptr<base::DictionaryValue> ConfigDictionaryFromMessage( | |
49 const base::DictionaryValue& message) { | |
50 scoped_ptr<base::DictionaryValue> result; | |
51 const base::DictionaryValue* config_dict; | |
52 if (message.GetDictionary("config", &config_dict)) { | |
53 result.reset(config_dict->DeepCopy()); | |
54 } else { | |
55 LOG(ERROR) << "'config' dictionary not found"; | |
56 } | |
57 return result.Pass(); | |
58 } | |
59 | |
60 } // namespace | |
61 | |
62 namespace remoting { | |
63 | |
64 NativeMessagingHost::NativeMessagingHost( | |
65 scoped_refptr<DaemonController> daemon_controller, | |
66 scoped_refptr<protocol::PairingRegistry> pairing_registry, | |
67 scoped_ptr<OAuthClient> oauth_client) | |
68 : daemon_controller_(daemon_controller), | |
69 pairing_registry_(pairing_registry), | |
70 oauth_client_(oauth_client.Pass()), | |
71 weak_factory_(this) { | |
72 weak_ptr_ = weak_factory_.GetWeakPtr(); | |
73 } | |
74 | |
75 NativeMessagingHost::~NativeMessagingHost() { | |
76 } | |
77 | |
78 void NativeMessagingHost::ProcessMessage( | |
79 scoped_ptr<base::DictionaryValue> message, | |
80 const SendResponseCallback& done) { | |
81 scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue()); | |
82 | |
83 // If the client supplies an ID, it will expect it in the response. This | |
84 // might be a string or a number, so cope with both. | |
85 const base::Value* id; | |
86 if (message->Get("id", &id)) | |
87 response->Set("id", id->DeepCopy()); | |
88 | |
89 std::string type; | |
90 if (!message->GetString("type", &type)) { | |
91 LOG(ERROR) << "'type' not found"; | |
92 done.Run(scoped_ptr<base::DictionaryValue>()); | |
93 return; | |
94 } | |
95 | |
96 response->SetString("type", type + "Response"); | |
97 | |
98 bool success = false; | |
99 if (type == "hello") { | |
100 success = ProcessHello(*message, response.Pass(), done); | |
101 } else if (type == "clearPairedClients") { | |
102 success = ProcessClearPairedClients(*message, response.Pass(), done); | |
103 } else if (type == "deletePairedClient") { | |
104 success = ProcessDeletePairedClient(*message, response.Pass(), done); | |
105 } else if (type == "getHostName") { | |
106 success = ProcessGetHostName(*message, response.Pass(), done); | |
107 } else if (type == "getPinHash") { | |
108 success = ProcessGetPinHash(*message, response.Pass(), done); | |
109 } else if (type == "generateKeyPair") { | |
110 success = ProcessGenerateKeyPair(*message, response.Pass(), done); | |
111 } else if (type == "updateDaemonConfig") { | |
112 success = ProcessUpdateDaemonConfig(*message, response.Pass(), done); | |
113 } else if (type == "getDaemonConfig") { | |
114 success = ProcessGetDaemonConfig(*message, response.Pass(), done); | |
115 } else if (type == "getPairedClients") { | |
116 success = ProcessGetPairedClients(*message, response.Pass(), done); | |
117 } else if (type == "getUsageStatsConsent") { | |
118 success = ProcessGetUsageStatsConsent(*message, response.Pass(), done); | |
119 } else if (type == "startDaemon") { | |
120 success = ProcessStartDaemon(*message, response.Pass(), done); | |
121 } else if (type == "stopDaemon") { | |
122 success = ProcessStopDaemon(*message, response.Pass(), done); | |
123 } else if (type == "getDaemonState") { | |
124 success = ProcessGetDaemonState(*message, response.Pass(), done); | |
125 } else if (type == "getHostClientId") { | |
126 success = ProcessGetHostClientId(*message, response.Pass(), done); | |
127 } else if (type == "getCredentialsFromAuthCode") { | |
128 success = ProcessGetCredentialsFromAuthCode(*message, response.Pass(), | |
129 done); | |
130 } else { | |
131 LOG(ERROR) << "Unsupported request type: " << type; | |
132 } | |
133 | |
134 if (!success) | |
135 done.Run(scoped_ptr<base::DictionaryValue>()); | |
136 } | |
137 | |
138 bool NativeMessagingHost::ProcessHello( | |
139 const base::DictionaryValue& message, | |
140 scoped_ptr<base::DictionaryValue> response, | |
141 const SendResponseCallback& done) { | |
142 response->SetString("version", STRINGIZE(VERSION)); | |
143 scoped_ptr<base::ListValue> supported_features_list(new base::ListValue()); | |
144 supported_features_list->AppendStrings(std::vector<std::string>( | |
145 kSupportedFeatures, kSupportedFeatures + arraysize(kSupportedFeatures))); | |
146 response->Set("supportedFeatures", supported_features_list.release()); | |
147 done.Run(response.Pass()); | |
148 return true; | |
149 } | |
150 | |
151 bool NativeMessagingHost::ProcessClearPairedClients( | |
152 const base::DictionaryValue& message, | |
153 scoped_ptr<base::DictionaryValue> response, | |
154 const SendResponseCallback& done) { | |
155 if (pairing_registry_) { | |
156 pairing_registry_->ClearAllPairings( | |
157 base::Bind(&NativeMessagingHost::SendBooleanResult, weak_ptr_, | |
158 done, base::Passed(&response))); | |
159 } else { | |
160 SendBooleanResult(done, response.Pass(), false); | |
161 } | |
162 return true; | |
163 } | |
164 | |
165 bool NativeMessagingHost::ProcessDeletePairedClient( | |
166 const base::DictionaryValue& message, | |
167 scoped_ptr<base::DictionaryValue> response, | |
168 const SendResponseCallback& done) { | |
169 std::string client_id; | |
170 if (!message.GetString(protocol::PairingRegistry::kClientIdKey, &client_id)) { | |
171 LOG(ERROR) << "'" << protocol::PairingRegistry::kClientIdKey | |
172 << "' string not found."; | |
173 return false; | |
174 } | |
175 | |
176 if (pairing_registry_) { | |
177 pairing_registry_->DeletePairing( | |
178 client_id, base::Bind(&NativeMessagingHost::SendBooleanResult, | |
179 weak_ptr_, done, base::Passed(&response))); | |
180 } else { | |
181 SendBooleanResult(done, response.Pass(), false); | |
182 } | |
183 return true; | |
184 } | |
185 | |
186 bool NativeMessagingHost::ProcessGetHostName( | |
187 const base::DictionaryValue& message, | |
188 scoped_ptr<base::DictionaryValue> response, | |
189 const SendResponseCallback& done) { | |
190 response->SetString("hostname", net::GetHostName()); | |
191 done.Run(response.Pass()); | |
192 return true; | |
193 } | |
194 | |
195 bool NativeMessagingHost::ProcessGetPinHash( | |
196 const base::DictionaryValue& message, | |
197 scoped_ptr<base::DictionaryValue> response, | |
198 const SendResponseCallback& done) { | |
199 std::string host_id; | |
200 if (!message.GetString("hostId", &host_id)) { | |
201 LOG(ERROR) << "'hostId' not found: " << message; | |
202 return false; | |
203 } | |
204 std::string pin; | |
205 if (!message.GetString("pin", &pin)) { | |
206 LOG(ERROR) << "'pin' not found: " << message; | |
207 return false; | |
208 } | |
209 response->SetString("hash", MakeHostPinHash(host_id, pin)); | |
210 done.Run(response.Pass()); | |
211 return true; | |
212 } | |
213 | |
214 bool NativeMessagingHost::ProcessGenerateKeyPair( | |
215 const base::DictionaryValue& message, | |
216 scoped_ptr<base::DictionaryValue> response, | |
217 const SendResponseCallback& done) { | |
218 scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::Generate(); | |
219 response->SetString("privateKey", key_pair->ToString()); | |
220 response->SetString("publicKey", key_pair->GetPublicKey()); | |
221 done.Run(response.Pass()); | |
222 return true; | |
223 } | |
224 | |
225 bool NativeMessagingHost::ProcessUpdateDaemonConfig( | |
226 const base::DictionaryValue& message, | |
227 scoped_ptr<base::DictionaryValue> response, | |
228 const SendResponseCallback& done) { | |
229 scoped_ptr<base::DictionaryValue> config_dict = | |
230 ConfigDictionaryFromMessage(message); | |
231 if (!config_dict) | |
232 return false; | |
233 | |
234 daemon_controller_->UpdateConfig( | |
235 config_dict.Pass(), | |
236 base::Bind(&NativeMessagingHost::SendAsyncResult, weak_ptr_, | |
237 done, base::Passed(&response))); | |
238 return true; | |
239 } | |
240 | |
241 bool NativeMessagingHost::ProcessGetDaemonConfig( | |
242 const base::DictionaryValue& message, | |
243 scoped_ptr<base::DictionaryValue> response, | |
244 const SendResponseCallback& done) { | |
245 daemon_controller_->GetConfig( | |
246 base::Bind(&NativeMessagingHost::SendConfigResponse, weak_ptr_, | |
247 done, base::Passed(&response))); | |
248 return true; | |
249 } | |
250 | |
251 bool NativeMessagingHost::ProcessGetPairedClients( | |
252 const base::DictionaryValue& message, | |
253 scoped_ptr<base::DictionaryValue> response, | |
254 const SendResponseCallback& done) { | |
255 if (pairing_registry_) { | |
256 pairing_registry_->GetAllPairings( | |
257 base::Bind(&NativeMessagingHost::SendPairedClientsResponse, weak_ptr_, | |
258 done, base::Passed(&response))); | |
259 } else { | |
260 scoped_ptr<base::ListValue> no_paired_clients(new base::ListValue); | |
261 SendPairedClientsResponse(done, response.Pass(), no_paired_clients.Pass()); | |
262 } | |
263 return true; | |
264 } | |
265 | |
266 bool NativeMessagingHost::ProcessGetUsageStatsConsent( | |
267 const base::DictionaryValue& message, | |
268 scoped_ptr<base::DictionaryValue> response, | |
269 const SendResponseCallback& done) { | |
270 daemon_controller_->GetUsageStatsConsent( | |
271 base::Bind(&NativeMessagingHost::SendUsageStatsConsentResponse, | |
272 weak_ptr_, done, base::Passed(&response))); | |
273 return true; | |
274 } | |
275 | |
276 bool NativeMessagingHost::ProcessStartDaemon( | |
277 const base::DictionaryValue& message, | |
278 scoped_ptr<base::DictionaryValue> response, | |
279 const SendResponseCallback& done) { | |
280 bool consent; | |
281 if (!message.GetBoolean("consent", &consent)) { | |
282 LOG(ERROR) << "'consent' not found."; | |
283 return false; | |
284 } | |
285 | |
286 scoped_ptr<base::DictionaryValue> config_dict = | |
287 ConfigDictionaryFromMessage(message); | |
288 if (!config_dict) | |
289 return false; | |
290 | |
291 daemon_controller_->SetConfigAndStart( | |
292 config_dict.Pass(), consent, | |
293 base::Bind(&NativeMessagingHost::SendAsyncResult, weak_ptr_, | |
294 done, base::Passed(&response))); | |
295 return true; | |
296 } | |
297 | |
298 bool NativeMessagingHost::ProcessStopDaemon( | |
299 const base::DictionaryValue& message, | |
300 scoped_ptr<base::DictionaryValue> response, | |
301 const SendResponseCallback& done) { | |
302 daemon_controller_->Stop( | |
303 base::Bind(&NativeMessagingHost::SendAsyncResult, weak_ptr_, | |
304 done, base::Passed(&response))); | |
305 return true; | |
306 } | |
307 | |
308 bool NativeMessagingHost::ProcessGetDaemonState( | |
309 const base::DictionaryValue& message, | |
310 scoped_ptr<base::DictionaryValue> response, | |
311 const SendResponseCallback& done) { | |
312 DaemonController::State state = daemon_controller_->GetState(); | |
313 switch (state) { | |
314 case DaemonController::STATE_NOT_IMPLEMENTED: | |
315 response->SetString("state", "NOT_IMPLEMENTED"); | |
316 break; | |
317 case DaemonController::STATE_NOT_INSTALLED: | |
318 response->SetString("state", "NOT_INSTALLED"); | |
319 break; | |
320 case DaemonController::STATE_INSTALLING: | |
321 response->SetString("state", "INSTALLING"); | |
322 break; | |
323 case DaemonController::STATE_STOPPED: | |
324 response->SetString("state", "STOPPED"); | |
325 break; | |
326 case DaemonController::STATE_STARTING: | |
327 response->SetString("state", "STARTING"); | |
328 break; | |
329 case DaemonController::STATE_STARTED: | |
330 response->SetString("state", "STARTED"); | |
331 break; | |
332 case DaemonController::STATE_STOPPING: | |
333 response->SetString("state", "STOPPING"); | |
334 break; | |
335 case DaemonController::STATE_UNKNOWN: | |
336 response->SetString("state", "UNKNOWN"); | |
337 break; | |
338 } | |
339 done.Run(response.Pass()); | |
340 return true; | |
341 } | |
342 | |
343 bool NativeMessagingHost::ProcessGetHostClientId( | |
344 const base::DictionaryValue& message, | |
345 scoped_ptr<base::DictionaryValue> response, | |
346 const SendResponseCallback& done) { | |
347 response->SetString("clientId", google_apis::GetOAuth2ClientID( | |
348 google_apis::CLIENT_REMOTING_HOST)); | |
349 done.Run(response.Pass()); | |
350 return true; | |
351 } | |
352 | |
353 bool NativeMessagingHost::ProcessGetCredentialsFromAuthCode( | |
354 const base::DictionaryValue& message, | |
355 scoped_ptr<base::DictionaryValue> response, | |
356 const SendResponseCallback& done) { | |
357 std::string auth_code; | |
358 if (!message.GetString("authorizationCode", &auth_code)) { | |
359 LOG(ERROR) << "'authorizationCode' string not found."; | |
360 return false; | |
361 } | |
362 | |
363 gaia::OAuthClientInfo oauth_client_info = { | |
364 google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING_HOST), | |
365 google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_REMOTING_HOST), | |
366 kServiceAccountRedirectUri | |
367 }; | |
368 | |
369 oauth_client_->GetCredentialsFromAuthCode( | |
370 oauth_client_info, auth_code, base::Bind( | |
371 &NativeMessagingHost::SendCredentialsResponse, weak_ptr_, | |
372 done, base::Passed(&response))); | |
373 | |
374 return true; | |
375 } | |
376 | |
377 void NativeMessagingHost::SendConfigResponse( | |
378 const SendResponseCallback& done, | |
379 scoped_ptr<base::DictionaryValue> response, | |
380 scoped_ptr<base::DictionaryValue> config) { | |
381 if (config) { | |
382 response->Set("config", config.release()); | |
383 } else { | |
384 response->Set("config", Value::CreateNullValue()); | |
385 } | |
386 done.Run(response.Pass()); | |
387 } | |
388 | |
389 void NativeMessagingHost::SendPairedClientsResponse( | |
390 const SendResponseCallback& done, | |
391 scoped_ptr<base::DictionaryValue> response, | |
392 scoped_ptr<base::ListValue> pairings) { | |
393 response->Set("pairedClients", pairings.release()); | |
394 done.Run(response.Pass()); | |
395 } | |
396 | |
397 void NativeMessagingHost::SendUsageStatsConsentResponse( | |
398 const SendResponseCallback& done, | |
399 scoped_ptr<base::DictionaryValue> response, | |
400 const DaemonController::UsageStatsConsent& consent) { | |
401 response->SetBoolean("supported", consent.supported); | |
402 response->SetBoolean("allowed", consent.allowed); | |
403 response->SetBoolean("setByPolicy", consent.set_by_policy); | |
404 done.Run(response.Pass()); | |
405 } | |
406 | |
407 void NativeMessagingHost::SendAsyncResult( | |
408 const SendResponseCallback& done, | |
409 scoped_ptr<base::DictionaryValue> response, | |
410 DaemonController::AsyncResult result) { | |
411 switch (result) { | |
412 case DaemonController::RESULT_OK: | |
413 response->SetString("result", "OK"); | |
414 break; | |
415 case DaemonController::RESULT_FAILED: | |
416 response->SetString("result", "FAILED"); | |
417 break; | |
418 case DaemonController::RESULT_CANCELLED: | |
419 response->SetString("result", "CANCELLED"); | |
420 break; | |
421 case DaemonController::RESULT_FAILED_DIRECTORY: | |
422 response->SetString("result", "FAILED_DIRECTORY"); | |
423 break; | |
424 } | |
425 done.Run(response.Pass()); | |
426 } | |
427 | |
428 void NativeMessagingHost::SendBooleanResult( | |
429 const SendResponseCallback& done, | |
430 scoped_ptr<base::DictionaryValue> response, bool result) { | |
431 response->SetBoolean("result", result); | |
432 done.Run(response.Pass()); | |
433 } | |
434 | |
435 void NativeMessagingHost::SendCredentialsResponse( | |
436 const SendResponseCallback& done, | |
437 scoped_ptr<base::DictionaryValue> response, | |
438 const std::string& user_email, | |
439 const std::string& refresh_token) { | |
440 response->SetString("userEmail", user_email); | |
441 response->SetString("refreshToken", refresh_token); | |
442 done.Run(response.Pass()); | |
443 } | |
444 | |
445 int NativeMessagingHostMain() { | |
446 #if defined(OS_WIN) | |
447 // GetStdHandle() returns pseudo-handles for stdin and stdout even if | |
448 // the hosting executable specifies "Windows" subsystem. However the returned | |
449 // handles are invalid in that case unless standard input and output are | |
450 // redirected to a pipe or file. | |
451 base::PlatformFile read_file = GetStdHandle(STD_INPUT_HANDLE); | |
452 base::PlatformFile write_file = GetStdHandle(STD_OUTPUT_HANDLE); | |
453 #elif defined(OS_POSIX) | |
454 base::PlatformFile read_file = STDIN_FILENO; | |
455 base::PlatformFile write_file = STDOUT_FILENO; | |
456 #else | |
457 #error Not implemented. | |
458 #endif | |
459 | |
460 // Mac OS X requires that the main thread be a UI message loop in order to | |
461 // receive distributed notifications from the System Preferences pane. An | |
462 // IO thread is needed for the pairing registry and URL context getter. | |
463 base::Thread io_thread("io_thread"); | |
464 io_thread.StartWithOptions( | |
465 base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); | |
466 | |
467 base::MessageLoopForUI message_loop; | |
468 base::RunLoop run_loop; | |
469 | |
470 scoped_refptr<DaemonController> daemon_controller = | |
471 DaemonController::Create(); | |
472 | |
473 // Pass handle of the native view to the controller so that the UAC prompts | |
474 // are focused properly. | |
475 const CommandLine* command_line = CommandLine::ForCurrentProcess(); | |
476 if (command_line->HasSwitch(kParentWindowSwitchName)) { | |
477 std::string native_view = | |
478 command_line->GetSwitchValueASCII(kParentWindowSwitchName); | |
479 int64 native_view_handle = 0; | |
480 if (base::StringToInt64(native_view, &native_view_handle)) { | |
481 daemon_controller->SetWindow(reinterpret_cast<void*>(native_view_handle)); | |
482 } else { | |
483 LOG(WARNING) << "Invalid parameter value --" << kParentWindowSwitchName | |
484 << "=" << native_view; | |
485 } | |
486 } | |
487 | |
488 // OAuth client (for credential requests). | |
489 scoped_refptr<net::URLRequestContextGetter> url_request_context_getter( | |
490 new URLRequestContextGetter(io_thread.message_loop_proxy())); | |
491 scoped_ptr<OAuthClient> oauth_client( | |
492 new OAuthClient(url_request_context_getter)); | |
493 | |
494 net::URLFetcher::SetIgnoreCertificateRequests(true); | |
495 | |
496 // Create the pairing registry and native messaging host. | |
497 scoped_refptr<protocol::PairingRegistry> pairing_registry = | |
498 CreatePairingRegistry(io_thread.message_loop_proxy()); | |
499 scoped_ptr<NativeMessagingChannel::Delegate> host( | |
500 new NativeMessagingHost(daemon_controller, | |
501 pairing_registry, | |
502 oauth_client.Pass())); | |
503 | |
504 // Set up the native messaging channel. | |
505 scoped_ptr<NativeMessagingChannel> channel( | |
506 new NativeMessagingChannel(host.Pass(), read_file, write_file)); | |
507 channel->Start(run_loop.QuitClosure()); | |
508 | |
509 // Run the loop until channel is alive. | |
510 run_loop.Run(); | |
511 return kSuccessExitCode; | |
512 } | |
513 | |
514 } // namespace remoting | |
OLD | NEW |