Index: remoting/host/win/wts_session_process_delegate.cc |
diff --git a/remoting/host/win/wts_session_process_delegate.cc b/remoting/host/win/wts_session_process_delegate.cc |
index 7469d5d74fc9a931d8d241ff7865c2b500c0e918..b0bc149505b67e58b76a51416344255c75fe7fd9 100644 |
--- a/remoting/host/win/wts_session_process_delegate.cc |
+++ b/remoting/host/win/wts_session_process_delegate.cc |
@@ -15,6 +15,7 @@ |
#include "base/logging.h" |
#include "base/macros.h" |
#include "base/message_loop/message_loop.h" |
+#include "base/process/process_handle.h" |
#include "base/single_thread_task_runner.h" |
#include "base/strings/utf_string_conversions.h" |
#include "base/threading/thread_task_runner_handle.h" |
@@ -24,9 +25,13 @@ |
#include "ipc/ipc_channel_proxy.h" |
#include "ipc/ipc_listener.h" |
#include "ipc/ipc_message.h" |
+#include "mojo/edk/embedder/embedder.h" |
+#include "mojo/edk/embedder/named_platform_channel_pair.h" |
+#include "mojo/edk/embedder/platform_channel_pair.h" |
+#include "mojo/edk/embedder/platform_handle_utils.h" |
+#include "mojo/edk/embedder/scoped_platform_handle.h" |
#include "remoting/host/host_main.h" |
#include "remoting/host/ipc_constants.h" |
-#include "remoting/host/ipc_util.h" |
#include "remoting/host/switches.h" |
#include "remoting/host/win/launch_process_with_token.h" |
#include "remoting/host/win/security_descriptor.h" |
@@ -101,8 +106,12 @@ class WtsSessionProcessDelegate::Core |
// Called when the number of processes running in the job reaches zero. |
void OnActiveProcessZero(); |
+ // Called when a process is launched in |job_|. |
+ void OnProcessLaunchDetected(base::ProcessId pid); |
+ |
void ReportFatalError(); |
- void ReportProcessLaunched(base::win::ScopedHandle worker_process); |
+ void ReportProcessLaunched(base::win::ScopedHandle worker_process, |
+ mojo::edk::ScopedPlatformHandle server_handle); |
// The task runner all public methods of this class should be called on. |
scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner_; |
@@ -122,10 +131,6 @@ class WtsSessionProcessDelegate::Core |
WorkerProcessLauncher* event_handler_; |
- // Pointer to GetNamedPipeClientProcessId() API if it is available. |
- typedef BOOL (WINAPI * GetNamedPipeClientProcessIdFn)(HANDLE, DWORD*); |
- GetNamedPipeClientProcessIdFn get_named_pipe_client_pid_; |
- |
// The job object used to control the lifetime of child processes. |
base::win::ScopedHandle job_; |
@@ -135,9 +140,6 @@ class WtsSessionProcessDelegate::Core |
// True if a laucnh attemp is pending. |
bool launch_pending_; |
- // The named pipe used as the transport by |channel_|. |
- base::win::ScopedHandle pipe_; |
- |
// The token to be used to launch a process in a different session. |
base::win::ScopedHandle session_token_; |
@@ -147,6 +149,16 @@ class WtsSessionProcessDelegate::Core |
// The handle of the worker process, if launched. |
base::win::ScopedHandle worker_process_; |
+ // If launching elevated, this holds the server handle after launch, until |
+ // the final process launches. |
+ mojo::edk::ScopedPlatformHandle elevated_server_handle_; |
+ |
+ // If launching elevated, this is the pid of the launcher process. |
+ base::ProcessId elevated_launcher_pid_ = base::kNullProcessId; |
+ |
+ // The mojo child token for the process being launched. |
+ std::string mojo_child_token_; |
+ |
DISALLOW_COPY_AND_ASSIGN(Core); |
}; |
@@ -161,7 +173,6 @@ WtsSessionProcessDelegate::Core::Core( |
channel_security_(channel_security), |
new_process_security_(new_process_security), |
event_handler_(nullptr), |
- get_named_pipe_client_pid_(nullptr), |
launch_elevated_(launch_elevated), |
launch_pending_(false), |
target_command_(std::move(target_command)) {} |
@@ -174,11 +185,6 @@ bool WtsSessionProcessDelegate::Core::Initialize(uint32_t session_id) { |
HMODULE kernel32 = ::GetModuleHandle(L"kernel32.dll"); |
CHECK(kernel32 != nullptr); |
- get_named_pipe_client_pid_ = |
- reinterpret_cast<GetNamedPipeClientProcessIdFn>( |
- GetProcAddress(kernel32, "GetNamedPipeClientProcessId")); |
- CHECK(get_named_pipe_client_pid_ != nullptr); |
- |
ScopedHandle job; |
job.Set(CreateJobObject(nullptr, nullptr)); |
if (!job.IsValid()) { |
@@ -253,7 +259,12 @@ void WtsSessionProcessDelegate::Core::CloseChannel() { |
IPC::AttachmentBroker::GetGlobal()->DeregisterCommunicationChannel( |
channel_.get()); |
channel_.reset(); |
- pipe_.Close(); |
+ elevated_server_handle_.reset(); |
+ elevated_launcher_pid_ = base::kNullProcessId; |
+ if (!mojo_child_token_.empty()) { |
+ mojo::edk::ChildProcessLaunchFailed(mojo_child_token_); |
+ mojo_child_token_.clear(); |
+ } |
} |
void WtsSessionProcessDelegate::Core::KillProcess() { |
@@ -278,7 +289,6 @@ void WtsSessionProcessDelegate::Core::KillProcess() { |
WtsSessionProcessDelegate::Core::~Core() { |
DCHECK(!channel_); |
DCHECK(!event_handler_); |
- DCHECK(!pipe_.IsValid()); |
DCHECK(!worker_process_.IsValid()); |
} |
@@ -290,9 +300,18 @@ void WtsSessionProcessDelegate::Core::OnIOCompleted( |
// |bytes_transferred| is used in job object notifications to supply |
// the message ID; |context| carries process ID. |
- if (bytes_transferred == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO) { |
- caller_task_runner_->PostTask(FROM_HERE, |
- base::Bind(&Core::OnActiveProcessZero, this)); |
+ switch (bytes_transferred) { |
+ case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO: { |
+ caller_task_runner_->PostTask( |
+ FROM_HERE, base::Bind(&Core::OnActiveProcessZero, this)); |
+ break; |
+ } |
+ case JOB_OBJECT_MSG_NEW_PROCESS: { |
+ caller_task_runner_->PostTask( |
+ FROM_HERE, base::Bind(&Core::OnProcessLaunchDetected, this, |
+ reinterpret_cast<base::ProcessId>(context))); |
+ break; |
+ } |
} |
} |
@@ -306,37 +325,6 @@ bool WtsSessionProcessDelegate::Core::OnMessageReceived( |
void WtsSessionProcessDelegate::Core::OnChannelConnected(int32_t peer_pid) { |
DCHECK(caller_task_runner_->BelongsToCurrentThread()); |
- // Report the worker PID now if the worker process is launched indirectly. |
- // Note that in this case the pipe's security descriptor is the only |
- // protection against a malicious processed connecting to the pipe. |
- if (launch_elevated_) { |
- DWORD pid; |
- if (!get_named_pipe_client_pid_(pipe_.Get(), &pid)) { |
- PLOG(ERROR) << "Failed to retrive PID of the client"; |
- ReportFatalError(); |
- return; |
- } |
- |
- if (pid != static_cast<DWORD>(peer_pid)) { |
- LOG(ERROR) << "The actual client PID " << pid |
- << " does not match the one reported by the client: " |
- << peer_pid; |
- ReportFatalError(); |
- return; |
- } |
- |
- DWORD desired_access = |
- SYNCHRONIZE | PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION; |
- ScopedHandle worker_process(OpenProcess(desired_access, false, pid)); |
- if (!worker_process.IsValid()) { |
- PLOG(ERROR) << "Failed to open process " << pid; |
- ReportFatalError(); |
- return; |
- } |
- |
- ReportProcessLaunched(std::move(worker_process)); |
- } |
- |
if (event_handler_) |
event_handler_->OnChannelConnected(peer_pid); |
} |
@@ -350,7 +338,6 @@ void WtsSessionProcessDelegate::Core::OnChannelError() { |
void WtsSessionProcessDelegate::Core::DoLaunchProcess() { |
DCHECK(caller_task_runner_->BelongsToCurrentThread()); |
DCHECK(!channel_); |
- DCHECK(!pipe_.IsValid()); |
DCHECK(!worker_process_.IsValid()); |
base::CommandLine command_line(target_command_->argv()); |
@@ -375,25 +362,34 @@ void WtsSessionProcessDelegate::Core::DoLaunchProcess() { |
target_command_->GetProgram()); |
} |
- // Create the server end of the IPC channel. |
- std::string channel_name = IPC::Channel::GenerateUniqueRandomChannelID(); |
- ScopedHandle pipe; |
- if (!CreateIpcChannel(channel_name, channel_security_, &pipe)) { |
- ReportFatalError(); |
- return; |
- } |
- |
- // Wrap the pipe into an IPC channel. |
+ const std::string mojo_message_pipe_token = mojo::edk::GenerateRandomToken(); |
+ mojo_child_token_ = mojo::edk::GenerateRandomToken(); |
std::unique_ptr<IPC::ChannelProxy> channel( |
new IPC::ChannelProxy(this, io_task_runner_)); |
IPC::AttachmentBroker::GetGlobal()->RegisterCommunicationChannel( |
channel.get(), io_task_runner_); |
- channel->Init(IPC::ChannelHandle(pipe.Get()), IPC::Channel::MODE_SERVER, |
- /*create_pipe_now=*/true); |
- |
- // Pass the name of the IPC channel to use. |
- command_line.AppendSwitchNative(kDaemonPipeSwitchName, |
- base::UTF8ToWide(channel_name)); |
+ channel->Init(mojo::edk::CreateParentMessagePipe(mojo_message_pipe_token, |
+ mojo_child_token_) |
+ .release(), |
+ IPC::Channel::MODE_SERVER, /*create_pipe_now=*/true); |
+ command_line.AppendSwitchASCII(kMojoPipeToken, mojo_message_pipe_token); |
+ |
+ std::unique_ptr<mojo::edk::PlatformChannelPair> normal_mojo_channel; |
+ std::unique_ptr<mojo::edk::NamedPlatformChannelPair> elevated_mojo_channel; |
+ base::HandlesToInheritVector handles_to_inherit; |
+ if (launch_elevated_) { |
+ // Pass the name of the IPC channel to use. |
+ mojo::edk::NamedPlatformChannelPair::Options options; |
+ options.security_descriptor = base::UTF8ToUTF16(channel_security_); |
+ elevated_mojo_channel = |
+ base::MakeUnique<mojo::edk::NamedPlatformChannelPair>(options); |
+ elevated_mojo_channel->PrepareToPassClientHandleToChildProcess( |
+ &command_line); |
+ } else { |
+ normal_mojo_channel = base::MakeUnique<mojo::edk::PlatformChannelPair>(); |
+ normal_mojo_channel->PrepareToPassClientHandleToChildProcess( |
+ &command_line, &handles_to_inherit); |
+ } |
ScopedSd security_descriptor; |
std::unique_ptr<SECURITY_ATTRIBUTES> security_attributes; |
@@ -417,7 +413,7 @@ void WtsSessionProcessDelegate::Core::DoLaunchProcess() { |
if (!LaunchProcessWithToken( |
command_line.GetProgram(), command_line.GetCommandLineString(), |
session_token_.Get(), security_attributes.get(), |
- /* thread_attributes= */ nullptr, /* handles_to_inherit=*/{}, |
+ /* thread_attributes= */ nullptr, handles_to_inherit, |
/* creation_flags= */ CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB, |
base::UTF8ToUTF16(kDefaultDesktopName).c_str(), &worker_process, |
&worker_thread)) { |
@@ -440,13 +436,19 @@ void WtsSessionProcessDelegate::Core::DoLaunchProcess() { |
} |
channel_ = std::move(channel); |
- pipe_ = std::move(pipe); |
- // Report success if the worker process is lauched directly. Otherwise, PID of |
- // the client connected to the pipe will be used later. See |
- // OnChannelConnected(). |
- if (!launch_elevated_) |
- ReportProcessLaunched(std::move(worker_process)); |
+ if (launch_elevated_) { |
+ // When launching an elevated worker process, an intermediate launcher |
+ // process launches the worker process. Reporting the launch waits until the |
+ // worker process launch is detected. Until then, store the values needed in |
+ // fields. See OnProcessLaunchDetected for their use. |
+ elevated_server_handle_ = elevated_mojo_channel->PassServerHandle(); |
+ elevated_launcher_pid_ = GetProcessId(worker_process.Get()); |
+ DCHECK(elevated_server_handle_.is_valid()); |
+ } else { |
+ ReportProcessLaunched(std::move(worker_process), |
+ normal_mojo_channel->PassServerHandle()); |
+ } |
} |
void WtsSessionProcessDelegate::Core::DrainJobNotifications() { |
@@ -506,6 +508,29 @@ void WtsSessionProcessDelegate::Core::OnActiveProcessZero() { |
} |
} |
+void WtsSessionProcessDelegate::Core::OnProcessLaunchDetected( |
+ base::ProcessId pid) { |
+ DCHECK(caller_task_runner_->BelongsToCurrentThread()); |
+ if (!elevated_server_handle_.is_valid()) |
+ return; |
+ |
+ if (pid == elevated_launcher_pid_) |
+ return; |
+ |
+ DWORD desired_access = |
+ SYNCHRONIZE | PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION; |
+ base::win::ScopedHandle worker_process( |
+ OpenProcess(desired_access, false, pid)); |
+ if (!worker_process.IsValid()) { |
+ PLOG(ERROR) << "Failed to open process " << pid; |
+ ReportFatalError(); |
+ return; |
+ } |
+ elevated_launcher_pid_ = base::kNullProcessId; |
+ ReportProcessLaunched(std::move(worker_process), |
+ std::move(elevated_server_handle_)); |
+} |
+ |
void WtsSessionProcessDelegate::Core::ReportFatalError() { |
DCHECK(caller_task_runner_->BelongsToCurrentThread()); |
@@ -517,10 +542,15 @@ void WtsSessionProcessDelegate::Core::ReportFatalError() { |
} |
void WtsSessionProcessDelegate::Core::ReportProcessLaunched( |
- base::win::ScopedHandle worker_process) { |
+ base::win::ScopedHandle worker_process, |
+ mojo::edk::ScopedPlatformHandle server_handle) { |
DCHECK(caller_task_runner_->BelongsToCurrentThread()); |
DCHECK(!worker_process_.IsValid()); |
+ mojo::edk::ChildProcessLaunched(worker_process.Get(), |
+ std::move(server_handle), |
+ mojo_child_token_); |
+ mojo_child_token_.clear(); |
worker_process_ = std::move(worker_process); |
// Report a handle that can be used to wait for the worker process completion, |