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_.Get(), 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( 225 delegate.PassAs<WorkerProcessLauncher::Delegate>(), this)); 226 } 227 228 scoped_ptr<DaemonProcess> DaemonProcess::Create( 229 scoped_refptr<AutoThreadTaskRunner> caller_task_runner, 230 scoped_refptr<AutoThreadTaskRunner> io_task_runner, 231 const base::Closure& stopped_callback) { 232 scoped_ptr<DaemonProcessWin> daemon_process( 233 new DaemonProcessWin(caller_task_runner, io_task_runner, 234 stopped_callback)); 235 daemon_process->Initialize(); 236 return daemon_process.PassAs<DaemonProcess>(); 237 } 238 239 void DaemonProcessWin::DisableAutoStart() { 240 ScopedScHandle scmanager( 241 OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, 242 SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE)); 243 if (!scmanager.IsValid()) { 244 PLOG(INFO) << "Failed to connect to the service control manager"; 245 return; 246 } 247 248 DWORD desired_access = SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS; 249 ScopedScHandle service( 250 OpenService(scmanager.Get(), kWindowsServiceName, desired_access)); 251 if (!service.IsValid()) { 252 PLOG(INFO) << "Failed to open to the '" << kWindowsServiceName 253 << "' service"; 254 return; 255 } 256 257 // Change the service start type to 'manual'. All |NULL| parameters below mean 258 // that there is no change to the corresponding service parameter. 259 if (!ChangeServiceConfig(service.Get(), 260 SERVICE_NO_CHANGE, 261 SERVICE_DEMAND_START, 262 SERVICE_NO_CHANGE, 263 NULL, 264 NULL, 265 NULL, 266 NULL, 267 NULL, 268 NULL, 269 NULL)) { 270 PLOG(INFO) << "Failed to change the '" << kWindowsServiceName 271 << "'service start type to 'manual'"; 272 } 273 } 274 275 bool DaemonProcessWin::InitializePairingRegistry() { 276 if (!pairing_registry_privileged_key_.Valid()) { 277 if (!OpenPairingRegistry()) 278 return false; 279 } 280 281 // Duplicate handles to the network process. 282 IPC::PlatformFileForTransit privileged_key = GetRegistryKeyForTransit( 283 network_process_.Get(), pairing_registry_privileged_key_); 284 IPC::PlatformFileForTransit unprivileged_key = GetRegistryKeyForTransit( 285 network_process_.Get(), pairing_registry_unprivileged_key_); 286 if (!(privileged_key && unprivileged_key)) 287 return false; 288 289 // Initialize the pairing registry in the network process. This has to be done 290 // before the host configuration is sent, otherwise the host will not use 291 // the passed handles. 292 SendToNetwork(new ChromotingDaemonNetworkMsg_InitializePairingRegistry( 293 privileged_key, unprivileged_key)); 294 return true; 295 } 296 297 bool DaemonProcessWin::OpenPairingRegistry() { 298 DCHECK(!pairing_registry_privileged_key_.Valid()); 299 DCHECK(!pairing_registry_unprivileged_key_.Valid()); 300 301 // Open the root of the pairing registry. 302 base::win::RegKey root; 303 LONG result = root.Open(HKEY_LOCAL_MACHINE, kPairingRegistryKeyName, 304 KEY_READ); 305 if (result != ERROR_SUCCESS) { 306 SetLastError(result); 307 PLOG(ERROR) << "Failed to open HKLM\\" << kPairingRegistryKeyName; 308 return false; 309 } 310 311 base::win::RegKey privileged; 312 result = privileged.Open(root.Handle(), kPairingRegistryClientsKeyName, 313 KEY_READ | KEY_WRITE); 314 if (result != ERROR_SUCCESS) { 315 SetLastError(result); 316 PLOG(ERROR) << "Failed to open HKLM\\" << kPairingRegistryKeyName << "\\" 317 << kPairingRegistryClientsKeyName; 318 return false; 319 } 320 321 base::win::RegKey unprivileged; 322 result = unprivileged.Open(root.Handle(), kPairingRegistrySecretsKeyName, 323 KEY_READ | KEY_WRITE); 324 if (result != ERROR_SUCCESS) { 325 SetLastError(result); 326 PLOG(ERROR) << "Failed to open HKLM\\" << kPairingRegistrySecretsKeyName 327 << "\\" << kPairingRegistrySecretsKeyName; 328 return false; 329 } 330 331 pairing_registry_privileged_key_.Set(privileged.Take()); 332 pairing_registry_unprivileged_key_.Set(unprivileged.Take()); 333 return true; 334 } 335 336 } // namespace remoting 337