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. 4 5#include "remoting/host/setup/me2me_native_messaging_host.h" 6#include <string> 7 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" 28 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) 34 35namespace { 36 37#if defined(OS_WIN) 38// Windows will use default buffer size when 0 is passed to CreateNamedPipeW(). 39const DWORD kBufferSize = 0; 40const int kTimeOutMilliseconds = 2000; 41const char kChromePipeNamePrefix[] = "\\\\.\\pipe\\chrome_remote_desktop."; 42const int kElevatedHostTimeoutSeconds = 300; 43#endif // defined(OS_WIN) 44 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). 47const char* kServiceAccountRedirectUri = "oob"; 48 49// Features supported in addition to the base protocol. 50const char* kSupportedFeatures[] = { 51 "pairingRegistry", 52 "oauthClient" 53}; 54 55// Helper to extract the "config" part of a message as a DictionaryValue. 56// Returns NULL on failure, and logs an error message. 57scoped_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"; 65 } 66 return result.Pass(); 67} 68 69} // namespace 70 71namespace remoting { 72 73Me2MeNativeMessagingHost::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 parent_window_handle_(parent_window_handle), 82 channel_(channel.Pass()), 83 daemon_controller_(daemon_controller), 84 pairing_registry_(pairing_registry), 85 oauth_client_(oauth_client.Pass()), 86 weak_factory_(this) { 87 weak_ptr_ = weak_factory_.GetWeakPtr(); 88} 89 90Me2MeNativeMessagingHost::~Me2MeNativeMessagingHost() { 91 DCHECK(thread_checker_.CalledOnValidThread()); 92} 93 94void Me2MeNativeMessagingHost::Start( 95 const base::Closure& quit_closure) { 96 DCHECK(thread_checker_.CalledOnValidThread()); 97 DCHECK(!quit_closure.is_null()); 98 99 quit_closure_ = quit_closure; 100 101 channel_->Start(this); 102} 103 104void Me2MeNativeMessagingHost::OnMessage(scoped_ptr<base::Value> message) { 105 DCHECK(thread_checker_.CalledOnValidThread()); 106 107 scoped_ptr<base::DictionaryValue> message_dict( 108 static_cast<base::DictionaryValue*>(message.release())); 109 scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue()); 110 111 // If the client supplies an ID, it will expect it in the response. This 112 // might be a string or a number, so cope with both. 113 const base::Value* id; 114 if (message_dict->Get("id", &id)) 115 response->Set("id", id->DeepCopy()); 116 117 std::string type; 118 if (!message_dict->GetString("type", &type)) { 119 LOG(ERROR) << "'type' not found"; 120 channel_->SendMessage(scoped_ptr<base::Value>()); 121 return; 122 } 123 124 response->SetString("type", type + "Response"); 125 126 if (type == "hello") { 127 ProcessHello(message_dict.Pass(), response.Pass()); 128 } else if (type == "clearPairedClients") { 129 ProcessClearPairedClients(message_dict.Pass(), response.Pass()); 130 } else if (type == "deletePairedClient") { 131 ProcessDeletePairedClient(message_dict.Pass(), response.Pass()); 132 } else if (type == "getHostName") { 133 ProcessGetHostName(message_dict.Pass(), response.Pass()); 134 } else if (type == "getPinHash") { 135 ProcessGetPinHash(message_dict.Pass(), response.Pass()); 136 } else if (type == "generateKeyPair") { 137 ProcessGenerateKeyPair(message_dict.Pass(), response.Pass()); 138 } else if (type == "updateDaemonConfig") { 139 ProcessUpdateDaemonConfig(message_dict.Pass(), response.Pass()); 140 } else if (type == "getDaemonConfig") { 141 ProcessGetDaemonConfig(message_dict.Pass(), response.Pass()); 142 } else if (type == "getPairedClients") { 143 ProcessGetPairedClients(message_dict.Pass(), response.Pass()); 144 } else if (type == "getUsageStatsConsent") { 145 ProcessGetUsageStatsConsent(message_dict.Pass(), response.Pass()); 146 } else if (type == "startDaemon") { 147 ProcessStartDaemon(message_dict.Pass(), response.Pass()); 148 } else if (type == "stopDaemon") { 149 ProcessStopDaemon(message_dict.Pass(), response.Pass()); 150 } else if (type == "getDaemonState") { 151 ProcessGetDaemonState(message_dict.Pass(), response.Pass()); 152 } else if (type == "getHostClientId") { 153 ProcessGetHostClientId(message_dict.Pass(), response.Pass()); 154 } else if (type == "getCredentialsFromAuthCode") { 155 ProcessGetCredentialsFromAuthCode(message_dict.Pass(), response.Pass()); 156 } else { 157 LOG(ERROR) << "Unsupported request type: " << type; 158 OnError(); 159 } 160} 161 162void Me2MeNativeMessagingHost::OnDisconnect() { 163 if (!quit_closure_.is_null()) 164 base::ResetAndReturn(&quit_closure_).Run(); 165} 166 167void Me2MeNativeMessagingHost::ProcessHello( 168 scoped_ptr<base::DictionaryValue> message, 169 scoped_ptr<base::DictionaryValue> response) { 170 DCHECK(thread_checker_.CalledOnValidThread()); 171 172 response->SetString("version", STRINGIZE(VERSION)); 173 scoped_ptr<base::ListValue> supported_features_list(new base::ListValue()); 174 supported_features_list->AppendStrings(std::vector<std::string>( 175 kSupportedFeatures, kSupportedFeatures + arraysize(kSupportedFeatures))); 176 response->Set("supportedFeatures", supported_features_list.release()); 177 channel_->SendMessage(response.PassAs<base::Value>()); 178} 179 180void Me2MeNativeMessagingHost::ProcessClearPairedClients( 181 scoped_ptr<base::DictionaryValue> message, 182 scoped_ptr<base::DictionaryValue> response) { 183 DCHECK(thread_checker_.CalledOnValidThread()); 184 185 if (needs_elevation_) { 186 if (!DelegateToElevatedHost(message.Pass())) 187 SendBooleanResult(response.Pass(), false); 188 return; 189 } 190 191 if (pairing_registry_.get()) { 192 pairing_registry_->ClearAllPairings( 193 base::Bind(&Me2MeNativeMessagingHost::SendBooleanResult, weak_ptr_, 194 base::Passed(&response))); 195 } else { 196 SendBooleanResult(response.Pass(), false); 197 } 198} 199 200void Me2MeNativeMessagingHost::ProcessDeletePairedClient( 201 scoped_ptr<base::DictionaryValue> message, 202 scoped_ptr<base::DictionaryValue> response) { 203 DCHECK(thread_checker_.CalledOnValidThread()); 204 205 if (needs_elevation_) { 206 if (!DelegateToElevatedHost(message.Pass())) 207 SendBooleanResult(response.Pass(), false); 208 return; 209 } 210 211 std::string client_id; 212 if (!message->GetString(protocol::PairingRegistry::kClientIdKey, 213 &client_id)) { 214 LOG(ERROR) << "'" << protocol::PairingRegistry::kClientIdKey 215 << "' string not found."; 216 OnError(); 217 return; 218 } 219 220 if (pairing_registry_.get()) { 221 pairing_registry_->DeletePairing( 222 client_id, base::Bind(&Me2MeNativeMessagingHost::SendBooleanResult, 223 weak_ptr_, base::Passed(&response))); 224 } else { 225 SendBooleanResult(response.Pass(), false); 226 } 227} 228 229void Me2MeNativeMessagingHost::ProcessGetHostName( 230 scoped_ptr<base::DictionaryValue> message, 231 scoped_ptr<base::DictionaryValue> response) { 232 DCHECK(thread_checker_.CalledOnValidThread()); 233 234 response->SetString("hostname", net::GetHostName()); 235 channel_->SendMessage(response.PassAs<base::Value>()); 236} 237 238void Me2MeNativeMessagingHost::ProcessGetPinHash( 239 scoped_ptr<base::DictionaryValue> message, 240 scoped_ptr<base::DictionaryValue> response) { 241 DCHECK(thread_checker_.CalledOnValidThread()); 242 243 std::string host_id; 244 if (!message->GetString("hostId", &host_id)) { 245 LOG(ERROR) << "'hostId' not found: " << message; 246 OnError(); 247 return; 248 } 249 std::string pin; 250 if (!message->GetString("pin", &pin)) { 251 LOG(ERROR) << "'pin' not found: " << message; 252 OnError(); 253 return; 254 } 255 response->SetString("hash", MakeHostPinHash(host_id, pin)); 256 channel_->SendMessage(response.PassAs<base::Value>()); 257} 258 259void Me2MeNativeMessagingHost::ProcessGenerateKeyPair( 260 scoped_ptr<base::DictionaryValue> message, 261 scoped_ptr<base::DictionaryValue> response) { 262 DCHECK(thread_checker_.CalledOnValidThread()); 263 264 scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::Generate(); 265 response->SetString("privateKey", key_pair->ToString()); 266 response->SetString("publicKey", key_pair->GetPublicKey()); 267 channel_->SendMessage(response.PassAs<base::Value>()); 268} 269 270void Me2MeNativeMessagingHost::ProcessUpdateDaemonConfig( 271 scoped_ptr<base::DictionaryValue> message, 272 scoped_ptr<base::DictionaryValue> response) { 273 DCHECK(thread_checker_.CalledOnValidThread()); 274 275 scoped_ptr<base::DictionaryValue> config_dict = 276 ConfigDictionaryFromMessage(message.Pass()); 277 if (!config_dict) { 278 OnError(); 279 return; 280 } 281 282 daemon_controller_->UpdateConfig( 283 config_dict.Pass(), 284 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_, 285 base::Passed(&response))); 286} 287 288void Me2MeNativeMessagingHost::ProcessGetDaemonConfig( 289 scoped_ptr<base::DictionaryValue> message, 290 scoped_ptr<base::DictionaryValue> response) { 291 DCHECK(thread_checker_.CalledOnValidThread()); 292 293 daemon_controller_->GetConfig( 294 base::Bind(&Me2MeNativeMessagingHost::SendConfigResponse, weak_ptr_, 295 base::Passed(&response))); 296} 297 298void Me2MeNativeMessagingHost::ProcessGetPairedClients( 299 scoped_ptr<base::DictionaryValue> message, 300 scoped_ptr<base::DictionaryValue> response) { 301 DCHECK(thread_checker_.CalledOnValidThread()); 302 303 if (pairing_registry_.get()) { 304 pairing_registry_->GetAllPairings( 305 base::Bind(&Me2MeNativeMessagingHost::SendPairedClientsResponse, 306 weak_ptr_, base::Passed(&response))); 307 } else { 308 scoped_ptr<base::ListValue> no_paired_clients(new base::ListValue); 309 SendPairedClientsResponse(response.Pass(), no_paired_clients.Pass()); 310 } 311} 312 313void Me2MeNativeMessagingHost::ProcessGetUsageStatsConsent( 314 scoped_ptr<base::DictionaryValue> message, 315 scoped_ptr<base::DictionaryValue> response) { 316 DCHECK(thread_checker_.CalledOnValidThread()); 317 318 daemon_controller_->GetUsageStatsConsent( 319 base::Bind(&Me2MeNativeMessagingHost::SendUsageStatsConsentResponse, 320 weak_ptr_, base::Passed(&response))); 321} 322 323void Me2MeNativeMessagingHost::ProcessStartDaemon( 324 scoped_ptr<base::DictionaryValue> message, 325 scoped_ptr<base::DictionaryValue> response) { 326 DCHECK(thread_checker_.CalledOnValidThread()); 327 328 bool consent; 329 if (!message->GetBoolean("consent", &consent)) { 330 LOG(ERROR) << "'consent' not found."; 331 OnError(); 332 return; 333 } 334 335 scoped_ptr<base::DictionaryValue> config_dict = 336 ConfigDictionaryFromMessage(message.Pass()); 337 if (!config_dict) { 338 OnError(); 339 return; 340 } 341 342 daemon_controller_->SetConfigAndStart( 343 config_dict.Pass(), consent, 344 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_, 345 base::Passed(&response))); 346} 347 348void Me2MeNativeMessagingHost::ProcessStopDaemon( 349 scoped_ptr<base::DictionaryValue> message, 350 scoped_ptr<base::DictionaryValue> response) { 351 DCHECK(thread_checker_.CalledOnValidThread()); 352 353 daemon_controller_->Stop( 354 base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_, 355 base::Passed(&response))); 356} 357 358void Me2MeNativeMessagingHost::ProcessGetDaemonState( 359 scoped_ptr<base::DictionaryValue> message, 360 scoped_ptr<base::DictionaryValue> response) { 361 DCHECK(thread_checker_.CalledOnValidThread()); 362 363 DaemonController::State state = daemon_controller_->GetState(); 364 switch (state) { 365 case DaemonController::STATE_NOT_IMPLEMENTED: 366 response->SetString("state", "NOT_IMPLEMENTED"); 367 break; 368 case DaemonController::STATE_NOT_INSTALLED: 369 response->SetString("state", "NOT_INSTALLED"); 370 break; 371 case DaemonController::STATE_INSTALLING: 372 response->SetString("state", "INSTALLING"); 373 break; 374 case DaemonController::STATE_STOPPED: 375 response->SetString("state", "STOPPED"); 376 break; 377 case DaemonController::STATE_STARTING: 378 response->SetString("state", "STARTING"); 379 break; 380 case DaemonController::STATE_STARTED: 381 response->SetString("state", "STARTED"); 382 break; 383 case DaemonController::STATE_STOPPING: 384 response->SetString("state", "STOPPING"); 385 break; 386 case DaemonController::STATE_UNKNOWN: 387 response->SetString("state", "UNKNOWN"); 388 break; 389 } 390 channel_->SendMessage(response.PassAs<base::Value>()); 391} 392 393void Me2MeNativeMessagingHost::ProcessGetHostClientId( 394 scoped_ptr<base::DictionaryValue> message, 395 scoped_ptr<base::DictionaryValue> response) { 396 DCHECK(thread_checker_.CalledOnValidThread()); 397 398 response->SetString("clientId", google_apis::GetOAuth2ClientID( 399 google_apis::CLIENT_REMOTING_HOST)); 400 channel_->SendMessage(response.PassAs<base::Value>()); 401} 402 403void Me2MeNativeMessagingHost::ProcessGetCredentialsFromAuthCode( 404 scoped_ptr<base::DictionaryValue> message, 405 scoped_ptr<base::DictionaryValue> response) { 406 DCHECK(thread_checker_.CalledOnValidThread()); 407 408 std::string auth_code; 409 if (!message->GetString("authorizationCode", &auth_code)) { 410 LOG(ERROR) << "'authorizationCode' string not found."; 411 OnError(); 412 return; 413 } 414 415 gaia::OAuthClientInfo oauth_client_info = { 416 google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING_HOST), 417 google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_REMOTING_HOST), 418 kServiceAccountRedirectUri 419 }; 420 421 oauth_client_->GetCredentialsFromAuthCode( 422 oauth_client_info, auth_code, base::Bind( 423 &Me2MeNativeMessagingHost::SendCredentialsResponse, weak_ptr_, 424 base::Passed(&response))); 425} 426 427void Me2MeNativeMessagingHost::SendConfigResponse( 428 scoped_ptr<base::DictionaryValue> response, 429 scoped_ptr<base::DictionaryValue> config) { 430 DCHECK(thread_checker_.CalledOnValidThread()); 431 432 if (config) { 433 response->Set("config", config.release()); 434 } else { 435 response->Set("config", base::Value::CreateNullValue()); 436 } 437 channel_->SendMessage(response.PassAs<base::Value>()); 438} 439 440void Me2MeNativeMessagingHost::SendPairedClientsResponse( 441 scoped_ptr<base::DictionaryValue> response, 442 scoped_ptr<base::ListValue> pairings) { 443 DCHECK(thread_checker_.CalledOnValidThread()); 444 445 response->Set("pairedClients", pairings.release()); 446 channel_->SendMessage(response.PassAs<base::Value>()); 447} 448 449void Me2MeNativeMessagingHost::SendUsageStatsConsentResponse( 450 scoped_ptr<base::DictionaryValue> response, 451 const DaemonController::UsageStatsConsent& consent) { 452 DCHECK(thread_checker_.CalledOnValidThread()); 453 454 response->SetBoolean("supported", consent.supported); 455 response->SetBoolean("allowed", consent.allowed); 456 response->SetBoolean("setByPolicy", consent.set_by_policy); 457 channel_->SendMessage(response.PassAs<base::Value>()); 458} 459 460void Me2MeNativeMessagingHost::SendAsyncResult( 461 scoped_ptr<base::DictionaryValue> response, 462 DaemonController::AsyncResult result) { 463 DCHECK(thread_checker_.CalledOnValidThread()); 464 465 switch (result) { 466 case DaemonController::RESULT_OK: 467 response->SetString("result", "OK"); 468 break; 469 case DaemonController::RESULT_FAILED: 470 response->SetString("result", "FAILED"); 471 break; 472 case DaemonController::RESULT_CANCELLED: 473 response->SetString("result", "CANCELLED"); 474 break; 475 case DaemonController::RESULT_FAILED_DIRECTORY: 476 response->SetString("result", "FAILED_DIRECTORY"); 477 break; 478 } 479 channel_->SendMessage(response.PassAs<base::Value>()); 480} 481 482void Me2MeNativeMessagingHost::SendBooleanResult( 483 scoped_ptr<base::DictionaryValue> response, bool result) { 484 DCHECK(thread_checker_.CalledOnValidThread()); 485 486 response->SetBoolean("result", result); 487 channel_->SendMessage(response.PassAs<base::Value>()); 488} 489 490void Me2MeNativeMessagingHost::SendCredentialsResponse( 491 scoped_ptr<base::DictionaryValue> response, 492 const std::string& user_email, 493 const std::string& refresh_token) { 494 DCHECK(thread_checker_.CalledOnValidThread()); 495 496 response->SetString("userEmail", user_email); 497 response->SetString("refreshToken", refresh_token); 498 channel_->SendMessage(response.PassAs<base::Value>()); 499} 500 501void Me2MeNativeMessagingHost::OnError() { 502 // Trigger a host shutdown by sending a NULL message. 503 channel_->SendMessage(scoped_ptr<base::Value>()); 504} 505 506void Me2MeNativeMessagingHost::Stop() { 507 DCHECK(thread_checker_.CalledOnValidThread()); 508 509 if (!quit_closure_.is_null()) 510 base::ResetAndReturn(&quit_closure_).Run(); 511} 512 513#if defined(OS_WIN) 514Me2MeNativeMessagingHost::ElevatedChannelEventHandler:: 515 ElevatedChannelEventHandler(Me2MeNativeMessagingHost* host) 516 : parent_(host) { 517} 518 519void Me2MeNativeMessagingHost::ElevatedChannelEventHandler::OnMessage( 520 scoped_ptr<base::Value> message) { 521 DCHECK(parent_->thread_checker_.CalledOnValidThread()); 522 523 // Simply pass along the response from the elevated host to the client. 524 parent_->channel_->SendMessage(message.Pass()); 525} 526 527void Me2MeNativeMessagingHost::ElevatedChannelEventHandler::OnDisconnect() { 528 parent_->OnDisconnect(); 529} 530 531bool Me2MeNativeMessagingHost::DelegateToElevatedHost( 532 scoped_ptr<base::DictionaryValue> message) { 533 DCHECK(thread_checker_.CalledOnValidThread()); 534 535 EnsureElevatedHostCreated(); 536 537 // elevated_channel_ will be null if user rejects the UAC request. 538 if (elevated_channel_) 539 elevated_channel_->SendMessage(message.PassAs<base::Value>()); 540 541 return elevated_channel_ != NULL; 542} 543 544void Me2MeNativeMessagingHost::EnsureElevatedHostCreated() { 545 DCHECK(thread_checker_.CalledOnValidThread()); 546 DCHECK(needs_elevation_); 547 548 if (elevated_channel_) 549 return; 550 551 // presubmit: allow wstring 552 std::wstring user_sid; 553 if (!base::win::GetUserSidString(&user_sid)) { 554 LOG(ERROR) << "Failed to query the current user SID."; 555 OnError(); 556 return; 557 } 558 559 // Create a security descriptor that gives full access to the caller and 560 // denies access by anyone else. 561 std::string security_descriptor = base::StringPrintf( 562 "O:%1$sG:%1$sD:(A;;GA;;;%1$s)", base::UTF16ToASCII(user_sid).c_str()); 563 564 ScopedSd sd = ConvertSddlToSd(security_descriptor); 565 if (!sd) { 566 PLOG(ERROR) << "Failed to create a security descriptor for the" 567 << "Chromoting Me2Me native messaging host."; 568 OnError(); 569 return; 570 } 571 572 SECURITY_ATTRIBUTES security_attributes = {0}; 573 security_attributes.nLength = sizeof(security_attributes); 574 security_attributes.lpSecurityDescriptor = sd.get(); 575 security_attributes.bInheritHandle = FALSE; 576 577 // Generate a unique name for the input channel. 578 std::string input_pipe_name(kChromePipeNamePrefix); 579 input_pipe_name.append(IPC::Channel::GenerateUniqueRandomChannelID()); 580 581 base::win::ScopedHandle delegate_write_handle(::CreateNamedPipe( 582 base::ASCIIToUTF16(input_pipe_name).c_str(), 583 PIPE_ACCESS_OUTBOUND, 584 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS, 585 1, 586 kBufferSize, 587 kBufferSize, 588 kTimeOutMilliseconds, 589 &security_attributes)); 590 591 if (!delegate_write_handle.IsValid()) { 592 PLOG(ERROR) << "Failed to create named pipe '" << input_pipe_name << "'"; 593 OnError(); 594 return; 595 } 596 597 // Generate a unique name for the input channel. 598 std::string output_pipe_name(kChromePipeNamePrefix); 599 output_pipe_name.append(IPC::Channel::GenerateUniqueRandomChannelID()); 600 601 base::win::ScopedHandle delegate_read_handle(::CreateNamedPipe( 602 base::ASCIIToUTF16(output_pipe_name).c_str(), 603 PIPE_ACCESS_INBOUND, 604 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS, 605 1, 606 kBufferSize, 607 kBufferSize, 608 kTimeOutMilliseconds, 609 &security_attributes)); 610 611 if (!delegate_read_handle.IsValid()) { 612 PLOG(ERROR) << "Failed to create named pipe '" << output_pipe_name << "'"; 613 OnError(); 614 return; 615 } 616 617 const base::CommandLine* current_command_line = 618 base::CommandLine::ForCurrentProcess(); 619 const base::CommandLine::SwitchMap& switches = 620 current_command_line->GetSwitches(); 621 base::CommandLine::StringVector args = current_command_line->GetArgs(); 622 623 // Create the child process command line by copying switches from the current 624 // command line. 625 base::CommandLine command_line(base::CommandLine::NO_PROGRAM); 626 command_line.AppendSwitch(kElevatingSwitchName); 627 command_line.AppendSwitchASCII(kInputSwitchName, input_pipe_name); 628 command_line.AppendSwitchASCII(kOutputSwitchName, output_pipe_name); 629 630 DCHECK(!current_command_line->HasSwitch(kElevatingSwitchName)); 631 for (base::CommandLine::SwitchMap::const_iterator i = switches.begin(); 632 i != switches.end(); ++i) { 633 command_line.AppendSwitchNative(i->first, i->second); 634 } 635 for (base::CommandLine::StringVector::const_iterator i = args.begin(); 636 i != args.end(); ++i) { 637 command_line.AppendArgNative(*i); 638 } 639 640 // Get the name of the binary to launch. 641 base::FilePath binary = current_command_line->GetProgram(); 642 base::CommandLine::StringType parameters = 643 command_line.GetCommandLineString(); 644 645 // Launch the child process requesting elevation. 646 SHELLEXECUTEINFO info; 647 memset(&info, 0, sizeof(info)); 648 info.cbSize = sizeof(info); 649 info.hwnd = reinterpret_cast<HWND>(parent_window_handle_); 650 info.lpVerb = L"runas"; 651 info.lpFile = binary.value().c_str(); 652 info.lpParameters = parameters.c_str(); 653 info.nShow = SW_HIDE; 654 655 if (!ShellExecuteEx(&info)) { 656 DWORD error = ::GetLastError(); 657 PLOG(ERROR) << "Unable to launch '" << binary.value() << "'"; 658 if (error != ERROR_CANCELLED) { 659 OnError(); 660 } 661 return; 662 } 663 664 if (!::ConnectNamedPipe(delegate_write_handle.Get(), NULL)) { 665 DWORD error = ::GetLastError(); 666 if (error != ERROR_PIPE_CONNECTED) { 667 PLOG(ERROR) << "Unable to connect '" << input_pipe_name << "'"; 668 OnError(); 669 return; 670 } 671 } 672 673 if (!::ConnectNamedPipe(delegate_read_handle.Get(), NULL)) { 674 DWORD error = ::GetLastError(); 675 if (error != ERROR_PIPE_CONNECTED) { 676 PLOG(ERROR) << "Unable to connect '" << output_pipe_name << "'"; 677 OnError(); 678 return; 679 } 680 } 681 682 // Set up the native messaging channel to talk to the elevated host. 683 // Note that input for the elevated channel is output for the elevated host. 684 elevated_channel_.reset( 685 new PipeMessagingChannel(base::File(delegate_read_handle.Take()), 686 base::File(delegate_write_handle.Take()))); 687 688 elevated_channel_event_handler_.reset( 689 new Me2MeNativeMessagingHost::ElevatedChannelEventHandler(this)); 690 elevated_channel_->Start(elevated_channel_event_handler_.get()); 691 692 elevated_host_timer_.Start( 693 FROM_HERE, base::TimeDelta::FromSeconds(kElevatedHostTimeoutSeconds), 694 this, &Me2MeNativeMessagingHost::DisconnectElevatedHost); 695} 696 697void Me2MeNativeMessagingHost::DisconnectElevatedHost() { 698 DCHECK(thread_checker_.CalledOnValidThread()); 699 700 // This will send an EOF to the elevated host, triggering its shutdown. 701 elevated_channel_.reset(); 702} 703 704#else // defined(OS_WIN) 705 706bool Me2MeNativeMessagingHost::DelegateToElevatedHost( 707 scoped_ptr<base::DictionaryValue> message) { 708 NOTREACHED(); 709 return false; 710} 711 712#endif // !defined(OS_WIN) 713 714} // namespace remoting 715