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#include <string>
7
8#include "base/basictypes.h"
9#include "base/bind.h"
10#include "base/callback.h"
11#include "base/callback_helpers.h"
12#include "base/command_line.h"
13#include "base/logging.h"
14#include "base/strings/stringize_macros.h"
15#include "base/strings/stringprintf.h"
16#include "base/strings/utf_string_conversions.h"
17#include "base/threading/thread.h"
18#include "base/values.h"
19#include "google_apis/gaia/gaia_oauth_client.h"
20#include "google_apis/google_api_keys.h"
21#include "ipc/ipc_channel.h"
22#include "net/base/net_util.h"
23#include "remoting/base/rsa_key_pair.h"
24#include "remoting/host/native_messaging/pipe_messaging_channel.h"
25#include "remoting/host/pin_hash.h"
26#include "remoting/host/setup/oauth_client.h"
27#include "remoting/protocol/pairing_registry.h"
28
29#if defined(OS_WIN)
30#include <shellapi.h>
31#include "base/win/win_util.h"
32#include "remoting/host/win/security_descriptor.h"
33#endif  // defined(OS_WIN)
34
35namespace {
36
37#if defined(OS_WIN)
38// Windows will use default buffer size when 0 is passed to CreateNamedPipeW().
39const DWORD kBufferSize = 0;
40const int kTimeOutMilliseconds = 2000;
41const char kChromePipeNamePrefix[] = "\\\\.\\pipe\\chrome_remote_desktop.";
42const int kElevatedHostTimeoutSeconds = 300;
43#endif  // defined(OS_WIN)
44
45// redirect_uri to use when authenticating service accounts (service account
46// codes are obtained "out-of-band", i.e., not through an OAuth redirect).
47const char* kServiceAccountRedirectUri = "oob";
48
49// Features supported in addition to the base protocol.
50const char* kSupportedFeatures[] = {
51  "pairingRegistry",
52  "oauthClient"
53};
54
55// Helper to extract the "config" part of a message as a DictionaryValue.
56// Returns NULL on failure, and logs an error message.
57scoped_ptr<base::DictionaryValue> ConfigDictionaryFromMessage(
58    scoped_ptr<base::DictionaryValue> message) {
59  scoped_ptr<base::DictionaryValue> result;
60  const base::DictionaryValue* config_dict;
61  if (message->GetDictionary("config", &config_dict)) {
62    result.reset(config_dict->DeepCopy());
63  } else {
64    LOG(ERROR) << "'config' dictionary not found";
65  }
66  return result.Pass();
67}
68
69}  // namespace
70
71namespace remoting {
72
73Me2MeNativeMessagingHost::Me2MeNativeMessagingHost(
74    bool needs_elevation,
75    intptr_t parent_window_handle,
76    scoped_ptr<extensions::NativeMessagingChannel> channel,
77    scoped_refptr<DaemonController> daemon_controller,
78    scoped_refptr<protocol::PairingRegistry> pairing_registry,
79    scoped_ptr<OAuthClient> oauth_client)
80    : needs_elevation_(needs_elevation),
81      parent_window_handle_(parent_window_handle),
82      channel_(channel.Pass()),
83      daemon_controller_(daemon_controller),
84      pairing_registry_(pairing_registry),
85      oauth_client_(oauth_client.Pass()),
86      weak_factory_(this) {
87  weak_ptr_ = weak_factory_.GetWeakPtr();
88}
89
90Me2MeNativeMessagingHost::~Me2MeNativeMessagingHost() {
91  DCHECK(thread_checker_.CalledOnValidThread());
92}
93
94void Me2MeNativeMessagingHost::Start(
95      const base::Closure& quit_closure) {
96  DCHECK(thread_checker_.CalledOnValidThread());
97  DCHECK(!quit_closure.is_null());
98
99  quit_closure_ = quit_closure;
100
101  channel_->Start(this);
102}
103
104void Me2MeNativeMessagingHost::OnMessage(scoped_ptr<base::Value> message) {
105  DCHECK(thread_checker_.CalledOnValidThread());
106
107  scoped_ptr<base::DictionaryValue> message_dict(
108      static_cast<base::DictionaryValue*>(message.release()));
109  scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue());
110
111  // If the client supplies an ID, it will expect it in the response. This
112  // might be a string or a number, so cope with both.
113  const base::Value* id;
114  if (message_dict->Get("id", &id))
115    response->Set("id", id->DeepCopy());
116
117  std::string type;
118  if (!message_dict->GetString("type", &type)) {
119    LOG(ERROR) << "'type' not found";
120    channel_->SendMessage(scoped_ptr<base::Value>());
121    return;
122  }
123
124  response->SetString("type", type + "Response");
125
126  if (type == "hello") {
127    ProcessHello(message_dict.Pass(), response.Pass());
128  } else if (type == "clearPairedClients") {
129    ProcessClearPairedClients(message_dict.Pass(), response.Pass());
130  } else if (type == "deletePairedClient") {
131    ProcessDeletePairedClient(message_dict.Pass(), response.Pass());
132  } else if (type == "getHostName") {
133    ProcessGetHostName(message_dict.Pass(), response.Pass());
134  } else if (type == "getPinHash") {
135    ProcessGetPinHash(message_dict.Pass(), response.Pass());
136  } else if (type == "generateKeyPair") {
137    ProcessGenerateKeyPair(message_dict.Pass(), response.Pass());
138  } else if (type == "updateDaemonConfig") {
139    ProcessUpdateDaemonConfig(message_dict.Pass(), response.Pass());
140  } else if (type == "getDaemonConfig") {
141    ProcessGetDaemonConfig(message_dict.Pass(), response.Pass());
142  } else if (type == "getPairedClients") {
143    ProcessGetPairedClients(message_dict.Pass(), response.Pass());
144  } else if (type == "getUsageStatsConsent") {
145    ProcessGetUsageStatsConsent(message_dict.Pass(), response.Pass());
146  } else if (type == "startDaemon") {
147    ProcessStartDaemon(message_dict.Pass(), response.Pass());
148  } else if (type == "stopDaemon") {
149    ProcessStopDaemon(message_dict.Pass(), response.Pass());
150  } else if (type == "getDaemonState") {
151    ProcessGetDaemonState(message_dict.Pass(), response.Pass());
152  } else if (type == "getHostClientId") {
153    ProcessGetHostClientId(message_dict.Pass(), response.Pass());
154  } else if (type == "getCredentialsFromAuthCode") {
155    ProcessGetCredentialsFromAuthCode(message_dict.Pass(), response.Pass());
156  } else {
157    LOG(ERROR) << "Unsupported request type: " << type;
158    OnError();
159  }
160}
161
162void Me2MeNativeMessagingHost::OnDisconnect() {
163  if (!quit_closure_.is_null())
164    base::ResetAndReturn(&quit_closure_).Run();
165}
166
167void Me2MeNativeMessagingHost::ProcessHello(
168    scoped_ptr<base::DictionaryValue> message,
169    scoped_ptr<base::DictionaryValue> response) {
170  DCHECK(thread_checker_.CalledOnValidThread());
171
172  response->SetString("version", STRINGIZE(VERSION));
173  scoped_ptr<base::ListValue> supported_features_list(new base::ListValue());
174  supported_features_list->AppendStrings(std::vector<std::string>(
175      kSupportedFeatures, kSupportedFeatures + arraysize(kSupportedFeatures)));
176  response->Set("supportedFeatures", supported_features_list.release());
177  channel_->SendMessage(response.PassAs<base::Value>());
178}
179
180void Me2MeNativeMessagingHost::ProcessClearPairedClients(
181    scoped_ptr<base::DictionaryValue> message,
182    scoped_ptr<base::DictionaryValue> response) {
183  DCHECK(thread_checker_.CalledOnValidThread());
184
185  if (needs_elevation_) {
186    if (!DelegateToElevatedHost(message.Pass()))
187      SendBooleanResult(response.Pass(), false);
188    return;
189  }
190
191  if (pairing_registry_.get()) {
192    pairing_registry_->ClearAllPairings(
193        base::Bind(&Me2MeNativeMessagingHost::SendBooleanResult, weak_ptr_,
194                   base::Passed(&response)));
195  } else {
196    SendBooleanResult(response.Pass(), false);
197  }
198}
199
200void Me2MeNativeMessagingHost::ProcessDeletePairedClient(
201    scoped_ptr<base::DictionaryValue> message,
202    scoped_ptr<base::DictionaryValue> response) {
203  DCHECK(thread_checker_.CalledOnValidThread());
204
205  if (needs_elevation_) {
206    if (!DelegateToElevatedHost(message.Pass()))
207      SendBooleanResult(response.Pass(), false);
208    return;
209  }
210
211  std::string client_id;
212  if (!message->GetString(protocol::PairingRegistry::kClientIdKey,
213                          &client_id)) {
214    LOG(ERROR) << "'" << protocol::PairingRegistry::kClientIdKey
215               << "' string not found.";
216    OnError();
217    return;
218  }
219
220  if (pairing_registry_.get()) {
221    pairing_registry_->DeletePairing(
222        client_id, base::Bind(&Me2MeNativeMessagingHost::SendBooleanResult,
223                              weak_ptr_, base::Passed(&response)));
224  } else {
225    SendBooleanResult(response.Pass(), false);
226  }
227}
228
229void Me2MeNativeMessagingHost::ProcessGetHostName(
230    scoped_ptr<base::DictionaryValue> message,
231    scoped_ptr<base::DictionaryValue> response) {
232  DCHECK(thread_checker_.CalledOnValidThread());
233
234  response->SetString("hostname", net::GetHostName());
235  channel_->SendMessage(response.PassAs<base::Value>());
236}
237
238void Me2MeNativeMessagingHost::ProcessGetPinHash(
239    scoped_ptr<base::DictionaryValue> message,
240    scoped_ptr<base::DictionaryValue> response) {
241  DCHECK(thread_checker_.CalledOnValidThread());
242
243  std::string host_id;
244  if (!message->GetString("hostId", &host_id)) {
245    LOG(ERROR) << "'hostId' not found: " << message;
246    OnError();
247    return;
248  }
249  std::string pin;
250  if (!message->GetString("pin", &pin)) {
251    LOG(ERROR) << "'pin' not found: " << message;
252    OnError();
253    return;
254  }
255  response->SetString("hash", MakeHostPinHash(host_id, pin));
256  channel_->SendMessage(response.PassAs<base::Value>());
257}
258
259void Me2MeNativeMessagingHost::ProcessGenerateKeyPair(
260    scoped_ptr<base::DictionaryValue> message,
261    scoped_ptr<base::DictionaryValue> response) {
262  DCHECK(thread_checker_.CalledOnValidThread());
263
264  scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::Generate();
265  response->SetString("privateKey", key_pair->ToString());
266  response->SetString("publicKey", key_pair->GetPublicKey());
267  channel_->SendMessage(response.PassAs<base::Value>());
268}
269
270void Me2MeNativeMessagingHost::ProcessUpdateDaemonConfig(
271    scoped_ptr<base::DictionaryValue> message,
272    scoped_ptr<base::DictionaryValue> response) {
273  DCHECK(thread_checker_.CalledOnValidThread());
274
275  scoped_ptr<base::DictionaryValue> config_dict =
276      ConfigDictionaryFromMessage(message.Pass());
277  if (!config_dict) {
278    OnError();
279    return;
280  }
281
282  daemon_controller_->UpdateConfig(
283      config_dict.Pass(),
284      base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
285                 base::Passed(&response)));
286}
287
288void Me2MeNativeMessagingHost::ProcessGetDaemonConfig(
289    scoped_ptr<base::DictionaryValue> message,
290    scoped_ptr<base::DictionaryValue> response) {
291  DCHECK(thread_checker_.CalledOnValidThread());
292
293  daemon_controller_->GetConfig(
294      base::Bind(&Me2MeNativeMessagingHost::SendConfigResponse, weak_ptr_,
295                 base::Passed(&response)));
296}
297
298void Me2MeNativeMessagingHost::ProcessGetPairedClients(
299    scoped_ptr<base::DictionaryValue> message,
300    scoped_ptr<base::DictionaryValue> response) {
301  DCHECK(thread_checker_.CalledOnValidThread());
302
303  if (pairing_registry_.get()) {
304    pairing_registry_->GetAllPairings(
305        base::Bind(&Me2MeNativeMessagingHost::SendPairedClientsResponse,
306                   weak_ptr_, base::Passed(&response)));
307  } else {
308    scoped_ptr<base::ListValue> no_paired_clients(new base::ListValue);
309    SendPairedClientsResponse(response.Pass(), no_paired_clients.Pass());
310  }
311}
312
313void Me2MeNativeMessagingHost::ProcessGetUsageStatsConsent(
314    scoped_ptr<base::DictionaryValue> message,
315    scoped_ptr<base::DictionaryValue> response) {
316  DCHECK(thread_checker_.CalledOnValidThread());
317
318  daemon_controller_->GetUsageStatsConsent(
319      base::Bind(&Me2MeNativeMessagingHost::SendUsageStatsConsentResponse,
320                 weak_ptr_, base::Passed(&response)));
321}
322
323void Me2MeNativeMessagingHost::ProcessStartDaemon(
324    scoped_ptr<base::DictionaryValue> message,
325    scoped_ptr<base::DictionaryValue> response) {
326  DCHECK(thread_checker_.CalledOnValidThread());
327
328  bool consent;
329  if (!message->GetBoolean("consent", &consent)) {
330    LOG(ERROR) << "'consent' not found.";
331    OnError();
332    return;
333  }
334
335  scoped_ptr<base::DictionaryValue> config_dict =
336      ConfigDictionaryFromMessage(message.Pass());
337  if (!config_dict) {
338    OnError();
339    return;
340  }
341
342  daemon_controller_->SetConfigAndStart(
343      config_dict.Pass(), consent,
344      base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
345                 base::Passed(&response)));
346}
347
348void Me2MeNativeMessagingHost::ProcessStopDaemon(
349    scoped_ptr<base::DictionaryValue> message,
350    scoped_ptr<base::DictionaryValue> response) {
351  DCHECK(thread_checker_.CalledOnValidThread());
352
353  daemon_controller_->Stop(
354      base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
355                 base::Passed(&response)));
356}
357
358void Me2MeNativeMessagingHost::ProcessGetDaemonState(
359    scoped_ptr<base::DictionaryValue> message,
360    scoped_ptr<base::DictionaryValue> response) {
361  DCHECK(thread_checker_.CalledOnValidThread());
362
363  DaemonController::State state = daemon_controller_->GetState();
364  switch (state) {
365    case DaemonController::STATE_NOT_IMPLEMENTED:
366      response->SetString("state", "NOT_IMPLEMENTED");
367      break;
368    case DaemonController::STATE_NOT_INSTALLED:
369      response->SetString("state", "NOT_INSTALLED");
370      break;
371    case DaemonController::STATE_INSTALLING:
372      response->SetString("state", "INSTALLING");
373      break;
374    case DaemonController::STATE_STOPPED:
375      response->SetString("state", "STOPPED");
376      break;
377    case DaemonController::STATE_STARTING:
378      response->SetString("state", "STARTING");
379      break;
380    case DaemonController::STATE_STARTED:
381      response->SetString("state", "STARTED");
382      break;
383    case DaemonController::STATE_STOPPING:
384      response->SetString("state", "STOPPING");
385      break;
386    case DaemonController::STATE_UNKNOWN:
387      response->SetString("state", "UNKNOWN");
388      break;
389  }
390  channel_->SendMessage(response.PassAs<base::Value>());
391}
392
393void Me2MeNativeMessagingHost::ProcessGetHostClientId(
394    scoped_ptr<base::DictionaryValue> message,
395    scoped_ptr<base::DictionaryValue> response) {
396  DCHECK(thread_checker_.CalledOnValidThread());
397
398  response->SetString("clientId", google_apis::GetOAuth2ClientID(
399      google_apis::CLIENT_REMOTING_HOST));
400  channel_->SendMessage(response.PassAs<base::Value>());
401}
402
403void Me2MeNativeMessagingHost::ProcessGetCredentialsFromAuthCode(
404    scoped_ptr<base::DictionaryValue> message,
405    scoped_ptr<base::DictionaryValue> response) {
406  DCHECK(thread_checker_.CalledOnValidThread());
407
408  std::string auth_code;
409  if (!message->GetString("authorizationCode", &auth_code)) {
410    LOG(ERROR) << "'authorizationCode' string not found.";
411    OnError();
412    return;
413  }
414
415  gaia::OAuthClientInfo oauth_client_info = {
416    google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING_HOST),
417    google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_REMOTING_HOST),
418    kServiceAccountRedirectUri
419  };
420
421  oauth_client_->GetCredentialsFromAuthCode(
422      oauth_client_info, auth_code, base::Bind(
423          &Me2MeNativeMessagingHost::SendCredentialsResponse, weak_ptr_,
424          base::Passed(&response)));
425}
426
427void Me2MeNativeMessagingHost::SendConfigResponse(
428    scoped_ptr<base::DictionaryValue> response,
429    scoped_ptr<base::DictionaryValue> config) {
430  DCHECK(thread_checker_.CalledOnValidThread());
431
432  if (config) {
433    response->Set("config", config.release());
434  } else {
435    response->Set("config", base::Value::CreateNullValue());
436  }
437  channel_->SendMessage(response.PassAs<base::Value>());
438}
439
440void Me2MeNativeMessagingHost::SendPairedClientsResponse(
441    scoped_ptr<base::DictionaryValue> response,
442    scoped_ptr<base::ListValue> pairings) {
443  DCHECK(thread_checker_.CalledOnValidThread());
444
445  response->Set("pairedClients", pairings.release());
446  channel_->SendMessage(response.PassAs<base::Value>());
447}
448
449void Me2MeNativeMessagingHost::SendUsageStatsConsentResponse(
450    scoped_ptr<base::DictionaryValue> response,
451    const DaemonController::UsageStatsConsent& consent) {
452  DCHECK(thread_checker_.CalledOnValidThread());
453
454  response->SetBoolean("supported", consent.supported);
455  response->SetBoolean("allowed", consent.allowed);
456  response->SetBoolean("setByPolicy", consent.set_by_policy);
457  channel_->SendMessage(response.PassAs<base::Value>());
458}
459
460void Me2MeNativeMessagingHost::SendAsyncResult(
461    scoped_ptr<base::DictionaryValue> response,
462    DaemonController::AsyncResult result) {
463  DCHECK(thread_checker_.CalledOnValidThread());
464
465  switch (result) {
466    case DaemonController::RESULT_OK:
467      response->SetString("result", "OK");
468      break;
469    case DaemonController::RESULT_FAILED:
470      response->SetString("result", "FAILED");
471      break;
472    case DaemonController::RESULT_CANCELLED:
473      response->SetString("result", "CANCELLED");
474      break;
475    case DaemonController::RESULT_FAILED_DIRECTORY:
476      response->SetString("result", "FAILED_DIRECTORY");
477      break;
478  }
479  channel_->SendMessage(response.PassAs<base::Value>());
480}
481
482void Me2MeNativeMessagingHost::SendBooleanResult(
483    scoped_ptr<base::DictionaryValue> response, bool result) {
484  DCHECK(thread_checker_.CalledOnValidThread());
485
486  response->SetBoolean("result", result);
487  channel_->SendMessage(response.PassAs<base::Value>());
488}
489
490void Me2MeNativeMessagingHost::SendCredentialsResponse(
491    scoped_ptr<base::DictionaryValue> response,
492    const std::string& user_email,
493    const std::string& refresh_token) {
494  DCHECK(thread_checker_.CalledOnValidThread());
495
496  response->SetString("userEmail", user_email);
497  response->SetString("refreshToken", refresh_token);
498  channel_->SendMessage(response.PassAs<base::Value>());
499}
500
501void Me2MeNativeMessagingHost::OnError() {
502  // Trigger a host shutdown by sending a NULL message.
503  channel_->SendMessage(scoped_ptr<base::Value>());
504}
505
506void Me2MeNativeMessagingHost::Stop() {
507  DCHECK(thread_checker_.CalledOnValidThread());
508
509  if (!quit_closure_.is_null())
510    base::ResetAndReturn(&quit_closure_).Run();
511}
512
513#if defined(OS_WIN)
514Me2MeNativeMessagingHost::ElevatedChannelEventHandler::
515    ElevatedChannelEventHandler(Me2MeNativeMessagingHost* host)
516    : parent_(host) {
517}
518
519void Me2MeNativeMessagingHost::ElevatedChannelEventHandler::OnMessage(
520    scoped_ptr<base::Value> message) {
521  DCHECK(parent_->thread_checker_.CalledOnValidThread());
522
523  // Simply pass along the response from the elevated host to the client.
524  parent_->channel_->SendMessage(message.Pass());
525}
526
527void Me2MeNativeMessagingHost::ElevatedChannelEventHandler::OnDisconnect() {
528  parent_->OnDisconnect();
529}
530
531bool Me2MeNativeMessagingHost::DelegateToElevatedHost(
532    scoped_ptr<base::DictionaryValue> message) {
533  DCHECK(thread_checker_.CalledOnValidThread());
534
535  EnsureElevatedHostCreated();
536
537  // elevated_channel_ will be null if user rejects the UAC request.
538  if (elevated_channel_)
539    elevated_channel_->SendMessage(message.PassAs<base::Value>());
540
541  return elevated_channel_ != NULL;
542}
543
544void Me2MeNativeMessagingHost::EnsureElevatedHostCreated() {
545  DCHECK(thread_checker_.CalledOnValidThread());
546  DCHECK(needs_elevation_);
547
548  if (elevated_channel_)
549    return;
550
551  // presubmit: allow wstring
552  std::wstring user_sid;
553  if (!base::win::GetUserSidString(&user_sid)) {
554    LOG(ERROR) << "Failed to query the current user SID.";
555    OnError();
556    return;
557  }
558
559  // Create a security descriptor that gives full access to the caller and
560  // denies access by anyone else.
561  std::string security_descriptor = base::StringPrintf(
562      "O:%1$sG:%1$sD:(A;;GA;;;%1$s)", base::UTF16ToASCII(user_sid).c_str());
563
564  ScopedSd sd = ConvertSddlToSd(security_descriptor);
565  if (!sd) {
566    PLOG(ERROR) << "Failed to create a security descriptor for the"
567                << "Chromoting Me2Me native messaging host.";
568    OnError();
569    return;
570  }
571
572  SECURITY_ATTRIBUTES security_attributes = {0};
573  security_attributes.nLength = sizeof(security_attributes);
574  security_attributes.lpSecurityDescriptor = sd.get();
575  security_attributes.bInheritHandle = FALSE;
576
577  // Generate a unique name for the input channel.
578  std::string input_pipe_name(kChromePipeNamePrefix);
579  input_pipe_name.append(IPC::Channel::GenerateUniqueRandomChannelID());
580
581  base::win::ScopedHandle delegate_write_handle(::CreateNamedPipe(
582      base::ASCIIToUTF16(input_pipe_name).c_str(),
583      PIPE_ACCESS_OUTBOUND,
584      PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS,
585      1,
586      kBufferSize,
587      kBufferSize,
588      kTimeOutMilliseconds,
589      &security_attributes));
590
591  if (!delegate_write_handle.IsValid()) {
592    PLOG(ERROR) << "Failed to create named pipe '" << input_pipe_name << "'";
593    OnError();
594    return;
595  }
596
597  // Generate a unique name for the input channel.
598  std::string output_pipe_name(kChromePipeNamePrefix);
599  output_pipe_name.append(IPC::Channel::GenerateUniqueRandomChannelID());
600
601  base::win::ScopedHandle delegate_read_handle(::CreateNamedPipe(
602      base::ASCIIToUTF16(output_pipe_name).c_str(),
603      PIPE_ACCESS_INBOUND,
604      PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS,
605      1,
606      kBufferSize,
607      kBufferSize,
608      kTimeOutMilliseconds,
609      &security_attributes));
610
611  if (!delegate_read_handle.IsValid()) {
612    PLOG(ERROR) << "Failed to create named pipe '" << output_pipe_name << "'";
613    OnError();
614    return;
615  }
616
617  const base::CommandLine* current_command_line =
618      base::CommandLine::ForCurrentProcess();
619  const base::CommandLine::SwitchMap& switches =
620      current_command_line->GetSwitches();
621  base::CommandLine::StringVector args = current_command_line->GetArgs();
622
623  // Create the child process command line by copying switches from the current
624  // command line.
625  base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
626  command_line.AppendSwitch(kElevatingSwitchName);
627  command_line.AppendSwitchASCII(kInputSwitchName, input_pipe_name);
628  command_line.AppendSwitchASCII(kOutputSwitchName, output_pipe_name);
629
630  DCHECK(!current_command_line->HasSwitch(kElevatingSwitchName));
631  for (base::CommandLine::SwitchMap::const_iterator i = switches.begin();
632       i != switches.end(); ++i) {
633      command_line.AppendSwitchNative(i->first, i->second);
634  }
635  for (base::CommandLine::StringVector::const_iterator i = args.begin();
636       i != args.end(); ++i) {
637    command_line.AppendArgNative(*i);
638  }
639
640  // Get the name of the binary to launch.
641  base::FilePath binary = current_command_line->GetProgram();
642  base::CommandLine::StringType parameters =
643      command_line.GetCommandLineString();
644
645  // Launch the child process requesting elevation.
646  SHELLEXECUTEINFO info;
647  memset(&info, 0, sizeof(info));
648  info.cbSize = sizeof(info);
649  info.hwnd = reinterpret_cast<HWND>(parent_window_handle_);
650  info.lpVerb = L"runas";
651  info.lpFile = binary.value().c_str();
652  info.lpParameters = parameters.c_str();
653  info.nShow = SW_HIDE;
654
655  if (!ShellExecuteEx(&info)) {
656    DWORD error = ::GetLastError();
657    PLOG(ERROR) << "Unable to launch '" << binary.value() << "'";
658    if (error != ERROR_CANCELLED) {
659      OnError();
660    }
661    return;
662  }
663
664  if (!::ConnectNamedPipe(delegate_write_handle.Get(), NULL)) {
665    DWORD error = ::GetLastError();
666    if (error != ERROR_PIPE_CONNECTED) {
667      PLOG(ERROR) << "Unable to connect '" << input_pipe_name << "'";
668      OnError();
669      return;
670    }
671  }
672
673  if (!::ConnectNamedPipe(delegate_read_handle.Get(), NULL)) {
674    DWORD error = ::GetLastError();
675    if (error != ERROR_PIPE_CONNECTED) {
676      PLOG(ERROR) << "Unable to connect '" << output_pipe_name << "'";
677      OnError();
678      return;
679    }
680  }
681
682  // Set up the native messaging channel to talk to the elevated host.
683  // Note that input for the elevated channel is output for the elevated host.
684  elevated_channel_.reset(
685      new PipeMessagingChannel(base::File(delegate_read_handle.Take()),
686                               base::File(delegate_write_handle.Take())));
687
688  elevated_channel_event_handler_.reset(
689      new Me2MeNativeMessagingHost::ElevatedChannelEventHandler(this));
690  elevated_channel_->Start(elevated_channel_event_handler_.get());
691
692  elevated_host_timer_.Start(
693      FROM_HERE, base::TimeDelta::FromSeconds(kElevatedHostTimeoutSeconds),
694      this, &Me2MeNativeMessagingHost::DisconnectElevatedHost);
695}
696
697void Me2MeNativeMessagingHost::DisconnectElevatedHost() {
698  DCHECK(thread_checker_.CalledOnValidThread());
699
700  // This will send an EOF to the elevated host, triggering its shutdown.
701  elevated_channel_.reset();
702}
703
704#else  // defined(OS_WIN)
705
706bool Me2MeNativeMessagingHost::DelegateToElevatedHost(
707    scoped_ptr<base::DictionaryValue> message) {
708  NOTREACHED();
709  return false;
710}
711
712#endif  // !defined(OS_WIN)
713
714}  // namespace remoting
715