Commit 41c99b46 authored by sammc's avatar sammc Committed by Commit bot

Use ChannelMojo between the remoting daemon and network processes.

This refactors LaunchProcessWithToken to take an explicit list of
handles for the child to inherit (copied from the equivalent process
launch code in //base).

This also changes WorkerProcessLauncherTest to use ChannelMojo.

BUG=604282

Review-Url: https://codereview.chromium.org/2424353002
Cr-Commit-Position: refs/heads/master@{#427238}
parent d3088b38
......@@ -116,6 +116,7 @@ source_set("unit_tests") {
":base",
":breakpad",
"//base",
"//mojo/edk/system",
"//net:test_support",
"//testing/gmock",
"//testing/gtest",
......
include_rules = [
"+breakpad",
"+google/protobuf",
"+mojo/edk/embedder",
"+net",
"+third_party/zlib",
"+ui/base",
......
......@@ -34,6 +34,10 @@ class AutoThreadTaskRunner : public base::SingleThreadTaskRunner {
base::TimeDelta delay) override;
bool RunsTasksOnCurrentThread() const override;
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner() {
return task_runner_;
}
private:
~AutoThreadTaskRunner() override;
......
......@@ -4,10 +4,13 @@
#include "base/test/launcher/unit_test_launcher.h"
#include "base/test/test_suite.h"
#include "mojo/edk/embedder/embedder.h"
int main(int argc, char** argv) {
base::TestSuite test_suite(argc, argv);
mojo::edk::Init();
return base::LaunchUnitTests(
argc, argv, base::Bind(&base::TestSuite::Run,
base::Unretained(&test_suite)));
......
......@@ -758,6 +758,10 @@ if (enable_me2me_host) {
if (is_mac && is_official_build) {
sources += [ "internal/internal_mac-inl.h" ]
}
if (remoting_multi_process != 0) {
deps += [ "//mojo/edk/system" ]
}
}
if (is_win) {
......
......@@ -6,6 +6,8 @@ include_rules = [
"+extensions/browser/api/messaging",
"+google_apis",
"+jingle/glue",
"+mojo/public",
"+mojo/edk/embedder",
"+net",
"+remoting/codec",
"+remoting/protocol",
......
......@@ -25,6 +25,7 @@
#include "base/win/win_util.h"
#include "ipc/ipc_message.h"
#include "ipc/ipc_message_macros.h"
#include "mojo/edk/embedder/scoped_ipc_support.h"
#include "remoting/base/auto_thread_task_runner.h"
#include "remoting/base/scoped_sc_handle_win.h"
#include "remoting/host/branding.h"
......@@ -105,6 +106,11 @@ class DaemonProcessWin : public DaemonProcess {
bool OpenPairingRegistry();
private:
// Mojo keeps the task runner passed to it alive forever, so an
// AutoThreadTaskRunner should not be passed to it. Otherwise, the process may
// never shut down cleanly.
mojo::edk::ScopedIPCSupport ipc_support_;
std::unique_ptr<WorkerProcessLauncher> network_launcher_;
// Handle of the network process.
......@@ -120,8 +126,8 @@ DaemonProcessWin::DaemonProcessWin(
scoped_refptr<AutoThreadTaskRunner> caller_task_runner,
scoped_refptr<AutoThreadTaskRunner> io_task_runner,
const base::Closure& stopped_callback)
: DaemonProcess(caller_task_runner, io_task_runner, stopped_callback) {
}
: DaemonProcess(caller_task_runner, io_task_runner, stopped_callback),
ipc_support_(io_task_runner->task_runner()) {}
DaemonProcessWin::~DaemonProcessWin() {
}
......
......@@ -19,6 +19,7 @@
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "mojo/edk/embedder/embedder.h"
#include "remoting/base/breakpad.h"
#include "remoting/host/host_exit_codes.h"
#include "remoting/host/logging.h"
......@@ -211,6 +212,10 @@ int HostMain(int argc, char** argv) {
remoting::LoadResources("");
#if defined(REMOTING_MULTI_PROCESS)
mojo::edk::Init();
#endif
// Invoke the entry point.
int exit_code = main_routine();
if (exit_code == kInvalidCommandLineExitCode) {
......
......@@ -33,6 +33,9 @@
#include "ipc/ipc_channel_proxy.h"
#include "ipc/ipc_listener.h"
#include "jingle/glue/thread_wrapper.h"
#include "mojo/edk/embedder/embedder.h"
#include "mojo/edk/embedder/platform_channel_pair.h"
#include "mojo/edk/embedder/scoped_ipc_support.h"
#include "net/base/network_change_notifier.h"
#include "net/base/url_util.h"
#include "net/socket/client_socket_factory.h"
......@@ -410,6 +413,8 @@ class HostProcess : public ConfigWatcher::Delegate,
scoped_refptr<HostProcess> self_;
#if defined(REMOTING_MULTI_PROCESS)
std::unique_ptr<mojo::edk::ScopedIPCSupport> ipc_support_;
// Accessed on the UI thread.
std::unique_ptr<IPC::ChannelProxy> daemon_channel_;
......@@ -454,25 +459,14 @@ HostProcess::~HostProcess() {
bool HostProcess::InitWithCommandLine(const base::CommandLine* cmd_line) {
#if defined(REMOTING_MULTI_PROCESS)
// Parse the handle value and convert it to a handle/file descriptor.
std::string channel_name =
cmd_line->GetSwitchValueASCII(kDaemonPipeSwitchName);
int pipe_handle = 0;
if (channel_name.empty() ||
!base::StringToInt(channel_name, &pipe_handle)) {
LOG(ERROR) << "Invalid '" << kDaemonPipeSwitchName
<< "' value: " << channel_name;
return false;
}
#if defined(OS_WIN)
base::win::ScopedHandle pipe(reinterpret_cast<HANDLE>(pipe_handle));
IPC::ChannelHandle channel_handle(pipe.Get());
#elif defined(OS_POSIX)
base::FileDescriptor pipe(pipe_handle, true);
IPC::ChannelHandle channel_handle(channel_name, pipe);
#endif // defined(OS_POSIX)
// Mojo keeps the task runner passed to it alive forever, so an
// AutoThreadTaskRunner should not be passed to it. Otherwise, the process may
// never shut down cleanly.
ipc_support_ = base::MakeUnique<mojo::edk::ScopedIPCSupport>(
context_->network_task_runner()->task_runner());
mojo::edk::SetParentPipeHandle(
mojo::edk::PlatformChannelPair::PassClientHandleFromParentProcess(
*cmd_line));
// Connect to the daemon process.
daemon_channel_.reset(
......@@ -481,7 +475,10 @@ bool HostProcess::InitWithCommandLine(const base::CommandLine* cmd_line) {
IPC::AttachmentBroker* broker = IPC::AttachmentBroker::GetGlobal();
if (broker && !broker->IsPrivilegedBroker())
broker->RegisterBrokerCommunicationChannel(daemon_channel_.get());
daemon_channel_->Init(channel_handle, IPC::Channel::MODE_CLIENT,
daemon_channel_->Init(mojo::edk::CreateChildMessagePipe(
cmd_line->GetSwitchValueASCII(kMojoPipeToken))
.release(),
IPC::Channel::MODE_CLIENT,
/*create_pipe_now=*/true);
#else // !defined(REMOTING_MULTI_PROCESS)
......
......@@ -24,4 +24,6 @@ const char kParentWindowSwitchName[] = "parent-window";
const char kInputSwitchName[] = "input";
const char kOutputSwitchName[] = "output";
const char kMojoPipeToken[] = "mojo-pipe-token";
} // namespace remoting
......@@ -41,6 +41,10 @@ extern const char kInputSwitchName[];
// Name of the pipe used to communicate from the child to the parent process.
extern const char kOutputSwitchName[];
// Token used to create a message pipe between a pair of child and parent
// processes.
extern const char kMojoPipeToken[];
} // namespace remoting
#endif // REMOTING_HOST_SWITCHES_H_
......@@ -378,6 +378,7 @@ shared_library("remoting_core") {
"//base/third_party/dynamic_annotations",
"//build/win:default_exe_manifest",
"//ipc",
"//mojo/edk/system",
"//net",
"//remoting/base",
"//remoting/base:breakpad",
......
......@@ -11,6 +11,7 @@
#include "base/logging.h"
#include "base/win/scoped_handle.h"
#include "base/win/scoped_process_information.h"
#include "base/win/startup_information.h"
using base::win::ScopedHandle;
......@@ -75,9 +76,6 @@ bool CreatePrivilegedToken(ScopedHandle* token_out) {
namespace remoting {
base::LazyInstance<base::Lock>::Leaky g_inherit_handles_lock =
LAZY_INSTANCE_INITIALIZER;
// Creates a copy of the current process token for the given |session_id| so
// it can be used to launch a process in that session.
bool CreateSessionToken(uint32_t session_id, ScopedHandle* token_out) {
......@@ -119,36 +117,62 @@ bool CreateSessionToken(uint32_t session_id, ScopedHandle* token_out) {
return true;
}
bool LaunchProcessWithToken(const base::FilePath& binary,
const base::CommandLine::StringType& command_line,
HANDLE user_token,
SECURITY_ATTRIBUTES* process_attributes,
SECURITY_ATTRIBUTES* thread_attributes,
bool inherit_handles,
DWORD creation_flags,
const base::char16* desktop_name,
ScopedHandle* process_out,
ScopedHandle* thread_out) {
bool LaunchProcessWithToken(
const base::FilePath& binary,
const base::CommandLine::StringType& command_line,
HANDLE user_token,
SECURITY_ATTRIBUTES* process_attributes,
SECURITY_ATTRIBUTES* thread_attributes,
const base::HandlesToInheritVector& handles_to_inherit,
DWORD creation_flags,
const base::char16* desktop_name,
ScopedHandle* process_out,
ScopedHandle* thread_out) {
base::FilePath::StringType application_name = binary.value();
STARTUPINFOW startup_info;
memset(&startup_info, 0, sizeof(startup_info));
startup_info.cb = sizeof(startup_info);
base::win::StartupInformation startup_info_wrapper;
STARTUPINFO* startup_info = startup_info_wrapper.startup_info();
if (desktop_name)
startup_info.lpDesktop = const_cast<base::char16*>(desktop_name);
startup_info->lpDesktop = const_cast<base::char16*>(desktop_name);
bool inherit_handles = false;
if (!handles_to_inherit.empty()) {
if (handles_to_inherit.size() >
std::numeric_limits<DWORD>::max() / sizeof(HANDLE)) {
DLOG(ERROR) << "Too many handles to inherit.";
return false;
}
// Ensure the handles can be inherited.
for (HANDLE handle : handles_to_inherit) {
BOOL result = SetHandleInformation(handle, HANDLE_FLAG_INHERIT,
HANDLE_FLAG_INHERIT);
PCHECK(result);
}
if (!startup_info_wrapper.InitializeProcThreadAttributeList(
/* attribute_count= */ 1)) {
PLOG(ERROR) << "InitializeProcThreadAttributeList()";
return false;
}
if (!startup_info_wrapper.UpdateProcThreadAttribute(
PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
const_cast<HANDLE*>(&handles_to_inherit.at(0)),
static_cast<DWORD>(handles_to_inherit.size() * sizeof(HANDLE)))) {
PLOG(ERROR) << "UpdateProcThreadAttribute()";
return false;
}
inherit_handles = true;
creation_flags |= EXTENDED_STARTUPINFO_PRESENT;
}
PROCESS_INFORMATION temp_process_info = {};
BOOL result = CreateProcessAsUser(user_token,
application_name.c_str(),
BOOL result = CreateProcessAsUser(user_token, application_name.c_str(),
const_cast<LPWSTR>(command_line.c_str()),
process_attributes,
thread_attributes,
inherit_handles,
creation_flags,
nullptr,
nullptr,
&startup_info,
&temp_process_info);
process_attributes, thread_attributes,
inherit_handles, creation_flags, nullptr,
nullptr, startup_info, &temp_process_info);
if (!result) {
PLOG(ERROR) << "Failed to launch a process with a user token";
......
......@@ -14,16 +14,12 @@
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/lazy_instance.h"
#include "base/process/launch.h"
#include "base/synchronization/lock.h"
#include "base/win/scoped_handle.h"
namespace remoting {
// This lock should be taken when creating handles that will be inherited by
// a child process. Without it the child process can inherit handles created for
// a different child process started at the same time.
extern base::LazyInstance<base::Lock>::Leaky g_inherit_handles_lock;
// Creates a copy of the current process token for the given |session_id| so
// it can be used to launch a process in that session.
bool CreateSessionToken(uint32_t session_id,
......@@ -31,19 +27,20 @@ bool CreateSessionToken(uint32_t session_id,
// Launches |binary| in the security context of the user represented by
// |user_token|. The session ID specified by the token is respected as well.
// The other parameters are passed directly to CreateProcessAsUser().
// If |inherit_handles| is true |g_inherit_handles_lock| should be taken while
// any inheritable handles are open.
bool LaunchProcessWithToken(const base::FilePath& binary,
const base::CommandLine::StringType& command_line,
HANDLE user_token,
SECURITY_ATTRIBUTES* process_attributes,
SECURITY_ATTRIBUTES* thread_attributes,
bool inherit_handles,
DWORD creation_flags,
const base::char16* desktop_name,
base::win::ScopedHandle* process_out,
base::win::ScopedHandle* thread_out);
// If |handles_to_inherit| is non-empty, these handles will be inherited by the
// new process. The other parameters are passed directly to
// CreateProcessAsUser().
bool LaunchProcessWithToken(
const base::FilePath& binary,
const base::CommandLine::StringType& command_line,
HANDLE user_token,
SECURITY_ATTRIBUTES* process_attributes,
SECURITY_ATTRIBUTES* thread_attributes,
const base::HandlesToInheritVector& handles_to_inherit,
DWORD creation_flags,
const base::char16* desktop_name,
base::win::ScopedHandle* process_out,
base::win::ScopedHandle* thread_out);
} // namespace remoting
......
......@@ -26,8 +26,9 @@
#include "ipc/ipc_channel.h"
#include "ipc/ipc_channel_proxy.h"
#include "ipc/ipc_message.h"
#include "mojo/edk/embedder/embedder.h"
#include "mojo/edk/embedder/platform_channel_pair.h"
#include "remoting/base/typed_buffer.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"
......@@ -239,8 +240,6 @@ void UnprivilegedProcessDelegate::LaunchProcess(
event_handler_ = event_handler;
std::unique_ptr<IPC::ChannelProxy> server;
// Create a restricted token that will be used to run the worker process.
ScopedHandle token;
if (!CreateRestrictedToken(&token)) {
......@@ -277,47 +276,50 @@ void UnprivilegedProcessDelegate::LaunchProcess(
thread_attributes.lpSecurityDescriptor = thread_sd.get();
thread_attributes.bInheritHandle = FALSE;
// Create our own window station and desktop accessible by |logon_sid|.
WindowStationAndDesktop handles;
if (!CreateWindowStationAndDesktop(std::move(logon_sid), &handles)) {
PLOG(ERROR) << "Failed to create a window station and desktop";
ReportFatalError();
return;
}
const std::string mojo_child_token = mojo::edk::GenerateRandomToken();
const std::string mojo_message_pipe_token = mojo::edk::GenerateRandomToken();
std::unique_ptr<IPC::ChannelProxy> server =
base::MakeUnique<IPC::ChannelProxy>(this, io_task_runner_);
IPC::AttachmentBroker::GetGlobal()->RegisterCommunicationChannel(
server.get(), io_task_runner_);
server->Init(mojo::edk::CreateParentMessagePipe(mojo_message_pipe_token,
mojo_child_token)
.release(),
IPC::Channel::MODE_SERVER, /*create_pipe_now=*/true);
base::CommandLine command_line(target_command_->argv());
command_line.AppendSwitchASCII(kMojoPipeToken, mojo_message_pipe_token);
base::HandlesToInheritVector handles_to_inherit = {
handles.desktop(), handles.window_station(),
};
mojo::edk::PlatformChannelPair mojo_channel;
mojo_channel.PrepareToPassClientHandleToChildProcess(&command_line,
&handles_to_inherit);
// Try to launch the worker process. The launched process inherits
// the window station, desktop and pipe handles, created above.
ScopedHandle worker_process;
{
// Take a lock when any inheritable handles are open to make sure that only
// one process inherits them.
base::AutoLock lock(g_inherit_handles_lock.Get());
// Create a connected IPC channel.
base::File client;
if (!CreateConnectedIpcChannel(io_task_runner_, this, &client, &server)) {
ReportFatalError();
return;
}
// Convert the handle value into a decimal integer. Handle values are 32bit
// even on 64bit platforms.
std::string pipe_handle = base::StringPrintf(
"%d", reinterpret_cast<ULONG_PTR>(client.GetPlatformFile()));
// Pass the IPC channel via the command line.
base::CommandLine command_line(target_command_->argv());
command_line.AppendSwitchASCII(kDaemonPipeSwitchName, pipe_handle);
// Create our own window station and desktop accessible by |logon_sid|.
WindowStationAndDesktop handles;
if (!CreateWindowStationAndDesktop(std::move(logon_sid), &handles)) {
PLOG(ERROR) << "Failed to create a window station and desktop";
ReportFatalError();
return;
}
// Try to launch the worker process. The launched process inherits
// the window station, desktop and pipe handles, created above.
ScopedHandle worker_thread;
if (!LaunchProcessWithToken(
command_line.GetProgram(), command_line.GetCommandLineString(),
token.Get(), &process_attributes, &thread_attributes, true, 0,
nullptr, &worker_process, &worker_thread)) {
ReportFatalError();
return;
}
ScopedHandle worker_thread;
if (!LaunchProcessWithToken(
command_line.GetProgram(), command_line.GetCommandLineString(),
token.Get(), &process_attributes, &thread_attributes,
handles_to_inherit, /* creation_flags= */ 0,
/* thread_attributes= */ nullptr, &worker_process, &worker_thread)) {
mojo::edk::ChildProcessLaunchFailed(mojo_child_token);
ReportFatalError();
return;
}
mojo::edk::ChildProcessLaunched(
worker_process.Get(), mojo_channel.PassServerHandle(), mojo_child_token);
channel_ = std::move(server);
......
......@@ -23,7 +23,6 @@
#include "remoting/base/auto_thread_task_runner.h"
#include "remoting/host/chromoting_messages.h"
#include "remoting/host/host_exit_codes.h"
#include "remoting/host/ipc_util.h"
#include "remoting/host/win/launch_process_with_token.h"
#include "remoting/host/worker_process_ipc_delegate.h"
#include "testing/gmock/include/gmock/gmock.h"
......@@ -44,8 +43,6 @@ namespace remoting {
namespace {
const char kIpcSecurityDescriptor[] = "D:(A;;GA;;;AU)";
class MockProcessLauncherDelegate : public WorkerProcessLauncher::Delegate {
public:
MockProcessLauncherDelegate() {}
......@@ -171,8 +168,8 @@ class WorkerProcessLauncherTest
// Implements WorkerProcessLauncher::Delegate.
std::unique_ptr<MockProcessLauncherDelegate> launcher_delegate_;
// The name of the IPC channel.
std::string channel_name_;
// The client handle to the channel.
mojo::ScopedMessagePipeHandle client_channel_handle_;
// Client and server ends of the IPC channel.
std::unique_ptr<IPC::ChannelProxy> channel_client_;
......@@ -281,10 +278,9 @@ void WorkerProcessLauncherTest::TerminateWorker(DWORD exit_code) {
}
void WorkerProcessLauncherTest::ConnectClient() {
channel_client_ = IPC::ChannelProxy::Create(IPC::ChannelHandle(channel_name_),
channel_client_ = IPC::ChannelProxy::Create(client_channel_handle_.release(),
IPC::Channel::MODE_CLIENT,
&client_listener_,
task_runner_);
&client_listener_, task_runner_);
// Pretend that |kLaunchSuccessTimeoutSeconds| passed since launching
// the worker process. This will make the backoff algorithm think that this
......@@ -330,7 +326,7 @@ void WorkerProcessLauncherTest::StartWorker() {
void WorkerProcessLauncherTest::StopWorker() {
launcher_.reset();
DisconnectClient();
channel_name_.clear();
client_channel_handle_.reset();
channel_server_.reset();
task_runner_ = nullptr;
}
......@@ -366,14 +362,12 @@ void WorkerProcessLauncherTest::DoLaunchProcess() {
worker_process_.Set(process_information.TakeProcessHandle());
ASSERT_TRUE(worker_process_.IsValid());
channel_name_ = IPC::Channel::GenerateUniqueRandomChannelID();
ScopedHandle pipe;
ASSERT_TRUE(CreateIpcChannel(channel_name_, kIpcSecurityDescriptor, &pipe));
mojo::MessagePipe pipe;
client_channel_handle_ = std::move(pipe.handle0);
// Wrap the pipe into an IPC channel.
channel_server_ = IPC::ChannelProxy::Create(
IPC::ChannelHandle(pipe.Get()), IPC::Channel::MODE_SERVER, this,
task_runner_);
pipe.handle1.release(), IPC::Channel::MODE_SERVER, this, task_runner_);
HANDLE temp_handle;
ASSERT_TRUE(DuplicateHandle(GetCurrentProcess(), worker_process_.Get(),
......
......@@ -414,16 +414,13 @@ void WtsSessionProcessDelegate::Core::DoLaunchProcess() {
// Try to launch the process.
ScopedHandle worker_process;
ScopedHandle worker_thread;
if (!LaunchProcessWithToken(command_line.GetProgram(),
command_line.GetCommandLineString(),
session_token_.Get(),
security_attributes.get(),
nullptr,
false,
CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB,
base::UTF8ToUTF16(kDefaultDesktopName).c_str(),
&worker_process,
&worker_thread)) {
if (!LaunchProcessWithToken(
command_line.GetProgram(), command_line.GetCommandLineString(),
session_token_.Get(), security_attributes.get(),
/* thread_attributes= */ nullptr, /* handles_to_inherit=*/{},
/* creation_flags= */ CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB,
base::UTF8ToUTF16(kDefaultDesktopName).c_str(), &worker_process,
&worker_thread)) {
ReportFatalError();
return;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment