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
25namespace {
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).
29const char* kServiceAccountRedirectUri = "oob";
30
31// Features supported in addition to the base protocol.
32const 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.
39scoped_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
53namespace remoting {
54
55Me2MeNativeMessagingHost::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
68Me2MeNativeMessagingHost::~Me2MeNativeMessagingHost() {
69  DCHECK(thread_checker_.CalledOnValidThread());
70}
71
72void 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
81void 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
141bool 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
155bool 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
170bool 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
192bool 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
202bool 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
222bool 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
234bool 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
251bool 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
262bool 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
278bool 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
289bool 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
312bool 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
323bool 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
359bool 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
370bool 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
395void 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
408void 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
417void 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
428void 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
450void 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
458void 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