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