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 // 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/win/launch_process_with_token.h"
28 #include "remoting/host/win/worker_process_launcher.h"
29 #include "remoting/host/win/wts_terminal_monitor.h"
30 #include "remoting/host/worker_process_ipc_delegate.h"
32 using base::win::ScopedHandle
;
34 // Name of the default session desktop.
35 const char kDefaultDesktopName
[] = "winsta0\\default";
39 // A private class actually implementing the functionality provided by
40 // |WtsSessionProcessDelegate|. This class is ref-counted and implements
41 // asynchronous fire-and-forget shutdown.
42 class WtsSessionProcessDelegate::Core
43 : public base::RefCountedThreadSafe
<Core
>,
44 public base::MessagePumpForIO::IOHandler
,
45 public IPC::Listener
{
47 Core(scoped_refptr
<base::SingleThreadTaskRunner
> io_task_runner
,
48 scoped_ptr
<base::CommandLine
> target
,
50 const std::string
& channel_security
);
52 // Initializes the object returning true on success.
53 bool Initialize(uint32 session_id
);
55 // Stops the object asynchronously.
58 // Mirrors WorkerProcessLauncher::Delegate.
59 void LaunchProcess(WorkerProcessLauncher
* event_handler
);
60 void Send(IPC::Message
* message
);
65 friend class base::RefCountedThreadSafe
<Core
>;
68 // base::MessagePumpForIO::IOHandler implementation.
69 virtual void OnIOCompleted(base::MessagePumpForIO::IOContext
* context
,
70 DWORD bytes_transferred
,
71 DWORD error
) override
;
73 // IPC::Listener implementation.
74 virtual bool OnMessageReceived(const IPC::Message
& message
) override
;
75 virtual void OnChannelConnected(int32 peer_pid
) override
;
76 virtual void OnChannelError() override
;
78 // The actual implementation of LaunchProcess()
79 void DoLaunchProcess();
81 // Drains the completion port queue to make sure that all job object
82 // notifications have been received.
83 void DrainJobNotifications();
85 // Notified that the completion port queue has been drained.
86 void DrainJobNotificationsCompleted();
88 // Creates and initializes the job object that will sandbox the launched child
90 void InitializeJob(scoped_ptr
<base::win::ScopedHandle
> job
);
92 // Notified that the job object initialization is complete.
93 void InitializeJobCompleted(scoped_ptr
<base::win::ScopedHandle
> job
);
95 // Called when the number of processes running in the job reaches zero.
96 void OnActiveProcessZero();
98 void ReportFatalError();
99 void ReportProcessLaunched(base::win::ScopedHandle worker_process
);
101 // The task runner all public methods of this class should be called on.
102 scoped_refptr
<base::SingleThreadTaskRunner
> caller_task_runner_
;
104 // The task runner serving job object notifications.
105 scoped_refptr
<base::SingleThreadTaskRunner
> io_task_runner_
;
107 // The server end of the IPC channel used to communicate to the worker
109 scoped_ptr
<IPC::ChannelProxy
> channel_
;
111 // Security descriptor (as SDDL) to be applied to |channel_|.
112 std::string channel_security_
;
114 WorkerProcessLauncher
* event_handler_
;
116 // Pointer to GetNamedPipeClientProcessId() API if it is available.
117 typedef BOOL (WINAPI
* GetNamedPipeClientProcessIdFn
)(HANDLE
, DWORD
*);
118 GetNamedPipeClientProcessIdFn get_named_pipe_client_pid_
;
120 // The job object used to control the lifetime of child processes.
121 base::win::ScopedHandle job_
;
123 // True if the worker process should be launched elevated.
124 bool launch_elevated_
;
126 // True if a laucnh attemp is pending.
127 bool launch_pending_
;
129 // The named pipe used as the transport by |channel_|.
130 base::win::ScopedHandle pipe_
;
132 // The token to be used to launch a process in a different session.
133 base::win::ScopedHandle session_token_
;
135 // Command line of the launched process.
136 scoped_ptr
<base::CommandLine
> target_command_
;
138 // The handle of the worker process, if launched.
139 base::win::ScopedHandle worker_process_
;
141 DISALLOW_COPY_AND_ASSIGN(Core
);
144 WtsSessionProcessDelegate::Core::Core(
145 scoped_refptr
<base::SingleThreadTaskRunner
> io_task_runner
,
146 scoped_ptr
<base::CommandLine
> target_command
,
147 bool launch_elevated
,
148 const std::string
& channel_security
)
149 : caller_task_runner_(base::ThreadTaskRunnerHandle::Get()),
150 io_task_runner_(io_task_runner
),
151 channel_security_(channel_security
),
152 event_handler_(nullptr),
153 get_named_pipe_client_pid_(nullptr),
154 launch_elevated_(launch_elevated
),
155 launch_pending_(false),
156 target_command_(target_command
.Pass()) {
159 bool WtsSessionProcessDelegate::Core::Initialize(uint32 session_id
) {
160 DCHECK(caller_task_runner_
->BelongsToCurrentThread());
162 // Windows XP does not support elevation.
163 if (base::win::GetVersion() < base::win::VERSION_VISTA
)
164 launch_elevated_
= false;
166 if (launch_elevated_
) {
167 // GetNamedPipeClientProcessId() is available starting from Vista.
168 HMODULE kernel32
= ::GetModuleHandle(L
"kernel32.dll");
169 CHECK(kernel32
!= nullptr);
171 get_named_pipe_client_pid_
=
172 reinterpret_cast<GetNamedPipeClientProcessIdFn
>(
173 GetProcAddress(kernel32
, "GetNamedPipeClientProcessId"));
174 CHECK(get_named_pipe_client_pid_
!= nullptr);
177 job
.Set(CreateJobObject(nullptr, nullptr));
178 if (!job
.IsValid()) {
179 PLOG(ERROR
) << "Failed to create a job object";
183 // Limit the number of active processes in the job to two (the helper
184 // process performing elevation and the worker process itself) and make sure
185 // that all processes will be killed once the job object is destroyed.
186 JOBOBJECT_EXTENDED_LIMIT_INFORMATION info
;
187 memset(&info
, 0, sizeof(info
));
188 info
.BasicLimitInformation
.LimitFlags
= JOB_OBJECT_LIMIT_ACTIVE_PROCESS
|
189 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
;
190 info
.BasicLimitInformation
.ActiveProcessLimit
= 2;
191 if (!SetInformationJobObject(job
.Get(),
192 JobObjectExtendedLimitInformation
,
195 PLOG(ERROR
) << "Failed to set limits on the job object";
199 // ScopedHandle is not compatible with base::Passed, so we wrap it to
201 scoped_ptr
<ScopedHandle
> job_wrapper(new ScopedHandle());
202 *job_wrapper
= job
.Pass();
204 // To receive job object notifications the job object is registered with
205 // the completion port represented by |io_task_runner|. The registration has
206 // to be done on the I/O thread because
207 // MessageLoopForIO::RegisterJobObject() can only be called via
208 // MessageLoopForIO::current().
209 io_task_runner_
->PostTask(
211 base::Bind(&Core::InitializeJob
, this, base::Passed(&job_wrapper
)));
214 // Create a session token for the launched process.
215 return CreateSessionToken(session_id
, &session_token_
);
218 void WtsSessionProcessDelegate::Core::Stop() {
219 DCHECK(caller_task_runner_
->BelongsToCurrentThread());
223 // Drain the completion queue to make sure all job object notifications have
225 DrainJobNotificationsCompleted();
228 void WtsSessionProcessDelegate::Core::LaunchProcess(
229 WorkerProcessLauncher
* event_handler
) {
230 DCHECK(caller_task_runner_
->BelongsToCurrentThread());
231 DCHECK(!event_handler_
);
233 event_handler_
= event_handler
;
237 void WtsSessionProcessDelegate::Core::Send(IPC::Message
* message
) {
238 DCHECK(caller_task_runner_
->BelongsToCurrentThread());
241 channel_
->Send(message
);
247 void WtsSessionProcessDelegate::Core::CloseChannel() {
248 DCHECK(caller_task_runner_
->BelongsToCurrentThread());
254 void WtsSessionProcessDelegate::Core::KillProcess() {
255 DCHECK(caller_task_runner_
->BelongsToCurrentThread());
258 event_handler_
= nullptr;
259 launch_pending_
= false;
262 if (launch_elevated_
) {
264 TerminateJobObject(job_
.Get(), CONTROL_C_EXIT
);
266 if (worker_process_
.IsValid())
267 TerminateProcess(worker_process_
.Get(), CONTROL_C_EXIT
);
270 worker_process_
.Close();
273 WtsSessionProcessDelegate::Core::~Core() {
275 DCHECK(!event_handler_
);
276 DCHECK(!pipe_
.IsValid());
277 DCHECK(!worker_process_
.IsValid());
280 void WtsSessionProcessDelegate::Core::OnIOCompleted(
281 base::MessagePumpForIO::IOContext
* context
,
282 DWORD bytes_transferred
,
284 DCHECK(io_task_runner_
->BelongsToCurrentThread());
286 // |bytes_transferred| is used in job object notifications to supply
287 // the message ID; |context| carries process ID.
288 if (bytes_transferred
== JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO
) {
289 caller_task_runner_
->PostTask(FROM_HERE
,
290 base::Bind(&Core::OnActiveProcessZero
, this));
294 bool WtsSessionProcessDelegate::Core::OnMessageReceived(
295 const IPC::Message
& message
) {
296 DCHECK(caller_task_runner_
->BelongsToCurrentThread());
298 return event_handler_
->OnMessageReceived(message
);
301 void WtsSessionProcessDelegate::Core::OnChannelConnected(int32 peer_pid
) {
302 DCHECK(caller_task_runner_
->BelongsToCurrentThread());
304 // Report the worker PID now if the worker process is launched indirectly.
305 // Note that in this case the pipe's security descriptor is the only
306 // protection against a malicious processed connecting to the pipe.
307 if (launch_elevated_
) {
309 if (!get_named_pipe_client_pid_(pipe_
.Get(), &pid
)) {
310 PLOG(ERROR
) << "Failed to retrive PID of the client";
315 if (pid
!= static_cast<DWORD
>(peer_pid
)) {
316 LOG(ERROR
) << "The actual client PID " << pid
317 << " does not match the one reported by the client: "
323 DWORD desired_access
=
324 SYNCHRONIZE
| PROCESS_DUP_HANDLE
| PROCESS_QUERY_INFORMATION
;
325 ScopedHandle
worker_process(OpenProcess(desired_access
, false, pid
));
326 if (!worker_process
.IsValid()) {
327 PLOG(ERROR
) << "Failed to open process " << pid
;
332 ReportProcessLaunched(worker_process
.Pass());
336 event_handler_
->OnChannelConnected(peer_pid
);
339 void WtsSessionProcessDelegate::Core::OnChannelError() {
340 DCHECK(caller_task_runner_
->BelongsToCurrentThread());
342 event_handler_
->OnChannelError();
345 void WtsSessionProcessDelegate::Core::DoLaunchProcess() {
346 DCHECK(caller_task_runner_
->BelongsToCurrentThread());
348 DCHECK(!pipe_
.IsValid());
349 DCHECK(!worker_process_
.IsValid());
351 base::CommandLine
command_line(target_command_
->argv());
352 if (launch_elevated_
) {
353 // The job object is not ready. Retry starting the host process later.
354 if (!job_
.IsValid()) {
355 launch_pending_
= true;
359 // Construct the helper binary name.
360 base::FilePath helper_binary
;
361 if (!GetInstalledBinaryPath(kHostBinaryName
, &helper_binary
)) {
366 // Create the command line passing the name of the IPC channel to use and
367 // copying known switches from the caller's command line.
368 command_line
.SetProgram(helper_binary
);
369 command_line
.AppendSwitchPath(kElevateSwitchName
,
370 target_command_
->GetProgram());
373 // Create the server end of the IPC channel.
374 std::string channel_name
= IPC::Channel::GenerateUniqueRandomChannelID();
376 if (!CreateIpcChannel(channel_name
, channel_security_
, &pipe
)) {
381 // Wrap the pipe into an IPC channel.
382 scoped_ptr
<IPC::ChannelProxy
> channel(
383 IPC::ChannelProxy::Create(IPC::ChannelHandle(pipe
.Get()),
384 IPC::Channel::MODE_SERVER
,
388 // Pass the name of the IPC channel to use.
389 command_line
.AppendSwitchNative(kDaemonPipeSwitchName
,
390 base::UTF8ToWide(channel_name
));
392 // Try to launch the process.
393 ScopedHandle worker_process
;
394 ScopedHandle worker_thread
;
395 if (!LaunchProcessWithToken(command_line
.GetProgram(),
396 command_line
.GetCommandLineString(),
397 session_token_
.Get(),
401 CREATE_SUSPENDED
| CREATE_BREAKAWAY_FROM_JOB
,
402 base::UTF8ToUTF16(kDefaultDesktopName
).c_str(),
409 if (launch_elevated_
) {
410 if (!AssignProcessToJobObject(job_
.Get(), worker_process
.Get())) {
411 PLOG(ERROR
) << "Failed to assign the worker to the job object";
417 if (!ResumeThread(worker_thread
.Get())) {
418 PLOG(ERROR
) << "Failed to resume the worker thread";
423 channel_
= channel
.Pass();
426 // Report success if the worker process is lauched directly. Otherwise, PID of
427 // the client connected to the pipe will be used later. See
428 // OnChannelConnected().
429 if (!launch_elevated_
)
430 ReportProcessLaunched(worker_process
.Pass());
433 void WtsSessionProcessDelegate::Core::DrainJobNotifications() {
434 DCHECK(io_task_runner_
->BelongsToCurrentThread());
436 // DrainJobNotifications() is posted after the job object is destroyed, so
437 // by this time all notifications from the job object have been processed
438 // already. Let the main thread know that the queue has been drained.
439 caller_task_runner_
->PostTask(FROM_HERE
, base::Bind(
440 &Core::DrainJobNotificationsCompleted
, this));
443 void WtsSessionProcessDelegate::Core::DrainJobNotificationsCompleted() {
444 DCHECK(caller_task_runner_
->BelongsToCurrentThread());
446 if (job_
.IsValid()) {
449 // Drain the completion queue to make sure all job object notification have
451 io_task_runner_
->PostTask(FROM_HERE
, base::Bind(
452 &Core::DrainJobNotifications
, this));
456 void WtsSessionProcessDelegate::Core::InitializeJob(
457 scoped_ptr
<base::win::ScopedHandle
> job
) {
458 DCHECK(io_task_runner_
->BelongsToCurrentThread());
460 // Register to receive job notifications via the I/O thread's completion port.
461 if (!base::MessageLoopForIO::current()->RegisterJobObject(job
->Get(), this)) {
462 PLOG(ERROR
) << "Failed to associate the job object with a completion port";
466 // Let the main thread know that initialization is complete.
467 caller_task_runner_
->PostTask(FROM_HERE
, base::Bind(
468 &Core::InitializeJobCompleted
, this, base::Passed(&job
)));
471 void WtsSessionProcessDelegate::Core::InitializeJobCompleted(
472 scoped_ptr
<ScopedHandle
> job
) {
473 DCHECK(caller_task_runner_
->BelongsToCurrentThread());
474 DCHECK(!job_
.IsValid());
482 void WtsSessionProcessDelegate::Core::OnActiveProcessZero() {
483 DCHECK(caller_task_runner_
->BelongsToCurrentThread());
485 if (launch_pending_
) {
486 LOG(ERROR
) << "The worker process exited before connecting via IPC.";
487 launch_pending_
= false;
492 void WtsSessionProcessDelegate::Core::ReportFatalError() {
493 DCHECK(caller_task_runner_
->BelongsToCurrentThread());
498 WorkerProcessLauncher
* event_handler
= event_handler_
;
499 event_handler_
= nullptr;
500 event_handler
->OnFatalError();
503 void WtsSessionProcessDelegate::Core::ReportProcessLaunched(
504 base::win::ScopedHandle worker_process
) {
505 DCHECK(caller_task_runner_
->BelongsToCurrentThread());
506 DCHECK(!worker_process_
.IsValid());
508 worker_process_
= worker_process
.Pass();
510 // Report a handle that can be used to wait for the worker process completion,
511 // query information about the process and duplicate handles.
512 DWORD desired_access
=
513 SYNCHRONIZE
| PROCESS_DUP_HANDLE
| PROCESS_QUERY_INFORMATION
;
515 if (!DuplicateHandle(GetCurrentProcess(),
516 worker_process_
.Get(),
522 PLOG(ERROR
) << "Failed to duplicate a handle";
526 ScopedHandle
limited_handle(temp_handle
);
528 event_handler_
->OnProcessLaunched(limited_handle
.Pass());
531 WtsSessionProcessDelegate::WtsSessionProcessDelegate(
532 scoped_refptr
<base::SingleThreadTaskRunner
> io_task_runner
,
533 scoped_ptr
<base::CommandLine
> target_command
,
534 bool launch_elevated
,
535 const std::string
& channel_security
) {
536 core_
= new Core(io_task_runner
,
537 target_command
.Pass(),
542 WtsSessionProcessDelegate::~WtsSessionProcessDelegate() {
546 bool WtsSessionProcessDelegate::Initialize(uint32 session_id
) {
547 return core_
->Initialize(session_id
);
550 void WtsSessionProcessDelegate::LaunchProcess(
551 WorkerProcessLauncher
* event_handler
) {
552 core_
->LaunchProcess(event_handler
);
555 void WtsSessionProcessDelegate::Send(IPC::Message
* message
) {
556 core_
->Send(message
);
559 void WtsSessionProcessDelegate::CloseChannel() {
560 core_
->CloseChannel();
563 void WtsSessionProcessDelegate::KillProcess() {
564 core_
->KillProcess();
567 } // namespace remoting