Home | History | Annotate | Download | only in setup
      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