The crash dumps revealed that the network process was shut down because the daemon...
[chromium-blink-merge.git] / remoting / host / setup / me2me_native_messaging_host.cc
blob018f28c645cc54b0c870dd09563231169d64d8b1
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"
6 #include <string>
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"
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)
35 namespace {
37 #if defined(OS_WIN)
38 // Windows will use default buffer size when 0 is passed to CreateNamedPipeW().
39 const DWORD kBufferSize = 0;
40 const int kTimeOutMilliseconds = 2000;
41 const char kChromePipeNamePrefix[] = "\\\\.\\pipe\\chrome_remote_desktop.";
42 const int kElevatedHostTimeoutSeconds = 300;
43 #endif // defined(OS_WIN)
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).
47 const char* kServiceAccountRedirectUri = "oob";
49 // Features supported in addition to the base protocol.
50 const char* kSupportedFeatures[] = {
51 "pairingRegistry",
52 "oauthClient"
55 // Helper to extract the "config" part of a message as a DictionaryValue.
56 // Returns NULL on failure, and logs an error message.
57 scoped_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";
66 return result.Pass();
69 } // namespace
71 namespace remoting {
73 Me2MeNativeMessagingHost::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();
90 Me2MeNativeMessagingHost::~Me2MeNativeMessagingHost() {
91 DCHECK(thread_checker_.CalledOnValidThread());
94 void Me2MeNativeMessagingHost::Start(
95 const base::Closure& quit_closure) {
96 DCHECK(thread_checker_.CalledOnValidThread());
97 DCHECK(!quit_closure.is_null());
99 quit_closure_ = quit_closure;
101 channel_->Start(this);
104 void Me2MeNativeMessagingHost::OnMessage(scoped_ptr<base::Value> message) {
105 DCHECK(thread_checker_.CalledOnValidThread());
107 if (!message->IsType(base::Value::TYPE_DICTIONARY)) {
108 LOG(ERROR) << "Received a message that's not a dictionary.";
109 channel_->SendMessage(nullptr);
110 return;
113 scoped_ptr<base::DictionaryValue> message_dict(
114 static_cast<base::DictionaryValue*>(message.release()));
115 scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue());
117 // If the client supplies an ID, it will expect it in the response. This
118 // might be a string or a number, so cope with both.
119 const base::Value* id;
120 if (message_dict->Get("id", &id))
121 response->Set("id", id->DeepCopy());
123 std::string type;
124 if (!message_dict->GetString("type", &type)) {
125 LOG(ERROR) << "'type' not found";
126 channel_->SendMessage(nullptr);
127 return;
130 response->SetString("type", type + "Response");
132 if (type == "hello") {
133 ProcessHello(message_dict.Pass(), response.Pass());
134 } else if (type == "clearPairedClients") {
135 ProcessClearPairedClients(message_dict.Pass(), response.Pass());
136 } else if (type == "deletePairedClient") {
137 ProcessDeletePairedClient(message_dict.Pass(), response.Pass());
138 } else if (type == "getHostName") {
139 ProcessGetHostName(message_dict.Pass(), response.Pass());
140 } else if (type == "getPinHash") {
141 ProcessGetPinHash(message_dict.Pass(), response.Pass());
142 } else if (type == "generateKeyPair") {
143 ProcessGenerateKeyPair(message_dict.Pass(), response.Pass());
144 } else if (type == "updateDaemonConfig") {
145 ProcessUpdateDaemonConfig(message_dict.Pass(), response.Pass());
146 } else if (type == "getDaemonConfig") {
147 ProcessGetDaemonConfig(message_dict.Pass(), response.Pass());
148 } else if (type == "getPairedClients") {
149 ProcessGetPairedClients(message_dict.Pass(), response.Pass());
150 } else if (type == "getUsageStatsConsent") {
151 ProcessGetUsageStatsConsent(message_dict.Pass(), response.Pass());
152 } else if (type == "startDaemon") {
153 ProcessStartDaemon(message_dict.Pass(), response.Pass());
154 } else if (type == "stopDaemon") {
155 ProcessStopDaemon(message_dict.Pass(), response.Pass());
156 } else if (type == "getDaemonState") {
157 ProcessGetDaemonState(message_dict.Pass(), response.Pass());
158 } else if (type == "getHostClientId") {
159 ProcessGetHostClientId(message_dict.Pass(), response.Pass());
160 } else if (type == "getCredentialsFromAuthCode") {
161 ProcessGetCredentialsFromAuthCode(message_dict.Pass(), response.Pass());
162 } else {
163 LOG(ERROR) << "Unsupported request type: " << type;
164 OnError();
168 void Me2MeNativeMessagingHost::OnDisconnect() {
169 if (!quit_closure_.is_null())
170 base::ResetAndReturn(&quit_closure_).Run();
173 void Me2MeNativeMessagingHost::ProcessHello(
174 scoped_ptr<base::DictionaryValue> message,
175 scoped_ptr<base::DictionaryValue> response) {
176 DCHECK(thread_checker_.CalledOnValidThread());
178 response->SetString("version", STRINGIZE(VERSION));
179 scoped_ptr<base::ListValue> supported_features_list(new base::ListValue());
180 supported_features_list->AppendStrings(std::vector<std::string>(
181 kSupportedFeatures, kSupportedFeatures + arraysize(kSupportedFeatures)));
182 response->Set("supportedFeatures", supported_features_list.release());
183 channel_->SendMessage(response.Pass());
186 void Me2MeNativeMessagingHost::ProcessClearPairedClients(
187 scoped_ptr<base::DictionaryValue> message,
188 scoped_ptr<base::DictionaryValue> response) {
189 DCHECK(thread_checker_.CalledOnValidThread());
191 if (needs_elevation_) {
192 if (!DelegateToElevatedHost(message.Pass()))
193 SendBooleanResult(response.Pass(), false);
194 return;
197 if (pairing_registry_.get()) {
198 pairing_registry_->ClearAllPairings(
199 base::Bind(&Me2MeNativeMessagingHost::SendBooleanResult, weak_ptr_,
200 base::Passed(&response)));
201 } else {
202 SendBooleanResult(response.Pass(), false);
206 void Me2MeNativeMessagingHost::ProcessDeletePairedClient(
207 scoped_ptr<base::DictionaryValue> message,
208 scoped_ptr<base::DictionaryValue> response) {
209 DCHECK(thread_checker_.CalledOnValidThread());
211 if (needs_elevation_) {
212 if (!DelegateToElevatedHost(message.Pass()))
213 SendBooleanResult(response.Pass(), false);
214 return;
217 std::string client_id;
218 if (!message->GetString(protocol::PairingRegistry::kClientIdKey,
219 &client_id)) {
220 LOG(ERROR) << "'" << protocol::PairingRegistry::kClientIdKey
221 << "' string not found.";
222 OnError();
223 return;
226 if (pairing_registry_.get()) {
227 pairing_registry_->DeletePairing(
228 client_id, base::Bind(&Me2MeNativeMessagingHost::SendBooleanResult,
229 weak_ptr_, base::Passed(&response)));
230 } else {
231 SendBooleanResult(response.Pass(), false);
235 void Me2MeNativeMessagingHost::ProcessGetHostName(
236 scoped_ptr<base::DictionaryValue> message,
237 scoped_ptr<base::DictionaryValue> response) {
238 DCHECK(thread_checker_.CalledOnValidThread());
240 response->SetString("hostname", net::GetHostName());
241 channel_->SendMessage(response.Pass());
244 void Me2MeNativeMessagingHost::ProcessGetPinHash(
245 scoped_ptr<base::DictionaryValue> message,
246 scoped_ptr<base::DictionaryValue> response) {
247 DCHECK(thread_checker_.CalledOnValidThread());
249 std::string host_id;
250 if (!message->GetString("hostId", &host_id)) {
251 LOG(ERROR) << "'hostId' not found: " << message;
252 OnError();
253 return;
255 std::string pin;
256 if (!message->GetString("pin", &pin)) {
257 LOG(ERROR) << "'pin' not found: " << message;
258 OnError();
259 return;
261 response->SetString("hash", MakeHostPinHash(host_id, pin));
262 channel_->SendMessage(response.Pass());
265 void Me2MeNativeMessagingHost::ProcessGenerateKeyPair(
266 scoped_ptr<base::DictionaryValue> message,
267 scoped_ptr<base::DictionaryValue> response) {
268 DCHECK(thread_checker_.CalledOnValidThread());
270 scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::Generate();
271 response->SetString("privateKey", key_pair->ToString());
272 response->SetString("publicKey", key_pair->GetPublicKey());
273 channel_->SendMessage(response.Pass());
276 void Me2MeNativeMessagingHost::ProcessUpdateDaemonConfig(
277 scoped_ptr<base::DictionaryValue> message,
278 scoped_ptr<base::DictionaryValue> response) {
279 DCHECK(thread_checker_.CalledOnValidThread());
281 scoped_ptr<base::DictionaryValue> config_dict =
282 ConfigDictionaryFromMessage(message.Pass());
283 if (!config_dict) {
284 OnError();
285 return;
288 daemon_controller_->UpdateConfig(
289 config_dict.Pass(),
290 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
291 base::Passed(&response)));
294 void Me2MeNativeMessagingHost::ProcessGetDaemonConfig(
295 scoped_ptr<base::DictionaryValue> message,
296 scoped_ptr<base::DictionaryValue> response) {
297 DCHECK(thread_checker_.CalledOnValidThread());
299 daemon_controller_->GetConfig(
300 base::Bind(&Me2MeNativeMessagingHost::SendConfigResponse, weak_ptr_,
301 base::Passed(&response)));
304 void Me2MeNativeMessagingHost::ProcessGetPairedClients(
305 scoped_ptr<base::DictionaryValue> message,
306 scoped_ptr<base::DictionaryValue> response) {
307 DCHECK(thread_checker_.CalledOnValidThread());
309 if (pairing_registry_.get()) {
310 pairing_registry_->GetAllPairings(
311 base::Bind(&Me2MeNativeMessagingHost::SendPairedClientsResponse,
312 weak_ptr_, base::Passed(&response)));
313 } else {
314 scoped_ptr<base::ListValue> no_paired_clients(new base::ListValue);
315 SendPairedClientsResponse(response.Pass(), no_paired_clients.Pass());
319 void Me2MeNativeMessagingHost::ProcessGetUsageStatsConsent(
320 scoped_ptr<base::DictionaryValue> message,
321 scoped_ptr<base::DictionaryValue> response) {
322 DCHECK(thread_checker_.CalledOnValidThread());
324 daemon_controller_->GetUsageStatsConsent(
325 base::Bind(&Me2MeNativeMessagingHost::SendUsageStatsConsentResponse,
326 weak_ptr_, base::Passed(&response)));
329 void Me2MeNativeMessagingHost::ProcessStartDaemon(
330 scoped_ptr<base::DictionaryValue> message,
331 scoped_ptr<base::DictionaryValue> response) {
332 DCHECK(thread_checker_.CalledOnValidThread());
334 bool consent;
335 if (!message->GetBoolean("consent", &consent)) {
336 LOG(ERROR) << "'consent' not found.";
337 OnError();
338 return;
341 scoped_ptr<base::DictionaryValue> config_dict =
342 ConfigDictionaryFromMessage(message.Pass());
343 if (!config_dict) {
344 OnError();
345 return;
348 daemon_controller_->SetConfigAndStart(
349 config_dict.Pass(), consent,
350 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
351 base::Passed(&response)));
354 void Me2MeNativeMessagingHost::ProcessStopDaemon(
355 scoped_ptr<base::DictionaryValue> message,
356 scoped_ptr<base::DictionaryValue> response) {
357 DCHECK(thread_checker_.CalledOnValidThread());
359 daemon_controller_->Stop(
360 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
361 base::Passed(&response)));
364 void Me2MeNativeMessagingHost::ProcessGetDaemonState(
365 scoped_ptr<base::DictionaryValue> message,
366 scoped_ptr<base::DictionaryValue> response) {
367 DCHECK(thread_checker_.CalledOnValidThread());
369 DaemonController::State state = daemon_controller_->GetState();
370 switch (state) {
371 case DaemonController::STATE_NOT_IMPLEMENTED:
372 response->SetString("state", "NOT_IMPLEMENTED");
373 break;
374 case DaemonController::STATE_NOT_INSTALLED:
375 response->SetString("state", "NOT_INSTALLED");
376 break;
377 case DaemonController::STATE_INSTALLING:
378 response->SetString("state", "INSTALLING");
379 break;
380 case DaemonController::STATE_STOPPED:
381 response->SetString("state", "STOPPED");
382 break;
383 case DaemonController::STATE_STARTING:
384 response->SetString("state", "STARTING");
385 break;
386 case DaemonController::STATE_STARTED:
387 response->SetString("state", "STARTED");
388 break;
389 case DaemonController::STATE_STOPPING:
390 response->SetString("state", "STOPPING");
391 break;
392 case DaemonController::STATE_UNKNOWN:
393 response->SetString("state", "UNKNOWN");
394 break;
396 channel_->SendMessage(response.Pass());
399 void Me2MeNativeMessagingHost::ProcessGetHostClientId(
400 scoped_ptr<base::DictionaryValue> message,
401 scoped_ptr<base::DictionaryValue> response) {
402 DCHECK(thread_checker_.CalledOnValidThread());
404 response->SetString("clientId", google_apis::GetOAuth2ClientID(
405 google_apis::CLIENT_REMOTING_HOST));
406 channel_->SendMessage(response.Pass());
409 void Me2MeNativeMessagingHost::ProcessGetCredentialsFromAuthCode(
410 scoped_ptr<base::DictionaryValue> message,
411 scoped_ptr<base::DictionaryValue> response) {
412 DCHECK(thread_checker_.CalledOnValidThread());
414 std::string auth_code;
415 if (!message->GetString("authorizationCode", &auth_code)) {
416 LOG(ERROR) << "'authorizationCode' string not found.";
417 OnError();
418 return;
421 gaia::OAuthClientInfo oauth_client_info = {
422 google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING_HOST),
423 google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_REMOTING_HOST),
424 kServiceAccountRedirectUri
427 oauth_client_->GetCredentialsFromAuthCode(
428 oauth_client_info, auth_code, base::Bind(
429 &Me2MeNativeMessagingHost::SendCredentialsResponse, weak_ptr_,
430 base::Passed(&response)));
433 void Me2MeNativeMessagingHost::SendConfigResponse(
434 scoped_ptr<base::DictionaryValue> response,
435 scoped_ptr<base::DictionaryValue> config) {
436 DCHECK(thread_checker_.CalledOnValidThread());
438 if (config) {
439 response->Set("config", config.release());
440 } else {
441 response->Set("config", base::Value::CreateNullValue());
443 channel_->SendMessage(response.Pass());
446 void Me2MeNativeMessagingHost::SendPairedClientsResponse(
447 scoped_ptr<base::DictionaryValue> response,
448 scoped_ptr<base::ListValue> pairings) {
449 DCHECK(thread_checker_.CalledOnValidThread());
451 response->Set("pairedClients", pairings.release());
452 channel_->SendMessage(response.Pass());
455 void Me2MeNativeMessagingHost::SendUsageStatsConsentResponse(
456 scoped_ptr<base::DictionaryValue> response,
457 const DaemonController::UsageStatsConsent& consent) {
458 DCHECK(thread_checker_.CalledOnValidThread());
460 response->SetBoolean("supported", consent.supported);
461 response->SetBoolean("allowed", consent.allowed);
462 response->SetBoolean("setByPolicy", consent.set_by_policy);
463 channel_->SendMessage(response.Pass());
466 void Me2MeNativeMessagingHost::SendAsyncResult(
467 scoped_ptr<base::DictionaryValue> response,
468 DaemonController::AsyncResult result) {
469 DCHECK(thread_checker_.CalledOnValidThread());
471 switch (result) {
472 case DaemonController::RESULT_OK:
473 response->SetString("result", "OK");
474 break;
475 case DaemonController::RESULT_FAILED:
476 response->SetString("result", "FAILED");
477 break;
478 case DaemonController::RESULT_CANCELLED:
479 response->SetString("result", "CANCELLED");
480 break;
481 case DaemonController::RESULT_FAILED_DIRECTORY:
482 response->SetString("result", "FAILED_DIRECTORY");
483 break;
485 channel_->SendMessage(response.Pass());
488 void Me2MeNativeMessagingHost::SendBooleanResult(
489 scoped_ptr<base::DictionaryValue> response, bool result) {
490 DCHECK(thread_checker_.CalledOnValidThread());
492 response->SetBoolean("result", result);
493 channel_->SendMessage(response.Pass());
496 void Me2MeNativeMessagingHost::SendCredentialsResponse(
497 scoped_ptr<base::DictionaryValue> response,
498 const std::string& user_email,
499 const std::string& refresh_token) {
500 DCHECK(thread_checker_.CalledOnValidThread());
502 response->SetString("userEmail", user_email);
503 response->SetString("refreshToken", refresh_token);
504 channel_->SendMessage(response.Pass());
507 void Me2MeNativeMessagingHost::OnError() {
508 // Trigger a host shutdown by sending a NULL message.
509 channel_->SendMessage(nullptr);
512 void Me2MeNativeMessagingHost::Stop() {
513 DCHECK(thread_checker_.CalledOnValidThread());
515 if (!quit_closure_.is_null())
516 base::ResetAndReturn(&quit_closure_).Run();
519 #if defined(OS_WIN)
520 Me2MeNativeMessagingHost::ElevatedChannelEventHandler::
521 ElevatedChannelEventHandler(Me2MeNativeMessagingHost* host)
522 : parent_(host) {
525 void Me2MeNativeMessagingHost::ElevatedChannelEventHandler::OnMessage(
526 scoped_ptr<base::Value> message) {
527 DCHECK(parent_->thread_checker_.CalledOnValidThread());
529 // Simply pass along the response from the elevated host to the client.
530 parent_->channel_->SendMessage(message.Pass());
533 void Me2MeNativeMessagingHost::ElevatedChannelEventHandler::OnDisconnect() {
534 parent_->OnDisconnect();
537 bool Me2MeNativeMessagingHost::DelegateToElevatedHost(
538 scoped_ptr<base::DictionaryValue> message) {
539 DCHECK(thread_checker_.CalledOnValidThread());
541 EnsureElevatedHostCreated();
543 // elevated_channel_ will be null if user rejects the UAC request.
544 if (elevated_channel_)
545 elevated_channel_->SendMessage(message.Pass());
547 return elevated_channel_ != NULL;
550 void Me2MeNativeMessagingHost::EnsureElevatedHostCreated() {
551 DCHECK(thread_checker_.CalledOnValidThread());
552 DCHECK(needs_elevation_);
554 if (elevated_channel_)
555 return;
557 // presubmit: allow wstring
558 std::wstring user_sid;
559 if (!base::win::GetUserSidString(&user_sid)) {
560 LOG(ERROR) << "Failed to query the current user SID.";
561 OnError();
562 return;
565 // Create a security descriptor that gives full access to the caller and
566 // denies access by anyone else.
567 std::string security_descriptor = base::StringPrintf(
568 "O:%1$sG:%1$sD:(A;;GA;;;%1$s)", base::UTF16ToASCII(user_sid).c_str());
570 ScopedSd sd = ConvertSddlToSd(security_descriptor);
571 if (!sd) {
572 PLOG(ERROR) << "Failed to create a security descriptor for the"
573 << "Chromoting Me2Me native messaging host.";
574 OnError();
575 return;
578 SECURITY_ATTRIBUTES security_attributes = {0};
579 security_attributes.nLength = sizeof(security_attributes);
580 security_attributes.lpSecurityDescriptor = sd.get();
581 security_attributes.bInheritHandle = FALSE;
583 // Generate a unique name for the input channel.
584 std::string input_pipe_name(kChromePipeNamePrefix);
585 input_pipe_name.append(IPC::Channel::GenerateUniqueRandomChannelID());
587 base::win::ScopedHandle delegate_write_handle(::CreateNamedPipe(
588 base::ASCIIToUTF16(input_pipe_name).c_str(),
589 PIPE_ACCESS_OUTBOUND,
590 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS,
592 kBufferSize,
593 kBufferSize,
594 kTimeOutMilliseconds,
595 &security_attributes));
597 if (!delegate_write_handle.IsValid()) {
598 PLOG(ERROR) << "Failed to create named pipe '" << input_pipe_name << "'";
599 OnError();
600 return;
603 // Generate a unique name for the input channel.
604 std::string output_pipe_name(kChromePipeNamePrefix);
605 output_pipe_name.append(IPC::Channel::GenerateUniqueRandomChannelID());
607 base::win::ScopedHandle delegate_read_handle(::CreateNamedPipe(
608 base::ASCIIToUTF16(output_pipe_name).c_str(),
609 PIPE_ACCESS_INBOUND,
610 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS,
612 kBufferSize,
613 kBufferSize,
614 kTimeOutMilliseconds,
615 &security_attributes));
617 if (!delegate_read_handle.IsValid()) {
618 PLOG(ERROR) << "Failed to create named pipe '" << output_pipe_name << "'";
619 OnError();
620 return;
623 const base::CommandLine* current_command_line =
624 base::CommandLine::ForCurrentProcess();
625 const base::CommandLine::SwitchMap& switches =
626 current_command_line->GetSwitches();
627 base::CommandLine::StringVector args = current_command_line->GetArgs();
629 // Create the child process command line by copying switches from the current
630 // command line.
631 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
632 command_line.AppendSwitch(kElevatingSwitchName);
633 command_line.AppendSwitchASCII(kInputSwitchName, input_pipe_name);
634 command_line.AppendSwitchASCII(kOutputSwitchName, output_pipe_name);
636 DCHECK(!current_command_line->HasSwitch(kElevatingSwitchName));
637 for (base::CommandLine::SwitchMap::const_iterator i = switches.begin();
638 i != switches.end(); ++i) {
639 command_line.AppendSwitchNative(i->first, i->second);
641 for (base::CommandLine::StringVector::const_iterator i = args.begin();
642 i != args.end(); ++i) {
643 command_line.AppendArgNative(*i);
646 // Get the name of the binary to launch.
647 base::FilePath binary = current_command_line->GetProgram();
648 base::CommandLine::StringType parameters =
649 command_line.GetCommandLineString();
651 // Launch the child process requesting elevation.
652 SHELLEXECUTEINFO info;
653 memset(&info, 0, sizeof(info));
654 info.cbSize = sizeof(info);
655 info.hwnd = reinterpret_cast<HWND>(parent_window_handle_);
656 info.lpVerb = L"runas";
657 info.lpFile = binary.value().c_str();
658 info.lpParameters = parameters.c_str();
659 info.nShow = SW_HIDE;
661 if (!ShellExecuteEx(&info)) {
662 DWORD error = ::GetLastError();
663 PLOG(ERROR) << "Unable to launch '" << binary.value() << "'";
664 if (error != ERROR_CANCELLED) {
665 OnError();
667 return;
670 if (!::ConnectNamedPipe(delegate_write_handle.Get(), NULL)) {
671 DWORD error = ::GetLastError();
672 if (error != ERROR_PIPE_CONNECTED) {
673 PLOG(ERROR) << "Unable to connect '" << input_pipe_name << "'";
674 OnError();
675 return;
679 if (!::ConnectNamedPipe(delegate_read_handle.Get(), NULL)) {
680 DWORD error = ::GetLastError();
681 if (error != ERROR_PIPE_CONNECTED) {
682 PLOG(ERROR) << "Unable to connect '" << output_pipe_name << "'";
683 OnError();
684 return;
688 // Set up the native messaging channel to talk to the elevated host.
689 // Note that input for the elevated channel is output for the elevated host.
690 elevated_channel_.reset(
691 new PipeMessagingChannel(base::File(delegate_read_handle.Take()),
692 base::File(delegate_write_handle.Take())));
694 elevated_channel_event_handler_.reset(
695 new Me2MeNativeMessagingHost::ElevatedChannelEventHandler(this));
696 elevated_channel_->Start(elevated_channel_event_handler_.get());
698 elevated_host_timer_.Start(
699 FROM_HERE, base::TimeDelta::FromSeconds(kElevatedHostTimeoutSeconds),
700 this, &Me2MeNativeMessagingHost::DisconnectElevatedHost);
703 void Me2MeNativeMessagingHost::DisconnectElevatedHost() {
704 DCHECK(thread_checker_.CalledOnValidThread());
706 // This will send an EOF to the elevated host, triggering its shutdown.
707 elevated_channel_.reset();
710 #else // defined(OS_WIN)
712 bool Me2MeNativeMessagingHost::DelegateToElevatedHost(
713 scoped_ptr<base::DictionaryValue> message) {
714 NOTREACHED();
715 return false;
718 #endif // !defined(OS_WIN)
720 } // namespace remoting