summaryrefslogtreecommitdiffstats
path: root/security/sandbox/chromium/sandbox/win/src/broker_services.cc
diff options
context:
space:
mode:
Diffstat (limited to 'security/sandbox/chromium/sandbox/win/src/broker_services.cc')
-rw-r--r--security/sandbox/chromium/sandbox/win/src/broker_services.cc745
1 files changed, 745 insertions, 0 deletions
diff --git a/security/sandbox/chromium/sandbox/win/src/broker_services.cc b/security/sandbox/chromium/sandbox/win/src/broker_services.cc
new file mode 100644
index 0000000000..15ed321071
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/broker_services.cc
@@ -0,0 +1,745 @@
+// 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/win/src/broker_services.h"
+
+#include <aclapi.h>
+
+#include <stddef.h>
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/threading/platform_thread.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/scoped_process_information.h"
+#include "base/win/startup_information.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/app_container_profile.h"
+#include "sandbox/win/src/process_mitigations.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+#include "sandbox/win/src/sandbox_policy_diagnostic.h"
+#include "sandbox/win/src/target_process.h"
+#include "sandbox/win/src/win2k_threadpool.h"
+#include "sandbox/win/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) {
+ target->Terminate();
+ delete target;
+ 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_NEW_JOB_TRACKER,
+ THREAD_CTRL_NEW_PROCESS_TRACKER,
+ THREAD_CTRL_PROCESS_SIGNALLED,
+ THREAD_CTRL_GET_POLICY_INFO,
+ 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 {
+ JobTracker(base::win::ScopedHandle job,
+ scoped_refptr<sandbox::PolicyBase> policy,
+ DWORD process_id)
+ : job(std::move(job)), policy(policy), process_id(process_id) {}
+ ~JobTracker() { FreeResources(); }
+
+ // Releases the Job and notifies the associated Policy object to release its
+ // resources as well.
+ void FreeResources();
+
+ base::win::ScopedHandle job;
+ scoped_refptr<sandbox::PolicyBase> policy;
+ DWORD process_id;
+};
+
+void JobTracker::FreeResources() {
+ if (policy) {
+ bool res = ::TerminateJobObject(job.Get(), sandbox::SBOX_ALL_OK);
+ DCHECK(res);
+ // Closing the job causes the target process to be destroyed so this needs
+ // to happen before calling OnJobEmpty().
+ HANDLE stale_job_handle = job.Get();
+ job.Close();
+
+ // In OnJobEmpty() we don't actually use the job handle directly.
+ policy->OnJobEmpty(stale_job_handle);
+ policy = nullptr;
+ }
+}
+
+// tracks processes that are not in jobs
+struct ProcessTracker {
+ ProcessTracker(scoped_refptr<sandbox::PolicyBase> policy,
+ DWORD process_id,
+ base::win::ScopedHandle process)
+ : policy(policy), process_id(process_id), process(std::move(process)) {}
+ ~ProcessTracker() { FreeResources(); }
+
+ void FreeResources();
+
+ scoped_refptr<sandbox::PolicyBase> policy;
+ DWORD process_id;
+ base::win::ScopedHandle process;
+ // Used to UnregisterWait. Not a real handle so cannot CloseHandle().
+ HANDLE wait_handle;
+ // IOCP that is tracking this non-job process
+ HANDLE iocp;
+};
+
+void ProcessTracker::FreeResources() {
+ if (policy) {
+ policy->OnJobEmpty(nullptr);
+ policy = nullptr;
+ }
+}
+
+// Helper redispatches process events to tracker thread.
+void WINAPI ProcessEventCallback(PVOID param, BOOLEAN ignored) {
+ // This callback should do very little, and must be threadpool safe.
+ ProcessTracker* tracker = reinterpret_cast<ProcessTracker*>(param);
+ // If this fails we can do nothing... we will leak the policy.
+ ::PostQueuedCompletionStatus(tracker->iocp, 0, THREAD_CTRL_PROCESS_SIGNALLED,
+ reinterpret_cast<LPOVERLAPPED>(tracker));
+}
+
+// Helper class to send policy lists
+class PolicyDiagnosticList final : public sandbox::PolicyList {
+ public:
+ PolicyDiagnosticList() {}
+ ~PolicyDiagnosticList() override {}
+ void push_back(std::unique_ptr<sandbox::PolicyInfo> info) {
+ internal_list_.push_back(std::move(info));
+ }
+ std::vector<std::unique_ptr<sandbox::PolicyInfo>>::iterator begin() override {
+ return internal_list_.begin();
+ }
+ std::vector<std::unique_ptr<sandbox::PolicyInfo>>::iterator end() override {
+ return internal_list_.end();
+ }
+ size_t size() const override { return internal_list_.size(); }
+
+ private:
+ std::vector<std::unique_ptr<sandbox::PolicyInfo>> internal_list_;
+};
+
+} // namespace
+
+namespace sandbox {
+
+BrokerServicesBase::BrokerServicesBase() {}
+
+// 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 (job_port_.IsValid() || thread_pool_)
+ return SBOX_ERROR_UNEXPECTED_CALL;
+
+ ::InitializeCriticalSection(&lock_);
+
+ job_port_.Set(::CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 0));
+ if (!job_port_.IsValid())
+ return SBOX_ERROR_CANNOT_INIT_BROKERSERVICES;
+
+ no_targets_.Set(::CreateEventW(nullptr, true, false, nullptr));
+
+ job_thread_.Set(::CreateThread(nullptr, 0, // Default security and stack.
+ TargetEventsThread, this, 0, nullptr));
+ if (!job_thread_.IsValid())
+ return SBOX_ERROR_CANNOT_INIT_BROKERSERVICES;
+
+ 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_.IsValid())
+ 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_.Get(), 0, THREAD_CTRL_QUIT, nullptr);
+
+ if (job_thread_.IsValid() &&
+ WAIT_TIMEOUT == ::WaitForSingleObject(job_thread_.Get(), 1000)) {
+ // Cannot clean broker services.
+ NOTREACHED();
+ return;
+ }
+ thread_pool_.reset();
+ ::DeleteCriticalSection(&lock_);
+}
+
+scoped_refptr<TargetPolicy> BrokerServicesBase::CreatePolicy() {
+ // If you change the type of the object being created here you must also
+ // change the downcast to it in SpawnTarget().
+ scoped_refptr<TargetPolicy> policy(new PolicyBase);
+ // PolicyBase starts with refcount 1.
+ policy->Release();
+ return policy;
+}
+
+// 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 (!param)
+ return 1;
+
+ base::PlatformThread::SetName("BrokerEvent");
+
+ BrokerServicesBase* broker = reinterpret_cast<BrokerServicesBase*>(param);
+ HANDLE port = broker->job_port_.Get();
+ HANDLE no_targets = broker->no_targets_.Get();
+
+ std::set<DWORD> child_process_ids;
+ std::list<std::unique_ptr<JobTracker>> jobs;
+ std::list<std::unique_ptr<ProcessTracker>> processes;
+ int target_counter = 0;
+ int untracked_target_counter = 0;
+ ::ResetEvent(no_targets);
+
+ while (true) {
+ DWORD events = 0;
+ ULONG_PTR key = 0;
+ LPOVERLAPPED ovl = nullptr;
+
+ 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);
+
+ // Processes may be added to a job after the process count has
+ // reached zero, leading us to manipulate a freed JobTracker
+ // object or job handle (as the key is no longer valid). We
+ // therefore check if the tracker has already been deleted.
+ if (std::find_if(jobs.begin(), jobs.end(), [&](auto&& p) -> bool {
+ return p.get() == tracker;
+ }) == jobs.end()) {
+ CHECK(false);
+ }
+
+ switch (events) {
+ case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO: {
+ // The job object has signaled that the last process associated
+ // with it has terminated. It is safe to free the tracker
+ // and release its reference to the associated policy object
+ // which will Close the job handle.
+ HANDLE job_handle = tracker->job.Get();
+
+ // Erase by comparing with the job handle.
+ jobs.erase(std::remove_if(jobs.begin(), jobs.end(),
+ [&](auto&& p) -> bool {
+ return p->job.Get() == job_handle;
+ }),
+ jobs.end());
+ break;
+ }
+
+ case JOB_OBJECT_MSG_NEW_PROCESS: {
+ // Child process created from sandboxed process.
+ DWORD process_id =
+ static_cast<DWORD>(reinterpret_cast<uintptr_t>(ovl));
+ size_t count = child_process_ids.count(process_id);
+ if (count == 0)
+ untracked_target_counter++;
+ ++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->active_targets_.erase(
+ static_cast<DWORD>(reinterpret_cast<uintptr_t>(ovl)));
+ }
+ size_t erase_result = child_process_ids.erase(
+ static_cast<DWORD>(reinterpret_cast<uintptr_t>(ovl)));
+ if (erase_result != 1U) {
+ // The process was untracked e.g. a child process of the target.
+ --untracked_target_counter;
+ DCHECK(untracked_target_counter >= 0);
+ }
+ --target_counter;
+ if (0 == target_counter)
+ ::SetEvent(no_targets);
+
+ DCHECK(target_counter >= 0);
+ break;
+ }
+
+ case JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT: {
+ // A child process attempted and failed to create a child process.
+ // Windows does not reveal the process id.
+ untracked_target_counter++;
+ target_counter++;
+ break;
+ }
+
+ case JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT: {
+ bool res = ::TerminateJobObject(tracker->job.Get(),
+ SBOX_FATAL_MEMORY_EXCEEDED);
+ DCHECK(res);
+ break;
+ }
+
+ default: {
+ NOTREACHED();
+ break;
+ }
+ }
+ } else if (THREAD_CTRL_NEW_JOB_TRACKER == key) {
+ std::unique_ptr<JobTracker> tracker;
+ tracker.reset(reinterpret_cast<JobTracker*>(ovl));
+ DCHECK(tracker->job.IsValid());
+
+ child_process_ids.insert(tracker->process_id);
+ jobs.push_back(std::move(tracker));
+
+ } else if (THREAD_CTRL_NEW_PROCESS_TRACKER == key) {
+ std::unique_ptr<ProcessTracker> tracker;
+ tracker.reset(reinterpret_cast<ProcessTracker*>(ovl));
+
+ if (child_process_ids.empty()) {
+ ::SetEvent(broker->no_targets_.Get());
+ }
+
+ tracker->iocp = port;
+ if (!::RegisterWaitForSingleObject(&(tracker->wait_handle),
+ tracker->process.Get(),
+ ProcessEventCallback, tracker.get(),
+ INFINITE, WT_EXECUTEONLYONCE)) {
+ // Failed. Invalidate the wait_handle and store anyway.
+ tracker->wait_handle = INVALID_HANDLE_VALUE;
+ }
+ processes.push_back(std::move(tracker));
+
+ } else if (THREAD_CTRL_PROCESS_SIGNALLED == key) {
+ ProcessTracker* tracker =
+ static_cast<ProcessTracker*>(reinterpret_cast<void*>(ovl));
+
+ {
+ AutoLock lock(&broker->lock_);
+ broker->active_targets_.erase(tracker->process_id);
+ }
+
+ ::UnregisterWait(tracker->wait_handle);
+ tracker->wait_handle = INVALID_HANDLE_VALUE;
+ // Copy process_id so that we can legally reference it even after we have
+ // found the ProcessTracker object to delete.
+ const DWORD process_id = tracker->process_id;
+ // PID is unique until the process handle is closed in dtor.
+ processes.erase(std::remove_if(processes.begin(), processes.end(),
+ [&](auto&& p) -> bool {
+ return p->process_id == process_id;
+ }),
+ processes.end());
+ } else if (THREAD_CTRL_GET_POLICY_INFO == key) {
+ // Clone the policies for sandbox diagnostics.
+ std::unique_ptr<PolicyDiagnosticsReceiver> receiver;
+ receiver.reset(static_cast<PolicyDiagnosticsReceiver*>(
+ reinterpret_cast<void*>(ovl)));
+ // The PollicyInfo ctor copies essential information from the trackers.
+ auto policy_list = std::make_unique<PolicyDiagnosticList>();
+ for (auto&& process_tracker : processes) {
+ if (process_tracker->policy) {
+ policy_list->push_back(std::make_unique<PolicyDiagnostic>(
+ process_tracker->policy.get()));
+ }
+ }
+ for (auto&& job_tracker : jobs) {
+ if (job_tracker->policy) {
+ policy_list->push_back(
+ std::make_unique<PolicyDiagnostic>(job_tracker->policy.get()));
+ }
+ }
+ // Receiver should return quickly.
+ receiver->ReceiveDiagnostics(std::move(policy_list));
+
+ } else if (THREAD_CTRL_QUIT == key) {
+ // The broker object is being destroyed so the thread needs to exit.
+ for (auto&& tracker : processes) {
+ ::UnregisterWait(tracker->wait_handle);
+ tracker->wait_handle = INVALID_HANDLE_VALUE;
+ }
+ // After this point, so further calls to ProcessEventCallback can
+ // occur. Other tracked objects are destroyed as this thread ends.
+ 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,
+ base::EnvironmentMap& env_map,
+ scoped_refptr<TargetPolicy> policy,
+ ResultCode* last_warning,
+ DWORD* last_error,
+ 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 one
+ // thread. This is to protect the global variables used while setting up the
+ // child process, and to make sure launcher thread mitigations are applied
+ // correctly.
+ static DWORD thread_id = ::GetCurrentThreadId();
+ DCHECK(thread_id == ::GetCurrentThreadId());
+ *last_warning = SBOX_ALL_OK;
+
+ // Launcher thread only needs to be opted out of ACG once. Do this on the
+ // first child process being spawned.
+ static bool launcher_thread_opted_out = false;
+
+ if (!launcher_thread_opted_out) {
+ // Soft fail this call. It will fail if ACG is not enabled for this process.
+ sandbox::ApplyMitigationsToCurrentThread(
+ sandbox::MITIGATION_DYNAMIC_CODE_OPT_OUT_THIS_THREAD);
+ launcher_thread_opted_out = true;
+ }
+
+ // This downcast is safe as long as we control CreatePolicy()
+ scoped_refptr<PolicyBase> policy_base(static_cast<PolicyBase*>(policy.get()));
+
+ // Construct the tokens and the job object that we are going to associate
+ // with the soon to be created target process.
+ base::win::ScopedHandle initial_token;
+ base::win::ScopedHandle lockdown_token;
+ base::win::ScopedHandle lowbox_token;
+ ResultCode result = SBOX_ALL_OK;
+
+ result =
+ policy_base->MakeTokens(&initial_token, &lockdown_token, &lowbox_token);
+ if (SBOX_ALL_OK != result)
+ return result;
+ if (lowbox_token.IsValid() &&
+ base::win::GetVersion() < base::win::Version::WIN8) {
+ // We don't allow lowbox_token below Windows 8.
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+
+ base::win::ScopedHandle job;
+ result = policy_base->MakeJobObject(&job);
+ if (SBOX_ALL_OK != result)
+ return result;
+
+ // Initialize the startup information from the policy.
+ base::win::StartupInformation startup_info;
+
+ // We don't want any child processes causing the IDC_APPSTARTING cursor.
+ startup_info.startup_info()->dwFlags |= STARTF_FORCEOFFFEEDBACK;
+
+ // The liftime of |mitigations|, |inherit_handle_list| and
+ // |child_process_creation| have to be at least as long as
+ // |startup_info| because |UpdateProcThreadAttribute| requires that
+ // its |lpValue| parameter persist until |DeleteProcThreadAttributeList| is
+ // called; StartupInformation's destructor makes such a call.
+ DWORD64 mitigations[2];
+ std::vector<HANDLE> inherited_handle_list;
+ DWORD child_process_creation = PROCESS_CREATION_CHILD_PROCESS_RESTRICTED;
+
+ std::wstring desktop = policy_base->GetAlternateDesktop();
+ if (!desktop.empty()) {
+ startup_info.startup_info()->lpDesktop =
+ const_cast<wchar_t*>(desktop.c_str());
+ }
+
+ bool inherit_handles = false;
+
+ int attribute_count = 0;
+
+ size_t mitigations_size;
+ ConvertProcessMitigationsToPolicy(policy_base->GetProcessMitigations(),
+ &mitigations[0], &mitigations_size);
+ if (mitigations[0] || mitigations[1])
+ ++attribute_count;
+
+ bool restrict_child_process_creation = false;
+ if (base::win::GetVersion() >= base::win::Version::WIN10_TH2 &&
+ policy_base->GetJobLevel() <= JOB_LIMITED_USER) {
+ restrict_child_process_creation = true;
+ ++attribute_count;
+ }
+
+ HANDLE stdout_handle = policy_base->GetStdoutHandle();
+ HANDLE stderr_handle = policy_base->GetStderrHandle();
+
+ if (stdout_handle != INVALID_HANDLE_VALUE)
+ inherited_handle_list.push_back(stdout_handle);
+
+ // Handles in the list must be unique.
+ if (stderr_handle != stdout_handle && stderr_handle != INVALID_HANDLE_VALUE)
+ inherited_handle_list.push_back(stderr_handle);
+
+ const auto& policy_handle_list = policy_base->GetHandlesBeingShared();
+
+ for (HANDLE handle : policy_handle_list)
+ inherited_handle_list.push_back(handle);
+
+ if (inherited_handle_list.size())
+ ++attribute_count;
+
+ scoped_refptr<AppContainerProfileBase> profile =
+ policy_base->GetAppContainerProfileBase();
+ if (profile) {
+ if (base::win::GetVersion() < base::win::Version::WIN8)
+ return SBOX_ERROR_BAD_PARAMS;
+ ++attribute_count;
+ if (profile->GetEnableLowPrivilegeAppContainer()) {
+ // LPAC first supported in RS1.
+ if (base::win::GetVersion() < base::win::Version::WIN10_RS1)
+ return SBOX_ERROR_BAD_PARAMS;
+ ++attribute_count;
+ }
+ }
+
+ if (!startup_info.InitializeProcThreadAttributeList(attribute_count))
+ return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
+
+ if (mitigations[0] || mitigations[1]) {
+ if (!startup_info.UpdateProcThreadAttribute(
+ PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, &mitigations[0],
+ mitigations_size)) {
+ return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
+ }
+ }
+
+ if (restrict_child_process_creation) {
+ if (!startup_info.UpdateProcThreadAttribute(
+ PROC_THREAD_ATTRIBUTE_CHILD_PROCESS_POLICY, &child_process_creation,
+ sizeof(child_process_creation))) {
+ return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
+ }
+ }
+
+ if (inherited_handle_list.size()) {
+ if (!startup_info.UpdateProcThreadAttribute(
+ PROC_THREAD_ATTRIBUTE_HANDLE_LIST, &inherited_handle_list[0],
+ sizeof(HANDLE) * inherited_handle_list.size())) {
+ return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
+ }
+ startup_info.startup_info()->dwFlags |= STARTF_USESTDHANDLES;
+ startup_info.startup_info()->hStdInput = INVALID_HANDLE_VALUE;
+ startup_info.startup_info()->hStdOutput = stdout_handle;
+ startup_info.startup_info()->hStdError = stderr_handle;
+ // Allowing inheritance of handles is only secure now that we
+ // have limited which handles will be inherited.
+ inherit_handles = true;
+ }
+
+ // Declared here to ensure they stay in scope until after process creation.
+ std::unique_ptr<SecurityCapabilities> security_capabilities;
+ DWORD all_applications_package_policy =
+ PROCESS_CREATION_ALL_APPLICATION_PACKAGES_OPT_OUT;
+
+ if (profile) {
+ security_capabilities = profile->GetSecurityCapabilities();
+ if (!startup_info.UpdateProcThreadAttribute(
+ PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES,
+ security_capabilities.get(), sizeof(SECURITY_CAPABILITIES))) {
+ return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
+ }
+ if (profile->GetEnableLowPrivilegeAppContainer()) {
+ if (!startup_info.UpdateProcThreadAttribute(
+ PROC_THREAD_ATTRIBUTE_ALL_APPLICATION_PACKAGES_POLICY,
+ &all_applications_package_policy,
+ sizeof(all_applications_package_policy))) {
+ return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
+ }
+ }
+ }
+
+ // Construct the thread pool here in case it is expensive.
+ // The thread pool is shared by all the targets
+ if (!thread_pool_)
+ thread_pool_ = std::make_unique<Win2kThreadPool>();
+
+ // Create the TargetProcess 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(
+ std::move(initial_token), std::move(lockdown_token), job.Get(),
+ thread_pool_.get(),
+ profile ? profile->GetImpersonationCapabilities() : std::vector<Sid>());
+
+ result = target->Create(exe_path, command_line, inherit_handles, startup_info,
+ &process_info, env_map, last_error);
+
+ if (result != SBOX_ALL_OK) {
+ SpawnCleanup(target);
+ return result;
+ }
+
+ if (lowbox_token.IsValid()) {
+ *last_warning = target->AssignLowBoxToken(lowbox_token);
+ // If this fails we continue, but report the error as a warning.
+ // This is due to certain configurations causing the setting of the
+ // token to fail post creation, and we'd rather continue if possible.
+ if (*last_warning != SBOX_ALL_OK)
+ *last_error = ::GetLastError();
+ }
+
+ // Now the policy is the owner of the target.
+ result = policy_base->AddTarget(target);
+
+ if (result != SBOX_ALL_OK) {
+ *last_error = ::GetLastError();
+ SpawnCleanup(target);
+ return result;
+ }
+
+ if (job.IsValid()) {
+ JobTracker* tracker =
+ new JobTracker(std::move(job), policy_base, process_info.process_id());
+
+ // Post the tracker to the tracking thread, then associate the job with
+ // the tracker. The worker thread takes ownership of these objects.
+ CHECK(::PostQueuedCompletionStatus(
+ job_port_.Get(), 0, THREAD_CTRL_NEW_JOB_TRACKER,
+ reinterpret_cast<LPOVERLAPPED>(tracker)));
+ // There is no obvious recovery after failure here. Previous version with
+ // SpawnCleanup() caused deletion of TargetProcess twice. crbug.com/480639
+ CHECK(
+ AssociateCompletionPort(tracker->job.Get(), job_port_.Get(), tracker));
+
+ AutoLock lock(&lock_);
+ active_targets_.insert(process_info.process_id());
+ } else {
+ result = AddTargetPeerInternal(process_info.process_handle(),
+ process_info.process_id(),
+ policy_base, last_error);
+ if (result != SBOX_ALL_OK) {
+ // This may fail in the same way as Job associated processes.
+ // crbug.com/480639.
+ target->Terminate();
+ return result;
+ }
+ }
+
+ *target_info = process_info.Take();
+ return result;
+}
+
+ResultCode BrokerServicesBase::WaitForAllTargets() {
+ ::WaitForSingleObject(no_targets_.Get(), INFINITE);
+ return SBOX_ALL_OK;
+}
+
+bool BrokerServicesBase::IsSafeDuplicationTarget(DWORD process_id) {
+ AutoLock lock(&lock_);
+ return active_targets_.find(process_id) != active_targets_.end();
+}
+
+ResultCode BrokerServicesBase::AddTargetPeerInternal(
+ HANDLE peer_process_handle,
+ DWORD peer_process_id,
+ scoped_refptr<PolicyBase> policy_base,
+ DWORD* last_error) {
+ // Duplicate the process handle to give the tracking machinery
+ // something valid to wait on in the tracking thread.
+ HANDLE tmp_process_handle = INVALID_HANDLE_VALUE;
+ if (!::DuplicateHandle(::GetCurrentProcess(), peer_process_handle,
+ ::GetCurrentProcess(), &tmp_process_handle,
+ SYNCHRONIZE, false, 0 /*no options*/)) {
+ *last_error = ::GetLastError();
+ return SBOX_ERROR_CANNOT_DUPLICATE_PROCESS_HANDLE;
+ }
+ base::win::ScopedHandle dup_process_handle(tmp_process_handle);
+ ProcessTracker* tracker = new ProcessTracker(
+ policy_base, peer_process_id, std::move(dup_process_handle));
+ // The tracker and policy will leak if this call fails.
+ ::PostQueuedCompletionStatus(job_port_.Get(), 0,
+ THREAD_CTRL_NEW_PROCESS_TRACKER,
+ reinterpret_cast<LPOVERLAPPED>(tracker));
+
+ AutoLock lock(&lock_);
+ active_targets_.insert(peer_process_id);
+
+ return SBOX_ALL_OK;
+}
+
+ResultCode BrokerServicesBase::AddTargetPeer(HANDLE peer_process) {
+ DWORD last_error;
+ return AddTargetPeerInternal(peer_process, ::GetProcessId(peer_process),
+ nullptr, &last_error);
+}
+
+ResultCode BrokerServicesBase::GetPolicyDiagnostics(
+ std::unique_ptr<PolicyDiagnosticsReceiver> receiver) {
+ CHECK(job_thread_.IsValid());
+ // Post to the job thread.
+ if (!::PostQueuedCompletionStatus(
+ job_port_.Get(), 0, THREAD_CTRL_GET_POLICY_INFO,
+ reinterpret_cast<LPOVERLAPPED>(receiver.get()))) {
+ receiver->OnError(SBOX_ERROR_GENERIC);
+ return SBOX_ERROR_GENERIC;
+ }
+
+ // Ownership has passed to tracker thread.
+ receiver.release();
+ return SBOX_ALL_OK;
+}
+
+bool BrokerServicesBase::DeriveCapabilitySidFromName(const wchar_t* name,
+ PSID derived_sid,
+ DWORD sid_buffer_length) {
+ return ::CopySid(sid_buffer_length, derived_sid,
+ Sid::FromNamedCapability(name).GetPSID());
+}
+
+} // namespace sandbox