Roll src/third_party/WebKit b41a10f:afd8afd (svn 202201:202202)
[chromium-blink-merge.git] / remoting / host / daemon_process_win.cc
blob08ffff66818732bf9df84c1f8e06357dbfd9ade0
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.
5 #include "remoting/host/daemon_process.h"
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/win/registry.h"
19 #include "base/win/scoped_handle.h"
20 #include "base/win/win_util.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/security_descriptor.h"
35 #include "remoting/host/win/unprivileged_process_delegate.h"
36 #include "remoting/host/win/worker_process_launcher.h"
38 using base::win::ScopedHandle;
39 using base::TimeDelta;
41 namespace {
43 // Duplicates |key| into |target_process| and returns the value that can be sent
44 // over IPC.
45 IPC::PlatformFileForTransit GetRegistryKeyForTransit(
46 base::ProcessHandle target_process,
47 const base::win::RegKey& key) {
48 base::PlatformFile handle =
49 reinterpret_cast<base::PlatformFile>(key.Handle());
50 return IPC::GetFileHandleForProcess(handle, target_process, false);
53 } // namespace
55 namespace remoting {
57 class WtsTerminalMonitor;
59 // The command line parameters that should be copied from the service's command
60 // line to the host process.
61 const char kEnableVp9SwitchName[] = "enable-vp9";
62 const char* kCopiedSwitchNames[] =
63 { switches::kV, switches::kVModule, kEnableVp9SwitchName };
65 class DaemonProcessWin : public DaemonProcess {
66 public:
67 DaemonProcessWin(
68 scoped_refptr<AutoThreadTaskRunner> caller_task_runner,
69 scoped_refptr<AutoThreadTaskRunner> io_task_runner,
70 const base::Closure& stopped_callback);
71 ~DaemonProcessWin() override;
73 // WorkerProcessIpcDelegate implementation.
74 void OnChannelConnected(int32 peer_pid) override;
75 void OnPermanentError(int exit_code) override;
77 // DaemonProcess overrides.
78 void SendToNetwork(IPC::Message* message) override;
79 bool OnDesktopSessionAgentAttached(
80 int terminal_id,
81 base::ProcessHandle desktop_process,
82 IPC::PlatformFileForTransit desktop_pipe) override;
84 protected:
85 // DaemonProcess implementation.
86 scoped_ptr<DesktopSession> DoCreateDesktopSession(
87 int terminal_id,
88 const ScreenResolution& resolution,
89 bool virtual_terminal) override;
90 void DoCrashNetworkProcess(
91 const tracked_objects::Location& location) override;
92 void LaunchNetworkProcess() override;
94 // Changes the service start type to 'manual'.
95 void DisableAutoStart();
97 // Initializes the pairing registry on the host side by sending
98 // ChromotingDaemonNetworkMsg_InitializePairingRegistry message.
99 bool InitializePairingRegistry();
101 // Opens the pairing registry keys.
102 bool OpenPairingRegistry();
104 private:
105 scoped_ptr<WorkerProcessLauncher> network_launcher_;
107 // Handle of the network process.
108 ScopedHandle network_process_;
110 base::win::RegKey pairing_registry_privileged_key_;
111 base::win::RegKey pairing_registry_unprivileged_key_;
113 DISALLOW_COPY_AND_ASSIGN(DaemonProcessWin);
116 DaemonProcessWin::DaemonProcessWin(
117 scoped_refptr<AutoThreadTaskRunner> caller_task_runner,
118 scoped_refptr<AutoThreadTaskRunner> io_task_runner,
119 const base::Closure& stopped_callback)
120 : DaemonProcess(caller_task_runner, io_task_runner, stopped_callback) {
123 DaemonProcessWin::~DaemonProcessWin() {
126 void DaemonProcessWin::OnChannelConnected(int32 peer_pid) {
127 // Obtain the handle of the network process.
128 network_process_.Set(OpenProcess(PROCESS_DUP_HANDLE, false, peer_pid));
129 if (!network_process_.IsValid()) {
130 CrashNetworkProcess(FROM_HERE);
131 return;
134 if (!InitializePairingRegistry()) {
135 CrashNetworkProcess(FROM_HERE);
136 return;
139 DaemonProcess::OnChannelConnected(peer_pid);
142 void DaemonProcessWin::OnPermanentError(int exit_code) {
143 DCHECK(kMinPermanentErrorExitCode <= exit_code &&
144 exit_code <= kMaxPermanentErrorExitCode);
146 // Both kInvalidHostIdExitCode and kInvalidOauthCredentialsExitCode are
147 // errors then will never go away with the current config.
148 // Disabling automatic service start until the host is re-enabled and config
149 // updated.
150 if (exit_code == kInvalidHostIdExitCode ||
151 exit_code == kInvalidOauthCredentialsExitCode) {
152 DisableAutoStart();
155 DaemonProcess::OnPermanentError(exit_code);
158 void DaemonProcessWin::SendToNetwork(IPC::Message* message) {
159 if (network_launcher_) {
160 network_launcher_->Send(message);
161 } else {
162 delete message;
166 bool DaemonProcessWin::OnDesktopSessionAgentAttached(
167 int terminal_id,
168 base::ProcessHandle desktop_process,
169 IPC::PlatformFileForTransit desktop_pipe) {
170 // Prepare |desktop_process| handle for sending over to the network process.
171 base::ProcessHandle desktop_process_for_transit;
172 if (!DuplicateHandle(GetCurrentProcess(),
173 desktop_process,
174 network_process_.Get(),
175 &desktop_process_for_transit,
177 FALSE,
178 DUPLICATE_SAME_ACCESS)) {
179 PLOG(ERROR) << "Failed to duplicate the desktop process handle";
180 return false;
183 // |desktop_pipe| is a handle in the desktop process. It will be duplicated
184 // by the network process directly from the desktop process.
185 SendToNetwork(new ChromotingDaemonNetworkMsg_DesktopAttached(
186 terminal_id, desktop_process_for_transit, desktop_pipe));
187 return true;
190 scoped_ptr<DesktopSession> DaemonProcessWin::DoCreateDesktopSession(
191 int terminal_id,
192 const ScreenResolution& resolution,
193 bool virtual_terminal) {
194 DCHECK(caller_task_runner()->BelongsToCurrentThread());
196 if (virtual_terminal) {
197 return DesktopSessionWin::CreateForVirtualTerminal(
198 caller_task_runner(), io_task_runner(), this, terminal_id, resolution);
199 } else {
200 return DesktopSessionWin::CreateForConsole(
201 caller_task_runner(), io_task_runner(), this, terminal_id, resolution);
205 void DaemonProcessWin::DoCrashNetworkProcess(
206 const tracked_objects::Location& location) {
207 DCHECK(caller_task_runner()->BelongsToCurrentThread());
209 network_launcher_->Crash(location);
212 void DaemonProcessWin::LaunchNetworkProcess() {
213 DCHECK(caller_task_runner()->BelongsToCurrentThread());
214 DCHECK(!network_launcher_);
216 // Construct the host binary name.
217 base::FilePath host_binary;
218 if (!GetInstalledBinaryPath(kHostBinaryName, &host_binary)) {
219 Stop();
220 return;
223 scoped_ptr<base::CommandLine> target(new base::CommandLine(host_binary));
224 target->AppendSwitchASCII(kProcessTypeSwitchName, kProcessTypeHost);
225 target->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(),
226 kCopiedSwitchNames, arraysize(kCopiedSwitchNames));
228 scoped_ptr<UnprivilegedProcessDelegate> delegate(
229 new UnprivilegedProcessDelegate(io_task_runner(), target.Pass()));
230 network_launcher_.reset(new WorkerProcessLauncher(delegate.Pass(), this));
233 scoped_ptr<DaemonProcess> DaemonProcess::Create(
234 scoped_refptr<AutoThreadTaskRunner> caller_task_runner,
235 scoped_refptr<AutoThreadTaskRunner> io_task_runner,
236 const base::Closure& stopped_callback) {
237 scoped_ptr<DaemonProcessWin> daemon_process(
238 new DaemonProcessWin(caller_task_runner, io_task_runner,
239 stopped_callback));
240 daemon_process->Initialize();
241 return daemon_process.Pass();
244 void DaemonProcessWin::DisableAutoStart() {
245 ScopedScHandle scmanager(
246 OpenSCManager(nullptr, SERVICES_ACTIVE_DATABASE,
247 SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE));
248 if (!scmanager.IsValid()) {
249 PLOG(INFO) << "Failed to connect to the service control manager";
250 return;
253 DWORD desired_access = SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS;
254 ScopedScHandle service(
255 OpenService(scmanager.Get(), kWindowsServiceName, desired_access));
256 if (!service.IsValid()) {
257 PLOG(INFO) << "Failed to open to the '" << kWindowsServiceName
258 << "' service";
259 return;
262 // Change the service start type to 'manual'. All |nullptr| parameters below
263 // mean that there is no change to the corresponding service parameter.
264 if (!ChangeServiceConfig(service.Get(),
265 SERVICE_NO_CHANGE,
266 SERVICE_DEMAND_START,
267 SERVICE_NO_CHANGE,
268 nullptr,
269 nullptr,
270 nullptr,
271 nullptr,
272 nullptr,
273 nullptr,
274 nullptr)) {
275 PLOG(INFO) << "Failed to change the '" << kWindowsServiceName
276 << "'service start type to 'manual'";
280 bool DaemonProcessWin::InitializePairingRegistry() {
281 if (!pairing_registry_privileged_key_.Valid()) {
282 if (!OpenPairingRegistry())
283 return false;
286 // Duplicate handles to the network process.
287 IPC::PlatformFileForTransit privileged_key = GetRegistryKeyForTransit(
288 network_process_.Get(), pairing_registry_privileged_key_);
289 IPC::PlatformFileForTransit unprivileged_key = GetRegistryKeyForTransit(
290 network_process_.Get(), pairing_registry_unprivileged_key_);
291 if (!(privileged_key && unprivileged_key))
292 return false;
294 // Initialize the pairing registry in the network process. This has to be done
295 // before the host configuration is sent, otherwise the host will not use
296 // the passed handles.
297 SendToNetwork(new ChromotingDaemonNetworkMsg_InitializePairingRegistry(
298 privileged_key, unprivileged_key));
300 return true;
303 // A chromoting top crasher revealed that the pairing registry keys sometimes
304 // cannot be opened. The speculation is that those keys are absent for some
305 // reason. To reduce the host crashes we create those keys here if they are
306 // absent. See crbug.com/379360 for details.
307 bool DaemonProcessWin::OpenPairingRegistry() {
308 DCHECK(!pairing_registry_privileged_key_.Valid());
309 DCHECK(!pairing_registry_unprivileged_key_.Valid());
311 // Open the root of the pairing registry. Create if absent.
312 base::win::RegKey root;
313 DWORD disposition;
314 LONG result = root.CreateWithDisposition(
315 HKEY_LOCAL_MACHINE, kPairingRegistryKeyName, &disposition,
316 KEY_READ | KEY_CREATE_SUB_KEY);
318 if (result != ERROR_SUCCESS) {
319 ::SetLastError(result);
320 PLOG(ERROR) << "Failed to open or create HKLM\\" << kPairingRegistryKeyName;
321 return false;
324 if (disposition == REG_CREATED_NEW_KEY)
325 LOG(WARNING) << "Created pairing registry root key which was absent.";
327 // Open the pairing registry clients key. Create if absent.
328 base::win::RegKey unprivileged;
329 result = unprivileged.CreateWithDisposition(
330 root.Handle(), kPairingRegistryClientsKeyName, &disposition,
331 KEY_READ | KEY_WRITE);
333 if (result != ERROR_SUCCESS) {
334 ::SetLastError(result);
335 PLOG(ERROR) << "Failed to open or create HKLM\\" << kPairingRegistryKeyName
336 << "\\" << kPairingRegistryClientsKeyName;
337 return false;
340 if (disposition == REG_CREATED_NEW_KEY)
341 LOG(WARNING) << "Created pairing registry client key which was absent.";
343 // Open the pairing registry secret key.
344 base::win::RegKey privileged;
345 result = privileged.Open(
346 root.Handle(), kPairingRegistrySecretsKeyName, KEY_READ | KEY_WRITE);
348 if (result == ERROR_FILE_NOT_FOUND) {
349 LOG(WARNING) << "Pairing registry privileged key absent, creating.";
351 // Create a security descriptor that gives full access to local system and
352 // administrators and denies access by anyone else.
353 std::string security_descriptor = "O:BAG:BAD:(A;;GA;;;BA)(A;;GA;;;SY)";
355 ScopedSd sd = ConvertSddlToSd(security_descriptor);
356 if (!sd) {
357 PLOG(ERROR) << "Failed to create a security descriptor for the pairing"
358 << "registry privileged key.";
359 return false;
362 SECURITY_ATTRIBUTES security_attributes = {0};
363 security_attributes.nLength = sizeof(security_attributes);
364 security_attributes.lpSecurityDescriptor = sd.get();
365 security_attributes.bInheritHandle = FALSE;
367 HKEY key = nullptr;
368 result = ::RegCreateKeyEx(
369 root.Handle(), kPairingRegistrySecretsKeyName, 0, nullptr, 0,
370 KEY_READ | KEY_WRITE, &security_attributes, &key, &disposition);
371 privileged.Set(key);
374 if (result != ERROR_SUCCESS) {
375 ::SetLastError(result);
376 PLOG(ERROR) << "Failed to open or create HKLM\\" << kPairingRegistryKeyName
377 << "\\" << kPairingRegistrySecretsKeyName;
378 return false;
381 pairing_registry_privileged_key_.Set(privileged.Take());
382 pairing_registry_unprivileged_key_.Set(unprivileged.Take());
383 return true;
386 } // namespace remoting