| Index: sandbox/src/broker_services.cc
|
| diff --git a/sandbox/src/broker_services.cc b/sandbox/src/broker_services.cc
|
| deleted file mode 100644
|
| index 7f46abedeb9fc8bbf9ddad90a010569b70bd88a6..0000000000000000000000000000000000000000
|
| --- a/sandbox/src/broker_services.cc
|
| +++ /dev/null
|
| @@ -1,405 +0,0 @@
|
| -// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -#include "sandbox/src/broker_services.h"
|
| -
|
| -#include "base/logging.h"
|
| -#include "base/memory/scoped_ptr.h"
|
| -#include "base/threading/platform_thread.h"
|
| -#include "base/win/scoped_handle.h"
|
| -#include "base/win/scoped_process_information.h"
|
| -#include "sandbox/src/sandbox_policy_base.h"
|
| -#include "sandbox/src/sandbox.h"
|
| -#include "sandbox/src/target_process.h"
|
| -#include "sandbox/src/win2k_threadpool.h"
|
| -#include "sandbox/src/win_utils.h"
|
| -
|
| -namespace {
|
| -
|
| -// Utility function to associate a completion port to a job object.
|
| -bool AssociateCompletionPort(HANDLE job, HANDLE port, void* key) {
|
| - JOBOBJECT_ASSOCIATE_COMPLETION_PORT job_acp = { key, port };
|
| - return ::SetInformationJobObject(job,
|
| - JobObjectAssociateCompletionPortInformation,
|
| - &job_acp, sizeof(job_acp))? true : false;
|
| -}
|
| -
|
| -// Utility function to do the cleanup necessary when something goes wrong
|
| -// while in SpawnTarget and we must terminate the target process.
|
| -sandbox::ResultCode SpawnCleanup(sandbox::TargetProcess* target, DWORD error) {
|
| - if (0 == error)
|
| - error = ::GetLastError();
|
| -
|
| - target->Terminate();
|
| - delete target;
|
| - ::SetLastError(error);
|
| - return sandbox::SBOX_ERROR_GENERIC;
|
| -}
|
| -
|
| -// the different commands that you can send to the worker thread that
|
| -// executes TargetEventsThread().
|
| -enum {
|
| - THREAD_CTRL_NONE,
|
| - THREAD_CTRL_REMOVE_PEER,
|
| - THREAD_CTRL_QUIT,
|
| - THREAD_CTRL_LAST,
|
| -};
|
| -
|
| -// Helper structure that allows the Broker to associate a job notification
|
| -// with a job object and with a policy.
|
| -struct JobTracker {
|
| - HANDLE job;
|
| - sandbox::PolicyBase* policy;
|
| - JobTracker(HANDLE cjob, sandbox::PolicyBase* cpolicy)
|
| - : job(cjob), policy(cpolicy) {
|
| - }
|
| -};
|
| -
|
| -// Helper structure that allows the broker to track peer processes
|
| -struct PeerTracker {
|
| - HANDLE wait_object;
|
| - base::win::ScopedHandle process;
|
| - DWORD id;
|
| - HANDLE job_port;
|
| - PeerTracker(DWORD process_id, HANDLE broker_job_port)
|
| - : wait_object(NULL), id(process_id), job_port(broker_job_port) {
|
| - }
|
| -};
|
| -
|
| -void DeregisterPeerTracker(PeerTracker* peer) {
|
| - // Deregistration shouldn't fail, but we leak rather than crash if it does.
|
| - if (::UnregisterWaitEx(peer->wait_object, INVALID_HANDLE_VALUE)) {
|
| - delete peer;
|
| - } else {
|
| - NOTREACHED();
|
| - }
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -namespace sandbox {
|
| -
|
| -BrokerServicesBase::BrokerServicesBase()
|
| - : thread_pool_(NULL), job_port_(NULL), no_targets_(NULL),
|
| - job_thread_(NULL) {
|
| -}
|
| -
|
| -// The broker uses a dedicated worker thread that services the job completion
|
| -// port to perform policy notifications and associated cleanup tasks.
|
| -ResultCode BrokerServicesBase::Init() {
|
| - if ((NULL != job_port_) || (NULL != thread_pool_))
|
| - return SBOX_ERROR_UNEXPECTED_CALL;
|
| -
|
| - ::InitializeCriticalSection(&lock_);
|
| -
|
| - job_port_ = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
|
| - if (NULL == job_port_)
|
| - return SBOX_ERROR_GENERIC;
|
| -
|
| - no_targets_ = ::CreateEventW(NULL, TRUE, FALSE, NULL);
|
| -
|
| - job_thread_ = ::CreateThread(NULL, 0, // Default security and stack.
|
| - TargetEventsThread, this, NULL, NULL);
|
| - if (NULL == job_thread_)
|
| - return SBOX_ERROR_GENERIC;
|
| -
|
| - return SBOX_ALL_OK;
|
| -}
|
| -
|
| -// The destructor should only be called when the Broker process is terminating.
|
| -// Since BrokerServicesBase is a singleton, this is called from the CRT
|
| -// termination handlers, if this code lives on a DLL it is called during
|
| -// DLL_PROCESS_DETACH in other words, holding the loader lock, so we cannot
|
| -// wait for threads here.
|
| -BrokerServicesBase::~BrokerServicesBase() {
|
| - // If there is no port Init() was never called successfully.
|
| - if (!job_port_)
|
| - return;
|
| -
|
| - // Closing the port causes, that no more Job notifications are delivered to
|
| - // the worker thread and also causes the thread to exit. This is what we
|
| - // want to do since we are going to close all outstanding Jobs and notifying
|
| - // the policy objects ourselves.
|
| - ::PostQueuedCompletionStatus(job_port_, 0, THREAD_CTRL_QUIT, FALSE);
|
| - ::CloseHandle(job_port_);
|
| -
|
| - if (WAIT_TIMEOUT == ::WaitForSingleObject(job_thread_, 1000)) {
|
| - // Cannot clean broker services.
|
| - NOTREACHED();
|
| - return;
|
| - }
|
| -
|
| - JobTrackerList::iterator it;
|
| - for (it = tracker_list_.begin(); it != tracker_list_.end(); ++it) {
|
| - JobTracker* tracker = (*it);
|
| - FreeResources(tracker);
|
| - delete tracker;
|
| - }
|
| - ::CloseHandle(job_thread_);
|
| - delete thread_pool_;
|
| - ::CloseHandle(no_targets_);
|
| -
|
| - // Cancel the wait events and delete remaining peer trackers.
|
| - for (PeerTrackerMap::iterator it = peer_map_.begin();
|
| - it != peer_map_.end(); ++it) {
|
| - DeregisterPeerTracker(it->second);
|
| - }
|
| -
|
| - // If job_port_ isn't NULL, assumes that the lock has been initialized.
|
| - if (job_port_)
|
| - ::DeleteCriticalSection(&lock_);
|
| -}
|
| -
|
| -TargetPolicy* BrokerServicesBase::CreatePolicy() {
|
| - // If you change the type of the object being created here you must also
|
| - // change the downcast to it in SpawnTarget().
|
| - return new PolicyBase;
|
| -}
|
| -
|
| -void BrokerServicesBase::FreeResources(JobTracker* tracker) {
|
| - if (NULL != tracker->policy) {
|
| - BOOL res = ::TerminateJobObject(tracker->job, SBOX_ALL_OK);
|
| - DCHECK(res);
|
| - // Closing the job causes the target process to be destroyed so this
|
| - // needs to happen before calling OnJobEmpty().
|
| - res = ::CloseHandle(tracker->job);
|
| - DCHECK(res);
|
| - // In OnJobEmpty() we don't actually use the job handle directly.
|
| - tracker->policy->OnJobEmpty(tracker->job);
|
| - tracker->policy->Release();
|
| - tracker->policy = NULL;
|
| - }
|
| -}
|
| -
|
| -// The worker thread stays in a loop waiting for asynchronous notifications
|
| -// from the job objects. Right now we only care about knowing when the last
|
| -// process on a job terminates, but in general this is the place to tell
|
| -// the policy about events.
|
| -DWORD WINAPI BrokerServicesBase::TargetEventsThread(PVOID param) {
|
| - if (NULL == param)
|
| - return 1;
|
| -
|
| - base::PlatformThread::SetName("BrokerEvent");
|
| -
|
| - BrokerServicesBase* broker = reinterpret_cast<BrokerServicesBase*>(param);
|
| - HANDLE port = broker->job_port_;
|
| - HANDLE no_targets = broker->no_targets_;
|
| -
|
| - int target_counter = 0;
|
| - ::ResetEvent(no_targets);
|
| -
|
| - while (true) {
|
| - DWORD events = 0;
|
| - ULONG_PTR key = 0;
|
| - LPOVERLAPPED ovl = NULL;
|
| -
|
| - if (!::GetQueuedCompletionStatus(port, &events, &key, &ovl, INFINITE))
|
| - // this call fails if the port has been closed before we have a
|
| - // chance to service the last packet which is 'exit' anyway so
|
| - // this is not an error.
|
| - return 1;
|
| -
|
| - if (key > THREAD_CTRL_LAST) {
|
| - // The notification comes from a job object. There are nine notifications
|
| - // that jobs can send and some of them depend on the job attributes set.
|
| - JobTracker* tracker = reinterpret_cast<JobTracker*>(key);
|
| -
|
| - switch (events) {
|
| - case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO: {
|
| - // The job object has signaled that the last process associated
|
| - // with it has terminated. Assuming there is no way for a process
|
| - // to appear out of thin air in this job, it safe to assume that
|
| - // we can tell the policy to destroy the target object, and for
|
| - // us to release our reference to the policy object.
|
| - FreeResources(tracker);
|
| - break;
|
| - }
|
| -
|
| - case JOB_OBJECT_MSG_NEW_PROCESS: {
|
| - ++target_counter;
|
| - if (1 == target_counter) {
|
| - ::ResetEvent(no_targets);
|
| - }
|
| - break;
|
| - }
|
| -
|
| - case JOB_OBJECT_MSG_EXIT_PROCESS:
|
| - case JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS: {
|
| - {
|
| - AutoLock lock(&broker->lock_);
|
| - broker->child_process_ids_.erase(reinterpret_cast<DWORD>(ovl));
|
| - }
|
| - --target_counter;
|
| - if (0 == target_counter)
|
| - ::SetEvent(no_targets);
|
| -
|
| - DCHECK(target_counter >= 0);
|
| - break;
|
| - }
|
| -
|
| - case JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT: {
|
| - break;
|
| - }
|
| -
|
| - default: {
|
| - NOTREACHED();
|
| - break;
|
| - }
|
| - }
|
| - } else if (THREAD_CTRL_REMOVE_PEER == key) {
|
| - // Remove a process from our list of peers.
|
| - AutoLock lock(&broker->lock_);
|
| - PeerTrackerMap::iterator it =
|
| - broker->peer_map_.find(reinterpret_cast<DWORD>(ovl));
|
| - DeregisterPeerTracker(it->second);
|
| - broker->peer_map_.erase(it);
|
| - } else if (THREAD_CTRL_QUIT == key) {
|
| - // The broker object is being destroyed so the thread needs to exit.
|
| - return 0;
|
| - } else {
|
| - // We have not implemented more commands.
|
| - NOTREACHED();
|
| - }
|
| - }
|
| -
|
| - NOTREACHED();
|
| - return 0;
|
| -}
|
| -
|
| -// SpawnTarget does all the interesting sandbox setup and creates the target
|
| -// process inside the sandbox.
|
| -ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path,
|
| - const wchar_t* command_line,
|
| - TargetPolicy* policy,
|
| - PROCESS_INFORMATION* target_info) {
|
| - if (!exe_path)
|
| - return SBOX_ERROR_BAD_PARAMS;
|
| -
|
| - if (!policy)
|
| - return SBOX_ERROR_BAD_PARAMS;
|
| -
|
| - // Even though the resources touched by SpawnTarget can be accessed in
|
| - // multiple threads, the method itself cannot be called from more than
|
| - // 1 thread. This is to protect the global variables used while setting up
|
| - // the child process.
|
| - static DWORD thread_id = ::GetCurrentThreadId();
|
| - DCHECK(thread_id == ::GetCurrentThreadId());
|
| -
|
| - AutoLock lock(&lock_);
|
| -
|
| - // This downcast is safe as long as we control CreatePolicy()
|
| - PolicyBase* policy_base = static_cast<PolicyBase*>(policy);
|
| -
|
| - // Construct the tokens and the job object that we are going to associate
|
| - // with the soon to be created target process.
|
| - HANDLE initial_token_temp;
|
| - HANDLE lockdown_token_temp;
|
| - DWORD win_result = policy_base->MakeTokens(&initial_token_temp,
|
| - &lockdown_token_temp);
|
| - base::win::ScopedHandle initial_token(initial_token_temp);
|
| - base::win::ScopedHandle lockdown_token(lockdown_token_temp);
|
| -
|
| - if (ERROR_SUCCESS != win_result)
|
| - return SBOX_ERROR_GENERIC;
|
| -
|
| - HANDLE job_temp;
|
| - win_result = policy_base->MakeJobObject(&job_temp);
|
| - base::win::ScopedHandle job(job_temp);
|
| - if (ERROR_SUCCESS != win_result)
|
| - return SBOX_ERROR_GENERIC;
|
| -
|
| - if (ERROR_ALREADY_EXISTS == ::GetLastError())
|
| - return SBOX_ERROR_GENERIC;
|
| -
|
| - // Construct the thread pool here in case it is expensive.
|
| - // The thread pool is shared by all the targets
|
| - if (NULL == thread_pool_)
|
| - thread_pool_ = new Win2kThreadPool();
|
| -
|
| - // Create the TargetProces object and spawn the target suspended. Note that
|
| - // Brokerservices does not own the target object. It is owned by the Policy.
|
| - base::win::ScopedProcessInformation process_info;
|
| - TargetProcess* target = new TargetProcess(initial_token.Take(),
|
| - lockdown_token.Take(),
|
| - job,
|
| - thread_pool_);
|
| -
|
| - std::wstring desktop = policy_base->GetAlternateDesktop();
|
| -
|
| - win_result = target->Create(exe_path, command_line,
|
| - desktop.empty() ? NULL : desktop.c_str(),
|
| - &process_info);
|
| - if (ERROR_SUCCESS != win_result)
|
| - return SpawnCleanup(target, win_result);
|
| -
|
| - // Now the policy is the owner of the target.
|
| - if (!policy_base->AddTarget(target)) {
|
| - return SpawnCleanup(target, 0);
|
| - }
|
| -
|
| - // We are going to keep a pointer to the policy because we'll call it when
|
| - // the job object generates notifications using the completion port.
|
| - policy_base->AddRef();
|
| - scoped_ptr<JobTracker> tracker(new JobTracker(job.Take(), policy_base));
|
| - if (!AssociateCompletionPort(tracker->job, job_port_, tracker.get()))
|
| - return SpawnCleanup(target, 0);
|
| - // Save the tracker because in cleanup we might need to force closing
|
| - // the Jobs.
|
| - tracker_list_.push_back(tracker.release());
|
| - child_process_ids_.insert(process_info.process_id());
|
| -
|
| - *target_info = process_info.Take();
|
| - return SBOX_ALL_OK;
|
| -}
|
| -
|
| -
|
| -ResultCode BrokerServicesBase::WaitForAllTargets() {
|
| - ::WaitForSingleObject(no_targets_, INFINITE);
|
| - return SBOX_ALL_OK;
|
| -}
|
| -
|
| -bool BrokerServicesBase::IsActiveTarget(DWORD process_id) {
|
| - AutoLock lock(&lock_);
|
| - return child_process_ids_.find(process_id) != child_process_ids_.end() ||
|
| - peer_map_.find(process_id) != peer_map_.end();
|
| -}
|
| -
|
| -VOID CALLBACK BrokerServicesBase::RemovePeer(PVOID parameter, BOOLEAN timeout) {
|
| - PeerTracker* peer = reinterpret_cast<PeerTracker*>(parameter);
|
| - // Don't check the return code because we this may fail (safely) at shutdown.
|
| - ::PostQueuedCompletionStatus(peer->job_port, 0, THREAD_CTRL_REMOVE_PEER,
|
| - reinterpret_cast<LPOVERLAPPED>(peer->id));
|
| -}
|
| -
|
| -ResultCode BrokerServicesBase::AddTargetPeer(HANDLE peer_process) {
|
| - scoped_ptr<PeerTracker> peer(new PeerTracker(::GetProcessId(peer_process),
|
| - job_port_));
|
| - if (!peer->id)
|
| - return SBOX_ERROR_GENERIC;
|
| -
|
| - HANDLE process_handle;
|
| - if (!::DuplicateHandle(::GetCurrentProcess(), peer_process,
|
| - ::GetCurrentProcess(), &process_handle,
|
| - SYNCHRONIZE, FALSE, 0)) {
|
| - return SBOX_ERROR_GENERIC;
|
| - }
|
| - peer->process.Set(process_handle);
|
| -
|
| - AutoLock lock(&lock_);
|
| - if (!peer_map_.insert(std::make_pair(peer->id, peer.get())).second)
|
| - return SBOX_ERROR_BAD_PARAMS;
|
| -
|
| - if (!::RegisterWaitForSingleObject(
|
| - &peer->wait_object, peer->process, RemovePeer, peer.get(), INFINITE,
|
| - WT_EXECUTEONLYONCE | WT_EXECUTEINWAITTHREAD)) {
|
| - peer_map_.erase(peer->id);
|
| - return SBOX_ERROR_GENERIC;
|
| - }
|
| -
|
| - // Release the pointer since it will be cleaned up by the callback.
|
| - peer.release();
|
| - return SBOX_ALL_OK;
|
| -}
|
| -
|
| -} // namespace sandbox
|
|
|