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/me2me_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/callback_helpers.h"
13 #include "base/logging.h"
14 #include "base/strings/stringize_macros.h"
15 #include "base/threading/thread.h"
16 #include "base/values.h"
17 #include "google_apis/gaia/gaia_oauth_client.h"
18 #include "google_apis/google_api_keys.h"
19 #include "net/base/net_util.h"
20 #include "remoting/base/rsa_key_pair.h"
21 #include "remoting/host/pin_hash.h"
22 #include "remoting/host/setup/oauth_client.h"
23 #include "remoting/protocol/pairing_registry.h"
24
25 namespace {
26
27 // redirect_uri to use when authenticating service accounts (service account
28 // codes are obtained "out-of-band", i.e., not through an OAuth redirect).
29 const char* kServiceAccountRedirectUri = "oob";
30
31 // Features supported in addition to the base protocol.
32 const char* kSupportedFeatures[] = {
33 "pairingRegistry",
34 "oauthClient"
35 };
36
37 // Helper to extract the "config" part of a message as a DictionaryValue.
38 // Returns NULL on failure, and logs an error message.
ConfigDictionaryFromMessage(const base::DictionaryValue & message)39 scoped_ptr<base::DictionaryValue> ConfigDictionaryFromMessage(
40 const base::DictionaryValue& message) {
41 scoped_ptr<base::DictionaryValue> result;
42 const base::DictionaryValue* config_dict;
43 if (message.GetDictionary("config", &config_dict)) {
44 result.reset(config_dict->DeepCopy());
45 } else {
46 LOG(ERROR) << "'config' dictionary not found";
47 }
48 return result.Pass();
49 }
50
51 } // namespace
52
53 namespace remoting {
54
Me2MeNativeMessagingHost(scoped_ptr<NativeMessagingChannel> channel,scoped_refptr<DaemonController> daemon_controller,scoped_refptr<protocol::PairingRegistry> pairing_registry,scoped_ptr<OAuthClient> oauth_client)55 Me2MeNativeMessagingHost::Me2MeNativeMessagingHost(
56 scoped_ptr<NativeMessagingChannel> channel,
57 scoped_refptr<DaemonController> daemon_controller,
58 scoped_refptr<protocol::PairingRegistry> pairing_registry,
59 scoped_ptr<OAuthClient> oauth_client)
60 : channel_(channel.Pass()),
61 daemon_controller_(daemon_controller),
62 pairing_registry_(pairing_registry),
63 oauth_client_(oauth_client.Pass()),
64 weak_factory_(this) {
65 weak_ptr_ = weak_factory_.GetWeakPtr();
66 }
67
~Me2MeNativeMessagingHost()68 Me2MeNativeMessagingHost::~Me2MeNativeMessagingHost() {
69 DCHECK(thread_checker_.CalledOnValidThread());
70 }
71
Start(const base::Closure & quit_closure)72 void Me2MeNativeMessagingHost::Start(
73 const base::Closure& quit_closure) {
74 DCHECK(thread_checker_.CalledOnValidThread());
75
76 channel_->Start(
77 base::Bind(&Me2MeNativeMessagingHost::ProcessMessage, weak_ptr_),
78 quit_closure);
79 }
80
ProcessMessage(scoped_ptr<base::DictionaryValue> message)81 void Me2MeNativeMessagingHost::ProcessMessage(
82 scoped_ptr<base::DictionaryValue> message) {
83 DCHECK(thread_checker_.CalledOnValidThread());
84
85 scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue());
86
87 // If the client supplies an ID, it will expect it in the response. This
88 // might be a string or a number, so cope with both.
89 const base::Value* id;
90 if (message->Get("id", &id))
91 response->Set("id", id->DeepCopy());
92
93 std::string type;
94 if (!message->GetString("type", &type)) {
95 LOG(ERROR) << "'type' not found";
96 channel_->SendMessage(scoped_ptr<base::DictionaryValue>());
97 return;
98 }
99
100 response->SetString("type", type + "Response");
101
102 bool success = false;
103 if (type == "hello") {
104 success = ProcessHello(*message, response.Pass());
105 } else if (type == "clearPairedClients") {
106 success = ProcessClearPairedClients(*message, response.Pass());
107 } else if (type == "deletePairedClient") {
108 success = ProcessDeletePairedClient(*message, response.Pass());
109 } else if (type == "getHostName") {
110 success = ProcessGetHostName(*message, response.Pass());
111 } else if (type == "getPinHash") {
112 success = ProcessGetPinHash(*message, response.Pass());
113 } else if (type == "generateKeyPair") {
114 success = ProcessGenerateKeyPair(*message, response.Pass());
115 } else if (type == "updateDaemonConfig") {
116 success = ProcessUpdateDaemonConfig(*message, response.Pass());
117 } else if (type == "getDaemonConfig") {
118 success = ProcessGetDaemonConfig(*message, response.Pass());
119 } else if (type == "getPairedClients") {
120 success = ProcessGetPairedClients(*message, response.Pass());
121 } else if (type == "getUsageStatsConsent") {
122 success = ProcessGetUsageStatsConsent(*message, response.Pass());
123 } else if (type == "startDaemon") {
124 success = ProcessStartDaemon(*message, response.Pass());
125 } else if (type == "stopDaemon") {
126 success = ProcessStopDaemon(*message, response.Pass());
127 } else if (type == "getDaemonState") {
128 success = ProcessGetDaemonState(*message, response.Pass());
129 } else if (type == "getHostClientId") {
130 success = ProcessGetHostClientId(*message, response.Pass());
131 } else if (type == "getCredentialsFromAuthCode") {
132 success = ProcessGetCredentialsFromAuthCode(*message, response.Pass());
133 } else {
134 LOG(ERROR) << "Unsupported request type: " << type;
135 }
136
137 if (!success)
138 channel_->SendMessage(scoped_ptr<base::DictionaryValue>());
139 }
140
ProcessHello(const base::DictionaryValue & message,scoped_ptr<base::DictionaryValue> response)141 bool Me2MeNativeMessagingHost::ProcessHello(
142 const base::DictionaryValue& message,
143 scoped_ptr<base::DictionaryValue> response) {
144 DCHECK(thread_checker_.CalledOnValidThread());
145
146 response->SetString("version", STRINGIZE(VERSION));
147 scoped_ptr<base::ListValue> supported_features_list(new base::ListValue());
148 supported_features_list->AppendStrings(std::vector<std::string>(
149 kSupportedFeatures, kSupportedFeatures + arraysize(kSupportedFeatures)));
150 response->Set("supportedFeatures", supported_features_list.release());
151 channel_->SendMessage(response.Pass());
152 return true;
153 }
154
ProcessClearPairedClients(const base::DictionaryValue & message,scoped_ptr<base::DictionaryValue> response)155 bool Me2MeNativeMessagingHost::ProcessClearPairedClients(
156 const base::DictionaryValue& message,
157 scoped_ptr<base::DictionaryValue> response) {
158 DCHECK(thread_checker_.CalledOnValidThread());
159
160 if (pairing_registry_) {
161 pairing_registry_->ClearAllPairings(
162 base::Bind(&Me2MeNativeMessagingHost::SendBooleanResult, weak_ptr_,
163 base::Passed(&response)));
164 } else {
165 SendBooleanResult(response.Pass(), false);
166 }
167 return true;
168 }
169
ProcessDeletePairedClient(const base::DictionaryValue & message,scoped_ptr<base::DictionaryValue> response)170 bool Me2MeNativeMessagingHost::ProcessDeletePairedClient(
171 const base::DictionaryValue& message,
172 scoped_ptr<base::DictionaryValue> response) {
173 DCHECK(thread_checker_.CalledOnValidThread());
174
175 std::string client_id;
176 if (!message.GetString(protocol::PairingRegistry::kClientIdKey, &client_id)) {
177 LOG(ERROR) << "'" << protocol::PairingRegistry::kClientIdKey
178 << "' string not found.";
179 return false;
180 }
181
182 if (pairing_registry_) {
183 pairing_registry_->DeletePairing(
184 client_id, base::Bind(&Me2MeNativeMessagingHost::SendBooleanResult,
185 weak_ptr_, base::Passed(&response)));
186 } else {
187 SendBooleanResult(response.Pass(), false);
188 }
189 return true;
190 }
191
ProcessGetHostName(const base::DictionaryValue & message,scoped_ptr<base::DictionaryValue> response)192 bool Me2MeNativeMessagingHost::ProcessGetHostName(
193 const base::DictionaryValue& message,
194 scoped_ptr<base::DictionaryValue> response) {
195 DCHECK(thread_checker_.CalledOnValidThread());
196
197 response->SetString("hostname", net::GetHostName());
198 channel_->SendMessage(response.Pass());
199 return true;
200 }
201
ProcessGetPinHash(const base::DictionaryValue & message,scoped_ptr<base::DictionaryValue> response)202 bool Me2MeNativeMessagingHost::ProcessGetPinHash(
203 const base::DictionaryValue& message,
204 scoped_ptr<base::DictionaryValue> response) {
205 DCHECK(thread_checker_.CalledOnValidThread());
206
207 std::string host_id;
208 if (!message.GetString("hostId", &host_id)) {
209 LOG(ERROR) << "'hostId' not found: " << message;
210 return false;
211 }
212 std::string pin;
213 if (!message.GetString("pin", &pin)) {
214 LOG(ERROR) << "'pin' not found: " << message;
215 return false;
216 }
217 response->SetString("hash", MakeHostPinHash(host_id, pin));
218 channel_->SendMessage(response.Pass());
219 return true;
220 }
221
ProcessGenerateKeyPair(const base::DictionaryValue & message,scoped_ptr<base::DictionaryValue> response)222 bool Me2MeNativeMessagingHost::ProcessGenerateKeyPair(
223 const base::DictionaryValue& message,
224 scoped_ptr<base::DictionaryValue> response) {
225 DCHECK(thread_checker_.CalledOnValidThread());
226
227 scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::Generate();
228 response->SetString("privateKey", key_pair->ToString());
229 response->SetString("publicKey", key_pair->GetPublicKey());
230 channel_->SendMessage(response.Pass());
231 return true;
232 }
233
ProcessUpdateDaemonConfig(const base::DictionaryValue & message,scoped_ptr<base::DictionaryValue> response)234 bool Me2MeNativeMessagingHost::ProcessUpdateDaemonConfig(
235 const base::DictionaryValue& message,
236 scoped_ptr<base::DictionaryValue> response) {
237 DCHECK(thread_checker_.CalledOnValidThread());
238
239 scoped_ptr<base::DictionaryValue> config_dict =
240 ConfigDictionaryFromMessage(message);
241 if (!config_dict)
242 return false;
243
244 daemon_controller_->UpdateConfig(
245 config_dict.Pass(),
246 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
247 base::Passed(&response)));
248 return true;
249 }
250
ProcessGetDaemonConfig(const base::DictionaryValue & message,scoped_ptr<base::DictionaryValue> response)251 bool Me2MeNativeMessagingHost::ProcessGetDaemonConfig(
252 const base::DictionaryValue& message,
253 scoped_ptr<base::DictionaryValue> response) {
254 DCHECK(thread_checker_.CalledOnValidThread());
255
256 daemon_controller_->GetConfig(
257 base::Bind(&Me2MeNativeMessagingHost::SendConfigResponse, weak_ptr_,
258 base::Passed(&response)));
259 return true;
260 }
261
ProcessGetPairedClients(const base::DictionaryValue & message,scoped_ptr<base::DictionaryValue> response)262 bool Me2MeNativeMessagingHost::ProcessGetPairedClients(
263 const base::DictionaryValue& message,
264 scoped_ptr<base::DictionaryValue> response) {
265 DCHECK(thread_checker_.CalledOnValidThread());
266
267 if (pairing_registry_) {
268 pairing_registry_->GetAllPairings(
269 base::Bind(&Me2MeNativeMessagingHost::SendPairedClientsResponse,
270 weak_ptr_, base::Passed(&response)));
271 } else {
272 scoped_ptr<base::ListValue> no_paired_clients(new base::ListValue);
273 SendPairedClientsResponse(response.Pass(), no_paired_clients.Pass());
274 }
275 return true;
276 }
277
ProcessGetUsageStatsConsent(const base::DictionaryValue & message,scoped_ptr<base::DictionaryValue> response)278 bool Me2MeNativeMessagingHost::ProcessGetUsageStatsConsent(
279 const base::DictionaryValue& message,
280 scoped_ptr<base::DictionaryValue> response) {
281 DCHECK(thread_checker_.CalledOnValidThread());
282
283 daemon_controller_->GetUsageStatsConsent(
284 base::Bind(&Me2MeNativeMessagingHost::SendUsageStatsConsentResponse,
285 weak_ptr_, base::Passed(&response)));
286 return true;
287 }
288
ProcessStartDaemon(const base::DictionaryValue & message,scoped_ptr<base::DictionaryValue> response)289 bool Me2MeNativeMessagingHost::ProcessStartDaemon(
290 const base::DictionaryValue& message,
291 scoped_ptr<base::DictionaryValue> response) {
292 DCHECK(thread_checker_.CalledOnValidThread());
293
294 bool consent;
295 if (!message.GetBoolean("consent", &consent)) {
296 LOG(ERROR) << "'consent' not found.";
297 return false;
298 }
299
300 scoped_ptr<base::DictionaryValue> config_dict =
301 ConfigDictionaryFromMessage(message);
302 if (!config_dict)
303 return false;
304
305 daemon_controller_->SetConfigAndStart(
306 config_dict.Pass(), consent,
307 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
308 base::Passed(&response)));
309 return true;
310 }
311
ProcessStopDaemon(const base::DictionaryValue & message,scoped_ptr<base::DictionaryValue> response)312 bool Me2MeNativeMessagingHost::ProcessStopDaemon(
313 const base::DictionaryValue& message,
314 scoped_ptr<base::DictionaryValue> response) {
315 DCHECK(thread_checker_.CalledOnValidThread());
316
317 daemon_controller_->Stop(
318 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
319 base::Passed(&response)));
320 return true;
321 }
322
ProcessGetDaemonState(const base::DictionaryValue & message,scoped_ptr<base::DictionaryValue> response)323 bool Me2MeNativeMessagingHost::ProcessGetDaemonState(
324 const base::DictionaryValue& message,
325 scoped_ptr<base::DictionaryValue> response) {
326 DCHECK(thread_checker_.CalledOnValidThread());
327
328 DaemonController::State state = daemon_controller_->GetState();
329 switch (state) {
330 case DaemonController::STATE_NOT_IMPLEMENTED:
331 response->SetString("state", "NOT_IMPLEMENTED");
332 break;
333 case DaemonController::STATE_NOT_INSTALLED:
334 response->SetString("state", "NOT_INSTALLED");
335 break;
336 case DaemonController::STATE_INSTALLING:
337 response->SetString("state", "INSTALLING");
338 break;
339 case DaemonController::STATE_STOPPED:
340 response->SetString("state", "STOPPED");
341 break;
342 case DaemonController::STATE_STARTING:
343 response->SetString("state", "STARTING");
344 break;
345 case DaemonController::STATE_STARTED:
346 response->SetString("state", "STARTED");
347 break;
348 case DaemonController::STATE_STOPPING:
349 response->SetString("state", "STOPPING");
350 break;
351 case DaemonController::STATE_UNKNOWN:
352 response->SetString("state", "UNKNOWN");
353 break;
354 }
355 channel_->SendMessage(response.Pass());
356 return true;
357 }
358
ProcessGetHostClientId(const base::DictionaryValue & message,scoped_ptr<base::DictionaryValue> response)359 bool Me2MeNativeMessagingHost::ProcessGetHostClientId(
360 const base::DictionaryValue& message,
361 scoped_ptr<base::DictionaryValue> response) {
362 DCHECK(thread_checker_.CalledOnValidThread());
363
364 response->SetString("clientId", google_apis::GetOAuth2ClientID(
365 google_apis::CLIENT_REMOTING_HOST));
366 channel_->SendMessage(response.Pass());
367 return true;
368 }
369
ProcessGetCredentialsFromAuthCode(const base::DictionaryValue & message,scoped_ptr<base::DictionaryValue> response)370 bool Me2MeNativeMessagingHost::ProcessGetCredentialsFromAuthCode(
371 const base::DictionaryValue& message,
372 scoped_ptr<base::DictionaryValue> response) {
373 DCHECK(thread_checker_.CalledOnValidThread());
374
375 std::string auth_code;
376 if (!message.GetString("authorizationCode", &auth_code)) {
377 LOG(ERROR) << "'authorizationCode' string not found.";
378 return false;
379 }
380
381 gaia::OAuthClientInfo oauth_client_info = {
382 google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING_HOST),
383 google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_REMOTING_HOST),
384 kServiceAccountRedirectUri
385 };
386
387 oauth_client_->GetCredentialsFromAuthCode(
388 oauth_client_info, auth_code, base::Bind(
389 &Me2MeNativeMessagingHost::SendCredentialsResponse, weak_ptr_,
390 base::Passed(&response)));
391
392 return true;
393 }
394
SendConfigResponse(scoped_ptr<base::DictionaryValue> response,scoped_ptr<base::DictionaryValue> config)395 void Me2MeNativeMessagingHost::SendConfigResponse(
396 scoped_ptr<base::DictionaryValue> response,
397 scoped_ptr<base::DictionaryValue> config) {
398 DCHECK(thread_checker_.CalledOnValidThread());
399
400 if (config) {
401 response->Set("config", config.release());
402 } else {
403 response->Set("config", Value::CreateNullValue());
404 }
405 channel_->SendMessage(response.Pass());
406 }
407
SendPairedClientsResponse(scoped_ptr<base::DictionaryValue> response,scoped_ptr<base::ListValue> pairings)408 void Me2MeNativeMessagingHost::SendPairedClientsResponse(
409 scoped_ptr<base::DictionaryValue> response,
410 scoped_ptr<base::ListValue> pairings) {
411 DCHECK(thread_checker_.CalledOnValidThread());
412
413 response->Set("pairedClients", pairings.release());
414 channel_->SendMessage(response.Pass());
415 }
416
SendUsageStatsConsentResponse(scoped_ptr<base::DictionaryValue> response,const DaemonController::UsageStatsConsent & consent)417 void Me2MeNativeMessagingHost::SendUsageStatsConsentResponse(
418 scoped_ptr<base::DictionaryValue> response,
419 const DaemonController::UsageStatsConsent& consent) {
420 DCHECK(thread_checker_.CalledOnValidThread());
421
422 response->SetBoolean("supported", consent.supported);
423 response->SetBoolean("allowed", consent.allowed);
424 response->SetBoolean("setByPolicy", consent.set_by_policy);
425 channel_->SendMessage(response.Pass());
426 }
427
SendAsyncResult(scoped_ptr<base::DictionaryValue> response,DaemonController::AsyncResult result)428 void Me2MeNativeMessagingHost::SendAsyncResult(
429 scoped_ptr<base::DictionaryValue> response,
430 DaemonController::AsyncResult result) {
431 DCHECK(thread_checker_.CalledOnValidThread());
432
433 switch (result) {
434 case DaemonController::RESULT_OK:
435 response->SetString("result", "OK");
436 break;
437 case DaemonController::RESULT_FAILED:
438 response->SetString("result", "FAILED");
439 break;
440 case DaemonController::RESULT_CANCELLED:
441 response->SetString("result", "CANCELLED");
442 break;
443 case DaemonController::RESULT_FAILED_DIRECTORY:
444 response->SetString("result", "FAILED_DIRECTORY");
445 break;
446 }
447 channel_->SendMessage(response.Pass());
448 }
449
SendBooleanResult(scoped_ptr<base::DictionaryValue> response,bool result)450 void Me2MeNativeMessagingHost::SendBooleanResult(
451 scoped_ptr<base::DictionaryValue> response, bool result) {
452 DCHECK(thread_checker_.CalledOnValidThread());
453
454 response->SetBoolean("result", result);
455 channel_->SendMessage(response.Pass());
456 }
457
SendCredentialsResponse(scoped_ptr<base::DictionaryValue> response,const std::string & user_email,const std::string & refresh_token)458 void Me2MeNativeMessagingHost::SendCredentialsResponse(
459 scoped_ptr<base::DictionaryValue> response,
460 const std::string& user_email,
461 const std::string& refresh_token) {
462 DCHECK(thread_checker_.CalledOnValidThread());
463
464 response->SetString("userEmail", user_email);
465 response->SetString("refreshToken", refresh_token);
466 channel_->SendMessage(response.Pass());
467 }
468
469 } // namespace remoting
470