diff options
author | weitaosu@chromium.org <weitaosu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-02-02 20:20:48 +0000 |
---|---|---|
committer | weitaosu@chromium.org <weitaosu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-02-02 20:20:48 +0000 |
commit | 2a0cfb54713b545f528b7dc128f9f14b60917ddd (patch) | |
tree | 7fb039139ca54aff094c79beb840762c13d59856 /remoting/host/setup | |
parent | 2bdedb610afd975b4b1cf1f2a15c07a9e26f18cf (diff) | |
download | chromium_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')
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))); |