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