Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(468)

Unified Diff: remoting/host/win/wts_session_process_launcher.cc

Issue 10831271: [Chromoting] Adding uiAccess='true' to the remoting_me2me_host.exe's manifest as it is required to … (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: CR feedback. Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: remoting/host/win/wts_session_process_launcher.cc
diff --git a/remoting/host/win/wts_session_process_launcher.cc b/remoting/host/win/wts_session_process_launcher.cc
index e57d37c93e03cd573d8db8ddb0e28543c9353256..6c35aa2b860a608a237c1918ef72ad0849cd3448 100644
--- a/remoting/host/win/wts_session_process_launcher.cc
+++ b/remoting/host/win/wts_session_process_launcher.cc
@@ -47,9 +47,14 @@ const int kMinLaunchDelaySeconds = 1;
const FilePath::CharType kMe2meHostBinaryName[] =
FILE_PATH_LITERAL("remoting_me2me_host.exe");
+const FilePath::CharType kMe2meServiceBinaryName[] =
+ FILE_PATH_LITERAL("remoting_service.exe");
+
// The IPC channel name is passed to the host in the command line.
const char kChromotingIpcSwitchName[] = "chromoting-ipc";
+const char kElevateSwitchName[] = "elevate";
+
// The command line parameters that should be copied from the service's command
// line to the host process.
const char* kCopiedSwitchNames[] = {
@@ -72,8 +77,17 @@ WtsSessionProcessLauncher::WtsSessionProcessLauncher(
attached_(false),
main_message_loop_(main_message_loop),
ipc_message_loop_(ipc_message_loop),
- monitor_(monitor) {
+ monitor_(monitor),
+ sandbox_state_(kSandboxUninitialized) {
monitor_->AddWtsConsoleObserver(this);
+
+ process_exit_event_.Set(CreateEvent(NULL, TRUE, FALSE, NULL));
+ CHECK(process_exit_event_.IsValid());
+
+ // Sandbox initalization has to be performed on the I/O thread.
+ ipc_message_loop_->PostTask(FROM_HERE, base::Bind(
+ &WtsSessionProcessLauncher::InitializeSandbox,
+ base::Unretained(this)));
}
WtsSessionProcessLauncher::~WtsSessionProcessLauncher() {
@@ -103,19 +117,22 @@ void WtsSessionProcessLauncher::LaunchProcess() {
void WtsSessionProcessLauncher::OnLauncherStopped() {
DCHECK(main_message_loop_->BelongsToCurrentThread());
- DWORD exit_code;
- if (!::GetExitCodeProcess(worker_process_, &exit_code)) {
- LOG_GETLASTERROR(INFO)
- << "Failed to query the exit code of the worker process";
- exit_code = CONTROL_C_EXIT;
+ DWORD exit_code = CONTROL_C_EXIT;
+ if (worker_process_.IsValid()) {
+ if (!::GetExitCodeProcess(worker_process_, &exit_code)) {
+ LOG_GETLASTERROR(INFO)
+ << "Failed to query the exit code of the worker process";
+ exit_code = CONTROL_C_EXIT;
+ }
+
+ worker_process_.Close();
}
launcher_.reset(NULL);
- worker_process_.Close();
// Do not relaunch the worker process if the caller has asked us to stop.
if (stoppable_state() != Stoppable::kRunning) {
- CompleteStopping();
+ Stop();
return;
}
@@ -148,12 +165,34 @@ void WtsSessionProcessLauncher::OnLauncherStopped() {
}
}
+void WtsSessionProcessLauncher::OnIOCompleted(
+ base::MessagePumpForIO::IOContext* context,
+ DWORD bytes_transfered,
+ DWORD error) {
+ DCHECK(ipc_message_loop_->BelongsToCurrentThread());
+
+ // |bytes_transfered| is repurposed by the job object notifications to be
+ // the message ID.
+ BOOL result;
+ switch (bytes_transfered) {
+ case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO:
+ result = SetEvent(process_exit_event_);
+ CHECK(result);
+ break;
+ }
+}
+
bool WtsSessionProcessLauncher::DoLaunchProcess(
const std::string& channel_name,
ScopedHandle* process_exit_event_out) {
DCHECK(main_message_loop_->BelongsToCurrentThread());
DCHECK(!worker_process_.IsValid());
+ // Wait until the sandbox is ready.
+ if (!job_.IsValid()) {
+ return false;
+ }
+
// Construct the host binary name.
FilePath dir_path;
if (!PathService::Get(base::DIR_EXE, &dir_path)) {
@@ -161,28 +200,49 @@ bool WtsSessionProcessLauncher::DoLaunchProcess(
return false;
}
FilePath host_binary = dir_path.Append(kMe2meHostBinaryName);
+ FilePath service_binary = dir_path.Append(kMe2meServiceBinaryName);
// Create the host process command line passing the name of the IPC channel
// to use and copying known switches from the service's command line.
- CommandLine command_line(host_binary);
+ CommandLine command_line(service_binary);
+ command_line.AppendSwitchPath(kElevateSwitchName, host_binary);
command_line.AppendSwitchNative(kChromotingIpcSwitchName,
UTF8ToWide(channel_name));
command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(),
kCopiedSwitchNames,
_countof(kCopiedSwitchNames));
+ BOOL result = ResetEvent(process_exit_event_);
+ CHECK(result);
+
// Try to launch the process and attach an object watcher to the returned
// handle so that we get notified when the process terminates.
- if (!LaunchProcessWithToken(host_binary,
+ base::win::ScopedHandle worker_process;
+ base::win::ScopedHandle worker_thread;
+ if (!LaunchProcessWithToken(service_binary,
command_line.GetCommandLineString(),
session_token_,
- &worker_process_)) {
+ CREATE_SUSPENDED,
+ &worker_process,
+ &worker_thread)) {
+ return false;
+ }
+
+ if (!AssignProcessToJobObject(job_, worker_process)) {
+ LOG_GETLASTERROR(ERROR) << "Failed to assign the worker to the job object";
+ TerminateProcess(worker_process, CONTROL_C_EXIT);
+ return false;
+ }
+
+ if (!ResumeThread(worker_thread)) {
+ LOG_GETLASTERROR(ERROR) << "Failed to resume the worker thread";
+ DoKillProcess(CONTROL_C_EXIT);
return false;
}
ScopedHandle process_exit_event;
if (!DuplicateHandle(GetCurrentProcess(),
- worker_process_,
+ process_exit_event_,
GetCurrentProcess(),
process_exit_event.Receive(),
SYNCHRONIZE,
@@ -200,21 +260,21 @@ bool WtsSessionProcessLauncher::DoLaunchProcess(
void WtsSessionProcessLauncher::DoKillProcess(DWORD exit_code) {
DCHECK(main_message_loop_->BelongsToCurrentThread());
- if (worker_process_.IsValid()) {
- TerminateProcess(worker_process_, exit_code);
+ if (job_.IsValid()) {
+ TerminateJobObject(job_, exit_code);
}
}
void WtsSessionProcessLauncher::OnChannelConnected(DWORD peer_pid) {
DCHECK(main_message_loop_->BelongsToCurrentThread());
- DWORD expected_pid = GetProcessId(worker_process_);
- if (peer_pid != expected_pid) {
- LOG(ERROR)
- << "Unexpected client connected: expected=" << expected_pid
- << ", actual=" << peer_pid;
- Stop();
- }
+ // Nothing to validate. Only processes running as SYSTEM can connect to
+ // the pipe, so if it was a malicious process the game would be over already.
+
+ // Get a handle to the peer process so we could query its exit code later.
+ // Ignore the error. If the processes cannot be open the error code is generic
+ // |CONTROL_C_EXIT|.
+ worker_process_.Set(OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, peer_pid));
}
bool WtsSessionProcessLauncher::OnMessageReceived(const IPC::Message& message) {
@@ -284,8 +344,105 @@ void WtsSessionProcessLauncher::DoStop() {
OnSessionDetached();
}
- if (launcher_.get() == NULL) {
- CompleteStopping();
+ job_.Close();
+
+ // Drain the completion queue to make sure all job object notification have
+ // been received.
+ if (sandbox_state_ > kSandboxUninitialized) {
+ DCHECK_EQ(sandbox_state_, kSandboxRunning);
+ sandbox_state_ = kSandboxStopping;
+ ipc_message_loop_->PostTask(FROM_HERE, base::Bind(
+ &WtsSessionProcessLauncher::DrainSandboxNotifications,
+ base::Unretained(this)));
+ }
+
+ // Wait for |launcher_| to be completely stopped.
+ if (launcher_.get() != NULL) {
+ return;
+ }
+
+ // Wait for the completion queue to be drained.
+ if (sandbox_state_ != kSandboxUninitialized &&
+ sandbox_state_ != kSandboxStopped) {
+ return;
+ }
+
+ CompleteStopping();
+}
+
+void WtsSessionProcessLauncher::DrainSandboxNotifications() {
+ DCHECK(ipc_message_loop_->BelongsToCurrentThread());
+
+ // DrainSandboxNotifications() is posted after the job object is destroyed, so
+ // by this time all notifications from the job object have been processed
+ // already. Let the main thread know that the queue has been drained.
+ main_message_loop_->PostTask(FROM_HERE, base::Bind(
+ &WtsSessionProcessLauncher::DrainSandboxNotificationsCompleted,
+ base::Unretained(this)));
+}
+
+void WtsSessionProcessLauncher::DrainSandboxNotificationsCompleted() {
+ DCHECK(main_message_loop_->BelongsToCurrentThread());
+ DCHECK_EQ(sandbox_state_, kSandboxStopping);
+
+ sandbox_state_ = kSandboxStopped;
+ Stop();
+}
+
+void WtsSessionProcessLauncher::InitializeSandbox() {
+ DCHECK(ipc_message_loop_->BelongsToCurrentThread());
+
+ ScopedHandle job;
+ job.Set(CreateJobObject(NULL, NULL));
+ if (!job.IsValid()) {
+ LOG_GETLASTERROR(ERROR) << "Failed to create a job object";
+ return;
+ }
+
+ // Limit number of active processes in the job to two (the process performing
+ // elevation and the host) and make sure that all processes will be killed
+ // once the job object is destroyed.
+ JOBOBJECT_EXTENDED_LIMIT_INFORMATION info;
+ memset(&info, 0, sizeof(info));
+ info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_ACTIVE_PROCESS |
+ JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+ info.BasicLimitInformation.ActiveProcessLimit = 2;
+ if (!SetInformationJobObject(job,
+ JobObjectExtendedLimitInformation,
+ &info,
+ sizeof(info))) {
+ LOG_GETLASTERROR(ERROR) << "Failed to set limits on the job object";
+ return;
+ }
+
+ // Register the job object with the completion port in the I/O thread to
+ // receive job notifications.
+ if (!MessageLoopForIO::current()->RegisterJobObject(job, this)) {
+ LOG_GETLASTERROR(ERROR)
+ << "Failed to associate the job object with a completion port";
+ return;
+ }
+
+ // ScopedHandle is not compatible with base::Passed, so we wrap it to a scoped
+ // pointer.
+ scoped_ptr<ScopedHandle> job_wrapper(new ScopedHandle());
+ *job_wrapper = job.Pass();
+
+ // Let the main thread know that initialization is complete.
+ main_message_loop_->PostTask(FROM_HERE, base::Bind(
+ &WtsSessionProcessLauncher::InitializeSandboxCompleted,
+ base::Unretained(this), base::Passed(&job_wrapper)));
+}
+
+void WtsSessionProcessLauncher::InitializeSandboxCompleted(
+ scoped_ptr<ScopedHandle> job) {
+ DCHECK(main_message_loop_->BelongsToCurrentThread());
+ DCHECK(!job_.IsValid());
+ DCHECK_EQ(sandbox_state_, kSandboxUninitialized);
+
+ if (stoppable_state() == Stoppable::kRunning) {
+ job_ = job->Pass();
+ sandbox_state_ = kSandboxRunning;
}
}

Powered by Google App Engine
This is Rietveld 408576698