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 |