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
37using base::win::ScopedHandle;
38using base::TimeDelta;
39
40namespace {
41
42// Duplicates |key| into |target_process| and returns the value that can be sent
43// over IPC.
44IPC::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
54namespace remoting {
55
56class WtsTerminalMonitor;
57
58// The command line parameters that should be copied from the service's command
59// line to the host process.
60const char kEnableVp9SwitchName[] = "enable-vp9";
61const char* kCopiedSwitchNames[] =
62    { switches::kV, switches::kVModule, kEnableVp9SwitchName };
63
64class 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
115DaemonProcessWin::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
122DaemonProcessWin::~DaemonProcessWin() {
123}
124
125void 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
141void 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
151void DaemonProcessWin::SendToNetwork(IPC::Message* message) {
152  if (network_launcher_) {
153    network_launcher_->Send(message);
154  } else {
155    delete message;
156  }
157}
158
159bool 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
183scoped_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
198void DaemonProcessWin::DoCrashNetworkProcess(
199    const tracked_objects::Location& location) {
200  DCHECK(caller_task_runner()->BelongsToCurrentThread());
201
202  network_launcher_->Crash(location);
203}
204
205void 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
228scoped_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
239void 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
275bool 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
297bool 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