me2me_native_messaging_host.cc revision 0f1bc08d4cfcc34181b0b5cbf065c40f687bf740
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/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
32namespace {
33
34const 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).
38const char* kServiceAccountRedirectUri = "oob";
39
40// Features supported in addition to the base protocol.
41const 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.
48scoped_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
62namespace remoting {
63
64NativeMessagingHost::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
75NativeMessagingHost::~NativeMessagingHost() {
76}
77
78void 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
138bool 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
151bool 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
165bool 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
186bool 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
195bool 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
214bool 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
225bool 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
241bool 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
251bool 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
266bool 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
276bool 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
298bool 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
308bool 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
343bool 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
353bool 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
377void 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
389void 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
397void 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
407void 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
428void 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
435void 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
445int 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
515