Update SplitString calls to new form
[chromium-blink-merge.git] / remoting / host / win / wts_session_process_delegate.cc
blob0cd0e870662848e19831bd2c36299f4b07af59d3
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 // This file implements the Windows service controlling Me2Me host processes
6 // running within user sessions.
8 #include "remoting/host/win/wts_session_process_delegate.h"
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/files/file_path.h"
13 #include "base/logging.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/thread_task_runner_handle.h"
18 #include "base/win/scoped_handle.h"
19 #include "base/win/windows_version.h"
20 #include "ipc/ipc_channel.h"
21 #include "ipc/ipc_channel_proxy.h"
22 #include "ipc/ipc_listener.h"
23 #include "ipc/ipc_message.h"
24 #include "remoting/host/host_main.h"
25 #include "remoting/host/ipc_constants.h"
26 #include "remoting/host/ipc_util.h"
27 #include "remoting/host/switches.h"
28 #include "remoting/host/win/launch_process_with_token.h"
29 #include "remoting/host/win/worker_process_launcher.h"
30 #include "remoting/host/win/wts_terminal_monitor.h"
31 #include "remoting/host/worker_process_ipc_delegate.h"
33 using base::win::ScopedHandle;
35 // Name of the default session desktop.
36 const char kDefaultDesktopName[] = "winsta0\\default";
38 namespace remoting {
40 // A private class actually implementing the functionality provided by
41 // |WtsSessionProcessDelegate|. This class is ref-counted and implements
42 // asynchronous fire-and-forget shutdown.
43 class WtsSessionProcessDelegate::Core
44 : public base::RefCountedThreadSafe<Core>,
45 public base::MessagePumpForIO::IOHandler,
46 public IPC::Listener {
47 public:
48 Core(scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
49 scoped_ptr<base::CommandLine> target,
50 bool launch_elevated,
51 const std::string& channel_security);
53 // Initializes the object returning true on success.
54 bool Initialize(uint32 session_id);
56 // Stops the object asynchronously.
57 void Stop();
59 // Mirrors WorkerProcessLauncher::Delegate.
60 void LaunchProcess(WorkerProcessLauncher* event_handler);
61 void Send(IPC::Message* message);
62 void CloseChannel();
63 void KillProcess();
65 private:
66 friend class base::RefCountedThreadSafe<Core>;
67 ~Core() override;
69 // base::MessagePumpForIO::IOHandler implementation.
70 void OnIOCompleted(base::MessagePumpForIO::IOContext* context,
71 DWORD bytes_transferred,
72 DWORD error) override;
74 // IPC::Listener implementation.
75 bool OnMessageReceived(const IPC::Message& message) override;
76 void OnChannelConnected(int32 peer_pid) override;
77 void OnChannelError() override;
79 // The actual implementation of LaunchProcess()
80 void DoLaunchProcess();
82 // Drains the completion port queue to make sure that all job object
83 // notifications have been received.
84 void DrainJobNotifications();
86 // Notified that the completion port queue has been drained.
87 void DrainJobNotificationsCompleted();
89 // Creates and initializes the job object that will sandbox the launched child
90 // processes.
91 void InitializeJob(scoped_ptr<base::win::ScopedHandle> job);
93 // Notified that the job object initialization is complete.
94 void InitializeJobCompleted(scoped_ptr<base::win::ScopedHandle> job);
96 // Called when the number of processes running in the job reaches zero.
97 void OnActiveProcessZero();
99 void ReportFatalError();
100 void ReportProcessLaunched(base::win::ScopedHandle worker_process);
102 // The task runner all public methods of this class should be called on.
103 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner_;
105 // The task runner serving job object notifications.
106 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
108 // The server end of the IPC channel used to communicate to the worker
109 // process.
110 scoped_ptr<IPC::ChannelProxy> channel_;
112 // Security descriptor (as SDDL) to be applied to |channel_|.
113 std::string channel_security_;
115 WorkerProcessLauncher* event_handler_;
117 // Pointer to GetNamedPipeClientProcessId() API if it is available.
118 typedef BOOL (WINAPI * GetNamedPipeClientProcessIdFn)(HANDLE, DWORD*);
119 GetNamedPipeClientProcessIdFn get_named_pipe_client_pid_;
121 // The job object used to control the lifetime of child processes.
122 base::win::ScopedHandle job_;
124 // True if the worker process should be launched elevated.
125 bool launch_elevated_;
127 // True if a laucnh attemp is pending.
128 bool launch_pending_;
130 // The named pipe used as the transport by |channel_|.
131 base::win::ScopedHandle pipe_;
133 // The token to be used to launch a process in a different session.
134 base::win::ScopedHandle session_token_;
136 // Command line of the launched process.
137 scoped_ptr<base::CommandLine> target_command_;
139 // The handle of the worker process, if launched.
140 base::win::ScopedHandle worker_process_;
142 DISALLOW_COPY_AND_ASSIGN(Core);
145 WtsSessionProcessDelegate::Core::Core(
146 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
147 scoped_ptr<base::CommandLine> target_command,
148 bool launch_elevated,
149 const std::string& channel_security)
150 : caller_task_runner_(base::ThreadTaskRunnerHandle::Get()),
151 io_task_runner_(io_task_runner),
152 channel_security_(channel_security),
153 event_handler_(nullptr),
154 get_named_pipe_client_pid_(nullptr),
155 launch_elevated_(launch_elevated),
156 launch_pending_(false),
157 target_command_(target_command.Pass()) {
160 bool WtsSessionProcessDelegate::Core::Initialize(uint32 session_id) {
161 DCHECK(caller_task_runner_->BelongsToCurrentThread());
163 // Windows XP does not support elevation.
164 if (base::win::GetVersion() < base::win::VERSION_VISTA)
165 launch_elevated_ = false;
167 if (launch_elevated_) {
168 // GetNamedPipeClientProcessId() is available starting from Vista.
169 HMODULE kernel32 = ::GetModuleHandle(L"kernel32.dll");
170 CHECK(kernel32 != nullptr);
172 get_named_pipe_client_pid_ =
173 reinterpret_cast<GetNamedPipeClientProcessIdFn>(
174 GetProcAddress(kernel32, "GetNamedPipeClientProcessId"));
175 CHECK(get_named_pipe_client_pid_ != nullptr);
177 ScopedHandle job;
178 job.Set(CreateJobObject(nullptr, nullptr));
179 if (!job.IsValid()) {
180 PLOG(ERROR) << "Failed to create a job object";
181 return false;
184 // Limit the number of active processes in the job to two (the helper
185 // process performing elevation and the worker process itself) and make sure
186 // that all processes will be killed once the job object is destroyed.
187 JOBOBJECT_EXTENDED_LIMIT_INFORMATION info;
188 memset(&info, 0, sizeof(info));
189 info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_ACTIVE_PROCESS |
190 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
191 info.BasicLimitInformation.ActiveProcessLimit = 2;
192 if (!SetInformationJobObject(job.Get(),
193 JobObjectExtendedLimitInformation,
194 &info,
195 sizeof(info))) {
196 PLOG(ERROR) << "Failed to set limits on the job object";
197 return false;
200 // ScopedHandle is not compatible with base::Passed, so we wrap it to
201 // a scoped pointer.
202 scoped_ptr<ScopedHandle> job_wrapper(new ScopedHandle());
203 *job_wrapper = job.Pass();
205 // To receive job object notifications the job object is registered with
206 // the completion port represented by |io_task_runner|. The registration has
207 // to be done on the I/O thread because
208 // MessageLoopForIO::RegisterJobObject() can only be called via
209 // MessageLoopForIO::current().
210 io_task_runner_->PostTask(
211 FROM_HERE,
212 base::Bind(&Core::InitializeJob, this, base::Passed(&job_wrapper)));
215 // Create a session token for the launched process.
216 return CreateSessionToken(session_id, &session_token_);
219 void WtsSessionProcessDelegate::Core::Stop() {
220 DCHECK(caller_task_runner_->BelongsToCurrentThread());
222 KillProcess();
224 // Drain the completion queue to make sure all job object notifications have
225 // been received.
226 DrainJobNotificationsCompleted();
229 void WtsSessionProcessDelegate::Core::LaunchProcess(
230 WorkerProcessLauncher* event_handler) {
231 DCHECK(caller_task_runner_->BelongsToCurrentThread());
232 DCHECK(!event_handler_);
234 event_handler_ = event_handler;
235 DoLaunchProcess();
238 void WtsSessionProcessDelegate::Core::Send(IPC::Message* message) {
239 DCHECK(caller_task_runner_->BelongsToCurrentThread());
241 if (channel_) {
242 channel_->Send(message);
243 } else {
244 delete message;
248 void WtsSessionProcessDelegate::Core::CloseChannel() {
249 DCHECK(caller_task_runner_->BelongsToCurrentThread());
251 channel_.reset();
252 pipe_.Close();
255 void WtsSessionProcessDelegate::Core::KillProcess() {
256 DCHECK(caller_task_runner_->BelongsToCurrentThread());
258 channel_.reset();
259 event_handler_ = nullptr;
260 launch_pending_ = false;
261 pipe_.Close();
263 if (launch_elevated_) {
264 if (job_.IsValid())
265 TerminateJobObject(job_.Get(), CONTROL_C_EXIT);
266 } else {
267 if (worker_process_.IsValid())
268 TerminateProcess(worker_process_.Get(), CONTROL_C_EXIT);
271 worker_process_.Close();
274 WtsSessionProcessDelegate::Core::~Core() {
275 DCHECK(!channel_);
276 DCHECK(!event_handler_);
277 DCHECK(!pipe_.IsValid());
278 DCHECK(!worker_process_.IsValid());
281 void WtsSessionProcessDelegate::Core::OnIOCompleted(
282 base::MessagePumpForIO::IOContext* context,
283 DWORD bytes_transferred,
284 DWORD error) {
285 DCHECK(io_task_runner_->BelongsToCurrentThread());
287 // |bytes_transferred| is used in job object notifications to supply
288 // the message ID; |context| carries process ID.
289 if (bytes_transferred == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO) {
290 caller_task_runner_->PostTask(FROM_HERE,
291 base::Bind(&Core::OnActiveProcessZero, this));
295 bool WtsSessionProcessDelegate::Core::OnMessageReceived(
296 const IPC::Message& message) {
297 DCHECK(caller_task_runner_->BelongsToCurrentThread());
299 return event_handler_->OnMessageReceived(message);
302 void WtsSessionProcessDelegate::Core::OnChannelConnected(int32 peer_pid) {
303 DCHECK(caller_task_runner_->BelongsToCurrentThread());
305 // Report the worker PID now if the worker process is launched indirectly.
306 // Note that in this case the pipe's security descriptor is the only
307 // protection against a malicious processed connecting to the pipe.
308 if (launch_elevated_) {
309 DWORD pid;
310 if (!get_named_pipe_client_pid_(pipe_.Get(), &pid)) {
311 PLOG(ERROR) << "Failed to retrive PID of the client";
312 ReportFatalError();
313 return;
316 if (pid != static_cast<DWORD>(peer_pid)) {
317 LOG(ERROR) << "The actual client PID " << pid
318 << " does not match the one reported by the client: "
319 << peer_pid;
320 ReportFatalError();
321 return;
324 DWORD desired_access =
325 SYNCHRONIZE | PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION;
326 ScopedHandle worker_process(OpenProcess(desired_access, false, pid));
327 if (!worker_process.IsValid()) {
328 PLOG(ERROR) << "Failed to open process " << pid;
329 ReportFatalError();
330 return;
333 ReportProcessLaunched(worker_process.Pass());
336 if (event_handler_)
337 event_handler_->OnChannelConnected(peer_pid);
340 void WtsSessionProcessDelegate::Core::OnChannelError() {
341 DCHECK(caller_task_runner_->BelongsToCurrentThread());
343 event_handler_->OnChannelError();
346 void WtsSessionProcessDelegate::Core::DoLaunchProcess() {
347 DCHECK(caller_task_runner_->BelongsToCurrentThread());
348 DCHECK(!channel_);
349 DCHECK(!pipe_.IsValid());
350 DCHECK(!worker_process_.IsValid());
352 base::CommandLine command_line(target_command_->argv());
353 if (launch_elevated_) {
354 // The job object is not ready. Retry starting the host process later.
355 if (!job_.IsValid()) {
356 launch_pending_ = true;
357 return;
360 // Construct the helper binary name.
361 base::FilePath helper_binary;
362 if (!GetInstalledBinaryPath(kHostBinaryName, &helper_binary)) {
363 ReportFatalError();
364 return;
367 // Create the command line passing the name of the IPC channel to use and
368 // copying known switches from the caller's command line.
369 command_line.SetProgram(helper_binary);
370 command_line.AppendSwitchPath(kElevateSwitchName,
371 target_command_->GetProgram());
374 // Create the server end of the IPC channel.
375 std::string channel_name = IPC::Channel::GenerateUniqueRandomChannelID();
376 ScopedHandle pipe;
377 if (!CreateIpcChannel(channel_name, channel_security_, &pipe)) {
378 ReportFatalError();
379 return;
382 // Wrap the pipe into an IPC channel.
383 scoped_ptr<IPC::ChannelProxy> channel(
384 IPC::ChannelProxy::Create(IPC::ChannelHandle(pipe.Get()),
385 IPC::Channel::MODE_SERVER,
386 this,
387 io_task_runner_,
388 nullptr));
390 // Pass the name of the IPC channel to use.
391 command_line.AppendSwitchNative(kDaemonPipeSwitchName,
392 base::UTF8ToWide(channel_name));
394 // Try to launch the process.
395 ScopedHandle worker_process;
396 ScopedHandle worker_thread;
397 if (!LaunchProcessWithToken(command_line.GetProgram(),
398 command_line.GetCommandLineString(),
399 session_token_.Get(),
400 nullptr,
401 nullptr,
402 false,
403 CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB,
404 base::UTF8ToUTF16(kDefaultDesktopName).c_str(),
405 &worker_process,
406 &worker_thread)) {
407 ReportFatalError();
408 return;
411 if (launch_elevated_) {
412 if (!AssignProcessToJobObject(job_.Get(), worker_process.Get())) {
413 PLOG(ERROR) << "Failed to assign the worker to the job object";
414 ReportFatalError();
415 return;
419 if (!ResumeThread(worker_thread.Get())) {
420 PLOG(ERROR) << "Failed to resume the worker thread";
421 ReportFatalError();
422 return;
425 channel_ = channel.Pass();
426 pipe_ = pipe.Pass();
428 // Report success if the worker process is lauched directly. Otherwise, PID of
429 // the client connected to the pipe will be used later. See
430 // OnChannelConnected().
431 if (!launch_elevated_)
432 ReportProcessLaunched(worker_process.Pass());
435 void WtsSessionProcessDelegate::Core::DrainJobNotifications() {
436 DCHECK(io_task_runner_->BelongsToCurrentThread());
438 // DrainJobNotifications() is posted after the job object is destroyed, so
439 // by this time all notifications from the job object have been processed
440 // already. Let the main thread know that the queue has been drained.
441 caller_task_runner_->PostTask(FROM_HERE, base::Bind(
442 &Core::DrainJobNotificationsCompleted, this));
445 void WtsSessionProcessDelegate::Core::DrainJobNotificationsCompleted() {
446 DCHECK(caller_task_runner_->BelongsToCurrentThread());
448 if (job_.IsValid()) {
449 job_.Close();
451 // Drain the completion queue to make sure all job object notification have
452 // been received.
453 io_task_runner_->PostTask(FROM_HERE, base::Bind(
454 &Core::DrainJobNotifications, this));
458 void WtsSessionProcessDelegate::Core::InitializeJob(
459 scoped_ptr<base::win::ScopedHandle> job) {
460 DCHECK(io_task_runner_->BelongsToCurrentThread());
462 // Register to receive job notifications via the I/O thread's completion port.
463 if (!base::MessageLoopForIO::current()->RegisterJobObject(job->Get(), this)) {
464 PLOG(ERROR) << "Failed to associate the job object with a completion port";
465 return;
468 // Let the main thread know that initialization is complete.
469 caller_task_runner_->PostTask(FROM_HERE, base::Bind(
470 &Core::InitializeJobCompleted, this, base::Passed(&job)));
473 void WtsSessionProcessDelegate::Core::InitializeJobCompleted(
474 scoped_ptr<ScopedHandle> job) {
475 DCHECK(caller_task_runner_->BelongsToCurrentThread());
476 DCHECK(!job_.IsValid());
478 job_ = job->Pass();
480 if (launch_pending_)
481 DoLaunchProcess();
484 void WtsSessionProcessDelegate::Core::OnActiveProcessZero() {
485 DCHECK(caller_task_runner_->BelongsToCurrentThread());
487 if (launch_pending_) {
488 LOG(ERROR) << "The worker process exited before connecting via IPC.";
489 launch_pending_ = false;
490 ReportFatalError();
494 void WtsSessionProcessDelegate::Core::ReportFatalError() {
495 DCHECK(caller_task_runner_->BelongsToCurrentThread());
497 channel_.reset();
498 pipe_.Close();
500 WorkerProcessLauncher* event_handler = event_handler_;
501 event_handler_ = nullptr;
502 event_handler->OnFatalError();
505 void WtsSessionProcessDelegate::Core::ReportProcessLaunched(
506 base::win::ScopedHandle worker_process) {
507 DCHECK(caller_task_runner_->BelongsToCurrentThread());
508 DCHECK(!worker_process_.IsValid());
510 worker_process_ = worker_process.Pass();
512 // Report a handle that can be used to wait for the worker process completion,
513 // query information about the process and duplicate handles.
514 DWORD desired_access =
515 SYNCHRONIZE | PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION;
516 HANDLE temp_handle;
517 if (!DuplicateHandle(GetCurrentProcess(),
518 worker_process_.Get(),
519 GetCurrentProcess(),
520 &temp_handle,
521 desired_access,
522 FALSE,
523 0)) {
524 PLOG(ERROR) << "Failed to duplicate a handle";
525 ReportFatalError();
526 return;
528 ScopedHandle limited_handle(temp_handle);
530 event_handler_->OnProcessLaunched(limited_handle.Pass());
533 WtsSessionProcessDelegate::WtsSessionProcessDelegate(
534 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
535 scoped_ptr<base::CommandLine> target_command,
536 bool launch_elevated,
537 const std::string& channel_security) {
538 core_ = new Core(io_task_runner,
539 target_command.Pass(),
540 launch_elevated,
541 channel_security);
544 WtsSessionProcessDelegate::~WtsSessionProcessDelegate() {
545 core_->Stop();
548 bool WtsSessionProcessDelegate::Initialize(uint32 session_id) {
549 return core_->Initialize(session_id);
552 void WtsSessionProcessDelegate::LaunchProcess(
553 WorkerProcessLauncher* event_handler) {
554 core_->LaunchProcess(event_handler);
557 void WtsSessionProcessDelegate::Send(IPC::Message* message) {
558 core_->Send(message);
561 void WtsSessionProcessDelegate::CloseChannel() {
562 core_->CloseChannel();
565 void WtsSessionProcessDelegate::KillProcess() {
566 core_->KillProcess();
569 } // namespace remoting