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/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"
37 using base::win::ScopedHandle
;
38 using base::TimeDelta
;
42 // Duplicates |key| into |target_process| and returns the value that can be sent
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);
56 class WtsTerminalMonitor
;
58 // The command line parameters that should be copied from the service's command
59 // line to the host process.
60 const char* kCopiedSwitchNames
[] = { switches::kV
, switches::kVModule
};
62 class DaemonProcessWin
: public DaemonProcess
{
65 scoped_refptr
<AutoThreadTaskRunner
> caller_task_runner
,
66 scoped_refptr
<AutoThreadTaskRunner
> io_task_runner
,
67 const base::Closure
& stopped_callback
);
68 virtual ~DaemonProcessWin();
70 // WorkerProcessIpcDelegate implementation.
71 virtual void OnChannelConnected(int32 peer_pid
) OVERRIDE
;
72 virtual void OnPermanentError(int exit_code
) OVERRIDE
;
74 // DaemonProcess overrides.
75 virtual void SendToNetwork(IPC::Message
* message
) OVERRIDE
;
76 virtual bool OnDesktopSessionAgentAttached(
78 base::ProcessHandle desktop_process
,
79 IPC::PlatformFileForTransit desktop_pipe
) OVERRIDE
;
82 // DaemonProcess implementation.
83 virtual scoped_ptr
<DesktopSession
> DoCreateDesktopSession(
85 const ScreenResolution
& resolution
,
86 bool virtual_terminal
) OVERRIDE
;
87 virtual void DoCrashNetworkProcess(
88 const tracked_objects::Location
& location
) OVERRIDE
;
89 virtual void LaunchNetworkProcess() OVERRIDE
;
91 // Changes the service start type to 'manual'.
92 void DisableAutoStart();
94 // Initializes the pairing registry on the host side by sending
95 // ChromotingDaemonNetworkMsg_InitializePairingRegistry message.
96 bool InitializePairingRegistry();
98 // Opens the pairing registry keys.
99 bool OpenPairingRegistry();
102 scoped_ptr
<WorkerProcessLauncher
> network_launcher_
;
104 // Handle of the network process.
105 ScopedHandle network_process_
;
107 base::win::RegKey pairing_registry_privileged_key_
;
108 base::win::RegKey pairing_registry_unprivileged_key_
;
110 DISALLOW_COPY_AND_ASSIGN(DaemonProcessWin
);
113 DaemonProcessWin::DaemonProcessWin(
114 scoped_refptr
<AutoThreadTaskRunner
> caller_task_runner
,
115 scoped_refptr
<AutoThreadTaskRunner
> io_task_runner
,
116 const base::Closure
& stopped_callback
)
117 : DaemonProcess(caller_task_runner
, io_task_runner
, stopped_callback
) {
120 DaemonProcessWin::~DaemonProcessWin() {
123 void DaemonProcessWin::OnChannelConnected(int32 peer_pid
) {
124 // Obtain the handle of the network process.
125 network_process_
.Set(OpenProcess(PROCESS_DUP_HANDLE
, false, peer_pid
));
126 if (!network_process_
.IsValid()) {
127 CrashNetworkProcess(FROM_HERE
);
131 if (!InitializePairingRegistry()) {
132 CrashNetworkProcess(FROM_HERE
);
136 DaemonProcess::OnChannelConnected(peer_pid
);
139 void DaemonProcessWin::OnPermanentError(int exit_code
) {
140 // Change the service start type to 'manual' if the host has been deleted
141 // remotely. This way the host will not be started every time the machine
142 // boots until the user re-enable it again.
143 if (exit_code
== kInvalidHostIdExitCode
)
146 DaemonProcess::OnPermanentError(exit_code
);
149 void DaemonProcessWin::SendToNetwork(IPC::Message
* message
) {
150 if (network_launcher_
) {
151 network_launcher_
->Send(message
);
157 bool DaemonProcessWin::OnDesktopSessionAgentAttached(
159 base::ProcessHandle desktop_process
,
160 IPC::PlatformFileForTransit desktop_pipe
) {
161 // Prepare |desktop_process| handle for sending over to the network process.
162 base::ProcessHandle desktop_process_for_transit
;
163 if (!DuplicateHandle(GetCurrentProcess(),
166 &desktop_process_for_transit
,
169 DUPLICATE_SAME_ACCESS
)) {
170 LOG_GETLASTERROR(ERROR
) << "Failed to duplicate the desktop process handle";
174 // |desktop_pipe| is a handle in the desktop process. It will be duplicated
175 // by the network process directly from the desktop process.
176 SendToNetwork(new ChromotingDaemonNetworkMsg_DesktopAttached(
177 terminal_id
, desktop_process_for_transit
, desktop_pipe
));
181 scoped_ptr
<DesktopSession
> DaemonProcessWin::DoCreateDesktopSession(
183 const ScreenResolution
& resolution
,
184 bool virtual_terminal
) {
185 DCHECK(caller_task_runner()->BelongsToCurrentThread());
187 if (virtual_terminal
) {
188 return DesktopSessionWin::CreateForVirtualTerminal(
189 caller_task_runner(), io_task_runner(), this, terminal_id
, resolution
);
191 return DesktopSessionWin::CreateForConsole(
192 caller_task_runner(), io_task_runner(), this, terminal_id
, resolution
);
196 void DaemonProcessWin::DoCrashNetworkProcess(
197 const tracked_objects::Location
& location
) {
198 DCHECK(caller_task_runner()->BelongsToCurrentThread());
200 network_launcher_
->Crash(location
);
203 void DaemonProcessWin::LaunchNetworkProcess() {
204 DCHECK(caller_task_runner()->BelongsToCurrentThread());
205 DCHECK(!network_launcher_
);
207 // Construct the host binary name.
208 base::FilePath host_binary
;
209 if (!GetInstalledBinaryPath(kHostBinaryName
, &host_binary
)) {
214 scoped_ptr
<CommandLine
> target(new CommandLine(host_binary
));
215 target
->AppendSwitchASCII(kProcessTypeSwitchName
, kProcessTypeHost
);
216 target
->CopySwitchesFrom(*CommandLine::ForCurrentProcess(),
218 arraysize(kCopiedSwitchNames
));
220 scoped_ptr
<UnprivilegedProcessDelegate
> delegate(
221 new UnprivilegedProcessDelegate(io_task_runner(), target
.Pass()));
222 network_launcher_
.reset(new WorkerProcessLauncher(delegate
.Pass(), this));
225 scoped_ptr
<DaemonProcess
> DaemonProcess::Create(
226 scoped_refptr
<AutoThreadTaskRunner
> caller_task_runner
,
227 scoped_refptr
<AutoThreadTaskRunner
> io_task_runner
,
228 const base::Closure
& stopped_callback
) {
229 scoped_ptr
<DaemonProcessWin
> daemon_process(
230 new DaemonProcessWin(caller_task_runner
, io_task_runner
,
232 daemon_process
->Initialize();
233 return daemon_process
.PassAs
<DaemonProcess
>();
236 void DaemonProcessWin::DisableAutoStart() {
237 ScopedScHandle
scmanager(
238 OpenSCManager(NULL
, SERVICES_ACTIVE_DATABASE
,
239 SC_MANAGER_CONNECT
| SC_MANAGER_ENUMERATE_SERVICE
));
240 if (!scmanager
.IsValid()) {
241 LOG_GETLASTERROR(INFO
)
242 << "Failed to connect to the service control manager";
246 DWORD desired_access
= SERVICE_CHANGE_CONFIG
| SERVICE_QUERY_STATUS
;
247 ScopedScHandle
service(
248 OpenService(scmanager
, kWindowsServiceName
, desired_access
));
249 if (!service
.IsValid()) {
250 LOG_GETLASTERROR(INFO
)
251 << "Failed to open to the '" << kWindowsServiceName
<< "' service";
255 // Change the service start type to 'manual'. All |NULL| parameters below mean
256 // that there is no change to the corresponding service parameter.
257 if (!ChangeServiceConfig(service
,
259 SERVICE_DEMAND_START
,
268 LOG_GETLASTERROR(INFO
)
269 << "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_
, pairing_registry_privileged_key_
);
283 IPC::PlatformFileForTransit unprivileged_key
= GetRegistryKeyForTransit(
284 network_process_
, 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
));
296 bool DaemonProcessWin::OpenPairingRegistry() {
297 DCHECK(!pairing_registry_privileged_key_
.Valid());
298 DCHECK(!pairing_registry_unprivileged_key_
.Valid());
300 // Open the root of the pairing registry.
301 base::win::RegKey root
;
302 LONG result
= root
.Open(HKEY_LOCAL_MACHINE
, kPairingRegistryKeyName
,
304 if (result
!= ERROR_SUCCESS
) {
305 SetLastError(result
);
306 PLOG(ERROR
) << "Failed to open HKLM\\" << kPairingRegistryKeyName
;
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
;
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
;
330 pairing_registry_privileged_key_
.Set(privileged
.Take());
331 pairing_registry_unprivileged_key_
.Set(unprivileged
.Take());
335 } // namespace remoting