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