summaryrefslogtreecommitdiffstats
path: root/sandbox/win/src/sandbox_policy_base.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sandbox/win/src/sandbox_policy_base.cc')
-rw-r--r--sandbox/win/src/sandbox_policy_base.cc542
1 files changed, 542 insertions, 0 deletions
diff --git a/sandbox/win/src/sandbox_policy_base.cc b/sandbox/win/src/sandbox_policy_base.cc
new file mode 100644
index 0000000..4438641
--- /dev/null
+++ b/sandbox/win/src/sandbox_policy_base.cc
@@ -0,0 +1,542 @@
+// 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/sandbox_policy_base.h"
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "sandbox/src/filesystem_dispatcher.h"
+#include "sandbox/src/filesystem_policy.h"
+#include "sandbox/src/handle_dispatcher.h"
+#include "sandbox/src/handle_policy.h"
+#include "sandbox/src/job.h"
+#include "sandbox/src/interception.h"
+#include "sandbox/src/named_pipe_dispatcher.h"
+#include "sandbox/src/named_pipe_policy.h"
+#include "sandbox/src/policy_broker.h"
+#include "sandbox/src/policy_engine_processor.h"
+#include "sandbox/src/policy_low_level.h"
+#include "sandbox/src/process_thread_dispatcher.h"
+#include "sandbox/src/process_thread_policy.h"
+#include "sandbox/src/registry_dispatcher.h"
+#include "sandbox/src/registry_policy.h"
+#include "sandbox/src/restricted_token_utils.h"
+#include "sandbox/src/sandbox_policy.h"
+#include "sandbox/src/sync_dispatcher.h"
+#include "sandbox/src/sync_policy.h"
+#include "sandbox/src/target_process.h"
+#include "sandbox/src/window.h"
+
+namespace {
+// The standard windows size for one memory page.
+const size_t kOneMemPage = 4096;
+// The IPC and Policy shared memory sizes.
+const size_t kIPCMemSize = kOneMemPage * 2;
+const size_t kPolMemSize = kOneMemPage * 14;
+
+// Helper function to allocate space (on the heap) for policy.
+sandbox::PolicyGlobal* MakeBrokerPolicyMemory() {
+ const size_t kTotalPolicySz = kPolMemSize;
+ char* mem = new char[kTotalPolicySz];
+ DCHECK(mem);
+ memset(mem, 0, kTotalPolicySz);
+ sandbox::PolicyGlobal* policy = reinterpret_cast<sandbox::PolicyGlobal*>(mem);
+ policy->data_size = kTotalPolicySz - sizeof(sandbox::PolicyGlobal);
+ return policy;
+}
+}
+
+namespace sandbox {
+
+SANDBOX_INTERCEPT IntegrityLevel g_shared_delayed_integrity_level;
+
+// Initializes static members.
+HWINSTA PolicyBase::alternate_winstation_handle_ = NULL;
+HDESK PolicyBase::alternate_desktop_handle_ = NULL;
+
+PolicyBase::PolicyBase()
+ : ref_count(1),
+ lockdown_level_(USER_LOCKDOWN),
+ initial_level_(USER_LOCKDOWN),
+ job_level_(JOB_LOCKDOWN),
+ ui_exceptions_(0),
+ use_alternate_desktop_(false),
+ use_alternate_winstation_(false),
+ file_system_init_(false),
+ relaxed_interceptions_(true),
+ integrity_level_(INTEGRITY_LEVEL_LAST),
+ delayed_integrity_level_(INTEGRITY_LEVEL_LAST),
+ policy_maker_(NULL),
+ policy_(NULL) {
+ ::InitializeCriticalSection(&lock_);
+ // Initialize the IPC dispatcher array.
+ memset(&ipc_targets_, NULL, sizeof(ipc_targets_));
+ Dispatcher* dispatcher = NULL;
+
+ dispatcher = new FilesystemDispatcher(this);
+ ipc_targets_[IPC_NTCREATEFILE_TAG] = dispatcher;
+ ipc_targets_[IPC_NTOPENFILE_TAG] = dispatcher;
+ ipc_targets_[IPC_NTSETINFO_RENAME_TAG] = dispatcher;
+ ipc_targets_[IPC_NTQUERYATTRIBUTESFILE_TAG] = dispatcher;
+ ipc_targets_[IPC_NTQUERYFULLATTRIBUTESFILE_TAG] = dispatcher;
+
+ dispatcher = new NamedPipeDispatcher(this);
+ ipc_targets_[IPC_CREATENAMEDPIPEW_TAG] = dispatcher;
+
+ dispatcher = new ThreadProcessDispatcher(this);
+ ipc_targets_[IPC_NTOPENTHREAD_TAG] = dispatcher;
+ ipc_targets_[IPC_NTOPENPROCESS_TAG] = dispatcher;
+ ipc_targets_[IPC_CREATEPROCESSW_TAG] = dispatcher;
+ ipc_targets_[IPC_NTOPENPROCESSTOKEN_TAG] = dispatcher;
+ ipc_targets_[IPC_NTOPENPROCESSTOKENEX_TAG] = dispatcher;
+
+ dispatcher = new SyncDispatcher(this);
+ ipc_targets_[IPC_CREATEEVENT_TAG] = dispatcher;
+ ipc_targets_[IPC_OPENEVENT_TAG] = dispatcher;
+
+ dispatcher = new RegistryDispatcher(this);
+ ipc_targets_[IPC_NTCREATEKEY_TAG] = dispatcher;
+ ipc_targets_[IPC_NTOPENKEY_TAG] = dispatcher;
+
+ dispatcher = new HandleDispatcher(this);
+ ipc_targets_[IPC_DUPLICATEHANDLEPROXY_TAG] = dispatcher;
+}
+
+PolicyBase::~PolicyBase() {
+ TargetSet::iterator it;
+ for (it = targets_.begin(); it != targets_.end(); ++it) {
+ TargetProcess* target = (*it);
+ delete target;
+ }
+ delete ipc_targets_[IPC_NTCREATEFILE_TAG];
+ delete ipc_targets_[IPC_CREATENAMEDPIPEW_TAG];
+ delete ipc_targets_[IPC_NTOPENTHREAD_TAG];
+ delete ipc_targets_[IPC_CREATEEVENT_TAG];
+ delete ipc_targets_[IPC_NTCREATEKEY_TAG];
+ delete ipc_targets_[IPC_DUPLICATEHANDLEPROXY_TAG];
+ delete policy_maker_;
+ delete policy_;
+ ::DeleteCriticalSection(&lock_);
+}
+
+void PolicyBase::AddRef() {
+ ::InterlockedIncrement(&ref_count);
+}
+
+void PolicyBase::Release() {
+ if (0 == ::InterlockedDecrement(&ref_count))
+ delete this;
+}
+
+ResultCode PolicyBase::SetTokenLevel(TokenLevel initial, TokenLevel lockdown) {
+ if (initial < lockdown) {
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ initial_level_ = initial;
+ lockdown_level_ = lockdown;
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::SetJobLevel(JobLevel job_level, uint32 ui_exceptions) {
+ job_level_ = job_level;
+ ui_exceptions_ = ui_exceptions;
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::SetAlternateDesktop(bool alternate_winstation) {
+ use_alternate_desktop_ = true;
+ use_alternate_winstation_ = alternate_winstation;
+ return CreateAlternateDesktop(alternate_winstation);
+}
+
+std::wstring PolicyBase::GetAlternateDesktop() const {
+ // No alternate desktop or winstation. Return an empty string.
+ if (!use_alternate_desktop_ && !use_alternate_winstation_) {
+ return std::wstring();
+ }
+
+ // The desktop and winstation should have been created by now.
+ // If we hit this scenario, it means that the user ignored the failure
+ // during SetAlternateDesktop, so we ignore it here too.
+ if (use_alternate_desktop_ && !alternate_desktop_handle_) {
+ return std::wstring();
+ }
+ if (use_alternate_winstation_ && (!alternate_desktop_handle_ ||
+ !alternate_winstation_handle_)) {
+ return std::wstring();
+ }
+
+ return GetFullDesktopName(alternate_winstation_handle_,
+ alternate_desktop_handle_);
+}
+
+ResultCode PolicyBase::CreateAlternateDesktop(bool alternate_winstation) {
+ if (alternate_winstation) {
+ // Previously called with alternate_winstation = false?
+ if (!alternate_winstation_handle_ && alternate_desktop_handle_)
+ return SBOX_ERROR_UNSUPPORTED;
+
+ // Check if it's already created.
+ if (alternate_winstation_handle_ && alternate_desktop_handle_)
+ return SBOX_ALL_OK;
+
+ DCHECK(!alternate_winstation_handle_);
+ // Create the window station.
+ ResultCode result = CreateAltWindowStation(&alternate_winstation_handle_);
+ if (SBOX_ALL_OK != result)
+ return result;
+
+ // Verify that everything is fine.
+ if (!alternate_winstation_handle_ ||
+ GetWindowObjectName(alternate_winstation_handle_).empty())
+ return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
+
+ // Create the destkop.
+ result = CreateAltDesktop(alternate_winstation_handle_,
+ &alternate_desktop_handle_);
+ if (SBOX_ALL_OK != result)
+ return result;
+
+ // Verify that everything is fine.
+ if (!alternate_desktop_handle_ ||
+ GetWindowObjectName(alternate_desktop_handle_).empty())
+ return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
+ } else {
+ // Previously called with alternate_winstation = true?
+ if (alternate_winstation_handle_)
+ return SBOX_ERROR_UNSUPPORTED;
+
+ // Check if it already exists.
+ if (alternate_desktop_handle_)
+ return SBOX_ALL_OK;
+
+ // Create the destkop.
+ ResultCode result = CreateAltDesktop(NULL, &alternate_desktop_handle_);
+ if (SBOX_ALL_OK != result)
+ return result;
+
+ // Verify that everything is fine.
+ if (!alternate_desktop_handle_ ||
+ GetWindowObjectName(alternate_desktop_handle_).empty())
+ return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
+ }
+
+ return SBOX_ALL_OK;
+}
+
+void PolicyBase::DestroyAlternateDesktop() {
+ if (alternate_desktop_handle_) {
+ ::CloseDesktop(alternate_desktop_handle_);
+ alternate_desktop_handle_ = NULL;
+ }
+
+ if (alternate_winstation_handle_) {
+ ::CloseWindowStation(alternate_winstation_handle_);
+ alternate_winstation_handle_ = NULL;
+ }
+}
+
+ResultCode PolicyBase::SetIntegrityLevel(IntegrityLevel integrity_level) {
+ integrity_level_ = integrity_level;
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::SetDelayedIntegrityLevel(
+ IntegrityLevel integrity_level) {
+ delayed_integrity_level_ = integrity_level;
+ return SBOX_ALL_OK;
+}
+
+void PolicyBase::SetStrictInterceptions() {
+ relaxed_interceptions_ = false;
+}
+
+ResultCode PolicyBase::AddRule(SubSystem subsystem, Semantics semantics,
+ const wchar_t* pattern) {
+ if (NULL == policy_) {
+ policy_ = MakeBrokerPolicyMemory();
+ DCHECK(policy_);
+ policy_maker_ = new LowLevelPolicy(policy_);
+ DCHECK(policy_maker_);
+ }
+
+ switch (subsystem) {
+ case SUBSYS_FILES: {
+ if (!file_system_init_) {
+ if (!FileSystemPolicy::SetInitialRules(policy_maker_))
+ return SBOX_ERROR_BAD_PARAMS;
+ file_system_init_ = true;
+ }
+ if (!FileSystemPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ break;
+ }
+ case SUBSYS_SYNC: {
+ if (!SyncPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ break;
+ }
+ case SUBSYS_PROCESS: {
+ if (lockdown_level_ < USER_INTERACTIVE &&
+ TargetPolicy::PROCESS_ALL_EXEC == semantics) {
+ // This is unsupported. This is a huge security risk to give full access
+ // to a process handle.
+ return SBOX_ERROR_UNSUPPORTED;
+ }
+ if (!ProcessPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ break;
+ }
+ case SUBSYS_NAMED_PIPES: {
+ if (!NamedPipePolicy::GenerateRules(pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ break;
+ }
+ case SUBSYS_REGISTRY: {
+ if (!RegistryPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ break;
+ }
+ case SUBSYS_HANDLES: {
+ if (!HandlePolicy::GenerateRules(pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ break;
+ }
+ default: {
+ return SBOX_ERROR_UNSUPPORTED;
+ }
+ }
+
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::AddDllToUnload(const wchar_t* dll_name) {
+ blacklisted_dlls_.push_back(std::wstring(dll_name));
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::AddKernelObjectToClose(const char16* handle_type,
+ const char16* handle_name) {
+ return handle_closer_.AddHandle(handle_type, handle_name);
+}
+
+// When an IPC is ready in any of the targets we get called. We manage an array
+// of IPC dispatchers which are keyed on the IPC tag so we normally delegate
+// to the appropriate dispatcher unless we can handle the IPC call ourselves.
+Dispatcher* PolicyBase::OnMessageReady(IPCParams* ipc,
+ CallbackGeneric* callback) {
+ DCHECK(callback);
+ static const IPCParams ping1 = {IPC_PING1_TAG, ULONG_TYPE};
+ static const IPCParams ping2 = {IPC_PING2_TAG, INOUTPTR_TYPE};
+
+ if (ping1.Matches(ipc) || ping2.Matches(ipc)) {
+ *callback = reinterpret_cast<CallbackGeneric>(
+ static_cast<Callback1>(&PolicyBase::Ping));
+ return this;
+ }
+
+ Dispatcher* dispatch = GetDispatcher(ipc->ipc_tag);
+ if (!dispatch) {
+ NOTREACHED();
+ return NULL;
+ }
+ return dispatch->OnMessageReady(ipc, callback);
+}
+
+// Delegate to the appropriate dispatcher.
+bool PolicyBase::SetupService(InterceptionManager* manager, int service) {
+ if (IPC_PING1_TAG == service || IPC_PING2_TAG == service)
+ return true;
+
+ Dispatcher* dispatch = GetDispatcher(service);
+ if (!dispatch) {
+ NOTREACHED();
+ return false;
+ }
+ return dispatch->SetupService(manager, service);
+}
+
+DWORD PolicyBase::MakeJobObject(HANDLE* job) {
+ // Create the windows job object.
+ Job job_obj;
+ DWORD result = job_obj.Init(job_level_, NULL, ui_exceptions_);
+ if (ERROR_SUCCESS != result) {
+ return result;
+ }
+ *job = job_obj.Detach();
+ return ERROR_SUCCESS;
+}
+
+DWORD PolicyBase::MakeTokens(HANDLE* initial, HANDLE* lockdown) {
+ // Create the 'naked' token. This will be the permanent token associated
+ // with the process and therefore with any thread that is not impersonating.
+ DWORD result = CreateRestrictedToken(lockdown, lockdown_level_,
+ integrity_level_, PRIMARY);
+ if (ERROR_SUCCESS != result) {
+ return result;
+ }
+ // Create the 'better' token. We use this token as the one that the main
+ // thread uses when booting up the process. It should contain most of
+ // what we need (before reaching main( ))
+ result = CreateRestrictedToken(initial, initial_level_,
+ integrity_level_, IMPERSONATION);
+ if (ERROR_SUCCESS != result) {
+ ::CloseHandle(*lockdown);
+ return result;
+ }
+ return SBOX_ALL_OK;
+}
+
+bool PolicyBase::AddTarget(TargetProcess* target) {
+ if (NULL != policy_)
+ policy_maker_->Done();
+
+ if (!SetupAllInterceptions(target))
+ return false;
+
+ if (!SetupHandleCloser(target))
+ return false;
+
+ // Initialize the sandbox infrastructure for the target.
+ if (ERROR_SUCCESS != target->Init(this, policy_, kIPCMemSize, kPolMemSize))
+ return false;
+
+ g_shared_delayed_integrity_level = delayed_integrity_level_;
+ ResultCode ret = target->TransferVariable(
+ "g_shared_delayed_integrity_level",
+ &g_shared_delayed_integrity_level,
+ sizeof(g_shared_delayed_integrity_level));
+ g_shared_delayed_integrity_level = INTEGRITY_LEVEL_LAST;
+ if (SBOX_ALL_OK != ret)
+ return false;
+
+ AutoLock lock(&lock_);
+ targets_.push_back(target);
+ return true;
+}
+
+bool PolicyBase::OnJobEmpty(HANDLE job) {
+ AutoLock lock(&lock_);
+ TargetSet::iterator it;
+ for (it = targets_.begin(); it != targets_.end(); ++it) {
+ if ((*it)->Job() == job)
+ break;
+ }
+ if (it == targets_.end()) {
+ return false;
+ }
+ TargetProcess* target = *it;
+ targets_.erase(it);
+ delete target;
+ return true;
+}
+
+EvalResult PolicyBase::EvalPolicy(int service,
+ CountedParameterSetBase* params) {
+ if (NULL != policy_) {
+ if (NULL == policy_->entry[service]) {
+ // There is no policy for this particular service. This is not a big
+ // deal.
+ return DENY_ACCESS;
+ }
+ for (int i = 0; i < params->count; i++) {
+ if (!params->parameters[i].IsValid()) {
+ NOTREACHED();
+ return SIGNAL_ALARM;
+ }
+ }
+ PolicyProcessor pol_evaluator(policy_->entry[service]);
+ PolicyResult result = pol_evaluator.Evaluate(kShortEval,
+ params->parameters,
+ params->count);
+ if (POLICY_MATCH == result) {
+ return pol_evaluator.GetAction();
+ }
+ DCHECK(POLICY_ERROR != result);
+ }
+
+ return DENY_ACCESS;
+}
+
+// We service IPC_PING_TAG message which is a way to test a round trip of the
+// IPC subsystem. We receive a integer cookie and we are expected to return the
+// cookie times two (or three) and the current tick count.
+bool PolicyBase::Ping(IPCInfo* ipc, void* arg1) {
+ switch (ipc->ipc_tag) {
+ case IPC_PING1_TAG: {
+ IPCInt ipc_int(arg1);
+ uint32 cookie = ipc_int.As32Bit();
+ ipc->return_info.extended_count = 2;
+ ipc->return_info.extended[0].unsigned_int = ::GetTickCount();
+ ipc->return_info.extended[1].unsigned_int = 2 * cookie;
+ return true;
+ }
+ case IPC_PING2_TAG: {
+ CountedBuffer* io_buffer = reinterpret_cast<CountedBuffer*>(arg1);
+ if (sizeof(uint32) != io_buffer->Size())
+ return false;
+
+ uint32* cookie = reinterpret_cast<uint32*>(io_buffer->Buffer());
+ *cookie = (*cookie) * 3;
+ return true;
+ }
+ default: return false;
+ }
+}
+
+Dispatcher* PolicyBase::GetDispatcher(int ipc_tag) {
+ if (ipc_tag >= IPC_LAST_TAG || ipc_tag <= IPC_UNUSED_TAG)
+ return NULL;
+
+ return ipc_targets_[ipc_tag];
+}
+
+bool PolicyBase::SetupAllInterceptions(TargetProcess* target) {
+ InterceptionManager manager(target, relaxed_interceptions_);
+
+ if (policy_) {
+ for (int i = 0; i < IPC_LAST_TAG; i++) {
+ if (policy_->entry[i] && !ipc_targets_[i]->SetupService(&manager, i))
+ return false;
+ }
+ }
+
+ if (!blacklisted_dlls_.empty()) {
+ std::vector<std::wstring>::iterator it = blacklisted_dlls_.begin();
+ for (; it != blacklisted_dlls_.end(); ++it) {
+ manager.AddToUnloadModules(it->c_str());
+ }
+ }
+
+ if (!handle_closer_.SetupHandleInterceptions(&manager))
+ return false;
+
+ if (!SetupBasicInterceptions(&manager))
+ return false;
+
+ if (!manager.InitializeInterceptions())
+ return false;
+
+ // Finally, setup imports on the target so the interceptions can work.
+ return SetupNtdllImports(target);
+}
+
+bool PolicyBase::SetupHandleCloser(TargetProcess* target) {
+ return handle_closer_.InitializeTargetHandles(target);
+}
+
+} // namespace sandbox