Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / remoting / host / setup / me2me_native_messaging_host.cc
blobd2957fdf8c5a0c276be641e503aec965714f72f1
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",
53 "getRefreshTokenFromAuthCode",
56 // Helper to extract the "config" part of a message as a DictionaryValue.
57 // Returns nullptr on failure, and logs an error message.
58 scoped_ptr<base::DictionaryValue> ConfigDictionaryFromMessage(
59 scoped_ptr<base::DictionaryValue> message) {
60 scoped_ptr<base::DictionaryValue> result;
61 const base::DictionaryValue* config_dict;
62 if (message->GetDictionary("config", &config_dict)) {
63 result.reset(config_dict->DeepCopy());
64 } else {
65 LOG(ERROR) << "'config' dictionary not found";
67 return result.Pass();
70 } // namespace
72 namespace remoting {
74 Me2MeNativeMessagingHost::Me2MeNativeMessagingHost(
75 bool needs_elevation,
76 intptr_t parent_window_handle,
77 scoped_ptr<extensions::NativeMessagingChannel> channel,
78 scoped_refptr<DaemonController> daemon_controller,
79 scoped_refptr<protocol::PairingRegistry> pairing_registry,
80 scoped_ptr<OAuthClient> oauth_client)
81 : needs_elevation_(needs_elevation),
82 #if defined(OS_WIN)
83 parent_window_handle_(parent_window_handle),
84 #endif
85 channel_(channel.Pass()),
86 daemon_controller_(daemon_controller),
87 pairing_registry_(pairing_registry),
88 oauth_client_(oauth_client.Pass()),
89 weak_factory_(this) {
90 weak_ptr_ = weak_factory_.GetWeakPtr();
93 Me2MeNativeMessagingHost::~Me2MeNativeMessagingHost() {
94 DCHECK(thread_checker_.CalledOnValidThread());
97 void Me2MeNativeMessagingHost::Start(
98 const base::Closure& quit_closure) {
99 DCHECK(thread_checker_.CalledOnValidThread());
100 DCHECK(!quit_closure.is_null());
102 quit_closure_ = quit_closure;
104 channel_->Start(this);
107 void Me2MeNativeMessagingHost::OnMessage(scoped_ptr<base::Value> message) {
108 DCHECK(thread_checker_.CalledOnValidThread());
110 if (!message->IsType(base::Value::TYPE_DICTIONARY)) {
111 LOG(ERROR) << "Received a message that's not a dictionary.";
112 channel_->SendMessage(nullptr);
113 return;
116 scoped_ptr<base::DictionaryValue> message_dict(
117 static_cast<base::DictionaryValue*>(message.release()));
118 scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue());
120 // If the client supplies an ID, it will expect it in the response. This
121 // might be a string or a number, so cope with both.
122 const base::Value* id;
123 if (message_dict->Get("id", &id))
124 response->Set("id", id->DeepCopy());
126 std::string type;
127 if (!message_dict->GetString("type", &type)) {
128 LOG(ERROR) << "'type' not found";
129 channel_->SendMessage(nullptr);
130 return;
133 response->SetString("type", type + "Response");
135 if (type == "hello") {
136 ProcessHello(message_dict.Pass(), response.Pass());
137 } else if (type == "clearPairedClients") {
138 ProcessClearPairedClients(message_dict.Pass(), response.Pass());
139 } else if (type == "deletePairedClient") {
140 ProcessDeletePairedClient(message_dict.Pass(), response.Pass());
141 } else if (type == "getHostName") {
142 ProcessGetHostName(message_dict.Pass(), response.Pass());
143 } else if (type == "getPinHash") {
144 ProcessGetPinHash(message_dict.Pass(), response.Pass());
145 } else if (type == "generateKeyPair") {
146 ProcessGenerateKeyPair(message_dict.Pass(), response.Pass());
147 } else if (type == "updateDaemonConfig") {
148 ProcessUpdateDaemonConfig(message_dict.Pass(), response.Pass());
149 } else if (type == "getDaemonConfig") {
150 ProcessGetDaemonConfig(message_dict.Pass(), response.Pass());
151 } else if (type == "getPairedClients") {
152 ProcessGetPairedClients(message_dict.Pass(), response.Pass());
153 } else if (type == "getUsageStatsConsent") {
154 ProcessGetUsageStatsConsent(message_dict.Pass(), response.Pass());
155 } else if (type == "startDaemon") {
156 ProcessStartDaemon(message_dict.Pass(), response.Pass());
157 } else if (type == "stopDaemon") {
158 ProcessStopDaemon(message_dict.Pass(), response.Pass());
159 } else if (type == "getDaemonState") {
160 ProcessGetDaemonState(message_dict.Pass(), response.Pass());
161 } else if (type == "getHostClientId") {
162 ProcessGetHostClientId(message_dict.Pass(), response.Pass());
163 } else if (type == "getCredentialsFromAuthCode") {
164 ProcessGetCredentialsFromAuthCode(
165 message_dict.Pass(), response.Pass(), true);
166 } else if (type == "getRefreshTokenFromAuthCode") {
167 ProcessGetCredentialsFromAuthCode(
168 message_dict.Pass(), response.Pass(), false);
169 } else {
170 LOG(ERROR) << "Unsupported request type: " << type;
171 OnError();
175 void Me2MeNativeMessagingHost::OnDisconnect() {
176 if (!quit_closure_.is_null())
177 base::ResetAndReturn(&quit_closure_).Run();
180 void Me2MeNativeMessagingHost::ProcessHello(
181 scoped_ptr<base::DictionaryValue> message,
182 scoped_ptr<base::DictionaryValue> response) {
183 DCHECK(thread_checker_.CalledOnValidThread());
185 response->SetString("version", STRINGIZE(VERSION));
186 scoped_ptr<base::ListValue> supported_features_list(new base::ListValue());
187 supported_features_list->AppendStrings(std::vector<std::string>(
188 kSupportedFeatures, kSupportedFeatures + arraysize(kSupportedFeatures)));
189 response->Set("supportedFeatures", supported_features_list.release());
190 channel_->SendMessage(response.Pass());
193 void Me2MeNativeMessagingHost::ProcessClearPairedClients(
194 scoped_ptr<base::DictionaryValue> message,
195 scoped_ptr<base::DictionaryValue> response) {
196 DCHECK(thread_checker_.CalledOnValidThread());
198 if (needs_elevation_) {
199 if (!DelegateToElevatedHost(message.Pass()))
200 SendBooleanResult(response.Pass(), false);
201 return;
204 if (pairing_registry_.get()) {
205 pairing_registry_->ClearAllPairings(
206 base::Bind(&Me2MeNativeMessagingHost::SendBooleanResult, weak_ptr_,
207 base::Passed(&response)));
208 } else {
209 SendBooleanResult(response.Pass(), false);
213 void Me2MeNativeMessagingHost::ProcessDeletePairedClient(
214 scoped_ptr<base::DictionaryValue> message,
215 scoped_ptr<base::DictionaryValue> response) {
216 DCHECK(thread_checker_.CalledOnValidThread());
218 if (needs_elevation_) {
219 if (!DelegateToElevatedHost(message.Pass()))
220 SendBooleanResult(response.Pass(), false);
221 return;
224 std::string client_id;
225 if (!message->GetString(protocol::PairingRegistry::kClientIdKey,
226 &client_id)) {
227 LOG(ERROR) << "'" << protocol::PairingRegistry::kClientIdKey
228 << "' string not found.";
229 OnError();
230 return;
233 if (pairing_registry_.get()) {
234 pairing_registry_->DeletePairing(
235 client_id, base::Bind(&Me2MeNativeMessagingHost::SendBooleanResult,
236 weak_ptr_, base::Passed(&response)));
237 } else {
238 SendBooleanResult(response.Pass(), false);
242 void Me2MeNativeMessagingHost::ProcessGetHostName(
243 scoped_ptr<base::DictionaryValue> message,
244 scoped_ptr<base::DictionaryValue> response) {
245 DCHECK(thread_checker_.CalledOnValidThread());
247 response->SetString("hostname", net::GetHostName());
248 channel_->SendMessage(response.Pass());
251 void Me2MeNativeMessagingHost::ProcessGetPinHash(
252 scoped_ptr<base::DictionaryValue> message,
253 scoped_ptr<base::DictionaryValue> response) {
254 DCHECK(thread_checker_.CalledOnValidThread());
256 std::string host_id;
257 if (!message->GetString("hostId", &host_id)) {
258 LOG(ERROR) << "'hostId' not found: " << message;
259 OnError();
260 return;
262 std::string pin;
263 if (!message->GetString("pin", &pin)) {
264 LOG(ERROR) << "'pin' not found: " << message;
265 OnError();
266 return;
268 response->SetString("hash", MakeHostPinHash(host_id, pin));
269 channel_->SendMessage(response.Pass());
272 void Me2MeNativeMessagingHost::ProcessGenerateKeyPair(
273 scoped_ptr<base::DictionaryValue> message,
274 scoped_ptr<base::DictionaryValue> response) {
275 DCHECK(thread_checker_.CalledOnValidThread());
277 scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::Generate();
278 response->SetString("privateKey", key_pair->ToString());
279 response->SetString("publicKey", key_pair->GetPublicKey());
280 channel_->SendMessage(response.Pass());
283 void Me2MeNativeMessagingHost::ProcessUpdateDaemonConfig(
284 scoped_ptr<base::DictionaryValue> message,
285 scoped_ptr<base::DictionaryValue> response) {
286 DCHECK(thread_checker_.CalledOnValidThread());
288 if (needs_elevation_) {
289 if (!DelegateToElevatedHost(message.Pass()))
290 SendAsyncResult(response.Pass(), DaemonController::RESULT_FAILED);
291 return;
294 scoped_ptr<base::DictionaryValue> config_dict =
295 ConfigDictionaryFromMessage(message.Pass());
296 if (!config_dict) {
297 OnError();
298 return;
301 daemon_controller_->UpdateConfig(
302 config_dict.Pass(),
303 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
304 base::Passed(&response)));
307 void Me2MeNativeMessagingHost::ProcessGetDaemonConfig(
308 scoped_ptr<base::DictionaryValue> message,
309 scoped_ptr<base::DictionaryValue> response) {
310 DCHECK(thread_checker_.CalledOnValidThread());
312 daemon_controller_->GetConfig(
313 base::Bind(&Me2MeNativeMessagingHost::SendConfigResponse, weak_ptr_,
314 base::Passed(&response)));
317 void Me2MeNativeMessagingHost::ProcessGetPairedClients(
318 scoped_ptr<base::DictionaryValue> message,
319 scoped_ptr<base::DictionaryValue> response) {
320 DCHECK(thread_checker_.CalledOnValidThread());
322 if (pairing_registry_.get()) {
323 pairing_registry_->GetAllPairings(
324 base::Bind(&Me2MeNativeMessagingHost::SendPairedClientsResponse,
325 weak_ptr_, base::Passed(&response)));
326 } else {
327 scoped_ptr<base::ListValue> no_paired_clients(new base::ListValue);
328 SendPairedClientsResponse(response.Pass(), no_paired_clients.Pass());
332 void Me2MeNativeMessagingHost::ProcessGetUsageStatsConsent(
333 scoped_ptr<base::DictionaryValue> message,
334 scoped_ptr<base::DictionaryValue> response) {
335 DCHECK(thread_checker_.CalledOnValidThread());
337 daemon_controller_->GetUsageStatsConsent(
338 base::Bind(&Me2MeNativeMessagingHost::SendUsageStatsConsentResponse,
339 weak_ptr_, base::Passed(&response)));
342 void Me2MeNativeMessagingHost::ProcessStartDaemon(
343 scoped_ptr<base::DictionaryValue> message,
344 scoped_ptr<base::DictionaryValue> response) {
345 DCHECK(thread_checker_.CalledOnValidThread());
347 if (needs_elevation_) {
348 if (!DelegateToElevatedHost(message.Pass()))
349 SendAsyncResult(response.Pass(), DaemonController::RESULT_FAILED);
350 return;
353 bool consent;
354 if (!message->GetBoolean("consent", &consent)) {
355 LOG(ERROR) << "'consent' not found.";
356 OnError();
357 return;
360 scoped_ptr<base::DictionaryValue> config_dict =
361 ConfigDictionaryFromMessage(message.Pass());
362 if (!config_dict) {
363 OnError();
364 return;
367 daemon_controller_->SetConfigAndStart(
368 config_dict.Pass(), consent,
369 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
370 base::Passed(&response)));
373 void Me2MeNativeMessagingHost::ProcessStopDaemon(
374 scoped_ptr<base::DictionaryValue> message,
375 scoped_ptr<base::DictionaryValue> response) {
376 DCHECK(thread_checker_.CalledOnValidThread());
378 if (needs_elevation_) {
379 if (!DelegateToElevatedHost(message.Pass()))
380 SendAsyncResult(response.Pass(), DaemonController::RESULT_FAILED);
381 return;
384 daemon_controller_->Stop(
385 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
386 base::Passed(&response)));
389 void Me2MeNativeMessagingHost::ProcessGetDaemonState(
390 scoped_ptr<base::DictionaryValue> message,
391 scoped_ptr<base::DictionaryValue> response) {
392 DCHECK(thread_checker_.CalledOnValidThread());
394 DaemonController::State state = daemon_controller_->GetState();
395 switch (state) {
396 case DaemonController::STATE_NOT_IMPLEMENTED:
397 response->SetString("state", "NOT_IMPLEMENTED");
398 break;
399 case DaemonController::STATE_STOPPED:
400 response->SetString("state", "STOPPED");
401 break;
402 case DaemonController::STATE_STARTING:
403 response->SetString("state", "STARTING");
404 break;
405 case DaemonController::STATE_STARTED:
406 response->SetString("state", "STARTED");
407 break;
408 case DaemonController::STATE_STOPPING:
409 response->SetString("state", "STOPPING");
410 break;
411 case DaemonController::STATE_UNKNOWN:
412 response->SetString("state", "UNKNOWN");
413 break;
415 channel_->SendMessage(response.Pass());
418 void Me2MeNativeMessagingHost::ProcessGetHostClientId(
419 scoped_ptr<base::DictionaryValue> message,
420 scoped_ptr<base::DictionaryValue> response) {
421 DCHECK(thread_checker_.CalledOnValidThread());
423 response->SetString("clientId", google_apis::GetOAuth2ClientID(
424 google_apis::CLIENT_REMOTING_HOST));
425 channel_->SendMessage(response.Pass());
428 void Me2MeNativeMessagingHost::ProcessGetCredentialsFromAuthCode(
429 scoped_ptr<base::DictionaryValue> message,
430 scoped_ptr<base::DictionaryValue> response,
431 bool need_user_email) {
432 DCHECK(thread_checker_.CalledOnValidThread());
434 std::string auth_code;
435 if (!message->GetString("authorizationCode", &auth_code)) {
436 LOG(ERROR) << "'authorizationCode' string not found.";
437 OnError();
438 return;
441 gaia::OAuthClientInfo oauth_client_info = {
442 google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING_HOST),
443 google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_REMOTING_HOST),
444 kServiceAccountRedirectUri
447 oauth_client_->GetCredentialsFromAuthCode(
448 oauth_client_info, auth_code, need_user_email, base::Bind(
449 &Me2MeNativeMessagingHost::SendCredentialsResponse, weak_ptr_,
450 base::Passed(&response)));
453 void Me2MeNativeMessagingHost::SendConfigResponse(
454 scoped_ptr<base::DictionaryValue> response,
455 scoped_ptr<base::DictionaryValue> config) {
456 DCHECK(thread_checker_.CalledOnValidThread());
458 if (config) {
459 response->Set("config", config.release());
460 } else {
461 response->Set("config", base::Value::CreateNullValue());
463 channel_->SendMessage(response.Pass());
466 void Me2MeNativeMessagingHost::SendPairedClientsResponse(
467 scoped_ptr<base::DictionaryValue> response,
468 scoped_ptr<base::ListValue> pairings) {
469 DCHECK(thread_checker_.CalledOnValidThread());
471 response->Set("pairedClients", pairings.release());
472 channel_->SendMessage(response.Pass());
475 void Me2MeNativeMessagingHost::SendUsageStatsConsentResponse(
476 scoped_ptr<base::DictionaryValue> response,
477 const DaemonController::UsageStatsConsent& consent) {
478 DCHECK(thread_checker_.CalledOnValidThread());
480 response->SetBoolean("supported", consent.supported);
481 response->SetBoolean("allowed", consent.allowed);
482 response->SetBoolean("setByPolicy", consent.set_by_policy);
483 channel_->SendMessage(response.Pass());
486 void Me2MeNativeMessagingHost::SendAsyncResult(
487 scoped_ptr<base::DictionaryValue> response,
488 DaemonController::AsyncResult result) {
489 DCHECK(thread_checker_.CalledOnValidThread());
491 switch (result) {
492 case DaemonController::RESULT_OK:
493 response->SetString("result", "OK");
494 break;
495 case DaemonController::RESULT_FAILED:
496 response->SetString("result", "FAILED");
497 break;
498 case DaemonController::RESULT_CANCELLED:
499 response->SetString("result", "CANCELLED");
500 break;
501 case DaemonController::RESULT_FAILED_DIRECTORY:
502 response->SetString("result", "FAILED_DIRECTORY");
503 break;
505 channel_->SendMessage(response.Pass());
508 void Me2MeNativeMessagingHost::SendBooleanResult(
509 scoped_ptr<base::DictionaryValue> response, bool result) {
510 DCHECK(thread_checker_.CalledOnValidThread());
512 response->SetBoolean("result", result);
513 channel_->SendMessage(response.Pass());
516 void Me2MeNativeMessagingHost::SendCredentialsResponse(
517 scoped_ptr<base::DictionaryValue> response,
518 const std::string& user_email,
519 const std::string& refresh_token) {
520 DCHECK(thread_checker_.CalledOnValidThread());
522 if (!user_email.empty()) {
523 response->SetString("userEmail", user_email);
525 response->SetString("refreshToken", refresh_token);
526 channel_->SendMessage(response.Pass());
529 void Me2MeNativeMessagingHost::OnError() {
530 // Trigger a host shutdown by sending a nullptr message.
531 channel_->SendMessage(nullptr);
534 void Me2MeNativeMessagingHost::Stop() {
535 DCHECK(thread_checker_.CalledOnValidThread());
537 if (!quit_closure_.is_null())
538 base::ResetAndReturn(&quit_closure_).Run();
541 #if defined(OS_WIN)
542 Me2MeNativeMessagingHost::ElevatedChannelEventHandler::
543 ElevatedChannelEventHandler(Me2MeNativeMessagingHost* host)
544 : parent_(host) {
547 void Me2MeNativeMessagingHost::ElevatedChannelEventHandler::OnMessage(
548 scoped_ptr<base::Value> message) {
549 DCHECK(parent_->thread_checker_.CalledOnValidThread());
551 // Simply pass along the response from the elevated host to the client.
552 parent_->channel_->SendMessage(message.Pass());
555 void Me2MeNativeMessagingHost::ElevatedChannelEventHandler::OnDisconnect() {
556 parent_->OnDisconnect();
559 bool Me2MeNativeMessagingHost::DelegateToElevatedHost(
560 scoped_ptr<base::DictionaryValue> message) {
561 DCHECK(thread_checker_.CalledOnValidThread());
563 EnsureElevatedHostCreated();
565 // elevated_channel_ will be null if user rejects the UAC request.
566 if (elevated_channel_)
567 elevated_channel_->SendMessage(message.Pass());
569 return elevated_channel_ != nullptr;
572 void Me2MeNativeMessagingHost::EnsureElevatedHostCreated() {
573 DCHECK(thread_checker_.CalledOnValidThread());
574 DCHECK(needs_elevation_);
576 if (elevated_channel_)
577 return;
579 // presubmit: allow wstring
580 std::wstring user_sid;
581 if (!base::win::GetUserSidString(&user_sid)) {
582 LOG(ERROR) << "Failed to query the current user SID.";
583 OnError();
584 return;
587 // Create a security descriptor that gives full access to the caller and
588 // denies access by anyone else.
589 std::string user_sid_ascii = base::UTF16ToASCII(user_sid);
590 std::string security_descriptor =
591 base::StringPrintf("O:%sG:%sD:(A;;GA;;;%s)", user_sid_ascii.c_str(),
592 user_sid_ascii.c_str(), user_sid_ascii.c_str());
594 ScopedSd sd = ConvertSddlToSd(security_descriptor);
595 if (!sd) {
596 PLOG(ERROR) << "Failed to create a security descriptor for the"
597 << "Chromoting Me2Me native messaging host.";
598 OnError();
599 return;
602 SECURITY_ATTRIBUTES security_attributes = {0};
603 security_attributes.nLength = sizeof(security_attributes);
604 security_attributes.lpSecurityDescriptor = sd.get();
605 security_attributes.bInheritHandle = FALSE;
607 // Generate a unique name for the input channel.
608 std::string input_pipe_name(kChromePipeNamePrefix);
609 input_pipe_name.append(IPC::Channel::GenerateUniqueRandomChannelID());
611 base::win::ScopedHandle delegate_write_handle(::CreateNamedPipe(
612 base::ASCIIToUTF16(input_pipe_name).c_str(),
613 PIPE_ACCESS_OUTBOUND,
614 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS,
616 kBufferSize,
617 kBufferSize,
618 kTimeOutMilliseconds,
619 &security_attributes));
621 if (!delegate_write_handle.IsValid()) {
622 PLOG(ERROR) << "Failed to create named pipe '" << input_pipe_name << "'";
623 OnError();
624 return;
627 // Generate a unique name for the input channel.
628 std::string output_pipe_name(kChromePipeNamePrefix);
629 output_pipe_name.append(IPC::Channel::GenerateUniqueRandomChannelID());
631 base::win::ScopedHandle delegate_read_handle(::CreateNamedPipe(
632 base::ASCIIToUTF16(output_pipe_name).c_str(),
633 PIPE_ACCESS_INBOUND,
634 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS,
636 kBufferSize,
637 kBufferSize,
638 kTimeOutMilliseconds,
639 &security_attributes));
641 if (!delegate_read_handle.IsValid()) {
642 PLOG(ERROR) << "Failed to create named pipe '" << output_pipe_name << "'";
643 OnError();
644 return;
647 const base::CommandLine* current_command_line =
648 base::CommandLine::ForCurrentProcess();
649 const base::CommandLine::SwitchMap& switches =
650 current_command_line->GetSwitches();
651 base::CommandLine::StringVector args = current_command_line->GetArgs();
653 // Create the child process command line by copying switches from the current
654 // command line.
655 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
656 command_line.AppendSwitch(kElevatingSwitchName);
657 command_line.AppendSwitchASCII(kInputSwitchName, input_pipe_name);
658 command_line.AppendSwitchASCII(kOutputSwitchName, output_pipe_name);
660 DCHECK(!current_command_line->HasSwitch(kElevatingSwitchName));
661 for (base::CommandLine::SwitchMap::const_iterator i = switches.begin();
662 i != switches.end(); ++i) {
663 command_line.AppendSwitchNative(i->first, i->second);
665 for (base::CommandLine::StringVector::const_iterator i = args.begin();
666 i != args.end(); ++i) {
667 command_line.AppendArgNative(*i);
670 // Get the name of the binary to launch.
671 base::FilePath binary = current_command_line->GetProgram();
672 base::CommandLine::StringType parameters =
673 command_line.GetCommandLineString();
675 // Launch the child process requesting elevation.
676 SHELLEXECUTEINFO info;
677 memset(&info, 0, sizeof(info));
678 info.cbSize = sizeof(info);
679 info.hwnd = reinterpret_cast<HWND>(parent_window_handle_);
680 info.lpVerb = L"runas";
681 info.lpFile = binary.value().c_str();
682 info.lpParameters = parameters.c_str();
683 info.nShow = SW_HIDE;
685 if (!ShellExecuteEx(&info)) {
686 DWORD error = ::GetLastError();
687 PLOG(ERROR) << "Unable to launch '" << binary.value() << "'";
688 if (error != ERROR_CANCELLED) {
689 OnError();
691 return;
694 if (!::ConnectNamedPipe(delegate_write_handle.Get(), nullptr)) {
695 DWORD error = ::GetLastError();
696 if (error != ERROR_PIPE_CONNECTED) {
697 PLOG(ERROR) << "Unable to connect '" << input_pipe_name << "'";
698 OnError();
699 return;
703 if (!::ConnectNamedPipe(delegate_read_handle.Get(), nullptr)) {
704 DWORD error = ::GetLastError();
705 if (error != ERROR_PIPE_CONNECTED) {
706 PLOG(ERROR) << "Unable to connect '" << output_pipe_name << "'";
707 OnError();
708 return;
712 // Set up the native messaging channel to talk to the elevated host.
713 // Note that input for the elevated channel is output for the elevated host.
714 elevated_channel_.reset(
715 new PipeMessagingChannel(base::File(delegate_read_handle.Take()),
716 base::File(delegate_write_handle.Take())));
718 elevated_channel_event_handler_.reset(
719 new Me2MeNativeMessagingHost::ElevatedChannelEventHandler(this));
720 elevated_channel_->Start(elevated_channel_event_handler_.get());
722 elevated_host_timer_.Start(
723 FROM_HERE, base::TimeDelta::FromSeconds(kElevatedHostTimeoutSeconds),
724 this, &Me2MeNativeMessagingHost::DisconnectElevatedHost);
727 void Me2MeNativeMessagingHost::DisconnectElevatedHost() {
728 DCHECK(thread_checker_.CalledOnValidThread());
730 // This will send an EOF to the elevated host, triggering its shutdown.
731 elevated_channel_.reset();
734 #else // defined(OS_WIN)
736 bool Me2MeNativeMessagingHost::DelegateToElevatedHost(
737 scoped_ptr<base::DictionaryValue> message) {
738 NOTREACHED();
739 return false;
742 #endif // !defined(OS_WIN)
744 } // namespace remoting