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"
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
;
43 // Duplicates |key| into |target_process| and returns the value that can be sent
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);
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
{
68 scoped_refptr
<AutoThreadTaskRunner
> caller_task_runner
,
69 scoped_refptr
<AutoThreadTaskRunner
> io_task_runner
,
70 const base::Closure
& stopped_callback
);
71 virtual ~DaemonProcessWin();
73 // WorkerProcessIpcDelegate implementation.
74 virtual void OnChannelConnected(int32 peer_pid
) override
;
75 virtual void OnPermanentError(int exit_code
) override
;
77 // DaemonProcess overrides.
78 virtual void SendToNetwork(IPC::Message
* message
) override
;
79 virtual bool OnDesktopSessionAgentAttached(
81 base::ProcessHandle desktop_process
,
82 IPC::PlatformFileForTransit desktop_pipe
) override
;
85 // DaemonProcess implementation.
86 virtual scoped_ptr
<DesktopSession
> DoCreateDesktopSession(
88 const ScreenResolution
& resolution
,
89 bool virtual_terminal
) override
;
90 virtual void DoCrashNetworkProcess(
91 const tracked_objects::Location
& location
) override
;
92 virtual 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();
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
);
134 if (!InitializePairingRegistry()) {
135 CrashNetworkProcess(FROM_HERE
);
139 DaemonProcess::OnChannelConnected(peer_pid
);
142 void DaemonProcessWin::OnPermanentError(int exit_code
) {
143 // Change the service start type to 'manual' if the host has been deleted
144 // remotely. This way the host will not be started every time the machine
145 // boots until the user re-enable it again.
146 if (exit_code
== kInvalidHostIdExitCode
)
149 DaemonProcess::OnPermanentError(exit_code
);
152 void DaemonProcessWin::SendToNetwork(IPC::Message
* message
) {
153 if (network_launcher_
) {
154 network_launcher_
->Send(message
);
160 bool DaemonProcessWin::OnDesktopSessionAgentAttached(
162 base::ProcessHandle desktop_process
,
163 IPC::PlatformFileForTransit desktop_pipe
) {
164 // Prepare |desktop_process| handle for sending over to the network process.
165 base::ProcessHandle desktop_process_for_transit
;
166 if (!DuplicateHandle(GetCurrentProcess(),
168 network_process_
.Get(),
169 &desktop_process_for_transit
,
172 DUPLICATE_SAME_ACCESS
)) {
173 PLOG(ERROR
) << "Failed to duplicate the desktop process handle";
177 // |desktop_pipe| is a handle in the desktop process. It will be duplicated
178 // by the network process directly from the desktop process.
179 SendToNetwork(new ChromotingDaemonNetworkMsg_DesktopAttached(
180 terminal_id
, desktop_process_for_transit
, desktop_pipe
));
184 scoped_ptr
<DesktopSession
> DaemonProcessWin::DoCreateDesktopSession(
186 const ScreenResolution
& resolution
,
187 bool virtual_terminal
) {
188 DCHECK(caller_task_runner()->BelongsToCurrentThread());
190 if (virtual_terminal
) {
191 return DesktopSessionWin::CreateForVirtualTerminal(
192 caller_task_runner(), io_task_runner(), this, terminal_id
, resolution
);
194 return DesktopSessionWin::CreateForConsole(
195 caller_task_runner(), io_task_runner(), this, terminal_id
, resolution
);
199 void DaemonProcessWin::DoCrashNetworkProcess(
200 const tracked_objects::Location
& location
) {
201 DCHECK(caller_task_runner()->BelongsToCurrentThread());
203 network_launcher_
->Crash(location
);
206 void DaemonProcessWin::LaunchNetworkProcess() {
207 DCHECK(caller_task_runner()->BelongsToCurrentThread());
208 DCHECK(!network_launcher_
);
210 // Construct the host binary name.
211 base::FilePath host_binary
;
212 if (!GetInstalledBinaryPath(kHostBinaryName
, &host_binary
)) {
217 scoped_ptr
<base::CommandLine
> target(new base::CommandLine(host_binary
));
218 target
->AppendSwitchASCII(kProcessTypeSwitchName
, kProcessTypeHost
);
219 target
->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(),
220 kCopiedSwitchNames
, arraysize(kCopiedSwitchNames
));
222 scoped_ptr
<UnprivilegedProcessDelegate
> delegate(
223 new UnprivilegedProcessDelegate(io_task_runner(), target
.Pass()));
224 network_launcher_
.reset(new WorkerProcessLauncher(delegate
.Pass(), this));
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
,
234 daemon_process
->Initialize();
235 return daemon_process
.Pass();
238 void DaemonProcessWin::DisableAutoStart() {
239 ScopedScHandle
scmanager(
240 OpenSCManager(nullptr, 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";
247 DWORD desired_access
= SERVICE_CHANGE_CONFIG
| SERVICE_QUERY_STATUS
;
248 ScopedScHandle
service(
249 OpenService(scmanager
.Get(), kWindowsServiceName
, desired_access
));
250 if (!service
.IsValid()) {
251 PLOG(INFO
) << "Failed to open to the '" << kWindowsServiceName
256 // Change the service start type to 'manual'. All |nullptr| parameters below
257 // mean that there is no change to the corresponding service parameter.
258 if (!ChangeServiceConfig(service
.Get(),
260 SERVICE_DEMAND_START
,
269 PLOG(INFO
) << "Failed to change the '" << kWindowsServiceName
270 << "'service start type to 'manual'";
274 bool DaemonProcessWin::InitializePairingRegistry() {
275 if (!pairing_registry_privileged_key_
.Valid()) {
276 if (!OpenPairingRegistry())
280 // Duplicate handles to the network process.
281 IPC::PlatformFileForTransit privileged_key
= GetRegistryKeyForTransit(
282 network_process_
.Get(), pairing_registry_privileged_key_
);
283 IPC::PlatformFileForTransit unprivileged_key
= GetRegistryKeyForTransit(
284 network_process_
.Get(), pairing_registry_unprivileged_key_
);
285 if (!(privileged_key
&& unprivileged_key
))
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
));
297 // A chromoting top crasher revealed that the pairing registry keys sometimes
298 // cannot be opened. The speculation is that those keys are absent for some
299 // reason. To reduce the host crashes we create those keys here if they are
300 // absent. See crbug.com/379360 for details.
301 bool DaemonProcessWin::OpenPairingRegistry() {
302 DCHECK(!pairing_registry_privileged_key_
.Valid());
303 DCHECK(!pairing_registry_unprivileged_key_
.Valid());
305 // Open the root of the pairing registry. Create if absent.
306 base::win::RegKey root
;
308 LONG result
= root
.CreateWithDisposition(
309 HKEY_LOCAL_MACHINE
, kPairingRegistryKeyName
, &disposition
,
310 KEY_READ
| KEY_CREATE_SUB_KEY
);
312 if (result
!= ERROR_SUCCESS
) {
313 ::SetLastError(result
);
314 PLOG(ERROR
) << "Failed to open or create HKLM\\" << kPairingRegistryKeyName
;
318 if (disposition
== REG_CREATED_NEW_KEY
)
319 LOG(WARNING
) << "Created pairing registry root key which was absent.";
321 // Open the pairing registry clients key. Create if absent.
322 base::win::RegKey unprivileged
;
323 result
= unprivileged
.CreateWithDisposition(
324 root
.Handle(), kPairingRegistryClientsKeyName
, &disposition
,
325 KEY_READ
| KEY_WRITE
);
327 if (result
!= ERROR_SUCCESS
) {
328 ::SetLastError(result
);
329 PLOG(ERROR
) << "Failed to open or create HKLM\\" << kPairingRegistryKeyName
330 << "\\" << kPairingRegistryClientsKeyName
;
334 if (disposition
== REG_CREATED_NEW_KEY
)
335 LOG(WARNING
) << "Created pairing registry client key which was absent.";
337 // Open the pairing registry secret key.
338 base::win::RegKey privileged
;
339 result
= privileged
.Open(
340 root
.Handle(), kPairingRegistrySecretsKeyName
, KEY_READ
| KEY_WRITE
);
342 if (result
== ERROR_FILE_NOT_FOUND
) {
343 LOG(WARNING
) << "Pairing registry privileged key absent, creating.";
345 // Create a security descriptor that gives full access to local system and
346 // administrators and denies access by anyone else.
347 std::string security_descriptor
= "O:BAG:BAD:(A;;GA;;;BA)(A;;GA;;;SY)";
349 ScopedSd sd
= ConvertSddlToSd(security_descriptor
);
351 PLOG(ERROR
) << "Failed to create a security descriptor for the pairing"
352 << "registry privileged key.";
356 SECURITY_ATTRIBUTES security_attributes
= {0};
357 security_attributes
.nLength
= sizeof(security_attributes
);
358 security_attributes
.lpSecurityDescriptor
= sd
.get();
359 security_attributes
.bInheritHandle
= FALSE
;
362 result
= ::RegCreateKeyEx(
363 root
.Handle(), kPairingRegistrySecretsKeyName
, 0, nullptr, 0,
364 KEY_READ
| KEY_WRITE
, &security_attributes
, &key
, &disposition
);
368 if (result
!= ERROR_SUCCESS
) {
369 ::SetLastError(result
);
370 PLOG(ERROR
) << "Failed to open or create HKLM\\" << kPairingRegistryKeyName
371 << "\\" << kPairingRegistrySecretsKeyName
;
375 pairing_registry_privileged_key_
.Set(privileged
.Take());
376 pairing_registry_unprivileged_key_
.Set(unprivileged
.Take());
380 } // namespace remoting