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