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"
8 #include "base/basictypes.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"
31 #include "base/win/win_util.h"
32 #include "remoting/host/win/security_descriptor.h"
33 #endif // 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[] = {
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());
64 LOG(ERROR) << "'config' dictionary not found";
73 Me2MeNativeMessagingHost::Me2MeNativeMessagingHost(
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()),
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 scoped_ptr<base::DictionaryValue> message_dict(
108 static_cast<base::DictionaryValue*>(message.release()));
109 scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue());
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());
118 if (!message_dict->GetString("type", &type)) {
119 LOG(ERROR) << "'type' not found";
120 channel_->SendMessage(scoped_ptr<base::Value>());
124 response->SetString("type", type + "Response");
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());
157 LOG(ERROR) << "Unsupported request type: " << type;
162 void Me2MeNativeMessagingHost::OnDisconnect() {
163 if (!quit_closure_.is_null())
164 base::ResetAndReturn(&quit_closure_).Run();
167 void Me2MeNativeMessagingHost::ProcessHello(
168 scoped_ptr<base::DictionaryValue> message,
169 scoped_ptr<base::DictionaryValue> response) {
170 DCHECK(thread_checker_.CalledOnValidThread());
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>());
180 void Me2MeNativeMessagingHost::ProcessClearPairedClients(
181 scoped_ptr<base::DictionaryValue> message,
182 scoped_ptr<base::DictionaryValue> response) {
183 DCHECK(thread_checker_.CalledOnValidThread());
185 if (needs_elevation_) {
186 if (!DelegateToElevatedHost(message.Pass()))
187 SendBooleanResult(response.Pass(), false);
191 if (pairing_registry_.get()) {
192 pairing_registry_->ClearAllPairings(
193 base::Bind(&Me2MeNativeMessagingHost::SendBooleanResult, weak_ptr_,
194 base::Passed(&response)));
196 SendBooleanResult(response.Pass(), false);
200 void Me2MeNativeMessagingHost::ProcessDeletePairedClient(
201 scoped_ptr<base::DictionaryValue> message,
202 scoped_ptr<base::DictionaryValue> response) {
203 DCHECK(thread_checker_.CalledOnValidThread());
205 if (needs_elevation_) {
206 if (!DelegateToElevatedHost(message.Pass()))
207 SendBooleanResult(response.Pass(), false);
211 std::string client_id;
212 if (!message->GetString(protocol::PairingRegistry::kClientIdKey,
214 LOG(ERROR) << "'" << protocol::PairingRegistry::kClientIdKey
215 << "' string not found.";
220 if (pairing_registry_.get()) {
221 pairing_registry_->DeletePairing(
222 client_id, base::Bind(&Me2MeNativeMessagingHost::SendBooleanResult,
223 weak_ptr_, base::Passed(&response)));
225 SendBooleanResult(response.Pass(), false);
229 void Me2MeNativeMessagingHost::ProcessGetHostName(
230 scoped_ptr<base::DictionaryValue> message,
231 scoped_ptr<base::DictionaryValue> response) {
232 DCHECK(thread_checker_.CalledOnValidThread());
234 response->SetString("hostname", net::GetHostName());
235 channel_->SendMessage(response.PassAs<base::Value>());
238 void Me2MeNativeMessagingHost::ProcessGetPinHash(
239 scoped_ptr<base::DictionaryValue> message,
240 scoped_ptr<base::DictionaryValue> response) {
241 DCHECK(thread_checker_.CalledOnValidThread());
244 if (!message->GetString("hostId", &host_id)) {
245 LOG(ERROR) << "'hostId' not found: " << message;
250 if (!message->GetString("pin", &pin)) {
251 LOG(ERROR) << "'pin' not found: " << message;
255 response->SetString("hash", MakeHostPinHash(host_id, pin));
256 channel_->SendMessage(response.PassAs<base::Value>());
259 void Me2MeNativeMessagingHost::ProcessGenerateKeyPair(
260 scoped_ptr<base::DictionaryValue> message,
261 scoped_ptr<base::DictionaryValue> response) {
262 DCHECK(thread_checker_.CalledOnValidThread());
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>());
270 void Me2MeNativeMessagingHost::ProcessUpdateDaemonConfig(
271 scoped_ptr<base::DictionaryValue> message,
272 scoped_ptr<base::DictionaryValue> response) {
273 DCHECK(thread_checker_.CalledOnValidThread());
275 scoped_ptr<base::DictionaryValue> config_dict =
276 ConfigDictionaryFromMessage(message.Pass());
282 daemon_controller_->UpdateConfig(
284 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
285 base::Passed(&response)));
288 void Me2MeNativeMessagingHost::ProcessGetDaemonConfig(
289 scoped_ptr<base::DictionaryValue> message,
290 scoped_ptr<base::DictionaryValue> response) {
291 DCHECK(thread_checker_.CalledOnValidThread());
293 daemon_controller_->GetConfig(
294 base::Bind(&Me2MeNativeMessagingHost::SendConfigResponse, weak_ptr_,
295 base::Passed(&response)));
298 void Me2MeNativeMessagingHost::ProcessGetPairedClients(
299 scoped_ptr<base::DictionaryValue> message,
300 scoped_ptr<base::DictionaryValue> response) {
301 DCHECK(thread_checker_.CalledOnValidThread());
303 if (pairing_registry_.get()) {
304 pairing_registry_->GetAllPairings(
305 base::Bind(&Me2MeNativeMessagingHost::SendPairedClientsResponse,
306 weak_ptr_, base::Passed(&response)));
308 scoped_ptr<base::ListValue> no_paired_clients(new base::ListValue);
309 SendPairedClientsResponse(response.Pass(), no_paired_clients.Pass());
313 void Me2MeNativeMessagingHost::ProcessGetUsageStatsConsent(
314 scoped_ptr<base::DictionaryValue> message,
315 scoped_ptr<base::DictionaryValue> response) {
316 DCHECK(thread_checker_.CalledOnValidThread());
318 daemon_controller_->GetUsageStatsConsent(
319 base::Bind(&Me2MeNativeMessagingHost::SendUsageStatsConsentResponse,
320 weak_ptr_, base::Passed(&response)));
323 void Me2MeNativeMessagingHost::ProcessStartDaemon(
324 scoped_ptr<base::DictionaryValue> message,
325 scoped_ptr<base::DictionaryValue> response) {
326 DCHECK(thread_checker_.CalledOnValidThread());
329 if (!message->GetBoolean("consent", &consent)) {
330 LOG(ERROR) << "'consent' not found.";
335 scoped_ptr<base::DictionaryValue> config_dict =
336 ConfigDictionaryFromMessage(message.Pass());
342 daemon_controller_->SetConfigAndStart(
343 config_dict.Pass(), consent,
344 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
345 base::Passed(&response)));
348 void Me2MeNativeMessagingHost::ProcessStopDaemon(
349 scoped_ptr<base::DictionaryValue> message,
350 scoped_ptr<base::DictionaryValue> response) {
351 DCHECK(thread_checker_.CalledOnValidThread());
353 daemon_controller_->Stop(
354 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
355 base::Passed(&response)));
358 void Me2MeNativeMessagingHost::ProcessGetDaemonState(
359 scoped_ptr<base::DictionaryValue> message,
360 scoped_ptr<base::DictionaryValue> response) {
361 DCHECK(thread_checker_.CalledOnValidThread());
363 DaemonController::State state = daemon_controller_->GetState();
365 case DaemonController::STATE_NOT_IMPLEMENTED:
366 response->SetString("state", "NOT_IMPLEMENTED");
368 case DaemonController::STATE_NOT_INSTALLED:
369 response->SetString("state", "NOT_INSTALLED");
371 case DaemonController::STATE_INSTALLING:
372 response->SetString("state", "INSTALLING");
374 case DaemonController::STATE_STOPPED:
375 response->SetString("state", "STOPPED");
377 case DaemonController::STATE_STARTING:
378 response->SetString("state", "STARTING");
380 case DaemonController::STATE_STARTED:
381 response->SetString("state", "STARTED");
383 case DaemonController::STATE_STOPPING:
384 response->SetString("state", "STOPPING");
386 case DaemonController::STATE_UNKNOWN:
387 response->SetString("state", "UNKNOWN");
390 channel_->SendMessage(response.PassAs<base::Value>());
393 void Me2MeNativeMessagingHost::ProcessGetHostClientId(
394 scoped_ptr<base::DictionaryValue> message,
395 scoped_ptr<base::DictionaryValue> response) {
396 DCHECK(thread_checker_.CalledOnValidThread());
398 response->SetString("clientId", google_apis::GetOAuth2ClientID(
399 google_apis::CLIENT_REMOTING_HOST));
400 channel_->SendMessage(response.PassAs<base::Value>());
403 void Me2MeNativeMessagingHost::ProcessGetCredentialsFromAuthCode(
404 scoped_ptr<base::DictionaryValue> message,
405 scoped_ptr<base::DictionaryValue> response) {
406 DCHECK(thread_checker_.CalledOnValidThread());
408 std::string auth_code;
409 if (!message->GetString("authorizationCode", &auth_code)) {
410 LOG(ERROR) << "'authorizationCode' string not found.";
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
421 oauth_client_->GetCredentialsFromAuthCode(
422 oauth_client_info, auth_code, base::Bind(
423 &Me2MeNativeMessagingHost::SendCredentialsResponse, weak_ptr_,
424 base::Passed(&response)));
427 void Me2MeNativeMessagingHost::SendConfigResponse(
428 scoped_ptr<base::DictionaryValue> response,
429 scoped_ptr<base::DictionaryValue> config) {
430 DCHECK(thread_checker_.CalledOnValidThread());
433 response->Set("config", config.release());
435 response->Set("config", base::Value::CreateNullValue());
437 channel_->SendMessage(response.PassAs<base::Value>());
440 void Me2MeNativeMessagingHost::SendPairedClientsResponse(
441 scoped_ptr<base::DictionaryValue> response,
442 scoped_ptr<base::ListValue> pairings) {
443 DCHECK(thread_checker_.CalledOnValidThread());
445 response->Set("pairedClients", pairings.release());
446 channel_->SendMessage(response.PassAs<base::Value>());
449 void Me2MeNativeMessagingHost::SendUsageStatsConsentResponse(
450 scoped_ptr<base::DictionaryValue> response,
451 const DaemonController::UsageStatsConsent& consent) {
452 DCHECK(thread_checker_.CalledOnValidThread());
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>());
460 void Me2MeNativeMessagingHost::SendAsyncResult(
461 scoped_ptr<base::DictionaryValue> response,
462 DaemonController::AsyncResult result) {
463 DCHECK(thread_checker_.CalledOnValidThread());
466 case DaemonController::RESULT_OK:
467 response->SetString("result", "OK");
469 case DaemonController::RESULT_FAILED:
470 response->SetString("result", "FAILED");
472 case DaemonController::RESULT_CANCELLED:
473 response->SetString("result", "CANCELLED");
475 case DaemonController::RESULT_FAILED_DIRECTORY:
476 response->SetString("result", "FAILED_DIRECTORY");
479 channel_->SendMessage(response.PassAs<base::Value>());
482 void Me2MeNativeMessagingHost::SendBooleanResult(
483 scoped_ptr<base::DictionaryValue> response, bool result) {
484 DCHECK(thread_checker_.CalledOnValidThread());
486 response->SetBoolean("result", result);
487 channel_->SendMessage(response.PassAs<base::Value>());
490 void 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());
496 response->SetString("userEmail", user_email);
497 response->SetString("refreshToken", refresh_token);
498 channel_->SendMessage(response.PassAs<base::Value>());
501 void Me2MeNativeMessagingHost::OnError() {
502 // Trigger a host shutdown by sending a NULL message.
503 channel_->SendMessage(scoped_ptr<base::Value>());
506 void Me2MeNativeMessagingHost::Stop() {
507 DCHECK(thread_checker_.CalledOnValidThread());
509 if (!quit_closure_.is_null())
510 base::ResetAndReturn(&quit_closure_).Run();
514 Me2MeNativeMessagingHost::ElevatedChannelEventHandler::
515 ElevatedChannelEventHandler(Me2MeNativeMessagingHost* host)
519 void Me2MeNativeMessagingHost::ElevatedChannelEventHandler::OnMessage(
520 scoped_ptr<base::Value> message) {
521 DCHECK(parent_->thread_checker_.CalledOnValidThread());
523 // Simply pass along the response from the elevated host to the client.
524 parent_->channel_->SendMessage(message.Pass());
527 void Me2MeNativeMessagingHost::ElevatedChannelEventHandler::OnDisconnect() {
528 parent_->OnDisconnect();
531 bool Me2MeNativeMessagingHost::DelegateToElevatedHost(
532 scoped_ptr<base::DictionaryValue> message) {
533 DCHECK(thread_checker_.CalledOnValidThread());
535 EnsureElevatedHostCreated();
537 // elevated_channel_ will be null if user rejects the UAC request.
538 if (elevated_channel_)
539 elevated_channel_->SendMessage(message.PassAs<base::Value>());
541 return elevated_channel_ != NULL;
544 void Me2MeNativeMessagingHost::EnsureElevatedHostCreated() {
545 DCHECK(thread_checker_.CalledOnValidThread());
546 DCHECK(needs_elevation_);
548 if (elevated_channel_)
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.";
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());
564 ScopedSd sd = ConvertSddlToSd(security_descriptor);
566 PLOG(ERROR) << "Failed to create a security descriptor for the"
567 << "Chromoting Me2Me native messaging host.";
572 SECURITY_ATTRIBUTES security_attributes = {0};
573 security_attributes.nLength = sizeof(security_attributes);
574 security_attributes.lpSecurityDescriptor = sd.get();
575 security_attributes.bInheritHandle = FALSE;
577 // Generate a unique name for the input channel.
578 std::string input_pipe_name(kChromePipeNamePrefix);
579 input_pipe_name.append(IPC::Channel::GenerateUniqueRandomChannelID());
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,
588 kTimeOutMilliseconds,
589 &security_attributes));
591 if (!delegate_write_handle.IsValid()) {
592 PLOG(ERROR) << "Failed to create named pipe '" << input_pipe_name << "'";
597 // Generate a unique name for the input channel.
598 std::string output_pipe_name(kChromePipeNamePrefix);
599 output_pipe_name.append(IPC::Channel::GenerateUniqueRandomChannelID());
601 base::win::ScopedHandle delegate_read_handle(::CreateNamedPipe(
602 base::ASCIIToUTF16(output_pipe_name).c_str(),
604 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS,
608 kTimeOutMilliseconds,
609 &security_attributes));
611 if (!delegate_read_handle.IsValid()) {
612 PLOG(ERROR) << "Failed to create named pipe '" << output_pipe_name << "'";
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();
623 // Create the child process command line by copying switches from the current
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);
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);
635 for (base::CommandLine::StringVector::const_iterator i = args.begin();
636 i != args.end(); ++i) {
637 command_line.AppendArgNative(*i);
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();
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;
655 if (!ShellExecuteEx(&info)) {
656 DWORD error = ::GetLastError();
657 PLOG(ERROR) << "Unable to launch '" << binary.value() << "'";
658 if (error != ERROR_CANCELLED) {
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 << "'";
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 << "'";
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())));
688 elevated_channel_event_handler_.reset(
689 new Me2MeNativeMessagingHost::ElevatedChannelEventHandler(this));
690 elevated_channel_->Start(elevated_channel_event_handler_.get());
692 elevated_host_timer_.Start(
693 FROM_HERE, base::TimeDelta::FromSeconds(kElevatedHostTimeoutSeconds),
694 this, &Me2MeNativeMessagingHost::DisconnectElevatedHost);
697 void Me2MeNativeMessagingHost::DisconnectElevatedHost() {
698 DCHECK(thread_checker_.CalledOnValidThread());
700 // This will send an EOF to the elevated host, triggering its shutdown.
701 elevated_channel_.reset();
704 #else // defined(OS_WIN)
706 bool Me2MeNativeMessagingHost::DelegateToElevatedHost(
707 scoped_ptr<base::DictionaryValue> message) {
712 #endif // !defined(OS_WIN)
714 } // namespace remoting