Revert of Remove OneClickSigninHelper since it is no longer used. (patchset #5 id...
[chromium-blink-merge.git] / remoting / host / setup / me2me_native_messaging_host.cc
blob12e688b13856cb29876693648cf88aa6965aa5c4
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 nullptr 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 #if defined(OS_WIN)
82 parent_window_handle_(parent_window_handle),
83 #endif
84 channel_(channel.Pass()),
85 daemon_controller_(daemon_controller),
86 pairing_registry_(pairing_registry),
87 oauth_client_(oauth_client.Pass()),
88 weak_factory_(this) {
89 weak_ptr_ = weak_factory_.GetWeakPtr();
92 Me2MeNativeMessagingHost::~Me2MeNativeMessagingHost() {
93 DCHECK(thread_checker_.CalledOnValidThread());
96 void Me2MeNativeMessagingHost::Start(
97 const base::Closure& quit_closure) {
98 DCHECK(thread_checker_.CalledOnValidThread());
99 DCHECK(!quit_closure.is_null());
101 quit_closure_ = quit_closure;
103 channel_->Start(this);
106 void Me2MeNativeMessagingHost::OnMessage(scoped_ptr<base::Value> message) {
107 DCHECK(thread_checker_.CalledOnValidThread());
109 if (!message->IsType(base::Value::TYPE_DICTIONARY)) {
110 LOG(ERROR) << "Received a message that's not a dictionary.";
111 channel_->SendMessage(nullptr);
112 return;
115 scoped_ptr<base::DictionaryValue> message_dict(
116 static_cast<base::DictionaryValue*>(message.release()));
117 scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue());
119 // If the client supplies an ID, it will expect it in the response. This
120 // might be a string or a number, so cope with both.
121 const base::Value* id;
122 if (message_dict->Get("id", &id))
123 response->Set("id", id->DeepCopy());
125 std::string type;
126 if (!message_dict->GetString("type", &type)) {
127 LOG(ERROR) << "'type' not found";
128 channel_->SendMessage(nullptr);
129 return;
132 response->SetString("type", type + "Response");
134 if (type == "hello") {
135 ProcessHello(message_dict.Pass(), response.Pass());
136 } else if (type == "clearPairedClients") {
137 ProcessClearPairedClients(message_dict.Pass(), response.Pass());
138 } else if (type == "deletePairedClient") {
139 ProcessDeletePairedClient(message_dict.Pass(), response.Pass());
140 } else if (type == "getHostName") {
141 ProcessGetHostName(message_dict.Pass(), response.Pass());
142 } else if (type == "getPinHash") {
143 ProcessGetPinHash(message_dict.Pass(), response.Pass());
144 } else if (type == "generateKeyPair") {
145 ProcessGenerateKeyPair(message_dict.Pass(), response.Pass());
146 } else if (type == "updateDaemonConfig") {
147 ProcessUpdateDaemonConfig(message_dict.Pass(), response.Pass());
148 } else if (type == "getDaemonConfig") {
149 ProcessGetDaemonConfig(message_dict.Pass(), response.Pass());
150 } else if (type == "getPairedClients") {
151 ProcessGetPairedClients(message_dict.Pass(), response.Pass());
152 } else if (type == "getUsageStatsConsent") {
153 ProcessGetUsageStatsConsent(message_dict.Pass(), response.Pass());
154 } else if (type == "startDaemon") {
155 ProcessStartDaemon(message_dict.Pass(), response.Pass());
156 } else if (type == "stopDaemon") {
157 ProcessStopDaemon(message_dict.Pass(), response.Pass());
158 } else if (type == "getDaemonState") {
159 ProcessGetDaemonState(message_dict.Pass(), response.Pass());
160 } else if (type == "getHostClientId") {
161 ProcessGetHostClientId(message_dict.Pass(), response.Pass());
162 } else if (type == "getCredentialsFromAuthCode") {
163 ProcessGetCredentialsFromAuthCode(message_dict.Pass(), response.Pass());
164 } else {
165 LOG(ERROR) << "Unsupported request type: " << type;
166 OnError();
170 void Me2MeNativeMessagingHost::OnDisconnect() {
171 if (!quit_closure_.is_null())
172 base::ResetAndReturn(&quit_closure_).Run();
175 void Me2MeNativeMessagingHost::ProcessHello(
176 scoped_ptr<base::DictionaryValue> message,
177 scoped_ptr<base::DictionaryValue> response) {
178 DCHECK(thread_checker_.CalledOnValidThread());
180 response->SetString("version", STRINGIZE(VERSION));
181 scoped_ptr<base::ListValue> supported_features_list(new base::ListValue());
182 supported_features_list->AppendStrings(std::vector<std::string>(
183 kSupportedFeatures, kSupportedFeatures + arraysize(kSupportedFeatures)));
184 response->Set("supportedFeatures", supported_features_list.release());
185 channel_->SendMessage(response.Pass());
188 void Me2MeNativeMessagingHost::ProcessClearPairedClients(
189 scoped_ptr<base::DictionaryValue> message,
190 scoped_ptr<base::DictionaryValue> response) {
191 DCHECK(thread_checker_.CalledOnValidThread());
193 if (needs_elevation_) {
194 if (!DelegateToElevatedHost(message.Pass()))
195 SendBooleanResult(response.Pass(), false);
196 return;
199 if (pairing_registry_.get()) {
200 pairing_registry_->ClearAllPairings(
201 base::Bind(&Me2MeNativeMessagingHost::SendBooleanResult, weak_ptr_,
202 base::Passed(&response)));
203 } else {
204 SendBooleanResult(response.Pass(), false);
208 void Me2MeNativeMessagingHost::ProcessDeletePairedClient(
209 scoped_ptr<base::DictionaryValue> message,
210 scoped_ptr<base::DictionaryValue> response) {
211 DCHECK(thread_checker_.CalledOnValidThread());
213 if (needs_elevation_) {
214 if (!DelegateToElevatedHost(message.Pass()))
215 SendBooleanResult(response.Pass(), false);
216 return;
219 std::string client_id;
220 if (!message->GetString(protocol::PairingRegistry::kClientIdKey,
221 &client_id)) {
222 LOG(ERROR) << "'" << protocol::PairingRegistry::kClientIdKey
223 << "' string not found.";
224 OnError();
225 return;
228 if (pairing_registry_.get()) {
229 pairing_registry_->DeletePairing(
230 client_id, base::Bind(&Me2MeNativeMessagingHost::SendBooleanResult,
231 weak_ptr_, base::Passed(&response)));
232 } else {
233 SendBooleanResult(response.Pass(), false);
237 void Me2MeNativeMessagingHost::ProcessGetHostName(
238 scoped_ptr<base::DictionaryValue> message,
239 scoped_ptr<base::DictionaryValue> response) {
240 DCHECK(thread_checker_.CalledOnValidThread());
242 response->SetString("hostname", net::GetHostName());
243 channel_->SendMessage(response.Pass());
246 void Me2MeNativeMessagingHost::ProcessGetPinHash(
247 scoped_ptr<base::DictionaryValue> message,
248 scoped_ptr<base::DictionaryValue> response) {
249 DCHECK(thread_checker_.CalledOnValidThread());
251 std::string host_id;
252 if (!message->GetString("hostId", &host_id)) {
253 LOG(ERROR) << "'hostId' not found: " << message;
254 OnError();
255 return;
257 std::string pin;
258 if (!message->GetString("pin", &pin)) {
259 LOG(ERROR) << "'pin' not found: " << message;
260 OnError();
261 return;
263 response->SetString("hash", MakeHostPinHash(host_id, pin));
264 channel_->SendMessage(response.Pass());
267 void Me2MeNativeMessagingHost::ProcessGenerateKeyPair(
268 scoped_ptr<base::DictionaryValue> message,
269 scoped_ptr<base::DictionaryValue> response) {
270 DCHECK(thread_checker_.CalledOnValidThread());
272 scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::Generate();
273 response->SetString("privateKey", key_pair->ToString());
274 response->SetString("publicKey", key_pair->GetPublicKey());
275 channel_->SendMessage(response.Pass());
278 void Me2MeNativeMessagingHost::ProcessUpdateDaemonConfig(
279 scoped_ptr<base::DictionaryValue> message,
280 scoped_ptr<base::DictionaryValue> response) {
281 DCHECK(thread_checker_.CalledOnValidThread());
283 if (needs_elevation_) {
284 if (!DelegateToElevatedHost(message.Pass()))
285 SendAsyncResult(response.Pass(), DaemonController::RESULT_FAILED);
286 return;
289 scoped_ptr<base::DictionaryValue> config_dict =
290 ConfigDictionaryFromMessage(message.Pass());
291 if (!config_dict) {
292 OnError();
293 return;
296 daemon_controller_->UpdateConfig(
297 config_dict.Pass(),
298 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
299 base::Passed(&response)));
302 void Me2MeNativeMessagingHost::ProcessGetDaemonConfig(
303 scoped_ptr<base::DictionaryValue> message,
304 scoped_ptr<base::DictionaryValue> response) {
305 DCHECK(thread_checker_.CalledOnValidThread());
307 daemon_controller_->GetConfig(
308 base::Bind(&Me2MeNativeMessagingHost::SendConfigResponse, weak_ptr_,
309 base::Passed(&response)));
312 void Me2MeNativeMessagingHost::ProcessGetPairedClients(
313 scoped_ptr<base::DictionaryValue> message,
314 scoped_ptr<base::DictionaryValue> response) {
315 DCHECK(thread_checker_.CalledOnValidThread());
317 if (pairing_registry_.get()) {
318 pairing_registry_->GetAllPairings(
319 base::Bind(&Me2MeNativeMessagingHost::SendPairedClientsResponse,
320 weak_ptr_, base::Passed(&response)));
321 } else {
322 scoped_ptr<base::ListValue> no_paired_clients(new base::ListValue);
323 SendPairedClientsResponse(response.Pass(), no_paired_clients.Pass());
327 void Me2MeNativeMessagingHost::ProcessGetUsageStatsConsent(
328 scoped_ptr<base::DictionaryValue> message,
329 scoped_ptr<base::DictionaryValue> response) {
330 DCHECK(thread_checker_.CalledOnValidThread());
332 daemon_controller_->GetUsageStatsConsent(
333 base::Bind(&Me2MeNativeMessagingHost::SendUsageStatsConsentResponse,
334 weak_ptr_, base::Passed(&response)));
337 void Me2MeNativeMessagingHost::ProcessStartDaemon(
338 scoped_ptr<base::DictionaryValue> message,
339 scoped_ptr<base::DictionaryValue> response) {
340 DCHECK(thread_checker_.CalledOnValidThread());
342 if (needs_elevation_) {
343 if (!DelegateToElevatedHost(message.Pass()))
344 SendAsyncResult(response.Pass(), DaemonController::RESULT_FAILED);
345 return;
348 bool consent;
349 if (!message->GetBoolean("consent", &consent)) {
350 LOG(ERROR) << "'consent' not found.";
351 OnError();
352 return;
355 scoped_ptr<base::DictionaryValue> config_dict =
356 ConfigDictionaryFromMessage(message.Pass());
357 if (!config_dict) {
358 OnError();
359 return;
362 daemon_controller_->SetConfigAndStart(
363 config_dict.Pass(), consent,
364 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
365 base::Passed(&response)));
368 void Me2MeNativeMessagingHost::ProcessStopDaemon(
369 scoped_ptr<base::DictionaryValue> message,
370 scoped_ptr<base::DictionaryValue> response) {
371 DCHECK(thread_checker_.CalledOnValidThread());
373 if (needs_elevation_) {
374 if (!DelegateToElevatedHost(message.Pass()))
375 SendAsyncResult(response.Pass(), DaemonController::RESULT_FAILED);
376 return;
379 daemon_controller_->Stop(
380 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
381 base::Passed(&response)));
384 void Me2MeNativeMessagingHost::ProcessGetDaemonState(
385 scoped_ptr<base::DictionaryValue> message,
386 scoped_ptr<base::DictionaryValue> response) {
387 DCHECK(thread_checker_.CalledOnValidThread());
389 DaemonController::State state = daemon_controller_->GetState();
390 switch (state) {
391 case DaemonController::STATE_NOT_IMPLEMENTED:
392 response->SetString("state", "NOT_IMPLEMENTED");
393 break;
394 case DaemonController::STATE_STOPPED:
395 response->SetString("state", "STOPPED");
396 break;
397 case DaemonController::STATE_STARTING:
398 response->SetString("state", "STARTING");
399 break;
400 case DaemonController::STATE_STARTED:
401 response->SetString("state", "STARTED");
402 break;
403 case DaemonController::STATE_STOPPING:
404 response->SetString("state", "STOPPING");
405 break;
406 case DaemonController::STATE_UNKNOWN:
407 response->SetString("state", "UNKNOWN");
408 break;
410 channel_->SendMessage(response.Pass());
413 void Me2MeNativeMessagingHost::ProcessGetHostClientId(
414 scoped_ptr<base::DictionaryValue> message,
415 scoped_ptr<base::DictionaryValue> response) {
416 DCHECK(thread_checker_.CalledOnValidThread());
418 response->SetString("clientId", google_apis::GetOAuth2ClientID(
419 google_apis::CLIENT_REMOTING_HOST));
420 channel_->SendMessage(response.Pass());
423 void Me2MeNativeMessagingHost::ProcessGetCredentialsFromAuthCode(
424 scoped_ptr<base::DictionaryValue> message,
425 scoped_ptr<base::DictionaryValue> response) {
426 DCHECK(thread_checker_.CalledOnValidThread());
428 std::string auth_code;
429 if (!message->GetString("authorizationCode", &auth_code)) {
430 LOG(ERROR) << "'authorizationCode' string not found.";
431 OnError();
432 return;
435 gaia::OAuthClientInfo oauth_client_info = {
436 google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING_HOST),
437 google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_REMOTING_HOST),
438 kServiceAccountRedirectUri
441 oauth_client_->GetCredentialsFromAuthCode(
442 oauth_client_info, auth_code, base::Bind(
443 &Me2MeNativeMessagingHost::SendCredentialsResponse, weak_ptr_,
444 base::Passed(&response)));
447 void Me2MeNativeMessagingHost::SendConfigResponse(
448 scoped_ptr<base::DictionaryValue> response,
449 scoped_ptr<base::DictionaryValue> config) {
450 DCHECK(thread_checker_.CalledOnValidThread());
452 if (config) {
453 response->Set("config", config.release());
454 } else {
455 response->Set("config", base::Value::CreateNullValue());
457 channel_->SendMessage(response.Pass());
460 void Me2MeNativeMessagingHost::SendPairedClientsResponse(
461 scoped_ptr<base::DictionaryValue> response,
462 scoped_ptr<base::ListValue> pairings) {
463 DCHECK(thread_checker_.CalledOnValidThread());
465 response->Set("pairedClients", pairings.release());
466 channel_->SendMessage(response.Pass());
469 void Me2MeNativeMessagingHost::SendUsageStatsConsentResponse(
470 scoped_ptr<base::DictionaryValue> response,
471 const DaemonController::UsageStatsConsent& consent) {
472 DCHECK(thread_checker_.CalledOnValidThread());
474 response->SetBoolean("supported", consent.supported);
475 response->SetBoolean("allowed", consent.allowed);
476 response->SetBoolean("setByPolicy", consent.set_by_policy);
477 channel_->SendMessage(response.Pass());
480 void Me2MeNativeMessagingHost::SendAsyncResult(
481 scoped_ptr<base::DictionaryValue> response,
482 DaemonController::AsyncResult result) {
483 DCHECK(thread_checker_.CalledOnValidThread());
485 switch (result) {
486 case DaemonController::RESULT_OK:
487 response->SetString("result", "OK");
488 break;
489 case DaemonController::RESULT_FAILED:
490 response->SetString("result", "FAILED");
491 break;
492 case DaemonController::RESULT_CANCELLED:
493 response->SetString("result", "CANCELLED");
494 break;
495 case DaemonController::RESULT_FAILED_DIRECTORY:
496 response->SetString("result", "FAILED_DIRECTORY");
497 break;
499 channel_->SendMessage(response.Pass());
502 void Me2MeNativeMessagingHost::SendBooleanResult(
503 scoped_ptr<base::DictionaryValue> response, bool result) {
504 DCHECK(thread_checker_.CalledOnValidThread());
506 response->SetBoolean("result", result);
507 channel_->SendMessage(response.Pass());
510 void Me2MeNativeMessagingHost::SendCredentialsResponse(
511 scoped_ptr<base::DictionaryValue> response,
512 const std::string& user_email,
513 const std::string& refresh_token) {
514 DCHECK(thread_checker_.CalledOnValidThread());
516 response->SetString("userEmail", user_email);
517 response->SetString("refreshToken", refresh_token);
518 channel_->SendMessage(response.Pass());
521 void Me2MeNativeMessagingHost::OnError() {
522 // Trigger a host shutdown by sending a nullptr message.
523 channel_->SendMessage(nullptr);
526 void Me2MeNativeMessagingHost::Stop() {
527 DCHECK(thread_checker_.CalledOnValidThread());
529 if (!quit_closure_.is_null())
530 base::ResetAndReturn(&quit_closure_).Run();
533 #if defined(OS_WIN)
534 Me2MeNativeMessagingHost::ElevatedChannelEventHandler::
535 ElevatedChannelEventHandler(Me2MeNativeMessagingHost* host)
536 : parent_(host) {
539 void Me2MeNativeMessagingHost::ElevatedChannelEventHandler::OnMessage(
540 scoped_ptr<base::Value> message) {
541 DCHECK(parent_->thread_checker_.CalledOnValidThread());
543 // Simply pass along the response from the elevated host to the client.
544 parent_->channel_->SendMessage(message.Pass());
547 void Me2MeNativeMessagingHost::ElevatedChannelEventHandler::OnDisconnect() {
548 parent_->OnDisconnect();
551 bool Me2MeNativeMessagingHost::DelegateToElevatedHost(
552 scoped_ptr<base::DictionaryValue> message) {
553 DCHECK(thread_checker_.CalledOnValidThread());
555 EnsureElevatedHostCreated();
557 // elevated_channel_ will be null if user rejects the UAC request.
558 if (elevated_channel_)
559 elevated_channel_->SendMessage(message.Pass());
561 return elevated_channel_ != nullptr;
564 void Me2MeNativeMessagingHost::EnsureElevatedHostCreated() {
565 DCHECK(thread_checker_.CalledOnValidThread());
566 DCHECK(needs_elevation_);
568 if (elevated_channel_)
569 return;
571 // presubmit: allow wstring
572 std::wstring user_sid;
573 if (!base::win::GetUserSidString(&user_sid)) {
574 LOG(ERROR) << "Failed to query the current user SID.";
575 OnError();
576 return;
579 // Create a security descriptor that gives full access to the caller and
580 // denies access by anyone else.
581 std::string user_sid_ascii = base::UTF16ToASCII(user_sid);
582 std::string security_descriptor =
583 base::StringPrintf("O:%sG:%sD:(A;;GA;;;%s)", user_sid_ascii.c_str(),
584 user_sid_ascii.c_str(), user_sid_ascii.c_str());
586 ScopedSd sd = ConvertSddlToSd(security_descriptor);
587 if (!sd) {
588 PLOG(ERROR) << "Failed to create a security descriptor for the"
589 << "Chromoting Me2Me native messaging host.";
590 OnError();
591 return;
594 SECURITY_ATTRIBUTES security_attributes = {0};
595 security_attributes.nLength = sizeof(security_attributes);
596 security_attributes.lpSecurityDescriptor = sd.get();
597 security_attributes.bInheritHandle = FALSE;
599 // Generate a unique name for the input channel.
600 std::string input_pipe_name(kChromePipeNamePrefix);
601 input_pipe_name.append(IPC::Channel::GenerateUniqueRandomChannelID());
603 base::win::ScopedHandle delegate_write_handle(::CreateNamedPipe(
604 base::ASCIIToUTF16(input_pipe_name).c_str(),
605 PIPE_ACCESS_OUTBOUND,
606 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS,
608 kBufferSize,
609 kBufferSize,
610 kTimeOutMilliseconds,
611 &security_attributes));
613 if (!delegate_write_handle.IsValid()) {
614 PLOG(ERROR) << "Failed to create named pipe '" << input_pipe_name << "'";
615 OnError();
616 return;
619 // Generate a unique name for the input channel.
620 std::string output_pipe_name(kChromePipeNamePrefix);
621 output_pipe_name.append(IPC::Channel::GenerateUniqueRandomChannelID());
623 base::win::ScopedHandle delegate_read_handle(::CreateNamedPipe(
624 base::ASCIIToUTF16(output_pipe_name).c_str(),
625 PIPE_ACCESS_INBOUND,
626 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS,
628 kBufferSize,
629 kBufferSize,
630 kTimeOutMilliseconds,
631 &security_attributes));
633 if (!delegate_read_handle.IsValid()) {
634 PLOG(ERROR) << "Failed to create named pipe '" << output_pipe_name << "'";
635 OnError();
636 return;
639 const base::CommandLine* current_command_line =
640 base::CommandLine::ForCurrentProcess();
641 const base::CommandLine::SwitchMap& switches =
642 current_command_line->GetSwitches();
643 base::CommandLine::StringVector args = current_command_line->GetArgs();
645 // Create the child process command line by copying switches from the current
646 // command line.
647 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
648 command_line.AppendSwitch(kElevatingSwitchName);
649 command_line.AppendSwitchASCII(kInputSwitchName, input_pipe_name);
650 command_line.AppendSwitchASCII(kOutputSwitchName, output_pipe_name);
652 DCHECK(!current_command_line->HasSwitch(kElevatingSwitchName));
653 for (base::CommandLine::SwitchMap::const_iterator i = switches.begin();
654 i != switches.end(); ++i) {
655 command_line.AppendSwitchNative(i->first, i->second);
657 for (base::CommandLine::StringVector::const_iterator i = args.begin();
658 i != args.end(); ++i) {
659 command_line.AppendArgNative(*i);
662 // Get the name of the binary to launch.
663 base::FilePath binary = current_command_line->GetProgram();
664 base::CommandLine::StringType parameters =
665 command_line.GetCommandLineString();
667 // Launch the child process requesting elevation.
668 SHELLEXECUTEINFO info;
669 memset(&info, 0, sizeof(info));
670 info.cbSize = sizeof(info);
671 info.hwnd = reinterpret_cast<HWND>(parent_window_handle_);
672 info.lpVerb = L"runas";
673 info.lpFile = binary.value().c_str();
674 info.lpParameters = parameters.c_str();
675 info.nShow = SW_HIDE;
677 if (!ShellExecuteEx(&info)) {
678 DWORD error = ::GetLastError();
679 PLOG(ERROR) << "Unable to launch '" << binary.value() << "'";
680 if (error != ERROR_CANCELLED) {
681 OnError();
683 return;
686 if (!::ConnectNamedPipe(delegate_write_handle.Get(), nullptr)) {
687 DWORD error = ::GetLastError();
688 if (error != ERROR_PIPE_CONNECTED) {
689 PLOG(ERROR) << "Unable to connect '" << input_pipe_name << "'";
690 OnError();
691 return;
695 if (!::ConnectNamedPipe(delegate_read_handle.Get(), nullptr)) {
696 DWORD error = ::GetLastError();
697 if (error != ERROR_PIPE_CONNECTED) {
698 PLOG(ERROR) << "Unable to connect '" << output_pipe_name << "'";
699 OnError();
700 return;
704 // Set up the native messaging channel to talk to the elevated host.
705 // Note that input for the elevated channel is output for the elevated host.
706 elevated_channel_.reset(
707 new PipeMessagingChannel(base::File(delegate_read_handle.Take()),
708 base::File(delegate_write_handle.Take())));
710 elevated_channel_event_handler_.reset(
711 new Me2MeNativeMessagingHost::ElevatedChannelEventHandler(this));
712 elevated_channel_->Start(elevated_channel_event_handler_.get());
714 elevated_host_timer_.Start(
715 FROM_HERE, base::TimeDelta::FromSeconds(kElevatedHostTimeoutSeconds),
716 this, &Me2MeNativeMessagingHost::DisconnectElevatedHost);
719 void Me2MeNativeMessagingHost::DisconnectElevatedHost() {
720 DCHECK(thread_checker_.CalledOnValidThread());
722 // This will send an EOF to the elevated host, triggering its shutdown.
723 elevated_channel_.reset();
726 #else // defined(OS_WIN)
728 bool Me2MeNativeMessagingHost::DelegateToElevatedHost(
729 scoped_ptr<base::DictionaryValue> message) {
730 NOTREACHED();
731 return false;
734 #endif // !defined(OS_WIN)
736 } // namespace remoting