michael@0: // Copyright (c) 2012 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: #include "sandbox/win/src/target_services.h" michael@0: michael@0: #include michael@0: michael@0: #include "base/basictypes.h" michael@0: #include "sandbox/win/src/crosscall_client.h" michael@0: #include "sandbox/win/src/handle_closer_agent.h" michael@0: #include "sandbox/win/src/handle_interception.h" michael@0: #include "sandbox/win/src/ipc_tags.h" michael@0: #include "sandbox/win/src/process_mitigations.h" michael@0: #include "sandbox/win/src/restricted_token_utils.h" michael@0: #include "sandbox/win/src/sandbox.h" michael@0: #include "sandbox/win/src/sandbox_types.h" michael@0: #include "sandbox/win/src/sharedmem_ipc_client.h" michael@0: #include "sandbox/win/src/sandbox_nt_util.h" michael@0: michael@0: namespace { michael@0: michael@0: // Flushing a cached key is triggered by just opening the key and closing the michael@0: // resulting handle. RegDisablePredefinedCache() is the documented way to flush michael@0: // HKCU so do not use it with this function. michael@0: bool FlushRegKey(HKEY root) { michael@0: HKEY key; michael@0: if (ERROR_SUCCESS == ::RegOpenKeyExW(root, NULL, 0, MAXIMUM_ALLOWED, &key)) { michael@0: if (ERROR_SUCCESS != ::RegCloseKey(key)) michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: // This function forces advapi32.dll to release some internally cached handles michael@0: // that were made during calls to RegOpenkey and RegOpenKeyEx if it is called michael@0: // with a more restrictive token. Returns true if the flushing is succesful michael@0: // although this behavior is undocumented and there is no guarantee that in michael@0: // fact this will happen in future versions of windows. michael@0: bool FlushCachedRegHandles() { michael@0: return (FlushRegKey(HKEY_LOCAL_MACHINE) && michael@0: FlushRegKey(HKEY_CLASSES_ROOT) && michael@0: FlushRegKey(HKEY_USERS)); michael@0: } michael@0: michael@0: // Checks if we have handle entries pending and runs the closer. michael@0: bool CloseOpenHandles() { michael@0: if (sandbox::HandleCloserAgent::NeedsHandlesClosed()) { michael@0: sandbox::HandleCloserAgent handle_closer; michael@0: michael@0: handle_closer.InitializeHandlesToClose(); michael@0: if (!handle_closer.CloseHandles()) michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: } // namespace michael@0: michael@0: namespace sandbox { michael@0: michael@0: SANDBOX_INTERCEPT IntegrityLevel g_shared_delayed_integrity_level = michael@0: INTEGRITY_LEVEL_LAST; michael@0: SANDBOX_INTERCEPT MitigationFlags g_shared_delayed_mitigations = 0; michael@0: michael@0: TargetServicesBase::TargetServicesBase() { michael@0: } michael@0: michael@0: ResultCode TargetServicesBase::Init() { michael@0: process_state_.SetInitCalled(); michael@0: return SBOX_ALL_OK; michael@0: } michael@0: michael@0: // Failure here is a breach of security so the process is terminated. michael@0: void TargetServicesBase::LowerToken() { michael@0: if (ERROR_SUCCESS != michael@0: SetProcessIntegrityLevel(g_shared_delayed_integrity_level)) michael@0: ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_INTEGRITY); michael@0: process_state_.SetRevertedToSelf(); michael@0: // If the client code as called RegOpenKey, advapi32.dll has cached some michael@0: // handles. The following code gets rid of them. michael@0: if (!::RevertToSelf()) michael@0: ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_DROPTOKEN); michael@0: if (!FlushCachedRegHandles()) michael@0: ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_FLUSHANDLES); michael@0: if (ERROR_SUCCESS != ::RegDisablePredefinedCache()) michael@0: ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_CACHEDISABLE); michael@0: if (!CloseOpenHandles()) michael@0: ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_CLOSEHANDLES); michael@0: // Enabling mitigations must happen last otherwise handle closing breaks michael@0: if (g_shared_delayed_mitigations && michael@0: !ApplyProcessMitigationsToCurrentProcess(g_shared_delayed_mitigations)) michael@0: ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_MITIGATION); michael@0: } michael@0: michael@0: ProcessState* TargetServicesBase::GetState() { michael@0: return &process_state_; michael@0: } michael@0: michael@0: TargetServicesBase* TargetServicesBase::GetInstance() { michael@0: static TargetServicesBase instance; michael@0: return &instance; michael@0: } michael@0: michael@0: // The broker services a 'test' IPC service with the IPC_PING_TAG tag. michael@0: bool TargetServicesBase::TestIPCPing(int version) { michael@0: void* memory = GetGlobalIPCMemory(); michael@0: if (NULL == memory) { michael@0: return false; michael@0: } michael@0: SharedMemIPCClient ipc(memory); michael@0: CrossCallReturn answer = {0}; michael@0: michael@0: if (1 == version) { michael@0: uint32 tick1 = ::GetTickCount(); michael@0: uint32 cookie = 717115; michael@0: ResultCode code = CrossCall(ipc, IPC_PING1_TAG, cookie, &answer); michael@0: michael@0: if (SBOX_ALL_OK != code) { michael@0: return false; michael@0: } michael@0: // We should get two extended returns values from the IPC, one is the michael@0: // tick count on the broker and the other is the cookie times two. michael@0: if ((answer.extended_count != 2)) { michael@0: return false; michael@0: } michael@0: // We test the first extended answer to be within the bounds of the tick michael@0: // count only if there was no tick count wraparound. michael@0: uint32 tick2 = ::GetTickCount(); michael@0: if (tick2 >= tick1) { michael@0: if ((answer.extended[0].unsigned_int < tick1) || michael@0: (answer.extended[0].unsigned_int > tick2)) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: if (answer.extended[1].unsigned_int != cookie * 2) { michael@0: return false; michael@0: } michael@0: } else if (2 == version) { michael@0: uint32 cookie = 717111; michael@0: InOutCountedBuffer counted_buffer(&cookie, sizeof(cookie)); michael@0: ResultCode code = CrossCall(ipc, IPC_PING2_TAG, counted_buffer, &answer); michael@0: michael@0: if (SBOX_ALL_OK != code) { michael@0: return false; michael@0: } michael@0: if (cookie != 717111 * 3) { michael@0: return false; michael@0: } michael@0: } else { michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool ProcessState::IsKernel32Loaded() { michael@0: return process_state_ != 0; michael@0: } michael@0: michael@0: bool ProcessState::InitCalled() { michael@0: return process_state_ > 1; michael@0: } michael@0: michael@0: bool ProcessState::RevertedToSelf() { michael@0: return process_state_ > 2; michael@0: } michael@0: michael@0: void ProcessState::SetKernel32Loaded() { michael@0: if (!process_state_) michael@0: process_state_ = 1; michael@0: } michael@0: michael@0: void ProcessState::SetInitCalled() { michael@0: if (process_state_ < 2) michael@0: process_state_ = 2; michael@0: } michael@0: michael@0: void ProcessState::SetRevertedToSelf() { michael@0: if (process_state_ < 3) michael@0: process_state_ = 3; michael@0: } michael@0: michael@0: ResultCode TargetServicesBase::DuplicateHandle(HANDLE source_handle, michael@0: DWORD target_process_id, michael@0: HANDLE* target_handle, michael@0: DWORD desired_access, michael@0: DWORD options) { michael@0: return sandbox::DuplicateHandleProxy(source_handle, target_process_id, michael@0: target_handle, desired_access, options); michael@0: } michael@0: michael@0: } // namespace sandbox