blob: 2d9d8114068c37da5d0ec7f362a54b9683000758 [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/host/setup/me2me_native_messaging_host.h"
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/strings/stringize_macros.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread.h"
#include "base/values.h"
#include "build/build_config.h"
#include "google_apis/gaia/gaia_oauth_client.h"
#include "google_apis/google_api_keys.h"
#include "ipc/ipc_channel.h"
#include "net/base/network_interfaces.h"
#include "remoting/base/rsa_key_pair.h"
#include "remoting/host/native_messaging/pipe_messaging_channel.h"
#include "remoting/host/pin_hash.h"
#include "remoting/host/setup/oauth_client.h"
#include "remoting/protocol/pairing_registry.h"
#if defined(OS_WIN)
#include <shellapi.h>
#include "base/win/win_util.h"
#include "remoting/host/win/security_descriptor.h"
#endif // defined(OS_WIN)
namespace {
#if defined(OS_WIN)
// Windows will use default buffer size when 0 is passed to CreateNamedPipeW().
const DWORD kBufferSize = 0;
const int kTimeOutMilliseconds = 2000;
const char kChromePipeNamePrefix[] = "\\\\.\\pipe\\chrome_remote_desktop.";
const int kElevatedHostTimeoutSeconds = 300;
#endif // defined(OS_WIN)
// redirect_uri to use when authenticating service accounts (service account
// codes are obtained "out-of-band", i.e., not through an OAuth redirect).
const char* kServiceAccountRedirectUri = "oob";
// Features supported in addition to the base protocol.
const char* kSupportedFeatures[] = {
"pairingRegistry",
"oauthClient",
"getRefreshTokenFromAuthCode",
};
// Helper to extract the "config" part of a message as a DictionaryValue.
// Returns nullptr on failure, and logs an error message.
std::unique_ptr<base::DictionaryValue> ConfigDictionaryFromMessage(
std::unique_ptr<base::DictionaryValue> message) {
std::unique_ptr<base::DictionaryValue> result;
const base::DictionaryValue* config_dict;
if (message->GetDictionary("config", &config_dict)) {
result = config_dict->CreateDeepCopy();
} else {
LOG(ERROR) << "'config' dictionary not found";
}
return result;
}
} // namespace
namespace remoting {
Me2MeNativeMessagingHost::Me2MeNativeMessagingHost(
bool needs_elevation,
intptr_t parent_window_handle,
std::unique_ptr<extensions::NativeMessagingChannel> channel,
scoped_refptr<DaemonController> daemon_controller,
scoped_refptr<protocol::PairingRegistry> pairing_registry,
std::unique_ptr<OAuthClient> oauth_client)
: needs_elevation_(needs_elevation),
#if defined(OS_WIN)
parent_window_handle_(parent_window_handle),
#endif
channel_(std::move(channel)),
log_message_handler_(
base::Bind(&extensions::NativeMessagingChannel::SendMessage,
base::Unretained(channel_.get()))),
daemon_controller_(daemon_controller),
pairing_registry_(pairing_registry),
oauth_client_(std::move(oauth_client)),
weak_factory_(this) {
weak_ptr_ = weak_factory_.GetWeakPtr();
}
Me2MeNativeMessagingHost::~Me2MeNativeMessagingHost() {
DCHECK(thread_checker_.CalledOnValidThread());
}
void Me2MeNativeMessagingHost::Start(
const base::Closure& quit_closure) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!quit_closure.is_null());
quit_closure_ = quit_closure;
channel_->Start(this);
}
void Me2MeNativeMessagingHost::OnMessage(std::unique_ptr<base::Value> message) {
DCHECK(thread_checker_.CalledOnValidThread());
if (!message->IsType(base::Value::TYPE_DICTIONARY)) {
LOG(ERROR) << "Received a message that's not a dictionary.";
channel_->SendMessage(nullptr);
return;
}
std::unique_ptr<base::DictionaryValue> message_dict(
static_cast<base::DictionaryValue*>(message.release()));
std::unique_ptr<base::DictionaryValue> response(new base::DictionaryValue());
// If the client supplies an ID, it will expect it in the response. This
// might be a string or a number, so cope with both.
const base::Value* id;
if (message_dict->Get("id", &id))
response->Set("id", id->CreateDeepCopy());
std::string type;
if (!message_dict->GetString("type", &type)) {
LOG(ERROR) << "'type' not found";
channel_->SendMessage(nullptr);
return;
}
response->SetString("type", type + "Response");
if (type == "hello") {
ProcessHello(std::move(message_dict), std::move(response));
} else if (type == "clearPairedClients") {
ProcessClearPairedClients(std::move(message_dict), std::move(response));
} else if (type == "deletePairedClient") {
ProcessDeletePairedClient(std::move(message_dict), std::move(response));
} else if (type == "getHostName") {
ProcessGetHostName(std::move(message_dict), std::move(response));
} else if (type == "getPinHash") {
ProcessGetPinHash(std::move(message_dict), std::move(response));
} else if (type == "generateKeyPair") {
ProcessGenerateKeyPair(std::move(message_dict), std::move(response));
} else if (type == "updateDaemonConfig") {
ProcessUpdateDaemonConfig(std::move(message_dict), std::move(response));
} else if (type == "getDaemonConfig") {
ProcessGetDaemonConfig(std::move(message_dict), std::move(response));
} else if (type == "getPairedClients") {
ProcessGetPairedClients(std::move(message_dict), std::move(response));
} else if (type == "getUsageStatsConsent") {
ProcessGetUsageStatsConsent(std::move(message_dict), std::move(response));
} else if (type == "startDaemon") {
ProcessStartDaemon(std::move(message_dict), std::move(response));
} else if (type == "stopDaemon") {
ProcessStopDaemon(std::move(message_dict), std::move(response));
} else if (type == "getDaemonState") {
ProcessGetDaemonState(std::move(message_dict), std::move(response));
} else if (type == "getHostClientId") {
ProcessGetHostClientId(std::move(message_dict), std::move(response));
} else if (type == "getCredentialsFromAuthCode") {
ProcessGetCredentialsFromAuthCode(
std::move(message_dict), std::move(response), true);
} else if (type == "getRefreshTokenFromAuthCode") {
ProcessGetCredentialsFromAuthCode(
std::move(message_dict), std::move(response), false);
} else {
LOG(ERROR) << "Unsupported request type: " << type;
OnError();
}
}
void Me2MeNativeMessagingHost::OnDisconnect() {
if (!quit_closure_.is_null())
base::ResetAndReturn(&quit_closure_).Run();
}
void Me2MeNativeMessagingHost::ProcessHello(
std::unique_ptr<base::DictionaryValue> message,
std::unique_ptr<base::DictionaryValue> response) {
DCHECK(thread_checker_.CalledOnValidThread());
response->SetString("version", STRINGIZE(VERSION));
std::unique_ptr<base::ListValue> supported_features_list(
new base::ListValue());
supported_features_list->AppendStrings(std::vector<std::string>(
kSupportedFeatures, kSupportedFeatures + arraysize(kSupportedFeatures)));
response->Set("supportedFeatures", supported_features_list.release());
channel_->SendMessage(std::move(response));
}
void Me2MeNativeMessagingHost::ProcessClearPairedClients(
std::unique_ptr<base::DictionaryValue> message,
std::unique_ptr<base::DictionaryValue> response) {
DCHECK(thread_checker_.CalledOnValidThread());
if (needs_elevation_) {
if (!DelegateToElevatedHost(std::move(message)))
SendBooleanResult(std::move(response), false);
return;
}
if (pairing_registry_.get()) {
pairing_registry_->ClearAllPairings(
base::Bind(&Me2MeNativeMessagingHost::SendBooleanResult, weak_ptr_,
base::Passed(&response)));
} else {
SendBooleanResult(std::move(response), false);
}
}
void Me2MeNativeMessagingHost::ProcessDeletePairedClient(
std::unique_ptr<base::DictionaryValue> message,
std::unique_ptr<base::DictionaryValue> response) {
DCHECK(thread_checker_.CalledOnValidThread());
if (needs_elevation_) {
if (!DelegateToElevatedHost(std::move(message)))
SendBooleanResult(std::move(response), false);
return;
}
std::string client_id;
if (!message->GetString(protocol::PairingRegistry::kClientIdKey,
&client_id)) {
LOG(ERROR) << "'" << protocol::PairingRegistry::kClientIdKey
<< "' string not found.";
OnError();
return;
}
if (pairing_registry_.get()) {
pairing_registry_->DeletePairing(
client_id, base::Bind(&Me2MeNativeMessagingHost::SendBooleanResult,
weak_ptr_, base::Passed(&response)));
} else {
SendBooleanResult(std::move(response), false);
}
}
void Me2MeNativeMessagingHost::ProcessGetHostName(
std::unique_ptr<base::DictionaryValue> message,
std::unique_ptr<base::DictionaryValue> response) {
DCHECK(thread_checker_.CalledOnValidThread());
response->SetString("hostname", net::GetHostName());
channel_->SendMessage(std::move(response));
}
void Me2MeNativeMessagingHost::ProcessGetPinHash(
std::unique_ptr<base::DictionaryValue> message,
std::unique_ptr<base::DictionaryValue> response) {
DCHECK(thread_checker_.CalledOnValidThread());
std::string host_id;
if (!message->GetString("hostId", &host_id)) {
LOG(ERROR) << "'hostId' not found: " << message.get();
OnError();
return;
}
std::string pin;
if (!message->GetString("pin", &pin)) {
LOG(ERROR) << "'pin' not found: " << message.get();
OnError();
return;
}
response->SetString("hash", MakeHostPinHash(host_id, pin));
channel_->SendMessage(std::move(response));
}
void Me2MeNativeMessagingHost::ProcessGenerateKeyPair(
std::unique_ptr<base::DictionaryValue> message,
std::unique_ptr<base::DictionaryValue> response) {
DCHECK(thread_checker_.CalledOnValidThread());
scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::Generate();
response->SetString("privateKey", key_pair->ToString());
response->SetString("publicKey", key_pair->GetPublicKey());
channel_->SendMessage(std::move(response));
}
void Me2MeNativeMessagingHost::ProcessUpdateDaemonConfig(
std::unique_ptr<base::DictionaryValue> message,
std::unique_ptr<base::DictionaryValue> response) {
DCHECK(thread_checker_.CalledOnValidThread());
if (needs_elevation_) {
if (!DelegateToElevatedHost(std::move(message)))
SendAsyncResult(std::move(response), DaemonController::RESULT_FAILED);
return;
}
std::unique_ptr<base::DictionaryValue> config_dict =
ConfigDictionaryFromMessage(std::move(message));
if (!config_dict) {
OnError();
return;
}
daemon_controller_->UpdateConfig(
std::move(config_dict),
base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
base::Passed(&response)));
}
void Me2MeNativeMessagingHost::ProcessGetDaemonConfig(
std::unique_ptr<base::DictionaryValue> message,
std::unique_ptr<base::DictionaryValue> response) {
DCHECK(thread_checker_.CalledOnValidThread());
daemon_controller_->GetConfig(
base::Bind(&Me2MeNativeMessagingHost::SendConfigResponse, weak_ptr_,
base::Passed(&response)));
}
void Me2MeNativeMessagingHost::ProcessGetPairedClients(
std::unique_ptr<base::DictionaryValue> message,
std::unique_ptr<base::DictionaryValue> response) {
DCHECK(thread_checker_.CalledOnValidThread());
if (pairing_registry_.get()) {
pairing_registry_->GetAllPairings(
base::Bind(&Me2MeNativeMessagingHost::SendPairedClientsResponse,
weak_ptr_, base::Passed(&response)));
} else {
std::unique_ptr<base::ListValue> no_paired_clients(new base::ListValue);
SendPairedClientsResponse(std::move(response),
std::move(no_paired_clients));
}
}
void Me2MeNativeMessagingHost::ProcessGetUsageStatsConsent(
std::unique_ptr<base::DictionaryValue> message,
std::unique_ptr<base::DictionaryValue> response) {
DCHECK(thread_checker_.CalledOnValidThread());
daemon_controller_->GetUsageStatsConsent(
base::Bind(&Me2MeNativeMessagingHost::SendUsageStatsConsentResponse,
weak_ptr_, base::Passed(&response)));
}
void Me2MeNativeMessagingHost::ProcessStartDaemon(
std::unique_ptr<base::DictionaryValue> message,
std::unique_ptr<base::DictionaryValue> response) {
DCHECK(thread_checker_.CalledOnValidThread());
if (needs_elevation_) {
if (!DelegateToElevatedHost(std::move(message)))
SendAsyncResult(std::move(response), DaemonController::RESULT_FAILED);
return;
}
bool consent;
if (!message->GetBoolean("consent", &consent)) {
LOG(ERROR) << "'consent' not found.";
OnError();
return;
}
std::unique_ptr<base::DictionaryValue> config_dict =
ConfigDictionaryFromMessage(std::move(message));
if (!config_dict) {
OnError();
return;
}
daemon_controller_->SetConfigAndStart(
std::move(config_dict), consent,
base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
base::Passed(&response)));
}
void Me2MeNativeMessagingHost::ProcessStopDaemon(
std::unique_ptr<base::DictionaryValue> message,
std::unique_ptr<base::DictionaryValue> response) {
DCHECK(thread_checker_.CalledOnValidThread());
if (needs_elevation_) {
if (!DelegateToElevatedHost(std::move(message)))
SendAsyncResult(std::move(response), DaemonController::RESULT_FAILED);
return;
}
daemon_controller_->Stop(
base::Bind(&Me2MeNativeMessagingHost::SendAsyncResult, weak_ptr_,
base::Passed(&response)));
}
void Me2MeNativeMessagingHost::ProcessGetDaemonState(
std::unique_ptr<base::DictionaryValue> message,
std::unique_ptr<base::DictionaryValue> response) {
DCHECK(thread_checker_.CalledOnValidThread());
DaemonController::State state = daemon_controller_->GetState();
switch (state) {
case DaemonController::STATE_NOT_IMPLEMENTED:
response->SetString("state", "NOT_IMPLEMENTED");
break;
case DaemonController::STATE_STOPPED:
response->SetString("state", "STOPPED");
break;
case DaemonController::STATE_STARTING:
response->SetString("state", "STARTING");
break;
case DaemonController::STATE_STARTED:
response->SetString("state", "STARTED");
break;
case DaemonController::STATE_STOPPING:
response->SetString("state", "STOPPING");
break;
case DaemonController::STATE_UNKNOWN:
response->SetString("state", "UNKNOWN");
break;
}
channel_->SendMessage(std::move(response));
}
void Me2MeNativeMessagingHost::ProcessGetHostClientId(
std::unique_ptr<base::DictionaryValue> message,
std::unique_ptr<base::DictionaryValue> response) {
DCHECK(thread_checker_.CalledOnValidThread());
response->SetString("clientId", google_apis::GetOAuth2ClientID(
google_apis::CLIENT_REMOTING_HOST));
channel_->SendMessage(std::move(response));
}
void Me2MeNativeMessagingHost::ProcessGetCredentialsFromAuthCode(
std::unique_ptr<base::DictionaryValue> message,
std::unique_ptr<base::DictionaryValue> response,
bool need_user_email) {
DCHECK(thread_checker_.CalledOnValidThread());
std::string auth_code;
if (!message->GetString("authorizationCode", &auth_code)) {
LOG(ERROR) << "'authorizationCode' string not found.";
OnError();
return;
}
gaia::OAuthClientInfo oauth_client_info = {
google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING_HOST),
google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_REMOTING_HOST),
kServiceAccountRedirectUri
};
oauth_client_->GetCredentialsFromAuthCode(
oauth_client_info, auth_code, need_user_email, base::Bind(
&Me2MeNativeMessagingHost::SendCredentialsResponse, weak_ptr_,
base::Passed(&response)));
}
void Me2MeNativeMessagingHost::SendConfigResponse(
std::unique_ptr<base::DictionaryValue> response,
std::unique_ptr<base::DictionaryValue> config) {
DCHECK(thread_checker_.CalledOnValidThread());
if (config) {
response->Set("config", config.release());
} else {
response->Set("config", base::Value::CreateNullValue());
}
channel_->SendMessage(std::move(response));
}
void Me2MeNativeMessagingHost::SendPairedClientsResponse(
std::unique_ptr<base::DictionaryValue> response,
std::unique_ptr<base::ListValue> pairings) {
DCHECK(thread_checker_.CalledOnValidThread());
response->Set("pairedClients", pairings.release());
channel_->SendMessage(std::move(response));
}
void Me2MeNativeMessagingHost::SendUsageStatsConsentResponse(
std::unique_ptr<base::DictionaryValue> response,
const DaemonController::UsageStatsConsent& consent) {
DCHECK(thread_checker_.CalledOnValidThread());
response->SetBoolean("supported", consent.supported);
response->SetBoolean("allowed", consent.allowed);
response->SetBoolean("setByPolicy", consent.set_by_policy);
channel_->SendMessage(std::move(response));
}
void Me2MeNativeMessagingHost::SendAsyncResult(
std::unique_ptr<base::DictionaryValue> response,
DaemonController::AsyncResult result) {
DCHECK(thread_checker_.CalledOnValidThread());
switch (result) {
case DaemonController::RESULT_OK:
response->SetString("result", "OK");
break;
case DaemonController::RESULT_FAILED:
response->SetString("result", "FAILED");
break;
case DaemonController::RESULT_CANCELLED:
response->SetString("result", "CANCELLED");
break;
}
channel_->SendMessage(std::move(response));
}
void Me2MeNativeMessagingHost::SendBooleanResult(
std::unique_ptr<base::DictionaryValue> response,
bool result) {
DCHECK(thread_checker_.CalledOnValidThread());
response->SetBoolean("result", result);
channel_->SendMessage(std::move(response));
}
void Me2MeNativeMessagingHost::SendCredentialsResponse(
std::unique_ptr<base::DictionaryValue> response,
const std::string& user_email,
const std::string& refresh_token) {
DCHECK(thread_checker_.CalledOnValidThread());
if (!user_email.empty()) {
response->SetString("userEmail", user_email);
}
response->SetString("refreshToken", refresh_token);
channel_->SendMessage(std::move(response));
}
void Me2MeNativeMessagingHost::OnError() {
// Trigger a host shutdown by sending a nullptr message.
channel_->SendMessage(nullptr);
}
void Me2MeNativeMessagingHost::Stop() {
DCHECK(thread_checker_.CalledOnValidThread());
if (!quit_closure_.is_null())
base::ResetAndReturn(&quit_closure_).Run();
}
#if defined(OS_WIN)
Me2MeNativeMessagingHost::ElevatedChannelEventHandler::
ElevatedChannelEventHandler(Me2MeNativeMessagingHost* host)
: parent_(host) {
}
void Me2MeNativeMessagingHost::ElevatedChannelEventHandler::OnMessage(
std::unique_ptr<base::Value> message) {
DCHECK(parent_->thread_checker_.CalledOnValidThread());
// Simply pass along the response from the elevated host to the client.
parent_->channel_->SendMessage(std::move(message));
}
void Me2MeNativeMessagingHost::ElevatedChannelEventHandler::OnDisconnect() {
parent_->OnDisconnect();
}
bool Me2MeNativeMessagingHost::DelegateToElevatedHost(
std::unique_ptr<base::DictionaryValue> message) {
DCHECK(thread_checker_.CalledOnValidThread());
EnsureElevatedHostCreated();
// elevated_channel_ will be null if user rejects the UAC request.
if (elevated_channel_)
elevated_channel_->SendMessage(std::move(message));
return elevated_channel_ != nullptr;
}
void Me2MeNativeMessagingHost::EnsureElevatedHostCreated() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(needs_elevation_);
if (elevated_channel_)
return;
// presubmit: allow wstring
std::wstring user_sid;
if (!base::win::GetUserSidString(&user_sid)) {
LOG(ERROR) << "Failed to query the current user SID.";
OnError();
return;
}
// Create a security descriptor that gives full access to the caller and
// BUILTIN_ADMINISTRATORS and denies access by anyone else.
// Local admins need access because the privileged host process will run
// as a local admin which may not be the same user as the current user.
std::string user_sid_ascii = base::UTF16ToASCII(user_sid);
std::string security_descriptor =
base::StringPrintf("O:%sG:%sD:(A;;GA;;;%s)(A;;GA;;;BA)",
user_sid_ascii.c_str(), user_sid_ascii.c_str(),
user_sid_ascii.c_str());
ScopedSd sd = ConvertSddlToSd(security_descriptor);
if (!sd) {
PLOG(ERROR) << "Failed to create a security descriptor for the"
<< "Chromoting Me2Me native messaging host.";
OnError();
return;
}
SECURITY_ATTRIBUTES security_attributes = {0};
security_attributes.nLength = sizeof(security_attributes);
security_attributes.lpSecurityDescriptor = sd.get();
security_attributes.bInheritHandle = FALSE;
// Generate a unique name for the input channel.
std::string input_pipe_name(kChromePipeNamePrefix);
input_pipe_name.append(IPC::Channel::GenerateUniqueRandomChannelID());
base::win::ScopedHandle delegate_write_handle(::CreateNamedPipe(
base::ASCIIToUTF16(input_pipe_name).c_str(),
PIPE_ACCESS_OUTBOUND,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS,
1,
kBufferSize,
kBufferSize,
kTimeOutMilliseconds,
&security_attributes));
if (!delegate_write_handle.IsValid()) {
PLOG(ERROR) << "Failed to create named pipe '" << input_pipe_name << "'";
OnError();
return;
}
// Generate a unique name for the input channel.
std::string output_pipe_name(kChromePipeNamePrefix);
output_pipe_name.append(IPC::Channel::GenerateUniqueRandomChannelID());
base::win::ScopedHandle delegate_read_handle(::CreateNamedPipe(
base::ASCIIToUTF16(output_pipe_name).c_str(),
PIPE_ACCESS_INBOUND,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS,
1,
kBufferSize,
kBufferSize,
kTimeOutMilliseconds,
&security_attributes));
if (!delegate_read_handle.IsValid()) {
PLOG(ERROR) << "Failed to create named pipe '" << output_pipe_name << "'";
OnError();
return;
}
const base::CommandLine* current_command_line =
base::CommandLine::ForCurrentProcess();
const base::CommandLine::SwitchMap& switches =
current_command_line->GetSwitches();
base::CommandLine::StringVector args = current_command_line->GetArgs();
// Create the child process command line by copying switches from the current
// command line.
base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
command_line.AppendSwitch(kElevatingSwitchName);
command_line.AppendSwitchASCII(kInputSwitchName, input_pipe_name);
command_line.AppendSwitchASCII(kOutputSwitchName, output_pipe_name);
DCHECK(!current_command_line->HasSwitch(kElevatingSwitchName));
for (base::CommandLine::SwitchMap::const_iterator i = switches.begin();
i != switches.end(); ++i) {
command_line.AppendSwitchNative(i->first, i->second);
}
for (base::CommandLine::StringVector::const_iterator i = args.begin();
i != args.end(); ++i) {
command_line.AppendArgNative(*i);
}
// Get the name of the binary to launch.
base::FilePath binary = current_command_line->GetProgram();
base::CommandLine::StringType parameters =
command_line.GetCommandLineString();
// Launch the child process requesting elevation.
SHELLEXECUTEINFO info;
memset(&info, 0, sizeof(info));
info.cbSize = sizeof(info);
info.hwnd = reinterpret_cast<HWND>(parent_window_handle_);
info.lpVerb = L"runas";
info.lpFile = binary.value().c_str();
info.lpParameters = parameters.c_str();
info.nShow = SW_HIDE;
if (!ShellExecuteEx(&info)) {
DWORD error = ::GetLastError();
PLOG(ERROR) << "Unable to launch '" << binary.value() << "'";
if (error != ERROR_CANCELLED) {
OnError();
}
return;
}
if (!::ConnectNamedPipe(delegate_write_handle.Get(), nullptr)) {
DWORD error = ::GetLastError();
if (error != ERROR_PIPE_CONNECTED) {
PLOG(ERROR) << "Unable to connect '" << input_pipe_name << "'";
OnError();
return;
}
}
if (!::ConnectNamedPipe(delegate_read_handle.Get(), nullptr)) {
DWORD error = ::GetLastError();
if (error != ERROR_PIPE_CONNECTED) {
PLOG(ERROR) << "Unable to connect '" << output_pipe_name << "'";
OnError();
return;
}
}
// Set up the native messaging channel to talk to the elevated host.
// Note that input for the elevated channel is output for the elevated host.
elevated_channel_.reset(
new PipeMessagingChannel(base::File(delegate_read_handle.Take()),
base::File(delegate_write_handle.Take())));
elevated_channel_event_handler_.reset(
new Me2MeNativeMessagingHost::ElevatedChannelEventHandler(this));
elevated_channel_->Start(elevated_channel_event_handler_.get());
elevated_host_timer_.Start(
FROM_HERE, base::TimeDelta::FromSeconds(kElevatedHostTimeoutSeconds),
this, &Me2MeNativeMessagingHost::DisconnectElevatedHost);
}
void Me2MeNativeMessagingHost::DisconnectElevatedHost() {
DCHECK(thread_checker_.CalledOnValidThread());
// This will send an EOF to the elevated host, triggering its shutdown.
elevated_channel_.reset();
}
#else // defined(OS_WIN)
bool Me2MeNativeMessagingHost::DelegateToElevatedHost(
std::unique_ptr<base::DictionaryValue> message) {
NOTREACHED();
return false;
}
#endif // !defined(OS_WIN)
} // namespace remoting