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/pin_hash.h"
25 #include "remoting/host/setup/oauth_client.h"
26 #include "remoting/protocol/pairing_registry.h"
30 #include "base/win/win_util.h"
31 #include "remoting/host/win/security_descriptor.h"
32 #endif // 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 const int kElevatedHostTimeoutSeconds
= 300;
42 #endif // defined(OS_WIN)
44 // redirect_uri to use when authenticating service accounts (service account
45 // codes are obtained "out-of-band", i.e., not through an OAuth redirect).
46 const char* kServiceAccountRedirectUri
= "oob";
48 // Features supported in addition to the base protocol.
49 const char* kSupportedFeatures
[] = {
54 // Helper to extract the "config" part of a message as a DictionaryValue.
55 // Returns NULL on failure, and logs an error message.
56 scoped_ptr
<base::DictionaryValue
> ConfigDictionaryFromMessage(
57 scoped_ptr
<base::DictionaryValue
> message
) {
58 scoped_ptr
<base::DictionaryValue
> result
;
59 const base::DictionaryValue
* config_dict
;
60 if (message
->GetDictionary("config", &config_dict
)) {
61 result
.reset(config_dict
->DeepCopy());
63 LOG(ERROR
) << "'config' dictionary not found";
72 Me2MeNativeMessagingHost::Me2MeNativeMessagingHost(
74 intptr_t parent_window_handle
,
75 scoped_ptr
<NativeMessagingChannel
> channel
,
76 scoped_refptr
<DaemonController
> daemon_controller
,
77 scoped_refptr
<protocol::PairingRegistry
> pairing_registry
,
78 scoped_ptr
<OAuthClient
> oauth_client
)
79 : needs_elevation_(needs_elevation
),
80 parent_window_handle_(parent_window_handle
),
81 channel_(channel
.Pass()),
82 daemon_controller_(daemon_controller
),
83 pairing_registry_(pairing_registry
),
84 oauth_client_(oauth_client
.Pass()),
86 weak_ptr_
= weak_factory_
.GetWeakPtr();
89 Me2MeNativeMessagingHost::~Me2MeNativeMessagingHost() {
90 DCHECK(thread_checker_
.CalledOnValidThread());
93 void Me2MeNativeMessagingHost::Start(
94 const base::Closure
& quit_closure
) {
95 DCHECK(thread_checker_
.CalledOnValidThread());
97 quit_closure_
= quit_closure
;
100 base::Bind(&Me2MeNativeMessagingHost::ProcessRequest
, weak_ptr_
),
101 base::Bind(&Me2MeNativeMessagingHost::Stop
, weak_ptr_
));
104 void Me2MeNativeMessagingHost::ProcessRequest(
105 scoped_ptr
<base::DictionaryValue
> message
) {
106 DCHECK(thread_checker_
.CalledOnValidThread());
108 scoped_ptr
<base::DictionaryValue
> response(new base::DictionaryValue());
110 // If the client supplies an ID, it will expect it in the response. This
111 // might be a string or a number, so cope with both.
112 const base::Value
* id
;
113 if (message
->Get("id", &id
))
114 response
->Set("id", id
->DeepCopy());
117 if (!message
->GetString("type", &type
)) {
118 LOG(ERROR
) << "'type' not found";
119 channel_
->SendMessage(scoped_ptr
<base::DictionaryValue
>());
123 response
->SetString("type", type
+ "Response");
125 if (type
== "hello") {
126 ProcessHello(message
.Pass(), response
.Pass());
127 } else if (type
== "clearPairedClients") {
128 ProcessClearPairedClients(message
.Pass(), response
.Pass());
129 } else if (type
== "deletePairedClient") {
130 ProcessDeletePairedClient(message
.Pass(), response
.Pass());
131 } else if (type
== "getHostName") {
132 ProcessGetHostName(message
.Pass(), response
.Pass());
133 } else if (type
== "getPinHash") {
134 ProcessGetPinHash(message
.Pass(), response
.Pass());
135 } else if (type
== "generateKeyPair") {
136 ProcessGenerateKeyPair(message
.Pass(), response
.Pass());
137 } else if (type
== "updateDaemonConfig") {
138 ProcessUpdateDaemonConfig(message
.Pass(), response
.Pass());
139 } else if (type
== "getDaemonConfig") {
140 ProcessGetDaemonConfig(message
.Pass(), response
.Pass());
141 } else if (type
== "getPairedClients") {
142 ProcessGetPairedClients(message
.Pass(), response
.Pass());
143 } else if (type
== "getUsageStatsConsent") {
144 ProcessGetUsageStatsConsent(message
.Pass(), response
.Pass());
145 } else if (type
== "startDaemon") {
146 ProcessStartDaemon(message
.Pass(), response
.Pass());
147 } else if (type
== "stopDaemon") {
148 ProcessStopDaemon(message
.Pass(), response
.Pass());
149 } else if (type
== "getDaemonState") {
150 ProcessGetDaemonState(message
.Pass(), response
.Pass());
151 } else if (type
== "getHostClientId") {
152 ProcessGetHostClientId(message
.Pass(), response
.Pass());
153 } else if (type
== "getCredentialsFromAuthCode") {
154 ProcessGetCredentialsFromAuthCode(message
.Pass(), response
.Pass());
156 LOG(ERROR
) << "Unsupported request type: " << type
;
161 void Me2MeNativeMessagingHost::ProcessHello(
162 scoped_ptr
<base::DictionaryValue
> message
,
163 scoped_ptr
<base::DictionaryValue
> response
) {
164 DCHECK(thread_checker_
.CalledOnValidThread());
166 response
->SetString("version", STRINGIZE(VERSION
));
167 scoped_ptr
<base::ListValue
> supported_features_list(new base::ListValue());
168 supported_features_list
->AppendStrings(std::vector
<std::string
>(
169 kSupportedFeatures
, kSupportedFeatures
+ arraysize(kSupportedFeatures
)));
170 response
->Set("supportedFeatures", supported_features_list
.release());
171 channel_
->SendMessage(response
.Pass());
174 void Me2MeNativeMessagingHost::ProcessClearPairedClients(
175 scoped_ptr
<base::DictionaryValue
> message
,
176 scoped_ptr
<base::DictionaryValue
> response
) {
177 DCHECK(thread_checker_
.CalledOnValidThread());
179 if (needs_elevation_
) {
180 if (!DelegateToElevatedHost(message
.Pass()))
181 SendBooleanResult(response
.Pass(), false);
185 if (pairing_registry_
.get()) {
186 pairing_registry_
->ClearAllPairings(
187 base::Bind(&Me2MeNativeMessagingHost::SendBooleanResult
, weak_ptr_
,
188 base::Passed(&response
)));
190 SendBooleanResult(response
.Pass(), false);
194 void Me2MeNativeMessagingHost::ProcessDeletePairedClient(
195 scoped_ptr
<base::DictionaryValue
> message
,
196 scoped_ptr
<base::DictionaryValue
> response
) {
197 DCHECK(thread_checker_
.CalledOnValidThread());
199 if (needs_elevation_
) {
200 if (!DelegateToElevatedHost(message
.Pass()))
201 SendBooleanResult(response
.Pass(), false);
205 std::string client_id
;
206 if (!message
->GetString(protocol::PairingRegistry::kClientIdKey
,
208 LOG(ERROR
) << "'" << protocol::PairingRegistry::kClientIdKey
209 << "' string not found.";
214 if (pairing_registry_
.get()) {
215 pairing_registry_
->DeletePairing(
216 client_id
, base::Bind(&Me2MeNativeMessagingHost::SendBooleanResult
,
217 weak_ptr_
, base::Passed(&response
)));
219 SendBooleanResult(response
.Pass(), false);
223 void Me2MeNativeMessagingHost::ProcessGetHostName(
224 scoped_ptr
<base::DictionaryValue
> message
,
225 scoped_ptr
<base::DictionaryValue
> response
) {
226 DCHECK(thread_checker_
.CalledOnValidThread());
228 response
->SetString("hostname", net::GetHostName());
229 channel_
->SendMessage(response
.Pass());
232 void Me2MeNativeMessagingHost::ProcessGetPinHash(
233 scoped_ptr
<base::DictionaryValue
> message
,
234 scoped_ptr
<base::DictionaryValue
> response
) {
235 DCHECK(thread_checker_
.CalledOnValidThread());
238 if (!message
->GetString("hostId", &host_id
)) {
239 LOG(ERROR
) << "'hostId' not found: " << message
;
244 if (!message
->GetString("pin", &pin
)) {
245 LOG(ERROR
) << "'pin' not found: " << message
;
249 response
->SetString("hash", MakeHostPinHash(host_id
, pin
));
250 channel_
->SendMessage(response
.Pass());
253 void Me2MeNativeMessagingHost::ProcessGenerateKeyPair(
254 scoped_ptr
<base::DictionaryValue
> message
,
255 scoped_ptr
<base::DictionaryValue
> response
) {
256 DCHECK(thread_checker_
.CalledOnValidThread());
258 scoped_refptr
<RsaKeyPair
> key_pair
= RsaKeyPair::Generate();
259 response
->SetString("privateKey", key_pair
->ToString());
260 response
->SetString("publicKey", key_pair
->GetPublicKey());
261 channel_
->SendMessage(response
.Pass());
264 void Me2MeNativeMessagingHost::ProcessUpdateDaemonConfig(
265 scoped_ptr
<base::DictionaryValue
> message
,
266 scoped_ptr
<base::DictionaryValue
> response
) {
267 DCHECK(thread_checker_
.CalledOnValidThread());
269 scoped_ptr
<base::DictionaryValue
> config_dict
=
270 ConfigDictionaryFromMessage(message
.Pass());
276 daemon_controller_
->UpdateConfig(
278 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult
, weak_ptr_
,
279 base::Passed(&response
)));
282 void Me2MeNativeMessagingHost::ProcessGetDaemonConfig(
283 scoped_ptr
<base::DictionaryValue
> message
,
284 scoped_ptr
<base::DictionaryValue
> response
) {
285 DCHECK(thread_checker_
.CalledOnValidThread());
287 daemon_controller_
->GetConfig(
288 base::Bind(&Me2MeNativeMessagingHost::SendConfigResponse
, weak_ptr_
,
289 base::Passed(&response
)));
292 void Me2MeNativeMessagingHost::ProcessGetPairedClients(
293 scoped_ptr
<base::DictionaryValue
> message
,
294 scoped_ptr
<base::DictionaryValue
> response
) {
295 DCHECK(thread_checker_
.CalledOnValidThread());
297 if (pairing_registry_
.get()) {
298 pairing_registry_
->GetAllPairings(
299 base::Bind(&Me2MeNativeMessagingHost::SendPairedClientsResponse
,
300 weak_ptr_
, base::Passed(&response
)));
302 scoped_ptr
<base::ListValue
> no_paired_clients(new base::ListValue
);
303 SendPairedClientsResponse(response
.Pass(), no_paired_clients
.Pass());
307 void Me2MeNativeMessagingHost::ProcessGetUsageStatsConsent(
308 scoped_ptr
<base::DictionaryValue
> message
,
309 scoped_ptr
<base::DictionaryValue
> response
) {
310 DCHECK(thread_checker_
.CalledOnValidThread());
312 daemon_controller_
->GetUsageStatsConsent(
313 base::Bind(&Me2MeNativeMessagingHost::SendUsageStatsConsentResponse
,
314 weak_ptr_
, base::Passed(&response
)));
317 void Me2MeNativeMessagingHost::ProcessStartDaemon(
318 scoped_ptr
<base::DictionaryValue
> message
,
319 scoped_ptr
<base::DictionaryValue
> response
) {
320 DCHECK(thread_checker_
.CalledOnValidThread());
323 if (!message
->GetBoolean("consent", &consent
)) {
324 LOG(ERROR
) << "'consent' not found.";
329 scoped_ptr
<base::DictionaryValue
> config_dict
=
330 ConfigDictionaryFromMessage(message
.Pass());
336 daemon_controller_
->SetConfigAndStart(
337 config_dict
.Pass(), consent
,
338 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult
, weak_ptr_
,
339 base::Passed(&response
)));
342 void Me2MeNativeMessagingHost::ProcessStopDaemon(
343 scoped_ptr
<base::DictionaryValue
> message
,
344 scoped_ptr
<base::DictionaryValue
> response
) {
345 DCHECK(thread_checker_
.CalledOnValidThread());
347 daemon_controller_
->Stop(
348 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult
, weak_ptr_
,
349 base::Passed(&response
)));
352 void Me2MeNativeMessagingHost::ProcessGetDaemonState(
353 scoped_ptr
<base::DictionaryValue
> message
,
354 scoped_ptr
<base::DictionaryValue
> response
) {
355 DCHECK(thread_checker_
.CalledOnValidThread());
357 DaemonController::State state
= daemon_controller_
->GetState();
359 case DaemonController::STATE_NOT_IMPLEMENTED
:
360 response
->SetString("state", "NOT_IMPLEMENTED");
362 case DaemonController::STATE_NOT_INSTALLED
:
363 response
->SetString("state", "NOT_INSTALLED");
365 case DaemonController::STATE_INSTALLING
:
366 response
->SetString("state", "INSTALLING");
368 case DaemonController::STATE_STOPPED
:
369 response
->SetString("state", "STOPPED");
371 case DaemonController::STATE_STARTING
:
372 response
->SetString("state", "STARTING");
374 case DaemonController::STATE_STARTED
:
375 response
->SetString("state", "STARTED");
377 case DaemonController::STATE_STOPPING
:
378 response
->SetString("state", "STOPPING");
380 case DaemonController::STATE_UNKNOWN
:
381 response
->SetString("state", "UNKNOWN");
384 channel_
->SendMessage(response
.Pass());
387 void Me2MeNativeMessagingHost::ProcessGetHostClientId(
388 scoped_ptr
<base::DictionaryValue
> message
,
389 scoped_ptr
<base::DictionaryValue
> response
) {
390 DCHECK(thread_checker_
.CalledOnValidThread());
392 response
->SetString("clientId", google_apis::GetOAuth2ClientID(
393 google_apis::CLIENT_REMOTING_HOST
));
394 channel_
->SendMessage(response
.Pass());
397 void Me2MeNativeMessagingHost::ProcessGetCredentialsFromAuthCode(
398 scoped_ptr
<base::DictionaryValue
> message
,
399 scoped_ptr
<base::DictionaryValue
> response
) {
400 DCHECK(thread_checker_
.CalledOnValidThread());
402 std::string auth_code
;
403 if (!message
->GetString("authorizationCode", &auth_code
)) {
404 LOG(ERROR
) << "'authorizationCode' string not found.";
409 gaia::OAuthClientInfo oauth_client_info
= {
410 google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING_HOST
),
411 google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_REMOTING_HOST
),
412 kServiceAccountRedirectUri
415 oauth_client_
->GetCredentialsFromAuthCode(
416 oauth_client_info
, auth_code
, base::Bind(
417 &Me2MeNativeMessagingHost::SendCredentialsResponse
, weak_ptr_
,
418 base::Passed(&response
)));
421 void Me2MeNativeMessagingHost::SendConfigResponse(
422 scoped_ptr
<base::DictionaryValue
> response
,
423 scoped_ptr
<base::DictionaryValue
> config
) {
424 DCHECK(thread_checker_
.CalledOnValidThread());
427 response
->Set("config", config
.release());
429 response
->Set("config", base::Value::CreateNullValue());
431 channel_
->SendMessage(response
.Pass());
434 void Me2MeNativeMessagingHost::SendPairedClientsResponse(
435 scoped_ptr
<base::DictionaryValue
> response
,
436 scoped_ptr
<base::ListValue
> pairings
) {
437 DCHECK(thread_checker_
.CalledOnValidThread());
439 response
->Set("pairedClients", pairings
.release());
440 channel_
->SendMessage(response
.Pass());
443 void Me2MeNativeMessagingHost::SendUsageStatsConsentResponse(
444 scoped_ptr
<base::DictionaryValue
> response
,
445 const DaemonController::UsageStatsConsent
& consent
) {
446 DCHECK(thread_checker_
.CalledOnValidThread());
448 response
->SetBoolean("supported", consent
.supported
);
449 response
->SetBoolean("allowed", consent
.allowed
);
450 response
->SetBoolean("setByPolicy", consent
.set_by_policy
);
451 channel_
->SendMessage(response
.Pass());
454 void Me2MeNativeMessagingHost::SendAsyncResult(
455 scoped_ptr
<base::DictionaryValue
> response
,
456 DaemonController::AsyncResult result
) {
457 DCHECK(thread_checker_
.CalledOnValidThread());
460 case DaemonController::RESULT_OK
:
461 response
->SetString("result", "OK");
463 case DaemonController::RESULT_FAILED
:
464 response
->SetString("result", "FAILED");
466 case DaemonController::RESULT_CANCELLED
:
467 response
->SetString("result", "CANCELLED");
469 case DaemonController::RESULT_FAILED_DIRECTORY
:
470 response
->SetString("result", "FAILED_DIRECTORY");
473 channel_
->SendMessage(response
.Pass());
476 void Me2MeNativeMessagingHost::SendBooleanResult(
477 scoped_ptr
<base::DictionaryValue
> response
, bool result
) {
478 DCHECK(thread_checker_
.CalledOnValidThread());
480 response
->SetBoolean("result", result
);
481 channel_
->SendMessage(response
.Pass());
484 void Me2MeNativeMessagingHost::SendCredentialsResponse(
485 scoped_ptr
<base::DictionaryValue
> response
,
486 const std::string
& user_email
,
487 const std::string
& refresh_token
) {
488 DCHECK(thread_checker_
.CalledOnValidThread());
490 response
->SetString("userEmail", user_email
);
491 response
->SetString("refreshToken", refresh_token
);
492 channel_
->SendMessage(response
.Pass());
495 void Me2MeNativeMessagingHost::OnError() {
496 // Trigger a host shutdown by sending a NULL message.
497 channel_
->SendMessage(scoped_ptr
<base::DictionaryValue
>());
500 void Me2MeNativeMessagingHost::Stop() {
501 DCHECK(thread_checker_
.CalledOnValidThread());
503 if (!quit_closure_
.is_null())
504 base::ResetAndReturn(&quit_closure_
).Run();
509 bool Me2MeNativeMessagingHost::DelegateToElevatedHost(
510 scoped_ptr
<base::DictionaryValue
> message
) {
511 DCHECK(thread_checker_
.CalledOnValidThread());
513 EnsureElevatedHostCreated();
515 // elevated_channel_ will be null if user rejects the UAC request.
516 if (elevated_channel_
)
517 elevated_channel_
->SendMessage(message
.Pass());
519 return elevated_channel_
!= NULL
;
522 void Me2MeNativeMessagingHost::EnsureElevatedHostCreated() {
523 DCHECK(thread_checker_
.CalledOnValidThread());
524 DCHECK(needs_elevation_
);
526 if (elevated_channel_
)
529 // presubmit: allow wstring
530 std::wstring user_sid
;
531 if (!base::win::GetUserSidString(&user_sid
)) {
532 LOG(ERROR
) << "Failed to query the current user SID.";
537 // Create a security descriptor that gives full access to the caller and
538 // denies access by anyone else.
539 std::string security_descriptor
= base::StringPrintf(
540 "O:%1$sG:%1$sD:(A;;GA;;;%1$s)", base::UTF16ToASCII(user_sid
).c_str());
542 ScopedSd sd
= ConvertSddlToSd(security_descriptor
);
544 PLOG(ERROR
) << "Failed to create a security descriptor for the"
545 << "Chromoting Me2Me native messaging host.";
550 SECURITY_ATTRIBUTES security_attributes
= {0};
551 security_attributes
.nLength
= sizeof(security_attributes
);
552 security_attributes
.lpSecurityDescriptor
= sd
.get();
553 security_attributes
.bInheritHandle
= FALSE
;
555 // Generate a unique name for the input channel.
556 std::string
input_pipe_name(kChromePipeNamePrefix
);
557 input_pipe_name
.append(IPC::Channel::GenerateUniqueRandomChannelID());
559 base::win::ScopedHandle
delegate_write_handle(::CreateNamedPipe(
560 base::ASCIIToUTF16(input_pipe_name
).c_str(),
561 PIPE_ACCESS_OUTBOUND
,
562 PIPE_TYPE_MESSAGE
| PIPE_READMODE_MESSAGE
| PIPE_REJECT_REMOTE_CLIENTS
,
566 kTimeOutMilliseconds
,
567 &security_attributes
));
569 if (!delegate_write_handle
.IsValid()) {
570 PLOG(ERROR
) << "Failed to create named pipe '" << input_pipe_name
<< "'";
575 // Generate a unique name for the input channel.
576 std::string
output_pipe_name(kChromePipeNamePrefix
);
577 output_pipe_name
.append(IPC::Channel::GenerateUniqueRandomChannelID());
579 base::win::ScopedHandle
delegate_read_handle(::CreateNamedPipe(
580 base::ASCIIToUTF16(output_pipe_name
).c_str(),
582 PIPE_TYPE_MESSAGE
| PIPE_READMODE_MESSAGE
| PIPE_REJECT_REMOTE_CLIENTS
,
586 kTimeOutMilliseconds
,
587 &security_attributes
));
589 if (!delegate_read_handle
.IsValid()) {
590 PLOG(ERROR
) << "Failed to create named pipe '" << output_pipe_name
<< "'";
595 const base::CommandLine
* current_command_line
=
596 base::CommandLine::ForCurrentProcess();
597 const base::CommandLine::SwitchMap
& switches
=
598 current_command_line
->GetSwitches();
599 base::CommandLine::StringVector args
= current_command_line
->GetArgs();
601 // Create the child process command line by copying switches from the current
603 base::CommandLine
command_line(base::CommandLine::NO_PROGRAM
);
604 command_line
.AppendSwitch(kElevatingSwitchName
);
605 command_line
.AppendSwitchASCII(kInputSwitchName
, input_pipe_name
);
606 command_line
.AppendSwitchASCII(kOutputSwitchName
, output_pipe_name
);
608 DCHECK(!current_command_line
->HasSwitch(kElevatingSwitchName
));
609 for (base::CommandLine::SwitchMap::const_iterator i
= switches
.begin();
610 i
!= switches
.end(); ++i
) {
611 command_line
.AppendSwitchNative(i
->first
, i
->second
);
613 for (base::CommandLine::StringVector::const_iterator i
= args
.begin();
614 i
!= args
.end(); ++i
) {
615 command_line
.AppendArgNative(*i
);
618 // Get the name of the binary to launch.
619 base::FilePath binary
= current_command_line
->GetProgram();
620 base::CommandLine::StringType parameters
=
621 command_line
.GetCommandLineString();
623 // Launch the child process requesting elevation.
624 SHELLEXECUTEINFO info
;
625 memset(&info
, 0, sizeof(info
));
626 info
.cbSize
= sizeof(info
);
627 info
.hwnd
= reinterpret_cast<HWND
>(parent_window_handle_
);
628 info
.lpVerb
= L
"runas";
629 info
.lpFile
= binary
.value().c_str();
630 info
.lpParameters
= parameters
.c_str();
631 info
.nShow
= SW_HIDE
;
633 if (!ShellExecuteEx(&info
)) {
634 DWORD error
= ::GetLastError();
635 PLOG(ERROR
) << "Unable to launch '" << binary
.value() << "'";
636 if (error
!= ERROR_CANCELLED
) {
642 if (!::ConnectNamedPipe(delegate_write_handle
.Get(), NULL
)) {
643 DWORD error
= ::GetLastError();
644 if (error
!= ERROR_PIPE_CONNECTED
) {
645 PLOG(ERROR
) << "Unable to connect '" << input_pipe_name
<< "'";
651 if (!::ConnectNamedPipe(delegate_read_handle
.Get(), NULL
)) {
652 DWORD error
= ::GetLastError();
653 if (error
!= ERROR_PIPE_CONNECTED
) {
654 PLOG(ERROR
) << "Unable to connect '" << output_pipe_name
<< "'";
660 // Set up the native messaging channel to talk to the elevated host.
661 // Note that input for the elevate channel is output forthe elevated host.
662 elevated_channel_
.reset(new NativeMessagingChannel(
663 base::File(delegate_read_handle
.Take()),
664 base::File(delegate_write_handle
.Take())));
666 elevated_channel_
->Start(
667 base::Bind(&Me2MeNativeMessagingHost::ProcessDelegateResponse
, weak_ptr_
),
668 base::Bind(&Me2MeNativeMessagingHost::Stop
, weak_ptr_
));
670 elevated_host_timer_
.Start(
671 FROM_HERE
, base::TimeDelta::FromSeconds(kElevatedHostTimeoutSeconds
),
672 this, &Me2MeNativeMessagingHost::DisconnectElevatedHost
);
675 void Me2MeNativeMessagingHost::ProcessDelegateResponse(
676 scoped_ptr
<base::DictionaryValue
> message
) {
677 DCHECK(thread_checker_
.CalledOnValidThread());
679 // Simply pass along the response from the elevated host to the client.
680 channel_
->SendMessage(message
.Pass());
683 void Me2MeNativeMessagingHost::DisconnectElevatedHost() {
684 DCHECK(thread_checker_
.CalledOnValidThread());
686 // This will send an EOF to the elevated host, triggering its shutdown.
687 elevated_channel_
.reset();
690 #else // defined(OS_WIN)
692 bool Me2MeNativeMessagingHost::DelegateToElevatedHost(
693 scoped_ptr
<base::DictionaryValue
> message
) {
698 #endif // !defined(OS_WIN)
700 } // namespace remoting