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.
5 #include "remoting/host/setup/me2me_native_messaging_host.h"
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"
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";
31 // Features supported in addition to the base protocol.
32 const char* kSupportedFeatures[] = {
37 // Helper to extract the "config" part of a message as a DictionaryValue.
38 // Returns NULL on failure, and logs an error 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());
46 LOG(ERROR) << "'config' dictionary not found";
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()),
65 weak_ptr_ = weak_factory_.GetWeakPtr();
68 Me2MeNativeMessagingHost::~Me2MeNativeMessagingHost() {
69 DCHECK(thread_checker_.CalledOnValidThread());
72 void Me2MeNativeMessagingHost::Start(
73 const base::Closure& quit_closure) {
74 DCHECK(thread_checker_.CalledOnValidThread());
77 base::Bind(&Me2MeNativeMessagingHost::ProcessMessage, weak_ptr_),
81 void Me2MeNativeMessagingHost::ProcessMessage(
82 scoped_ptr<base::DictionaryValue> message) {
83 DCHECK(thread_checker_.CalledOnValidThread());
85 scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue());
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());
94 if (!message->GetString("type", &type)) {
95 LOG(ERROR) << "'type' not found";
96 channel_->SendMessage(scoped_ptr<base::DictionaryValue>());
100 response->SetString("type", type + "Response");
102 if (type == "hello") {
103 ProcessHello(*message, response.Pass());
104 } else if (type == "clearPairedClients") {
105 ProcessClearPairedClients(*message, response.Pass());
106 } else if (type == "deletePairedClient") {
107 ProcessDeletePairedClient(*message, response.Pass());
108 } else if (type == "getHostName") {
109 ProcessGetHostName(*message, response.Pass());
110 } else if (type == "getPinHash") {
111 ProcessGetPinHash(*message, response.Pass());
112 } else if (type == "generateKeyPair") {
113 ProcessGenerateKeyPair(*message, response.Pass());
114 } else if (type == "updateDaemonConfig") {
115 ProcessUpdateDaemonConfig(*message, response.Pass());
116 } else if (type == "getDaemonConfig") {
117 ProcessGetDaemonConfig(*message, response.Pass());
118 } else if (type == "getPairedClients") {
119 ProcessGetPairedClients(*message, response.Pass());
120 } else if (type == "getUsageStatsConsent") {
121 ProcessGetUsageStatsConsent(*message, response.Pass());
122 } else if (type == "startDaemon") {
123 ProcessStartDaemon(*message, response.Pass());
124 } else if (type == "stopDaemon") {
125 ProcessStopDaemon(*message, response.Pass());
126 } else if (type == "getDaemonState") {
127 ProcessGetDaemonState(*message, response.Pass());
128 } else if (type == "getHostClientId") {
129 ProcessGetHostClientId(*message, response.Pass());
130 } else if (type == "getCredentialsFromAuthCode") {
131 ProcessGetCredentialsFromAuthCode(*message, response.Pass());
133 LOG(ERROR) << "Unsupported request type: " << type;
138 void Me2MeNativeMessagingHost::ProcessHello(
139 const base::DictionaryValue& message,
140 scoped_ptr<base::DictionaryValue> response) {
141 DCHECK(thread_checker_.CalledOnValidThread());
143 response->SetString("version", STRINGIZE(VERSION));
144 scoped_ptr<base::ListValue> supported_features_list(new base::ListValue());
145 supported_features_list->AppendStrings(std::vector<std::string>(
146 kSupportedFeatures, kSupportedFeatures + arraysize(kSupportedFeatures)));
147 response->Set("supportedFeatures", supported_features_list.release());
148 channel_->SendMessage(response.Pass());
151 void Me2MeNativeMessagingHost::ProcessClearPairedClients(
152 const base::DictionaryValue& message,
153 scoped_ptr<base::DictionaryValue> response) {
154 DCHECK(thread_checker_.CalledOnValidThread());
156 if (pairing_registry_) {
157 pairing_registry_->ClearAllPairings(
158 base::Bind(&Me2MeNativeMessagingHost::SendBooleanResult, weak_ptr_,
159 base::Passed(&response)));
161 SendBooleanResult(response.Pass(), false);
165 void Me2MeNativeMessagingHost::ProcessDeletePairedClient(
166 const base::DictionaryValue& message,
167 scoped_ptr<base::DictionaryValue> response) {
168 DCHECK(thread_checker_.CalledOnValidThread());
170 std::string client_id;
171 if (!message.GetString(protocol::PairingRegistry::kClientIdKey, &client_id)) {
172 LOG(ERROR) << "'" << protocol::PairingRegistry::kClientIdKey
173 << "' string not found.";
178 if (pairing_registry_) {
179 pairing_registry_->DeletePairing(
180 client_id, base::Bind(&Me2MeNativeMessagingHost::SendBooleanResult,
181 weak_ptr_, base::Passed(&response)));
183 SendBooleanResult(response.Pass(), false);
187 void Me2MeNativeMessagingHost::ProcessGetHostName(
188 const base::DictionaryValue& message,
189 scoped_ptr<base::DictionaryValue> response) {
190 DCHECK(thread_checker_.CalledOnValidThread());
192 response->SetString("hostname", net::GetHostName());
193 channel_->SendMessage(response.Pass());
196 void Me2MeNativeMessagingHost::ProcessGetPinHash(
197 const base::DictionaryValue& message,
198 scoped_ptr<base::DictionaryValue> response) {
199 DCHECK(thread_checker_.CalledOnValidThread());
202 if (!message.GetString("hostId", &host_id)) {
203 LOG(ERROR) << "'hostId' not found: " << message;
208 if (!message.GetString("pin", &pin)) {
209 LOG(ERROR) << "'pin' not found: " << message;
213 response->SetString("hash", MakeHostPinHash(host_id, pin));
214 channel_->SendMessage(response.Pass());
217 void Me2MeNativeMessagingHost::ProcessGenerateKeyPair(
218 const base::DictionaryValue& message,
219 scoped_ptr<base::DictionaryValue> response) {
220 DCHECK(thread_checker_.CalledOnValidThread());
222 scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::Generate();
223 response->SetString("privateKey", key_pair->ToString());
224 response->SetString("publicKey", key_pair->GetPublicKey());
225 channel_->SendMessage(response.Pass());
228 void Me2MeNativeMessagingHost::ProcessUpdateDaemonConfig(
229 const base::DictionaryValue& message,
230 scoped_ptr<base::DictionaryValue> response) {
231 DCHECK(thread_checker_.CalledOnValidThread());
233 scoped_ptr<base::DictionaryValue> config_dict =
234 ConfigDictionaryFromMessage(message);
240 daemon_controller_->UpdateConfig(
242 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
243 base::Passed(&response)));
246 void Me2MeNativeMessagingHost::ProcessGetDaemonConfig(
247 const base::DictionaryValue& message,
248 scoped_ptr<base::DictionaryValue> response) {
249 DCHECK(thread_checker_.CalledOnValidThread());
251 daemon_controller_->GetConfig(
252 base::Bind(&Me2MeNativeMessagingHost::SendConfigResponse, weak_ptr_,
253 base::Passed(&response)));
256 void Me2MeNativeMessagingHost::ProcessGetPairedClients(
257 const base::DictionaryValue& message,
258 scoped_ptr<base::DictionaryValue> response) {
259 DCHECK(thread_checker_.CalledOnValidThread());
261 if (pairing_registry_) {
262 pairing_registry_->GetAllPairings(
263 base::Bind(&Me2MeNativeMessagingHost::SendPairedClientsResponse,
264 weak_ptr_, base::Passed(&response)));
266 scoped_ptr<base::ListValue> no_paired_clients(new base::ListValue);
267 SendPairedClientsResponse(response.Pass(), no_paired_clients.Pass());
271 void Me2MeNativeMessagingHost::ProcessGetUsageStatsConsent(
272 const base::DictionaryValue& message,
273 scoped_ptr<base::DictionaryValue> response) {
274 DCHECK(thread_checker_.CalledOnValidThread());
276 daemon_controller_->GetUsageStatsConsent(
277 base::Bind(&Me2MeNativeMessagingHost::SendUsageStatsConsentResponse,
278 weak_ptr_, base::Passed(&response)));
281 void Me2MeNativeMessagingHost::ProcessStartDaemon(
282 const base::DictionaryValue& message,
283 scoped_ptr<base::DictionaryValue> response) {
284 DCHECK(thread_checker_.CalledOnValidThread());
287 if (!message.GetBoolean("consent", &consent)) {
288 LOG(ERROR) << "'consent' not found.";
293 scoped_ptr<base::DictionaryValue> config_dict =
294 ConfigDictionaryFromMessage(message);
300 daemon_controller_->SetConfigAndStart(
301 config_dict.Pass(), consent,
302 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
303 base::Passed(&response)));
306 void Me2MeNativeMessagingHost::ProcessStopDaemon(
307 const base::DictionaryValue& message,
308 scoped_ptr<base::DictionaryValue> response) {
309 DCHECK(thread_checker_.CalledOnValidThread());
311 daemon_controller_->Stop(
312 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
313 base::Passed(&response)));
316 void Me2MeNativeMessagingHost::ProcessGetDaemonState(
317 const base::DictionaryValue& message,
318 scoped_ptr<base::DictionaryValue> response) {
319 DCHECK(thread_checker_.CalledOnValidThread());
321 DaemonController::State state = daemon_controller_->GetState();
323 case DaemonController::STATE_NOT_IMPLEMENTED:
324 response->SetString("state", "NOT_IMPLEMENTED");
326 case DaemonController::STATE_NOT_INSTALLED:
327 response->SetString("state", "NOT_INSTALLED");
329 case DaemonController::STATE_INSTALLING:
330 response->SetString("state", "INSTALLING");
332 case DaemonController::STATE_STOPPED:
333 response->SetString("state", "STOPPED");
335 case DaemonController::STATE_STARTING:
336 response->SetString("state", "STARTING");
338 case DaemonController::STATE_STARTED:
339 response->SetString("state", "STARTED");
341 case DaemonController::STATE_STOPPING:
342 response->SetString("state", "STOPPING");
344 case DaemonController::STATE_UNKNOWN:
345 response->SetString("state", "UNKNOWN");
348 channel_->SendMessage(response.Pass());
351 void Me2MeNativeMessagingHost::ProcessGetHostClientId(
352 const base::DictionaryValue& message,
353 scoped_ptr<base::DictionaryValue> response) {
354 DCHECK(thread_checker_.CalledOnValidThread());
356 response->SetString("clientId", google_apis::GetOAuth2ClientID(
357 google_apis::CLIENT_REMOTING_HOST));
358 channel_->SendMessage(response.Pass());
361 void Me2MeNativeMessagingHost::ProcessGetCredentialsFromAuthCode(
362 const base::DictionaryValue& message,
363 scoped_ptr<base::DictionaryValue> response) {
364 DCHECK(thread_checker_.CalledOnValidThread());
366 std::string auth_code;
367 if (!message.GetString("authorizationCode", &auth_code)) {
368 LOG(ERROR) << "'authorizationCode' string not found.";
373 gaia::OAuthClientInfo oauth_client_info = {
374 google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING_HOST),
375 google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_REMOTING_HOST),
376 kServiceAccountRedirectUri
379 oauth_client_->GetCredentialsFromAuthCode(
380 oauth_client_info, auth_code, base::Bind(
381 &Me2MeNativeMessagingHost::SendCredentialsResponse, weak_ptr_,
382 base::Passed(&response)));
385 void Me2MeNativeMessagingHost::SendConfigResponse(
386 scoped_ptr<base::DictionaryValue> response,
387 scoped_ptr<base::DictionaryValue> config) {
388 DCHECK(thread_checker_.CalledOnValidThread());
391 response->Set("config", config.release());
393 response->Set("config", base::Value::CreateNullValue());
395 channel_->SendMessage(response.Pass());
398 void Me2MeNativeMessagingHost::SendPairedClientsResponse(
399 scoped_ptr<base::DictionaryValue> response,
400 scoped_ptr<base::ListValue> pairings) {
401 DCHECK(thread_checker_.CalledOnValidThread());
403 response->Set("pairedClients", pairings.release());
404 channel_->SendMessage(response.Pass());
407 void Me2MeNativeMessagingHost::SendUsageStatsConsentResponse(
408 scoped_ptr<base::DictionaryValue> response,
409 const DaemonController::UsageStatsConsent& consent) {
410 DCHECK(thread_checker_.CalledOnValidThread());
412 response->SetBoolean("supported", consent.supported);
413 response->SetBoolean("allowed", consent.allowed);
414 response->SetBoolean("setByPolicy", consent.set_by_policy);
415 channel_->SendMessage(response.Pass());
418 void Me2MeNativeMessagingHost::SendAsyncResult(
419 scoped_ptr<base::DictionaryValue> response,
420 DaemonController::AsyncResult result) {
421 DCHECK(thread_checker_.CalledOnValidThread());
424 case DaemonController::RESULT_OK:
425 response->SetString("result", "OK");
427 case DaemonController::RESULT_FAILED:
428 response->SetString("result", "FAILED");
430 case DaemonController::RESULT_CANCELLED:
431 response->SetString("result", "CANCELLED");
433 case DaemonController::RESULT_FAILED_DIRECTORY:
434 response->SetString("result", "FAILED_DIRECTORY");
437 channel_->SendMessage(response.Pass());
440 void Me2MeNativeMessagingHost::SendBooleanResult(
441 scoped_ptr<base::DictionaryValue> response, bool result) {
442 DCHECK(thread_checker_.CalledOnValidThread());
444 response->SetBoolean("result", result);
445 channel_->SendMessage(response.Pass());
448 void Me2MeNativeMessagingHost::SendCredentialsResponse(
449 scoped_ptr<base::DictionaryValue> response,
450 const std::string& user_email,
451 const std::string& refresh_token) {
452 DCHECK(thread_checker_.CalledOnValidThread());
454 response->SetString("userEmail", user_email);
455 response->SetString("refreshToken", refresh_token);
456 channel_->SendMessage(response.Pass());
459 void Me2MeNativeMessagingHost::OnError() {
460 // Trigger a host shutdown by sending a NULL message.
461 channel_->SendMessage(scoped_ptr<base::DictionaryValue>());
464 } // namespace remoting