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_main.h" 6 7#include "base/at_exit.h" 8#include "base/command_line.h" 9#include "base/files/file.h" 10#include "base/i18n/icu_util.h" 11#include "base/message_loop/message_loop.h" 12#include "base/run_loop.h" 13#include "base/strings/string_number_conversions.h" 14#include "base/threading/thread.h" 15#include "net/url_request/url_fetcher.h" 16#include "remoting/base/breakpad.h" 17#include "remoting/base/url_request_context_getter.h" 18#include "remoting/host/host_exit_codes.h" 19#include "remoting/host/logging.h" 20#include "remoting/host/native_messaging/pipe_messaging_channel.h" 21#include "remoting/host/pairing_registry_delegate.h" 22#include "remoting/host/setup/me2me_native_messaging_host.h" 23#include "remoting/host/usage_stats_consent.h" 24 25#if defined(OS_MACOSX) 26#include "base/mac/scoped_nsautorelease_pool.h" 27#endif // defined(OS_MACOSX) 28 29#if defined(OS_WIN) 30#include "base/win/registry.h" 31#include "base/win/windows_version.h" 32#include "remoting/host/pairing_registry_delegate_win.h" 33#endif // defined(OS_WIN) 34 35using remoting::protocol::PairingRegistry; 36 37namespace { 38 39const char kParentWindowSwitchName[] = "parent-window"; 40 41} // namespace 42 43namespace remoting { 44 45#if defined(OS_WIN) 46bool IsProcessElevated() { 47 // Conceptually, all processes running on a pre-VISTA version of Windows can 48 // be considered "elevated". 49 if (base::win::GetVersion() < base::win::VERSION_VISTA) 50 return true; 51 52 HANDLE process_token; 53 OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &process_token); 54 55 base::win::ScopedHandle scoped_process_token(process_token); 56 57 // Unlike TOKEN_ELEVATION_TYPE which returns TokenElevationTypeDefault when 58 // UAC is turned off, TOKEN_ELEVATION will tell you the process is elevated. 59 DWORD size; 60 TOKEN_ELEVATION elevation; 61 GetTokenInformation(process_token, TokenElevation, 62 &elevation, sizeof(elevation), &size); 63 return elevation.TokenIsElevated != 0; 64} 65#endif // defined(OS_WIN) 66 67int StartMe2MeNativeMessagingHost() { 68#if defined(OS_MACOSX) 69 // Needed so we don't leak objects when threads are created. 70 base::mac::ScopedNSAutoreleasePool pool; 71#endif // defined(OS_MACOSX) 72 73 // Required to find the ICU data file, used by some file_util routines. 74 base::i18n::InitializeICU(); 75 76#if defined(REMOTING_ENABLE_BREAKPAD) 77 // Initialize Breakpad as early as possible. On Mac the command-line needs to 78 // be initialized first, so that the preference for crash-reporting can be 79 // looked up in the config file. 80 if (IsUsageStatsAllowed()) { 81 InitializeCrashReporting(); 82 } 83#endif // defined(REMOTING_ENABLE_BREAKPAD) 84 85 // Mac OS X requires that the main thread be a UI message loop in order to 86 // receive distributed notifications from the System Preferences pane. An 87 // IO thread is needed for the pairing registry and URL context getter. 88 base::Thread io_thread("io_thread"); 89 io_thread.StartWithOptions( 90 base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); 91 92 base::Thread file_thread("file_thread"); 93 file_thread.StartWithOptions( 94 base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); 95 96 base::MessageLoopForUI message_loop; 97 base::RunLoop run_loop; 98 99 scoped_refptr<DaemonController> daemon_controller = 100 DaemonController::Create(); 101 102 // Pass handle of the native view to the controller so that the UAC prompts 103 // are focused properly. 104 const base::CommandLine* command_line = 105 base::CommandLine::ForCurrentProcess(); 106 int64 native_view_handle = 0; 107 if (command_line->HasSwitch(kParentWindowSwitchName)) { 108 std::string native_view = 109 command_line->GetSwitchValueASCII(kParentWindowSwitchName); 110 if (base::StringToInt64(native_view, &native_view_handle)) { 111 daemon_controller->SetWindow(reinterpret_cast<void*>(native_view_handle)); 112 } else { 113 LOG(WARNING) << "Invalid parameter value --" << kParentWindowSwitchName 114 << "=" << native_view; 115 } 116 } 117 118 base::File read_file; 119 base::File write_file; 120 bool needs_elevation = false; 121 122#if defined(OS_WIN) 123 needs_elevation = !IsProcessElevated(); 124 125 if (command_line->HasSwitch(kElevatingSwitchName)) { 126 DCHECK(!needs_elevation); 127 128 // The "elevate" switch is always accompanied by the "input" and "output" 129 // switches whose values name named pipes that should be used in place of 130 // stdin and stdout. 131 DCHECK(command_line->HasSwitch(kInputSwitchName)); 132 DCHECK(command_line->HasSwitch(kOutputSwitchName)); 133 134 // presubmit: allow wstring 135 std::wstring input_pipe_name = 136 command_line->GetSwitchValueNative(kInputSwitchName); 137 // presubmit: allow wstring 138 std::wstring output_pipe_name = 139 command_line->GetSwitchValueNative(kOutputSwitchName); 140 141 // A NULL SECURITY_ATTRIBUTES signifies that the handle can't be inherited 142 read_file = base::File(CreateFile( 143 input_pipe_name.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, 144 FILE_ATTRIBUTE_NORMAL, NULL)); 145 if (!read_file.IsValid()) { 146 PLOG(ERROR) << "CreateFile failed on '" << input_pipe_name << "'"; 147 return kInitializationFailed; 148 } 149 150 write_file = base::File(CreateFile( 151 output_pipe_name.c_str(), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 152 FILE_ATTRIBUTE_NORMAL, NULL)); 153 if (!write_file.IsValid()) { 154 PLOG(ERROR) << "CreateFile failed on '" << output_pipe_name << "'"; 155 return kInitializationFailed; 156 } 157 } else { 158 // GetStdHandle() returns pseudo-handles for stdin and stdout even if 159 // the hosting executable specifies "Windows" subsystem. However the 160 // returned handles are invalid in that case unless standard input and 161 // output are redirected to a pipe or file. 162 read_file = base::File(GetStdHandle(STD_INPUT_HANDLE)); 163 write_file = base::File(GetStdHandle(STD_OUTPUT_HANDLE)); 164 165 // After the native messaging channel starts the native messaging reader 166 // will keep doing blocking read operations on the input named pipe. 167 // If any other thread tries to perform any operation on STDIN, it will also 168 // block because the input named pipe is synchronous (non-overlapped). 169 // It is pretty common for a DLL to query the device info (GetFileType) of 170 // the STD* handles at startup. So any LoadLibrary request can potentially 171 // be blocked. To prevent that from happening we close STDIN and STDOUT 172 // handles as soon as we retrieve the corresponding file handles. 173 SetStdHandle(STD_INPUT_HANDLE, NULL); 174 SetStdHandle(STD_OUTPUT_HANDLE, NULL); 175 } 176#elif defined(OS_POSIX) 177 // The files will be automatically closed. 178 read_file = base::File(STDIN_FILENO); 179 write_file = base::File(STDOUT_FILENO); 180#else 181#error Not implemented. 182#endif 183 184 // OAuth client (for credential requests). IO thread is used for blocking 185 scoped_refptr<net::URLRequestContextGetter> url_request_context_getter( 186 new URLRequestContextGetter(io_thread.task_runner(), 187 file_thread.task_runner())); 188 scoped_ptr<OAuthClient> oauth_client( 189 new OAuthClient(url_request_context_getter)); 190 191 net::URLFetcher::SetIgnoreCertificateRequests(true); 192 193 // Create the pairing registry. 194 scoped_refptr<PairingRegistry> pairing_registry; 195 196#if defined(OS_WIN) 197 base::win::RegKey root; 198 LONG result = root.Open(HKEY_LOCAL_MACHINE, kPairingRegistryKeyName, 199 KEY_READ); 200 if (result != ERROR_SUCCESS) { 201 SetLastError(result); 202 PLOG(ERROR) << "Failed to open HKLM\\" << kPairingRegistryKeyName; 203 return kInitializationFailed; 204 } 205 206 base::win::RegKey unprivileged; 207 result = unprivileged.Open(root.Handle(), kPairingRegistrySecretsKeyName, 208 needs_elevation ? KEY_READ : KEY_READ | KEY_WRITE); 209 if (result != ERROR_SUCCESS) { 210 SetLastError(result); 211 PLOG(ERROR) << "Failed to open HKLM\\" << kPairingRegistrySecretsKeyName 212 << "\\" << kPairingRegistrySecretsKeyName; 213 return kInitializationFailed; 214 } 215 216 // Only try to open the privileged key if the current process is elevated. 217 base::win::RegKey privileged; 218 if (!needs_elevation) { 219 result = privileged.Open(root.Handle(), kPairingRegistryClientsKeyName, 220 KEY_READ | KEY_WRITE); 221 if (result != ERROR_SUCCESS) { 222 SetLastError(result); 223 PLOG(ERROR) << "Failed to open HKLM\\" << kPairingRegistryKeyName << "\\" 224 << kPairingRegistryClientsKeyName; 225 return kInitializationFailed; 226 } 227 } 228 229 // Initialize the pairing registry delegate and set the root keys. 230 scoped_ptr<PairingRegistryDelegateWin> delegate( 231 new PairingRegistryDelegateWin()); 232 if (!delegate->SetRootKeys(privileged.Take(), unprivileged.Take())) 233 return kInitializationFailed; 234 235 pairing_registry = new PairingRegistry( 236 io_thread.task_runner(), 237 delegate.PassAs<PairingRegistry::Delegate>()); 238#else // defined(OS_WIN) 239 pairing_registry = 240 CreatePairingRegistry(io_thread.task_runner()); 241#endif // !defined(OS_WIN) 242 243 // Set up the native messaging channel. 244 scoped_ptr<extensions::NativeMessagingChannel> channel( 245 new PipeMessagingChannel(read_file.Pass(), write_file.Pass())); 246 247 // Create the native messaging host. 248 scoped_ptr<Me2MeNativeMessagingHost> host( 249 new Me2MeNativeMessagingHost( 250 needs_elevation, 251 static_cast<intptr_t>(native_view_handle), 252 channel.Pass(), 253 daemon_controller, 254 pairing_registry, 255 oauth_client.Pass())); 256 host->Start(run_loop.QuitClosure()); 257 258 // Run the loop until channel is alive. 259 run_loop.Run(); 260 return kSuccessExitCode; 261} 262 263int Me2MeNativeMessagingHostMain(int argc, char** argv) { 264 // This object instance is required by Chrome code (such as MessageLoop). 265 base::AtExitManager exit_manager; 266 267 base::CommandLine::Init(argc, argv); 268 remoting::InitHostLogging(); 269 270 return StartMe2MeNativeMessagingHost(); 271} 272 273} // namespace remoting 274