MD Downloads: prevent search text from overlapping with the cancel search (X)
[chromium-blink-merge.git] / remoting / host / setup / me2me_native_messaging_host.cc
blob49d2b74588ed3d590647d5430a8d1d80c3cfb025
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 log_message_handler_(
87 base::Bind(&extensions::NativeMessagingChannel::SendMessage,
88 base::Unretained(channel_.get()))),
89 daemon_controller_(daemon_controller),
90 pairing_registry_(pairing_registry),
91 oauth_client_(oauth_client.Pass()),
92 weak_factory_(this) {
93 weak_ptr_ = weak_factory_.GetWeakPtr();
96 Me2MeNativeMessagingHost::~Me2MeNativeMessagingHost() {
97 DCHECK(thread_checker_.CalledOnValidThread());
100 void Me2MeNativeMessagingHost::Start(
101 const base::Closure& quit_closure) {
102 DCHECK(thread_checker_.CalledOnValidThread());
103 DCHECK(!quit_closure.is_null());
105 quit_closure_ = quit_closure;
107 channel_->Start(this);
110 void Me2MeNativeMessagingHost::OnMessage(scoped_ptr<base::Value> message) {
111 DCHECK(thread_checker_.CalledOnValidThread());
113 if (!message->IsType(base::Value::TYPE_DICTIONARY)) {
114 LOG(ERROR) << "Received a message that's not a dictionary.";
115 channel_->SendMessage(nullptr);
116 return;
119 scoped_ptr<base::DictionaryValue> message_dict(
120 static_cast<base::DictionaryValue*>(message.release()));
121 scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue());
123 // If the client supplies an ID, it will expect it in the response. This
124 // might be a string or a number, so cope with both.
125 const base::Value* id;
126 if (message_dict->Get("id", &id))
127 response->Set("id", id->DeepCopy());
129 std::string type;
130 if (!message_dict->GetString("type", &type)) {
131 LOG(ERROR) << "'type' not found";
132 channel_->SendMessage(nullptr);
133 return;
136 response->SetString("type", type + "Response");
138 if (type == "hello") {
139 ProcessHello(message_dict.Pass(), response.Pass());
140 } else if (type == "clearPairedClients") {
141 ProcessClearPairedClients(message_dict.Pass(), response.Pass());
142 } else if (type == "deletePairedClient") {
143 ProcessDeletePairedClient(message_dict.Pass(), response.Pass());
144 } else if (type == "getHostName") {
145 ProcessGetHostName(message_dict.Pass(), response.Pass());
146 } else if (type == "getPinHash") {
147 ProcessGetPinHash(message_dict.Pass(), response.Pass());
148 } else if (type == "generateKeyPair") {
149 ProcessGenerateKeyPair(message_dict.Pass(), response.Pass());
150 } else if (type == "updateDaemonConfig") {
151 ProcessUpdateDaemonConfig(message_dict.Pass(), response.Pass());
152 } else if (type == "getDaemonConfig") {
153 ProcessGetDaemonConfig(message_dict.Pass(), response.Pass());
154 } else if (type == "getPairedClients") {
155 ProcessGetPairedClients(message_dict.Pass(), response.Pass());
156 } else if (type == "getUsageStatsConsent") {
157 ProcessGetUsageStatsConsent(message_dict.Pass(), response.Pass());
158 } else if (type == "startDaemon") {
159 ProcessStartDaemon(message_dict.Pass(), response.Pass());
160 } else if (type == "stopDaemon") {
161 ProcessStopDaemon(message_dict.Pass(), response.Pass());
162 } else if (type == "getDaemonState") {
163 ProcessGetDaemonState(message_dict.Pass(), response.Pass());
164 } else if (type == "getHostClientId") {
165 ProcessGetHostClientId(message_dict.Pass(), response.Pass());
166 } else if (type == "getCredentialsFromAuthCode") {
167 ProcessGetCredentialsFromAuthCode(
168 message_dict.Pass(), response.Pass(), true);
169 } else if (type == "getRefreshTokenFromAuthCode") {
170 ProcessGetCredentialsFromAuthCode(
171 message_dict.Pass(), response.Pass(), false);
172 } else {
173 LOG(ERROR) << "Unsupported request type: " << type;
174 OnError();
178 void Me2MeNativeMessagingHost::OnDisconnect() {
179 if (!quit_closure_.is_null())
180 base::ResetAndReturn(&quit_closure_).Run();
183 void Me2MeNativeMessagingHost::ProcessHello(
184 scoped_ptr<base::DictionaryValue> message,
185 scoped_ptr<base::DictionaryValue> response) {
186 DCHECK(thread_checker_.CalledOnValidThread());
188 response->SetString("version", STRINGIZE(VERSION));
189 scoped_ptr<base::ListValue> supported_features_list(new base::ListValue());
190 supported_features_list->AppendStrings(std::vector<std::string>(
191 kSupportedFeatures, kSupportedFeatures + arraysize(kSupportedFeatures)));
192 response->Set("supportedFeatures", supported_features_list.release());
193 channel_->SendMessage(response.Pass());
196 void Me2MeNativeMessagingHost::ProcessClearPairedClients(
197 scoped_ptr<base::DictionaryValue> message,
198 scoped_ptr<base::DictionaryValue> response) {
199 DCHECK(thread_checker_.CalledOnValidThread());
201 if (needs_elevation_) {
202 if (!DelegateToElevatedHost(message.Pass()))
203 SendBooleanResult(response.Pass(), false);
204 return;
207 if (pairing_registry_.get()) {
208 pairing_registry_->ClearAllPairings(
209 base::Bind(&Me2MeNativeMessagingHost::SendBooleanResult, weak_ptr_,
210 base::Passed(&response)));
211 } else {
212 SendBooleanResult(response.Pass(), false);
216 void Me2MeNativeMessagingHost::ProcessDeletePairedClient(
217 scoped_ptr<base::DictionaryValue> message,
218 scoped_ptr<base::DictionaryValue> response) {
219 DCHECK(thread_checker_.CalledOnValidThread());
221 if (needs_elevation_) {
222 if (!DelegateToElevatedHost(message.Pass()))
223 SendBooleanResult(response.Pass(), false);
224 return;
227 std::string client_id;
228 if (!message->GetString(protocol::PairingRegistry::kClientIdKey,
229 &client_id)) {
230 LOG(ERROR) << "'" << protocol::PairingRegistry::kClientIdKey
231 << "' string not found.";
232 OnError();
233 return;
236 if (pairing_registry_.get()) {
237 pairing_registry_->DeletePairing(
238 client_id, base::Bind(&Me2MeNativeMessagingHost::SendBooleanResult,
239 weak_ptr_, base::Passed(&response)));
240 } else {
241 SendBooleanResult(response.Pass(), false);
245 void Me2MeNativeMessagingHost::ProcessGetHostName(
246 scoped_ptr<base::DictionaryValue> message,
247 scoped_ptr<base::DictionaryValue> response) {
248 DCHECK(thread_checker_.CalledOnValidThread());
250 response->SetString("hostname", net::GetHostName());
251 channel_->SendMessage(response.Pass());
254 void Me2MeNativeMessagingHost::ProcessGetPinHash(
255 scoped_ptr<base::DictionaryValue> message,
256 scoped_ptr<base::DictionaryValue> response) {
257 DCHECK(thread_checker_.CalledOnValidThread());
259 std::string host_id;
260 if (!message->GetString("hostId", &host_id)) {
261 LOG(ERROR) << "'hostId' not found: " << message;
262 OnError();
263 return;
265 std::string pin;
266 if (!message->GetString("pin", &pin)) {
267 LOG(ERROR) << "'pin' not found: " << message;
268 OnError();
269 return;
271 response->SetString("hash", MakeHostPinHash(host_id, pin));
272 channel_->SendMessage(response.Pass());
275 void Me2MeNativeMessagingHost::ProcessGenerateKeyPair(
276 scoped_ptr<base::DictionaryValue> message,
277 scoped_ptr<base::DictionaryValue> response) {
278 DCHECK(thread_checker_.CalledOnValidThread());
280 scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::Generate();
281 response->SetString("privateKey", key_pair->ToString());
282 response->SetString("publicKey", key_pair->GetPublicKey());
283 channel_->SendMessage(response.Pass());
286 void Me2MeNativeMessagingHost::ProcessUpdateDaemonConfig(
287 scoped_ptr<base::DictionaryValue> message,
288 scoped_ptr<base::DictionaryValue> response) {
289 DCHECK(thread_checker_.CalledOnValidThread());
291 if (needs_elevation_) {
292 if (!DelegateToElevatedHost(message.Pass()))
293 SendAsyncResult(response.Pass(), DaemonController::RESULT_FAILED);
294 return;
297 scoped_ptr<base::DictionaryValue> config_dict =
298 ConfigDictionaryFromMessage(message.Pass());
299 if (!config_dict) {
300 OnError();
301 return;
304 daemon_controller_->UpdateConfig(
305 config_dict.Pass(),
306 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
307 base::Passed(&response)));
310 void Me2MeNativeMessagingHost::ProcessGetDaemonConfig(
311 scoped_ptr<base::DictionaryValue> message,
312 scoped_ptr<base::DictionaryValue> response) {
313 DCHECK(thread_checker_.CalledOnValidThread());
315 daemon_controller_->GetConfig(
316 base::Bind(&Me2MeNativeMessagingHost::SendConfigResponse, weak_ptr_,
317 base::Passed(&response)));
320 void Me2MeNativeMessagingHost::ProcessGetPairedClients(
321 scoped_ptr<base::DictionaryValue> message,
322 scoped_ptr<base::DictionaryValue> response) {
323 DCHECK(thread_checker_.CalledOnValidThread());
325 if (pairing_registry_.get()) {
326 pairing_registry_->GetAllPairings(
327 base::Bind(&Me2MeNativeMessagingHost::SendPairedClientsResponse,
328 weak_ptr_, base::Passed(&response)));
329 } else {
330 scoped_ptr<base::ListValue> no_paired_clients(new base::ListValue);
331 SendPairedClientsResponse(response.Pass(), no_paired_clients.Pass());
335 void Me2MeNativeMessagingHost::ProcessGetUsageStatsConsent(
336 scoped_ptr<base::DictionaryValue> message,
337 scoped_ptr<base::DictionaryValue> response) {
338 DCHECK(thread_checker_.CalledOnValidThread());
340 daemon_controller_->GetUsageStatsConsent(
341 base::Bind(&Me2MeNativeMessagingHost::SendUsageStatsConsentResponse,
342 weak_ptr_, base::Passed(&response)));
345 void Me2MeNativeMessagingHost::ProcessStartDaemon(
346 scoped_ptr<base::DictionaryValue> message,
347 scoped_ptr<base::DictionaryValue> response) {
348 DCHECK(thread_checker_.CalledOnValidThread());
350 if (needs_elevation_) {
351 if (!DelegateToElevatedHost(message.Pass()))
352 SendAsyncResult(response.Pass(), DaemonController::RESULT_FAILED);
353 return;
356 bool consent;
357 if (!message->GetBoolean("consent", &consent)) {
358 LOG(ERROR) << "'consent' not found.";
359 OnError();
360 return;
363 scoped_ptr<base::DictionaryValue> config_dict =
364 ConfigDictionaryFromMessage(message.Pass());
365 if (!config_dict) {
366 OnError();
367 return;
370 daemon_controller_->SetConfigAndStart(
371 config_dict.Pass(), consent,
372 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
373 base::Passed(&response)));
376 void Me2MeNativeMessagingHost::ProcessStopDaemon(
377 scoped_ptr<base::DictionaryValue> message,
378 scoped_ptr<base::DictionaryValue> response) {
379 DCHECK(thread_checker_.CalledOnValidThread());
381 if (needs_elevation_) {
382 if (!DelegateToElevatedHost(message.Pass()))
383 SendAsyncResult(response.Pass(), DaemonController::RESULT_FAILED);
384 return;
387 daemon_controller_->Stop(
388 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
389 base::Passed(&response)));
392 void Me2MeNativeMessagingHost::ProcessGetDaemonState(
393 scoped_ptr<base::DictionaryValue> message,
394 scoped_ptr<base::DictionaryValue> response) {
395 DCHECK(thread_checker_.CalledOnValidThread());
397 DaemonController::State state = daemon_controller_->GetState();
398 switch (state) {
399 case DaemonController::STATE_NOT_IMPLEMENTED:
400 response->SetString("state", "NOT_IMPLEMENTED");
401 break;
402 case DaemonController::STATE_STOPPED:
403 response->SetString("state", "STOPPED");
404 break;
405 case DaemonController::STATE_STARTING:
406 response->SetString("state", "STARTING");
407 break;
408 case DaemonController::STATE_STARTED:
409 response->SetString("state", "STARTED");
410 break;
411 case DaemonController::STATE_STOPPING:
412 response->SetString("state", "STOPPING");
413 break;
414 case DaemonController::STATE_UNKNOWN:
415 response->SetString("state", "UNKNOWN");
416 break;
418 channel_->SendMessage(response.Pass());
421 void Me2MeNativeMessagingHost::ProcessGetHostClientId(
422 scoped_ptr<base::DictionaryValue> message,
423 scoped_ptr<base::DictionaryValue> response) {
424 DCHECK(thread_checker_.CalledOnValidThread());
426 response->SetString("clientId", google_apis::GetOAuth2ClientID(
427 google_apis::CLIENT_REMOTING_HOST));
428 channel_->SendMessage(response.Pass());
431 void Me2MeNativeMessagingHost::ProcessGetCredentialsFromAuthCode(
432 scoped_ptr<base::DictionaryValue> message,
433 scoped_ptr<base::DictionaryValue> response,
434 bool need_user_email) {
435 DCHECK(thread_checker_.CalledOnValidThread());
437 std::string auth_code;
438 if (!message->GetString("authorizationCode", &auth_code)) {
439 LOG(ERROR) << "'authorizationCode' string not found.";
440 OnError();
441 return;
444 gaia::OAuthClientInfo oauth_client_info = {
445 google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING_HOST),
446 google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_REMOTING_HOST),
447 kServiceAccountRedirectUri
450 oauth_client_->GetCredentialsFromAuthCode(
451 oauth_client_info, auth_code, need_user_email, base::Bind(
452 &Me2MeNativeMessagingHost::SendCredentialsResponse, weak_ptr_,
453 base::Passed(&response)));
456 void Me2MeNativeMessagingHost::SendConfigResponse(
457 scoped_ptr<base::DictionaryValue> response,
458 scoped_ptr<base::DictionaryValue> config) {
459 DCHECK(thread_checker_.CalledOnValidThread());
461 if (config) {
462 response->Set("config", config.release());
463 } else {
464 response->Set("config", base::Value::CreateNullValue());
466 channel_->SendMessage(response.Pass());
469 void Me2MeNativeMessagingHost::SendPairedClientsResponse(
470 scoped_ptr<base::DictionaryValue> response,
471 scoped_ptr<base::ListValue> pairings) {
472 DCHECK(thread_checker_.CalledOnValidThread());
474 response->Set("pairedClients", pairings.release());
475 channel_->SendMessage(response.Pass());
478 void Me2MeNativeMessagingHost::SendUsageStatsConsentResponse(
479 scoped_ptr<base::DictionaryValue> response,
480 const DaemonController::UsageStatsConsent& consent) {
481 DCHECK(thread_checker_.CalledOnValidThread());
483 response->SetBoolean("supported", consent.supported);
484 response->SetBoolean("allowed", consent.allowed);
485 response->SetBoolean("setByPolicy", consent.set_by_policy);
486 channel_->SendMessage(response.Pass());
489 void Me2MeNativeMessagingHost::SendAsyncResult(
490 scoped_ptr<base::DictionaryValue> response,
491 DaemonController::AsyncResult result) {
492 DCHECK(thread_checker_.CalledOnValidThread());
494 switch (result) {
495 case DaemonController::RESULT_OK:
496 response->SetString("result", "OK");
497 break;
498 case DaemonController::RESULT_FAILED:
499 response->SetString("result", "FAILED");
500 break;
501 case DaemonController::RESULT_CANCELLED:
502 response->SetString("result", "CANCELLED");
503 break;
504 case DaemonController::RESULT_FAILED_DIRECTORY:
505 response->SetString("result", "FAILED_DIRECTORY");
506 break;
508 channel_->SendMessage(response.Pass());
511 void Me2MeNativeMessagingHost::SendBooleanResult(
512 scoped_ptr<base::DictionaryValue> response, bool result) {
513 DCHECK(thread_checker_.CalledOnValidThread());
515 response->SetBoolean("result", result);
516 channel_->SendMessage(response.Pass());
519 void Me2MeNativeMessagingHost::SendCredentialsResponse(
520 scoped_ptr<base::DictionaryValue> response,
521 const std::string& user_email,
522 const std::string& refresh_token) {
523 DCHECK(thread_checker_.CalledOnValidThread());
525 if (!user_email.empty()) {
526 response->SetString("userEmail", user_email);
528 response->SetString("refreshToken", refresh_token);
529 channel_->SendMessage(response.Pass());
532 void Me2MeNativeMessagingHost::OnError() {
533 // Trigger a host shutdown by sending a nullptr message.
534 channel_->SendMessage(nullptr);
537 void Me2MeNativeMessagingHost::Stop() {
538 DCHECK(thread_checker_.CalledOnValidThread());
540 if (!quit_closure_.is_null())
541 base::ResetAndReturn(&quit_closure_).Run();
544 #if defined(OS_WIN)
545 Me2MeNativeMessagingHost::ElevatedChannelEventHandler::
546 ElevatedChannelEventHandler(Me2MeNativeMessagingHost* host)
547 : parent_(host) {
550 void Me2MeNativeMessagingHost::ElevatedChannelEventHandler::OnMessage(
551 scoped_ptr<base::Value> message) {
552 DCHECK(parent_->thread_checker_.CalledOnValidThread());
554 // Simply pass along the response from the elevated host to the client.
555 parent_->channel_->SendMessage(message.Pass());
558 void Me2MeNativeMessagingHost::ElevatedChannelEventHandler::OnDisconnect() {
559 parent_->OnDisconnect();
562 bool Me2MeNativeMessagingHost::DelegateToElevatedHost(
563 scoped_ptr<base::DictionaryValue> message) {
564 DCHECK(thread_checker_.CalledOnValidThread());
566 EnsureElevatedHostCreated();
568 // elevated_channel_ will be null if user rejects the UAC request.
569 if (elevated_channel_)
570 elevated_channel_->SendMessage(message.Pass());
572 return elevated_channel_ != nullptr;
575 void Me2MeNativeMessagingHost::EnsureElevatedHostCreated() {
576 DCHECK(thread_checker_.CalledOnValidThread());
577 DCHECK(needs_elevation_);
579 if (elevated_channel_)
580 return;
582 // presubmit: allow wstring
583 std::wstring user_sid;
584 if (!base::win::GetUserSidString(&user_sid)) {
585 LOG(ERROR) << "Failed to query the current user SID.";
586 OnError();
587 return;
590 // Create a security descriptor that gives full access to the caller and
591 // denies access by anyone else.
592 std::string user_sid_ascii = base::UTF16ToASCII(user_sid);
593 std::string security_descriptor =
594 base::StringPrintf("O:%sG:%sD:(A;;GA;;;%s)", user_sid_ascii.c_str(),
595 user_sid_ascii.c_str(), user_sid_ascii.c_str());
597 ScopedSd sd = ConvertSddlToSd(security_descriptor);
598 if (!sd) {
599 PLOG(ERROR) << "Failed to create a security descriptor for the"
600 << "Chromoting Me2Me native messaging host.";
601 OnError();
602 return;
605 SECURITY_ATTRIBUTES security_attributes = {0};
606 security_attributes.nLength = sizeof(security_attributes);
607 security_attributes.lpSecurityDescriptor = sd.get();
608 security_attributes.bInheritHandle = FALSE;
610 // Generate a unique name for the input channel.
611 std::string input_pipe_name(kChromePipeNamePrefix);
612 input_pipe_name.append(IPC::Channel::GenerateUniqueRandomChannelID());
614 base::win::ScopedHandle delegate_write_handle(::CreateNamedPipe(
615 base::ASCIIToUTF16(input_pipe_name).c_str(),
616 PIPE_ACCESS_OUTBOUND,
617 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS,
619 kBufferSize,
620 kBufferSize,
621 kTimeOutMilliseconds,
622 &security_attributes));
624 if (!delegate_write_handle.IsValid()) {
625 PLOG(ERROR) << "Failed to create named pipe '" << input_pipe_name << "'";
626 OnError();
627 return;
630 // Generate a unique name for the input channel.
631 std::string output_pipe_name(kChromePipeNamePrefix);
632 output_pipe_name.append(IPC::Channel::GenerateUniqueRandomChannelID());
634 base::win::ScopedHandle delegate_read_handle(::CreateNamedPipe(
635 base::ASCIIToUTF16(output_pipe_name).c_str(),
636 PIPE_ACCESS_INBOUND,
637 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS,
639 kBufferSize,
640 kBufferSize,
641 kTimeOutMilliseconds,
642 &security_attributes));
644 if (!delegate_read_handle.IsValid()) {
645 PLOG(ERROR) << "Failed to create named pipe '" << output_pipe_name << "'";
646 OnError();
647 return;
650 const base::CommandLine* current_command_line =
651 base::CommandLine::ForCurrentProcess();
652 const base::CommandLine::SwitchMap& switches =
653 current_command_line->GetSwitches();
654 base::CommandLine::StringVector args = current_command_line->GetArgs();
656 // Create the child process command line by copying switches from the current
657 // command line.
658 base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
659 command_line.AppendSwitch(kElevatingSwitchName);
660 command_line.AppendSwitchASCII(kInputSwitchName, input_pipe_name);
661 command_line.AppendSwitchASCII(kOutputSwitchName, output_pipe_name);
663 DCHECK(!current_command_line->HasSwitch(kElevatingSwitchName));
664 for (base::CommandLine::SwitchMap::const_iterator i = switches.begin();
665 i != switches.end(); ++i) {
666 command_line.AppendSwitchNative(i->first, i->second);
668 for (base::CommandLine::StringVector::const_iterator i = args.begin();
669 i != args.end(); ++i) {
670 command_line.AppendArgNative(*i);
673 // Get the name of the binary to launch.
674 base::FilePath binary = current_command_line->GetProgram();
675 base::CommandLine::StringType parameters =
676 command_line.GetCommandLineString();
678 // Launch the child process requesting elevation.
679 SHELLEXECUTEINFO info;
680 memset(&info, 0, sizeof(info));
681 info.cbSize = sizeof(info);
682 info.hwnd = reinterpret_cast<HWND>(parent_window_handle_);
683 info.lpVerb = L"runas";
684 info.lpFile = binary.value().c_str();
685 info.lpParameters = parameters.c_str();
686 info.nShow = SW_HIDE;
688 if (!ShellExecuteEx(&info)) {
689 DWORD error = ::GetLastError();
690 PLOG(ERROR) << "Unable to launch '" << binary.value() << "'";
691 if (error != ERROR_CANCELLED) {
692 OnError();
694 return;
697 if (!::ConnectNamedPipe(delegate_write_handle.Get(), nullptr)) {
698 DWORD error = ::GetLastError();
699 if (error != ERROR_PIPE_CONNECTED) {
700 PLOG(ERROR) << "Unable to connect '" << input_pipe_name << "'";
701 OnError();
702 return;
706 if (!::ConnectNamedPipe(delegate_read_handle.Get(), nullptr)) {
707 DWORD error = ::GetLastError();
708 if (error != ERROR_PIPE_CONNECTED) {
709 PLOG(ERROR) << "Unable to connect '" << output_pipe_name << "'";
710 OnError();
711 return;
715 // Set up the native messaging channel to talk to the elevated host.
716 // Note that input for the elevated channel is output for the elevated host.
717 elevated_channel_.reset(
718 new PipeMessagingChannel(base::File(delegate_read_handle.Take()),
719 base::File(delegate_write_handle.Take())));
721 elevated_channel_event_handler_.reset(
722 new Me2MeNativeMessagingHost::ElevatedChannelEventHandler(this));
723 elevated_channel_->Start(elevated_channel_event_handler_.get());
725 elevated_host_timer_.Start(
726 FROM_HERE, base::TimeDelta::FromSeconds(kElevatedHostTimeoutSeconds),
727 this, &Me2MeNativeMessagingHost::DisconnectElevatedHost);
730 void Me2MeNativeMessagingHost::DisconnectElevatedHost() {
731 DCHECK(thread_checker_.CalledOnValidThread());
733 // This will send an EOF to the elevated host, triggering its shutdown.
734 elevated_channel_.reset();
737 #else // defined(OS_WIN)
739 bool Me2MeNativeMessagingHost::DelegateToElevatedHost(
740 scoped_ptr<base::DictionaryValue> message) {
741 NOTREACHED();
742 return false;
745 #endif // !defined(OS_WIN)
747 } // namespace remoting