Home | History | Annotate | Download | only in host
      1 // Copyright (c) 2012 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/daemon_process.h"
      6 
      7 #include "base/base_switches.h"
      8 #include "base/bind.h"
      9 #include "base/bind_helpers.h"
     10 #include "base/location.h"
     11 #include "base/logging.h"
     12 #include "base/memory/ref_counted.h"
     13 #include "base/memory/scoped_ptr.h"
     14 #include "base/process/process.h"
     15 #include "base/single_thread_task_runner.h"
     16 #include "base/strings/utf_string_conversions.h"
     17 #include "base/time/time.h"
     18 #include "base/timer/timer.h"
     19 #include "base/win/registry.h"
     20 #include "base/win/scoped_handle.h"
     21 #include "ipc/ipc_message.h"
     22 #include "ipc/ipc_message_macros.h"
     23 #include "remoting/base/auto_thread_task_runner.h"
     24 #include "remoting/base/scoped_sc_handle_win.h"
     25 #include "remoting/host/branding.h"
     26 #include "remoting/host/chromoting_messages.h"
     27 #include "remoting/host/desktop_session_win.h"
     28 #include "remoting/host/host_exit_codes.h"
     29 #include "remoting/host/host_main.h"
     30 #include "remoting/host/ipc_constants.h"
     31 #include "remoting/host/pairing_registry_delegate_win.h"
     32 #include "remoting/host/screen_resolution.h"
     33 #include "remoting/host/win/launch_process_with_token.h"
     34 #include "remoting/host/win/unprivileged_process_delegate.h"
     35 #include "remoting/host/win/worker_process_launcher.h"
     36 
     37 using base::win::ScopedHandle;
     38 using base::TimeDelta;
     39 
     40 namespace {
     41 
     42 // Duplicates |key| into |target_process| and returns the value that can be sent
     43 // over IPC.
     44 IPC::PlatformFileForTransit GetRegistryKeyForTransit(
     45     base::ProcessHandle target_process,
     46     const base::win::RegKey& key) {
     47   base::PlatformFile handle =
     48       reinterpret_cast<base::PlatformFile>(key.Handle());
     49   return IPC::GetFileHandleForProcess(handle, target_process, false);
     50 }
     51 
     52 }  // namespace
     53 
     54 namespace remoting {
     55 
     56 class WtsTerminalMonitor;
     57 
     58 // The command line parameters that should be copied from the service's command
     59 // line to the host process.
     60 const char kEnableVp9SwitchName[] = "enable-vp9";
     61 const char* kCopiedSwitchNames[] =
     62     { switches::kV, switches::kVModule, kEnableVp9SwitchName };
     63 
     64 class DaemonProcessWin : public DaemonProcess {
     65  public:
     66   DaemonProcessWin(
     67       scoped_refptr<AutoThreadTaskRunner> caller_task_runner,
     68       scoped_refptr<AutoThreadTaskRunner> io_task_runner,
     69       const base::Closure& stopped_callback);
     70   virtual ~DaemonProcessWin();
     71 
     72   // WorkerProcessIpcDelegate implementation.
     73   virtual void OnChannelConnected(int32 peer_pid) OVERRIDE;
     74   virtual void OnPermanentError(int exit_code) OVERRIDE;
     75 
     76   // DaemonProcess overrides.
     77   virtual void SendToNetwork(IPC::Message* message) OVERRIDE;
     78   virtual bool OnDesktopSessionAgentAttached(
     79       int terminal_id,
     80       base::ProcessHandle desktop_process,
     81       IPC::PlatformFileForTransit desktop_pipe) OVERRIDE;
     82 
     83  protected:
     84   // DaemonProcess implementation.
     85   virtual scoped_ptr<DesktopSession> DoCreateDesktopSession(
     86       int terminal_id,
     87       const ScreenResolution& resolution,
     88       bool virtual_terminal) OVERRIDE;
     89   virtual void DoCrashNetworkProcess(
     90       const tracked_objects::Location& location) OVERRIDE;
     91   virtual void LaunchNetworkProcess() OVERRIDE;
     92 
     93   // Changes the service start type to 'manual'.
     94   void DisableAutoStart();
     95 
     96   // Initializes the pairing registry on the host side by sending
     97   // ChromotingDaemonNetworkMsg_InitializePairingRegistry message.
     98   bool InitializePairingRegistry();
     99 
    100   // Opens the pairing registry keys.
    101   bool OpenPairingRegistry();
    102 
    103  private:
    104   scoped_ptr<WorkerProcessLauncher> network_launcher_;
    105 
    106   // Handle of the network process.
    107   ScopedHandle network_process_;
    108 
    109   base::win::RegKey pairing_registry_privileged_key_;
    110   base::win::RegKey pairing_registry_unprivileged_key_;
    111 
    112   DISALLOW_COPY_AND_ASSIGN(DaemonProcessWin);
    113 };
    114 
    115 DaemonProcessWin::DaemonProcessWin(
    116     scoped_refptr<AutoThreadTaskRunner> caller_task_runner,
    117     scoped_refptr<AutoThreadTaskRunner> io_task_runner,
    118     const base::Closure& stopped_callback)
    119     : DaemonProcess(caller_task_runner, io_task_runner, stopped_callback) {
    120 }
    121 
    122 DaemonProcessWin::~DaemonProcessWin() {
    123 }
    124 
    125 void DaemonProcessWin::OnChannelConnected(int32 peer_pid) {
    126   // Obtain the handle of the network process.
    127   network_process_.Set(OpenProcess(PROCESS_DUP_HANDLE, false, peer_pid));
    128   if (!network_process_.IsValid()) {
    129     CrashNetworkProcess(FROM_HERE);
    130     return;
    131   }
    132 
    133   if (!InitializePairingRegistry()) {
    134     CrashNetworkProcess(FROM_HERE);
    135     return;
    136   }
    137 
    138   DaemonProcess::OnChannelConnected(peer_pid);
    139 }
    140 
    141 void DaemonProcessWin::OnPermanentError(int exit_code) {
    142   // Change the service start type to 'manual' if the host has been deleted
    143   // remotely. This way the host will not be started every time the machine
    144   // boots until the user re-enable it again.
    145   if (exit_code == kInvalidHostIdExitCode)
    146     DisableAutoStart();
    147 
    148   DaemonProcess::OnPermanentError(exit_code);
    149 }
    150 
    151 void DaemonProcessWin::SendToNetwork(IPC::Message* message) {
    152   if (network_launcher_) {
    153     network_launcher_->Send(message);
    154   } else {
    155     delete message;
    156   }
    157 }
    158 
    159 bool DaemonProcessWin::OnDesktopSessionAgentAttached(
    160     int terminal_id,
    161     base::ProcessHandle desktop_process,
    162     IPC::PlatformFileForTransit desktop_pipe) {
    163   // Prepare |desktop_process| handle for sending over to the network process.
    164   base::ProcessHandle desktop_process_for_transit;
    165   if (!DuplicateHandle(GetCurrentProcess(),
    166                        desktop_process,
    167                        network_process_,
    168                        &desktop_process_for_transit,
    169                        0,
    170                        FALSE,
    171                        DUPLICATE_SAME_ACCESS)) {
    172     PLOG(ERROR) << "Failed to duplicate the desktop process handle";
    173     return false;
    174   }
    175 
    176   // |desktop_pipe| is a handle in the desktop process. It will be duplicated
    177   // by the network process directly from the desktop process.
    178   SendToNetwork(new ChromotingDaemonNetworkMsg_DesktopAttached(
    179       terminal_id, desktop_process_for_transit, desktop_pipe));
    180   return true;
    181 }
    182 
    183 scoped_ptr<DesktopSession> DaemonProcessWin::DoCreateDesktopSession(
    184     int terminal_id,
    185     const ScreenResolution& resolution,
    186     bool virtual_terminal) {
    187   DCHECK(caller_task_runner()->BelongsToCurrentThread());
    188 
    189   if (virtual_terminal) {
    190     return DesktopSessionWin::CreateForVirtualTerminal(
    191         caller_task_runner(), io_task_runner(), this, terminal_id, resolution);
    192   } else {
    193     return DesktopSessionWin::CreateForConsole(
    194         caller_task_runner(), io_task_runner(), this, terminal_id, resolution);
    195   }
    196 }
    197 
    198 void DaemonProcessWin::DoCrashNetworkProcess(
    199     const tracked_objects::Location& location) {
    200   DCHECK(caller_task_runner()->BelongsToCurrentThread());
    201 
    202   network_launcher_->Crash(location);
    203 }
    204 
    205 void DaemonProcessWin::LaunchNetworkProcess() {
    206   DCHECK(caller_task_runner()->BelongsToCurrentThread());
    207   DCHECK(!network_launcher_);
    208 
    209   // Construct the host binary name.
    210   base::FilePath host_binary;
    211   if (!GetInstalledBinaryPath(kHostBinaryName, &host_binary)) {
    212     Stop();
    213     return;
    214   }
    215 
    216   scoped_ptr<CommandLine> target(new CommandLine(host_binary));
    217   target->AppendSwitchASCII(kProcessTypeSwitchName, kProcessTypeHost);
    218   target->CopySwitchesFrom(*CommandLine::ForCurrentProcess(),
    219                            kCopiedSwitchNames,
    220                            arraysize(kCopiedSwitchNames));
    221 
    222   scoped_ptr<UnprivilegedProcessDelegate> delegate(
    223       new UnprivilegedProcessDelegate(io_task_runner(), target.Pass()));
    224   network_launcher_.reset(new WorkerProcessLauncher(delegate.Pass(), this));
    225 }
    226 
    227 scoped_ptr<DaemonProcess> DaemonProcess::Create(
    228     scoped_refptr<AutoThreadTaskRunner> caller_task_runner,
    229     scoped_refptr<AutoThreadTaskRunner> io_task_runner,
    230     const base::Closure& stopped_callback) {
    231   scoped_ptr<DaemonProcessWin> daemon_process(
    232       new DaemonProcessWin(caller_task_runner, io_task_runner,
    233                            stopped_callback));
    234   daemon_process->Initialize();
    235   return daemon_process.PassAs<DaemonProcess>();
    236 }
    237 
    238 void DaemonProcessWin::DisableAutoStart() {
    239   ScopedScHandle scmanager(
    240       OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE,
    241                     SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE));
    242   if (!scmanager.IsValid()) {
    243     PLOG(INFO) << "Failed to connect to the service control manager";
    244     return;
    245   }
    246 
    247   DWORD desired_access = SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS;
    248   ScopedScHandle service(
    249       OpenService(scmanager, kWindowsServiceName, desired_access));
    250   if (!service.IsValid()) {
    251     PLOG(INFO) << "Failed to open to the '" << kWindowsServiceName
    252                << "' service";
    253     return;
    254   }
    255 
    256   // Change the service start type to 'manual'. All |NULL| parameters below mean
    257   // that there is no change to the corresponding service parameter.
    258   if (!ChangeServiceConfig(service,
    259                            SERVICE_NO_CHANGE,
    260                            SERVICE_DEMAND_START,
    261                            SERVICE_NO_CHANGE,
    262                            NULL,
    263                            NULL,
    264                            NULL,
    265                            NULL,
    266                            NULL,
    267                            NULL,
    268                            NULL)) {
    269     PLOG(INFO) << "Failed to change the '" << kWindowsServiceName
    270                << "'service start type to 'manual'";
    271   }
    272 }
    273 
    274 bool DaemonProcessWin::InitializePairingRegistry() {
    275   if (!pairing_registry_privileged_key_.Valid()) {
    276     if (!OpenPairingRegistry())
    277       return false;
    278   }
    279 
    280   // Duplicate handles to the network process.
    281   IPC::PlatformFileForTransit privileged_key = GetRegistryKeyForTransit(
    282       network_process_, pairing_registry_privileged_key_);
    283   IPC::PlatformFileForTransit unprivileged_key = GetRegistryKeyForTransit(
    284       network_process_, pairing_registry_unprivileged_key_);
    285   if (!(privileged_key && unprivileged_key))
    286     return false;
    287 
    288   // Initialize the pairing registry in the network process. This has to be done
    289   // before the host configuration is sent, otherwise the host will not use
    290   // the passed handles.
    291   SendToNetwork(new ChromotingDaemonNetworkMsg_InitializePairingRegistry(
    292       privileged_key, unprivileged_key));
    293   return true;
    294 }
    295 
    296 bool DaemonProcessWin::OpenPairingRegistry() {
    297   DCHECK(!pairing_registry_privileged_key_.Valid());
    298   DCHECK(!pairing_registry_unprivileged_key_.Valid());
    299 
    300   // Open the root of the pairing registry.
    301   base::win::RegKey root;
    302   LONG result = root.Open(HKEY_LOCAL_MACHINE, kPairingRegistryKeyName,
    303                           KEY_READ);
    304   if (result != ERROR_SUCCESS) {
    305     SetLastError(result);
    306     PLOG(ERROR) << "Failed to open HKLM\\" << kPairingRegistryKeyName;
    307     return false;
    308   }
    309 
    310   base::win::RegKey privileged;
    311   result = privileged.Open(root.Handle(), kPairingRegistryClientsKeyName,
    312                            KEY_READ | KEY_WRITE);
    313   if (result != ERROR_SUCCESS) {
    314     SetLastError(result);
    315     PLOG(ERROR) << "Failed to open HKLM\\" << kPairingRegistryKeyName << "\\"
    316                 << kPairingRegistryClientsKeyName;
    317     return false;
    318   }
    319 
    320   base::win::RegKey unprivileged;
    321   result = unprivileged.Open(root.Handle(), kPairingRegistrySecretsKeyName,
    322                              KEY_READ | KEY_WRITE);
    323   if (result != ERROR_SUCCESS) {
    324     SetLastError(result);
    325     PLOG(ERROR) << "Failed to open HKLM\\" << kPairingRegistrySecretsKeyName
    326                 << "\\" << kPairingRegistrySecretsKeyName;
    327     return false;
    328   }
    329 
    330   pairing_registry_privileged_key_.Set(privileged.Take());
    331   pairing_registry_unprivileged_key_.Set(unprivileged.Take());
    332   return true;
    333 }
    334 
    335 }  // namespace remoting
    336