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