summaryrefslogtreecommitdiffstats
path: root/remoting/host/setup
diff options
context:
space:
mode:
authorweitaosu@chromium.org <weitaosu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-02-02 20:20:48 +0000
committerweitaosu@chromium.org <weitaosu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-02-02 20:20:48 +0000
commit2a0cfb54713b545f528b7dc128f9f14b60917ddd (patch)
tree7fb039139ca54aff094c79beb840762c13d59856 /remoting/host/setup
parent2bdedb610afd975b4b1cf1f2a15c07a9e26f18cf (diff)
downloadchromium_src-2a0cfb54713b545f528b7dc128f9f14b60917ddd.zip
chromium_src-2a0cfb54713b545f528b7dc128f9f14b60917ddd.tar.gz
chromium_src-2a0cfb54713b545f528b7dc128f9f14b60917ddd.tar.bz2
Enable pairing registry on Windows by delegating all pairing registry related tasks to an elevated native messaging host.
The non-elevated host creates an elevated host process and communicates to it using named pipes. The elevated host runs just like a normal native messaging host except that the named pipes are used in place of stdin/stdout. BUG=325567 Review URL: https://codereview.chromium.org/132093004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@248428 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/host/setup')
-rw-r--r--remoting/host/setup/me2me_native_messaging_host.cc305
-rw-r--r--remoting/host/setup/me2me_native_messaging_host.h54
-rw-r--r--remoting/host/setup/me2me_native_messaging_host_main.cc104
-rw-r--r--remoting/host/setup/me2me_native_messaging_host_unittest.cc10
4 files changed, 392 insertions, 81 deletions
diff --git a/remoting/host/setup/me2me_native_messaging_host.cc b/remoting/host/setup/me2me_native_messaging_host.cc
index fabc8b0..ce8686f 100644
--- a/remoting/host/setup/me2me_native_messaging_host.cc
+++ b/remoting/host/setup/me2me_native_messaging_host.cc
@@ -3,27 +3,43 @@
// found in the LICENSE file.
#include "remoting/host/setup/me2me_native_messaging_host.h"
-
#include <string>
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
+#include "base/command_line.h"
#include "base/logging.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 "google_apis/gaia/gaia_oauth_client.h"
#include "google_apis/google_api_keys.h"
+#include "ipc/ipc_channel.h"
#include "net/base/net_util.h"
#include "remoting/base/rsa_key_pair.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.";
+#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";
@@ -37,10 +53,10 @@ const char* kSupportedFeatures[] = {
// Helper to extract the "config" part of a message as a DictionaryValue.
// Returns NULL on failure, and logs an error message.
scoped_ptr<base::DictionaryValue> ConfigDictionaryFromMessage(
- const base::DictionaryValue& message) {
+ scoped_ptr<base::DictionaryValue> message) {
scoped_ptr<base::DictionaryValue> result;
const base::DictionaryValue* config_dict;
- if (message.GetDictionary("config", &config_dict)) {
+ if (message->GetDictionary("config", &config_dict)) {
result.reset(config_dict->DeepCopy());
} else {
LOG(ERROR) << "'config' dictionary not found";
@@ -53,11 +69,13 @@ scoped_ptr<base::DictionaryValue> ConfigDictionaryFromMessage(
namespace remoting {
Me2MeNativeMessagingHost::Me2MeNativeMessagingHost(
+ bool needs_elevation,
scoped_ptr<NativeMessagingChannel> channel,
scoped_refptr<DaemonController> daemon_controller,
scoped_refptr<protocol::PairingRegistry> pairing_registry,
scoped_ptr<OAuthClient> oauth_client)
- : channel_(channel.Pass()),
+ : needs_elevation_(needs_elevation),
+ channel_(channel.Pass()),
daemon_controller_(daemon_controller),
pairing_registry_(pairing_registry),
oauth_client_(oauth_client.Pass()),
@@ -73,12 +91,14 @@ void Me2MeNativeMessagingHost::Start(
const base::Closure& quit_closure) {
DCHECK(thread_checker_.CalledOnValidThread());
+ quit_closure_ = quit_closure;
+
channel_->Start(
- base::Bind(&Me2MeNativeMessagingHost::ProcessMessage, weak_ptr_),
- quit_closure);
+ base::Bind(&Me2MeNativeMessagingHost::ProcessRequest, weak_ptr_),
+ base::Bind(&Me2MeNativeMessagingHost::Stop, weak_ptr_));
}
-void Me2MeNativeMessagingHost::ProcessMessage(
+void Me2MeNativeMessagingHost::ProcessRequest(
scoped_ptr<base::DictionaryValue> message) {
DCHECK(thread_checker_.CalledOnValidThread());
@@ -100,35 +120,35 @@ void Me2MeNativeMessagingHost::ProcessMessage(
response->SetString("type", type + "Response");
if (type == "hello") {
- ProcessHello(*message, response.Pass());
+ ProcessHello(message.Pass(), response.Pass());
} else if (type == "clearPairedClients") {
- ProcessClearPairedClients(*message, response.Pass());
+ ProcessClearPairedClients(message.Pass(), response.Pass());
} else if (type == "deletePairedClient") {
- ProcessDeletePairedClient(*message, response.Pass());
+ ProcessDeletePairedClient(message.Pass(), response.Pass());
} else if (type == "getHostName") {
- ProcessGetHostName(*message, response.Pass());
+ ProcessGetHostName(message.Pass(), response.Pass());
} else if (type == "getPinHash") {
- ProcessGetPinHash(*message, response.Pass());
+ ProcessGetPinHash(message.Pass(), response.Pass());
} else if (type == "generateKeyPair") {
- ProcessGenerateKeyPair(*message, response.Pass());
+ ProcessGenerateKeyPair(message.Pass(), response.Pass());
} else if (type == "updateDaemonConfig") {
- ProcessUpdateDaemonConfig(*message, response.Pass());
+ ProcessUpdateDaemonConfig(message.Pass(), response.Pass());
} else if (type == "getDaemonConfig") {
- ProcessGetDaemonConfig(*message, response.Pass());
+ ProcessGetDaemonConfig(message.Pass(), response.Pass());
} else if (type == "getPairedClients") {
- ProcessGetPairedClients(*message, response.Pass());
+ ProcessGetPairedClients(message.Pass(), response.Pass());
} else if (type == "getUsageStatsConsent") {
- ProcessGetUsageStatsConsent(*message, response.Pass());
+ ProcessGetUsageStatsConsent(message.Pass(), response.Pass());
} else if (type == "startDaemon") {
- ProcessStartDaemon(*message, response.Pass());
+ ProcessStartDaemon(message.Pass(), response.Pass());
} else if (type == "stopDaemon") {
- ProcessStopDaemon(*message, response.Pass());
+ ProcessStopDaemon(message.Pass(), response.Pass());
} else if (type == "getDaemonState") {
- ProcessGetDaemonState(*message, response.Pass());
+ ProcessGetDaemonState(message.Pass(), response.Pass());
} else if (type == "getHostClientId") {
- ProcessGetHostClientId(*message, response.Pass());
+ ProcessGetHostClientId(message.Pass(), response.Pass());
} else if (type == "getCredentialsFromAuthCode") {
- ProcessGetCredentialsFromAuthCode(*message, response.Pass());
+ ProcessGetCredentialsFromAuthCode(message.Pass(), response.Pass());
} else {
LOG(ERROR) << "Unsupported request type: " << type;
OnError();
@@ -136,7 +156,7 @@ void Me2MeNativeMessagingHost::ProcessMessage(
}
void Me2MeNativeMessagingHost::ProcessHello(
- const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> message,
scoped_ptr<base::DictionaryValue> response) {
DCHECK(thread_checker_.CalledOnValidThread());
@@ -149,10 +169,15 @@ void Me2MeNativeMessagingHost::ProcessHello(
}
void Me2MeNativeMessagingHost::ProcessClearPairedClients(
- const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> message,
scoped_ptr<base::DictionaryValue> response) {
DCHECK(thread_checker_.CalledOnValidThread());
+ if (needs_elevation_) {
+ DelegateToElevatedHost(message.Pass(), response.Pass());
+ return;
+ }
+
if (pairing_registry_) {
pairing_registry_->ClearAllPairings(
base::Bind(&Me2MeNativeMessagingHost::SendBooleanResult, weak_ptr_,
@@ -163,12 +188,18 @@ void Me2MeNativeMessagingHost::ProcessClearPairedClients(
}
void Me2MeNativeMessagingHost::ProcessDeletePairedClient(
- const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> message,
scoped_ptr<base::DictionaryValue> response) {
DCHECK(thread_checker_.CalledOnValidThread());
+ if (needs_elevation_) {
+ DelegateToElevatedHost(message.Pass(), response.Pass());
+ return;
+ }
+
std::string client_id;
- if (!message.GetString(protocol::PairingRegistry::kClientIdKey, &client_id)) {
+ if (!message->GetString(protocol::PairingRegistry::kClientIdKey,
+ &client_id)) {
LOG(ERROR) << "'" << protocol::PairingRegistry::kClientIdKey
<< "' string not found.";
OnError();
@@ -185,7 +216,7 @@ void Me2MeNativeMessagingHost::ProcessDeletePairedClient(
}
void Me2MeNativeMessagingHost::ProcessGetHostName(
- const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> message,
scoped_ptr<base::DictionaryValue> response) {
DCHECK(thread_checker_.CalledOnValidThread());
@@ -194,18 +225,18 @@ void Me2MeNativeMessagingHost::ProcessGetHostName(
}
void Me2MeNativeMessagingHost::ProcessGetPinHash(
- const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> message,
scoped_ptr<base::DictionaryValue> response) {
DCHECK(thread_checker_.CalledOnValidThread());
std::string host_id;
- if (!message.GetString("hostId", &host_id)) {
+ if (!message->GetString("hostId", &host_id)) {
LOG(ERROR) << "'hostId' not found: " << message;
OnError();
return;
}
std::string pin;
- if (!message.GetString("pin", &pin)) {
+ if (!message->GetString("pin", &pin)) {
LOG(ERROR) << "'pin' not found: " << message;
OnError();
return;
@@ -215,7 +246,7 @@ void Me2MeNativeMessagingHost::ProcessGetPinHash(
}
void Me2MeNativeMessagingHost::ProcessGenerateKeyPair(
- const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> message,
scoped_ptr<base::DictionaryValue> response) {
DCHECK(thread_checker_.CalledOnValidThread());
@@ -226,12 +257,12 @@ void Me2MeNativeMessagingHost::ProcessGenerateKeyPair(
}
void Me2MeNativeMessagingHost::ProcessUpdateDaemonConfig(
- const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> message,
scoped_ptr<base::DictionaryValue> response) {
DCHECK(thread_checker_.CalledOnValidThread());
scoped_ptr<base::DictionaryValue> config_dict =
- ConfigDictionaryFromMessage(message);
+ ConfigDictionaryFromMessage(message.Pass());
if (!config_dict) {
OnError();
return;
@@ -244,7 +275,7 @@ void Me2MeNativeMessagingHost::ProcessUpdateDaemonConfig(
}
void Me2MeNativeMessagingHost::ProcessGetDaemonConfig(
- const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> message,
scoped_ptr<base::DictionaryValue> response) {
DCHECK(thread_checker_.CalledOnValidThread());
@@ -254,10 +285,15 @@ void Me2MeNativeMessagingHost::ProcessGetDaemonConfig(
}
void Me2MeNativeMessagingHost::ProcessGetPairedClients(
- const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> message,
scoped_ptr<base::DictionaryValue> response) {
DCHECK(thread_checker_.CalledOnValidThread());
+ if (needs_elevation_) {
+ DelegateToElevatedHost(message.Pass(), response.Pass());
+ return;
+ }
+
if (pairing_registry_) {
pairing_registry_->GetAllPairings(
base::Bind(&Me2MeNativeMessagingHost::SendPairedClientsResponse,
@@ -269,7 +305,7 @@ void Me2MeNativeMessagingHost::ProcessGetPairedClients(
}
void Me2MeNativeMessagingHost::ProcessGetUsageStatsConsent(
- const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> message,
scoped_ptr<base::DictionaryValue> response) {
DCHECK(thread_checker_.CalledOnValidThread());
@@ -279,19 +315,19 @@ void Me2MeNativeMessagingHost::ProcessGetUsageStatsConsent(
}
void Me2MeNativeMessagingHost::ProcessStartDaemon(
- const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> message,
scoped_ptr<base::DictionaryValue> response) {
DCHECK(thread_checker_.CalledOnValidThread());
bool consent;
- if (!message.GetBoolean("consent", &consent)) {
+ if (!message->GetBoolean("consent", &consent)) {
LOG(ERROR) << "'consent' not found.";
OnError();
return;
}
scoped_ptr<base::DictionaryValue> config_dict =
- ConfigDictionaryFromMessage(message);
+ ConfigDictionaryFromMessage(message.Pass());
if (!config_dict) {
OnError();
return;
@@ -304,7 +340,7 @@ void Me2MeNativeMessagingHost::ProcessStartDaemon(
}
void Me2MeNativeMessagingHost::ProcessStopDaemon(
- const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> message,
scoped_ptr<base::DictionaryValue> response) {
DCHECK(thread_checker_.CalledOnValidThread());
@@ -314,7 +350,7 @@ void Me2MeNativeMessagingHost::ProcessStopDaemon(
}
void Me2MeNativeMessagingHost::ProcessGetDaemonState(
- const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> message,
scoped_ptr<base::DictionaryValue> response) {
DCHECK(thread_checker_.CalledOnValidThread());
@@ -349,7 +385,7 @@ void Me2MeNativeMessagingHost::ProcessGetDaemonState(
}
void Me2MeNativeMessagingHost::ProcessGetHostClientId(
- const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> message,
scoped_ptr<base::DictionaryValue> response) {
DCHECK(thread_checker_.CalledOnValidThread());
@@ -359,12 +395,12 @@ void Me2MeNativeMessagingHost::ProcessGetHostClientId(
}
void Me2MeNativeMessagingHost::ProcessGetCredentialsFromAuthCode(
- const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> message,
scoped_ptr<base::DictionaryValue> response) {
DCHECK(thread_checker_.CalledOnValidThread());
std::string auth_code;
- if (!message.GetString("authorizationCode", &auth_code)) {
+ if (!message->GetString("authorizationCode", &auth_code)) {
LOG(ERROR) << "'authorizationCode' string not found.";
OnError();
return;
@@ -461,4 +497,185 @@ void Me2MeNativeMessagingHost::OnError() {
channel_->SendMessage(scoped_ptr<base::DictionaryValue>());
}
+void Me2MeNativeMessagingHost::Stop() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (!quit_closure_.is_null())
+ base::ResetAndReturn(&quit_closure_).Run();
+}
+
+#if defined(OS_WIN)
+
+void Me2MeNativeMessagingHost::DelegateToElevatedHost(
+ scoped_ptr<base::DictionaryValue> message,
+ scoped_ptr<base::DictionaryValue> response) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ EnsureElevatedHostCreated();
+
+ DCHECK(elevated_channel_);
+ elevated_channel_->SendMessage(message.Pass());
+}
+
+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
+ // denies access by anyone else.
+ std::string security_descriptor = base::StringPrintf(
+ "O:%1$sG:%1$sD:(A;;GA;;;%1$s)", WideToASCII(user_sid).c_str());
+
+ ScopedSd sd = ConvertSddlToSd(security_descriptor);
+ if (!sd) {
+ LOG_GETLASTERROR(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());
+
+ delegate_write_handle_.Set(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()) {
+ LOG_GETLASTERROR(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());
+
+ delegate_read_handle_.Set(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()) {
+ LOG_GETLASTERROR(ERROR) <<
+ "Failed to create named pipe '" << output_pipe_name << "'";
+ OnError();
+ return;
+ }
+
+ const CommandLine* current_command_line = CommandLine::ForCurrentProcess();
+ const CommandLine::SwitchMap& switches = current_command_line->GetSwitches();
+ CommandLine::StringVector args = current_command_line->GetArgs();
+
+ // Create the child process command line by copying switches from the current
+ // command line.
+ CommandLine command_line(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 (CommandLine::SwitchMap::const_iterator i = switches.begin();
+ i != switches.end(); ++i) {
+ command_line.AppendSwitchNative(i->first, i->second);
+ }
+ for (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();
+ CommandLine::StringType parameters = command_line.GetCommandLineString();
+
+ // Launch the child process requesting elevation.
+ SHELLEXECUTEINFO info;
+ memset(&info, 0, sizeof(info));
+ info.cbSize = sizeof(info);
+ info.lpVerb = L"runas";
+ info.lpFile = binary.value().c_str();
+ info.lpParameters = parameters.c_str();
+ info.nShow = SW_SHOWNORMAL;
+
+ if (!ShellExecuteEx(&info)) {
+ LOG_GETLASTERROR(ERROR) << "Unable to launch '" << binary.value() << "'";
+ OnError();
+ return;
+ }
+
+ if (!ConnectNamedPipe(delegate_write_handle_.Get(), NULL)) {
+ DWORD error = ::GetLastError();
+ if (error != ERROR_PIPE_CONNECTED) {
+ LOG(ERROR) << "Unable to connect '" << input_pipe_name << "': " << error;
+ OnError();
+ return;
+ }
+ }
+
+ if (!ConnectNamedPipe(delegate_read_handle_.Get(), NULL)) {
+ DWORD error = ::GetLastError();
+ if (error != ERROR_PIPE_CONNECTED) {
+ LOG(ERROR) << "Unable to connect '" << output_pipe_name << "': " << error;
+ OnError();
+ return;
+ }
+ }
+
+ // Set up the native messaging channel to talk to the elevated host.
+ // Note that input for the elevate channel is output forthe elevated host.
+ elevated_channel_.reset(new NativeMessagingChannel(
+ delegate_read_handle_.Get(), delegate_write_handle_.Get()));
+
+ elevated_channel_->Start(
+ base::Bind(&Me2MeNativeMessagingHost::ProcessDelegateResponse, weak_ptr_),
+ base::Bind(&Me2MeNativeMessagingHost::Stop, weak_ptr_));
+}
+
+void Me2MeNativeMessagingHost::ProcessDelegateResponse(
+ scoped_ptr<base::DictionaryValue> message) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // Simply pass along the response from the elevated host to the client.
+ channel_->SendMessage(message.Pass());
+}
+
+#else // defined(OS_WIN)
+
+void Me2MeNativeMessagingHost::DelegateToElevatedHost(
+ scoped_ptr<base::DictionaryValue> message,
+ scoped_ptr<base::DictionaryValue> response) {
+ NOTREACHED();
+}
+
+#endif // !defined(OS_WIN)
+
} // namespace remoting
diff --git a/remoting/host/setup/me2me_native_messaging_host.h b/remoting/host/setup/me2me_native_messaging_host.h
index d17f48b..c21b50a 100644
--- a/remoting/host/setup/me2me_native_messaging_host.h
+++ b/remoting/host/setup/me2me_native_messaging_host.h
@@ -24,6 +24,10 @@ class GaiaOAuthClient;
namespace remoting {
+const char kElevatingSwitchName[] = "elevate";
+const char kInputSwitchName[] = "input";
+const char kOutputSwitchName[] = "output";
+
namespace protocol {
class PairingRegistry;
} // namespace protocol
@@ -34,6 +38,7 @@ class Me2MeNativeMessagingHost {
typedef NativeMessagingChannel::SendMessageCallback SendMessageCallback;
Me2MeNativeMessagingHost(
+ bool needs_elevation,
scoped_ptr<NativeMessagingChannel> channel,
scoped_refptr<DaemonController> daemon_controller,
scoped_refptr<protocol::PairingRegistry> pairing_registry,
@@ -43,55 +48,55 @@ class Me2MeNativeMessagingHost {
void Start(const base::Closure& quit_closure);
private:
- void ProcessMessage(scoped_ptr<base::DictionaryValue> message);
+ void ProcessRequest(scoped_ptr<base::DictionaryValue> message);
// These "Process.." methods handle specific request types. The |response|
// dictionary is pre-filled by ProcessMessage() with the parts of the
// response already known ("id" and "type" fields).
void ProcessHello(
- const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> message,
scoped_ptr<base::DictionaryValue> response);
void ProcessClearPairedClients(
- const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> message,
scoped_ptr<base::DictionaryValue> response);
void ProcessDeletePairedClient(
- const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> message,
scoped_ptr<base::DictionaryValue> response);
void ProcessGetHostName(
- const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> message,
scoped_ptr<base::DictionaryValue> response);
void ProcessGetPinHash(
- const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> message,
scoped_ptr<base::DictionaryValue> response);
void ProcessGenerateKeyPair(
- const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> message,
scoped_ptr<base::DictionaryValue> response);
void ProcessUpdateDaemonConfig(
- const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> message,
scoped_ptr<base::DictionaryValue> response);
void ProcessGetDaemonConfig(
- const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> message,
scoped_ptr<base::DictionaryValue> response);
void ProcessGetPairedClients(
- const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> message,
scoped_ptr<base::DictionaryValue> response);
void ProcessGetUsageStatsConsent(
- const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> message,
scoped_ptr<base::DictionaryValue> response);
void ProcessStartDaemon(
- const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> message,
scoped_ptr<base::DictionaryValue> response);
void ProcessStopDaemon(
- const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> message,
scoped_ptr<base::DictionaryValue> response);
void ProcessGetDaemonState(
- const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> message,
scoped_ptr<base::DictionaryValue> response);
void ProcessGetHostClientId(
- const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> message,
scoped_ptr<base::DictionaryValue> response);
void ProcessGetCredentialsFromAuthCode(
- const base::DictionaryValue& message,
+ scoped_ptr<base::DictionaryValue> message,
scoped_ptr<base::DictionaryValue> response);
// These Send... methods get called on the DaemonController's internal thread,
@@ -115,6 +120,23 @@ class Me2MeNativeMessagingHost {
void OnError();
+ void Stop();
+
+ void DelegateToElevatedHost(scoped_ptr<base::DictionaryValue> message,
+ scoped_ptr<base::DictionaryValue> response);
+
+#if defined(OS_WIN)
+ void Me2MeNativeMessagingHost::EnsureElevatedHostCreated();
+ void ProcessDelegateResponse(scoped_ptr<base::DictionaryValue> message);
+
+ // input/output handles for the channel to the elevated host.
+ base::win::ScopedHandle delegate_write_handle_;
+ base::win::ScopedHandle delegate_read_handle_;
+ scoped_ptr<NativeMessagingChannel> elevated_channel_;
+#endif // defined(OS_WIN)
+
+ bool needs_elevation_;
+ base::Closure quit_closure_;
scoped_ptr<NativeMessagingChannel> channel_;
scoped_refptr<DaemonController> daemon_controller_;
diff --git a/remoting/host/setup/me2me_native_messaging_host_main.cc b/remoting/host/setup/me2me_native_messaging_host_main.cc
index 7b48a74..638b15b 100644
--- a/remoting/host/setup/me2me_native_messaging_host_main.cc
+++ b/remoting/host/setup/me2me_native_messaging_host_main.cc
@@ -13,6 +13,10 @@
#include "remoting/host/pairing_registry_delegate.h"
#include "remoting/host/setup/me2me_native_messaging_host.h"
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#endif // defined(OS_WIN)
+
namespace {
const char kParentWindowSwitchName[] = "parent-window";
@@ -21,21 +25,29 @@ const char kParentWindowSwitchName[] = "parent-window";
namespace remoting {
-int Me2MeNativeMessagingHostMain() {
#if defined(OS_WIN)
- // GetStdHandle() returns pseudo-handles for stdin and stdout even if
- // the hosting executable specifies "Windows" subsystem. However the returned
- // handles are invalid in that case unless standard input and output are
- // redirected to a pipe or file.
- base::PlatformFile read_file = GetStdHandle(STD_INPUT_HANDLE);
- base::PlatformFile write_file = GetStdHandle(STD_OUTPUT_HANDLE);
-#elif defined(OS_POSIX)
- base::PlatformFile read_file = STDIN_FILENO;
- base::PlatformFile write_file = STDOUT_FILENO;
-#else
-#error Not implemented.
-#endif
+bool IsProcessElevated() {
+ // Conceptually, all processes running on a pre-VISTA version of Windows can
+ // be considered "elevated".
+ if (base::win::GetVersion() < base::win::VERSION_VISTA)
+ return true;
+
+ HANDLE process_token;
+ OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &process_token);
+
+ base::win::ScopedHandle scoped_process_token(process_token);
+
+ // Unlike TOKEN_ELEVATION_TYPE which returns TokenElevationTypeDefault when
+ // UAC is turned off, TOKEN_ELEVATION will tell you the process is elevated.
+ DWORD size;
+ TOKEN_ELEVATION elevation;
+ GetTokenInformation(process_token, TokenElevation,
+ &elevation, sizeof(elevation), &size);
+ return elevation.TokenIsElevated != 0;
+}
+#endif // defined(OS_WIN)
+int Me2MeNativeMessagingHostMain() {
// Mac OS X requires that the main thread be a UI message loop in order to
// receive distributed notifications from the System Preferences pane. An
// IO thread is needed for the pairing registry and URL context getter.
@@ -64,6 +76,62 @@ int Me2MeNativeMessagingHostMain() {
}
}
+ base::PlatformFile read_file;
+ base::PlatformFile write_file;
+ bool needs_elevation = false;
+
+#if defined(OS_WIN)
+ needs_elevation = !IsProcessElevated();
+
+ if (command_line->HasSwitch(kElevatingSwitchName)) {
+ DCHECK(!needs_elevation);
+
+ // The "elevate" switch is always accompanied by the "input" and "output"
+ // switches whose values name named pipes that should be used in place of
+ // stdin and stdout.
+ DCHECK(command_line->HasSwitch(kInputSwitchName));
+ DCHECK(command_line->HasSwitch(kOutputSwitchName));
+
+ // presubmit: allow wstring
+ std::wstring input_pipe_name =
+ command_line->GetSwitchValueNative(kInputSwitchName);
+ // presubmit: allow wstring
+ std::wstring output_pipe_name =
+ command_line->GetSwitchValueNative(kOutputSwitchName);
+
+ // A NULL SECURITY_ATTRIBUTES signifies that the handle can't be inherited
+ read_file = CreateFile(
+ input_pipe_name.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+ if (read_file == INVALID_HANDLE_VALUE) {
+ LOG_GETLASTERROR(ERROR) <<
+ "CreateFile failed on '" << input_pipe_name << "'";
+ return kInitializationFailed;
+ }
+
+ write_file = CreateFile(
+ output_pipe_name.c_str(), GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+ if (write_file == INVALID_HANDLE_VALUE) {
+ LOG_GETLASTERROR(ERROR) <<
+ "CreateFile failed on '" << output_pipe_name << "'";
+ return kInitializationFailed;
+ }
+ } else {
+ // GetStdHandle() returns pseudo-handles for stdin and stdout even if
+ // the hosting executable specifies "Windows" subsystem. However the
+ // returned handles are invalid in that case unless standard input and
+ // output are redirected to a pipe or file.
+ read_file = GetStdHandle(STD_INPUT_HANDLE);
+ write_file = GetStdHandle(STD_OUTPUT_HANDLE);
+ }
+#elif defined(OS_POSIX)
+ read_file = STDIN_FILENO;
+ write_file = STDOUT_FILENO;
+#else
+#error Not implemented.
+#endif
+
// OAuth client (for credential requests).
scoped_refptr<net::URLRequestContextGetter> url_request_context_getter(
new URLRequestContextGetter(io_thread.message_loop_proxy()));
@@ -81,10 +149,12 @@ int Me2MeNativeMessagingHostMain() {
new NativeMessagingChannel(read_file, write_file));
scoped_ptr<Me2MeNativeMessagingHost> host(
- new Me2MeNativeMessagingHost(channel.Pass(),
- daemon_controller,
- pairing_registry,
- oauth_client.Pass()));
+ new Me2MeNativeMessagingHost(
+ needs_elevation,
+ channel.Pass(),
+ daemon_controller,
+ pairing_registry,
+ oauth_client.Pass()));
host->Start(run_loop.QuitClosure());
// Run the loop until channel is alive.
diff --git a/remoting/host/setup/me2me_native_messaging_host_unittest.cc b/remoting/host/setup/me2me_native_messaging_host_unittest.cc
index 094db0c..a93ac65 100644
--- a/remoting/host/setup/me2me_native_messaging_host_unittest.cc
+++ b/remoting/host/setup/me2me_native_messaging_host_unittest.cc
@@ -319,10 +319,12 @@ void Me2MeNativeMessagingHostTest::StartHost() {
scoped_ptr<NativeMessagingChannel> channel(
new NativeMessagingChannel(input_read_handle, output_write_handle));
- host_.reset(new Me2MeNativeMessagingHost(channel.Pass(),
- daemon_controller,
- pairing_registry,
- scoped_ptr<remoting::OAuthClient>()));
+ host_.reset(new Me2MeNativeMessagingHost(
+ false,
+ channel.Pass(),
+ daemon_controller,
+ pairing_registry,
+ scoped_ptr<remoting::OAuthClient>()));
host_->Start(base::Bind(&Me2MeNativeMessagingHostTest::StopHost,
base::Unretained(this)));