123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- // 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/target_services.h"
- #include <windows.h>
- #include <winsock2.h>
- #include <new>
- #include <process.h>
- #include <stdint.h>
- #include "base/logging.h"
- #include "base/win/windows_version.h"
- #include "sandbox/win/src/crosscall_client.h"
- #include "sandbox/win/src/handle_closer_agent.h"
- #include "sandbox/win/src/heap_helper.h"
- #include "sandbox/win/src/ipc_tags.h"
- #include "sandbox/win/src/process_mitigations.h"
- #include "sandbox/win/src/restricted_token_utils.h"
- #include "sandbox/win/src/sandbox.h"
- #include "sandbox/win/src/sandbox_nt_util.h"
- #include "sandbox/win/src/sandbox_types.h"
- #include "sandbox/win/src/sharedmem_ipc_client.h"
- namespace sandbox {
- namespace {
- // Flushing a cached key is triggered by just opening the key and closing the
- // resulting handle. RegDisablePredefinedCache() is the documented way to flush
- // HKCU so do not use it with this function.
- bool FlushRegKey(HKEY root) {
- HKEY key;
- if (ERROR_SUCCESS ==
- ::RegOpenKeyExW(root, nullptr, 0, MAXIMUM_ALLOWED, &key)) {
- if (ERROR_SUCCESS != ::RegCloseKey(key))
- return false;
- }
- return true;
- }
- // This function forces advapi32.dll to release some internally cached handles
- // that were made during calls to RegOpenkey and RegOpenKeyEx if it is called
- // with a more restrictive token. Returns true if the flushing is succesful
- // although this behavior is undocumented and there is no guarantee that in
- // fact this will happen in future versions of windows.
- bool FlushCachedRegHandles() {
- return (FlushRegKey(HKEY_LOCAL_MACHINE) && FlushRegKey(HKEY_CLASSES_ROOT) &&
- FlushRegKey(HKEY_USERS));
- }
- // Cleans up this process if CSRSS will be disconnected, as this disconnection
- // is not supported Windows behavior.
- // Currently, this step requires closing a heap that this shared with csrss.exe.
- // Closing the ALPC Port handle to csrss.exe leaves this heap in an invalid
- // state. This causes problems if anyone enumerates the heap.
- bool CsrssDisconnectCleanup() {
- HANDLE csr_port_heap = FindCsrPortHeap();
- if (!csr_port_heap) {
- DLOG(ERROR) << "Failed to find CSR Port heap handle";
- return false;
- }
- HeapDestroy(csr_port_heap);
- return true;
- }
- // Used by EnumSystemLocales for warming up.
- static BOOL CALLBACK EnumLocalesProcEx(LPWSTR lpLocaleString,
- DWORD dwFlags,
- LPARAM lParam) {
- return TRUE;
- }
- // Additional warmup done just when CSRSS is being disconnected.
- bool CsrssDisconnectWarmup() {
- return ::EnumSystemLocalesEx(EnumLocalesProcEx, LOCALE_WINDOWS, 0, 0);
- }
- // Checks if we have handle entries pending and runs the closer.
- // Updates is_csrss_connected based on which handle types are closed.
- bool CloseOpenHandles(bool* is_csrss_connected) {
- if (HandleCloserAgent::NeedsHandlesClosed()) {
- HandleCloserAgent handle_closer;
- handle_closer.InitializeHandlesToClose(is_csrss_connected);
- if (!*is_csrss_connected) {
- if (!CsrssDisconnectWarmup() || !CsrssDisconnectCleanup()) {
- return false;
- }
- }
- if (!handle_closer.CloseHandles())
- return false;
- }
- return true;
- }
- // Warm up language subsystems before the sandbox is turned on.
- // Tested on Win8.1 x64:
- // This needs to happen after RevertToSelf() is called, because (at least) in
- // the case of GetUserDefaultLCID() it checks the TEB to see if the process is
- // impersonating (TEB!IsImpersonating). If it is, the cached locale information
- // is not used, nor is it set. Therefore, calls after RevertToSelf() will not
- // have warmed-up values to use.
- bool WarmupWindowsLocales() {
- // NOTE(liamjm): When last checked (Win 8.1 x64) it wasn't necessary to
- // warmup all of these functions, but let's not assume that.
- ::GetUserDefaultLangID();
- ::GetUserDefaultLCID();
- wchar_t localeName[LOCALE_NAME_MAX_LENGTH] = {0};
- return (0 != ::GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH));
- }
- // Used as storage for g_target_services, because other allocation facilities
- // are not available early. We can't use a regular function static because on
- // VS2015, because the CRT tries to acquire a lock to guard initialization, but
- // this code runs before the CRT is initialized.
- char g_target_services_memory[sizeof(TargetServicesBase)];
- TargetServicesBase* g_target_services = nullptr;
- } // namespace
- SANDBOX_INTERCEPT IntegrityLevel g_shared_delayed_integrity_level =
- INTEGRITY_LEVEL_LAST;
- SANDBOX_INTERCEPT MitigationFlags g_shared_delayed_mitigations = 0;
- TargetServicesBase::TargetServicesBase() {}
- ResultCode TargetServicesBase::Init() {
- process_state_.SetInitCalled();
- return SBOX_ALL_OK;
- }
- // Failure here is a breach of security so the process is terminated.
- void TargetServicesBase::LowerToken() {
- if (ERROR_SUCCESS !=
- SetProcessIntegrityLevel(g_shared_delayed_integrity_level))
- ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_INTEGRITY);
- process_state_.SetRevertedToSelf();
- // If the client code as called RegOpenKey, advapi32.dll has cached some
- // handles. The following code gets rid of them.
- if (!::RevertToSelf())
- ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_DROPTOKEN);
- if (!FlushCachedRegHandles())
- ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_FLUSHANDLES);
- if (ERROR_SUCCESS != ::RegDisablePredefinedCache())
- ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_CACHEDISABLE);
- if (!WarmupWindowsLocales())
- ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_WARMUP);
- bool is_csrss_connected = true;
- if (!CloseOpenHandles(&is_csrss_connected))
- ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_CLOSEHANDLES);
- process_state_.SetCsrssConnected(is_csrss_connected);
- // Enabling mitigations must happen last otherwise handle closing breaks
- if (g_shared_delayed_mitigations &&
- !ApplyProcessMitigationsToCurrentProcess(g_shared_delayed_mitigations))
- ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_MITIGATION);
- }
- ProcessState* TargetServicesBase::GetState() {
- return &process_state_;
- }
- TargetServicesBase* TargetServicesBase::GetInstance() {
- // Leak on purpose TargetServicesBase.
- if (!g_target_services)
- g_target_services = new (g_target_services_memory) TargetServicesBase;
- return g_target_services;
- }
- SOCKET TargetServicesBase::CreateBrokeredSocket(int af,
- int type,
- int protocol) {
- if (!GetState()->InitCalled())
- return INVALID_SOCKET;
- // IPC must be fully started.
- void* memory = GetGlobalIPCMemory();
- if (!memory)
- return INVALID_SOCKET;
- CrossCallReturn answer = {0};
- SharedMemIPCClient ipc(memory);
- WSAPROTOCOL_INFOW protocol_info = {};
- InOutCountedBuffer protocol_info_buffer(&protocol_info,
- sizeof(WSAPROTOCOL_INFOW));
- ResultCode code = CrossCall(ipc, IpcTag::WS2SOCKET, af, type, protocol,
- protocol_info_buffer, &answer);
- if (code != SBOX_ALL_OK)
- return INVALID_SOCKET;
- if (answer.extended_count == 1)
- WSASetLastError(static_cast<int>(answer.extended[0].unsigned_int));
- return ::WSASocket(af, type, protocol, &protocol_info, 0,
- WSA_FLAG_OVERLAPPED);
- }
- // The broker services a 'test' IPC service with the PING tag.
- bool TargetServicesBase::TestIPCPing(int version) {
- void* memory = GetGlobalIPCMemory();
- if (!memory)
- return false;
- SharedMemIPCClient ipc(memory);
- CrossCallReturn answer = {0};
- if (1 == version) {
- uint32_t tick1 = ::GetTickCount();
- uint32_t cookie = 717115;
- ResultCode code = CrossCall(ipc, IpcTag::PING1, cookie, &answer);
- if (SBOX_ALL_OK != code) {
- return false;
- }
- // We should get two extended returns values from the IPC, one is the
- // tick count on the broker and the other is the cookie times two.
- if ((answer.extended_count != 2)) {
- return false;
- }
- // We test the first extended answer to be within the bounds of the tick
- // count only if there was no tick count wraparound.
- uint32_t tick2 = ::GetTickCount();
- if (tick2 >= tick1) {
- if ((answer.extended[0].unsigned_int < tick1) ||
- (answer.extended[0].unsigned_int > tick2)) {
- return false;
- }
- }
- if (answer.extended[1].unsigned_int != cookie * 2) {
- return false;
- }
- } else if (2 == version) {
- uint32_t cookie = 717111;
- InOutCountedBuffer counted_buffer(&cookie, sizeof(cookie));
- ResultCode code = CrossCall(ipc, IpcTag::PING2, counted_buffer, &answer);
- if (SBOX_ALL_OK != code) {
- return false;
- }
- if (cookie != 717111 * 3) {
- return false;
- }
- } else {
- return false;
- }
- return true;
- }
- ProcessState::ProcessState()
- : process_state_(ProcessStateInternal::NONE), csrss_connected_(true) {}
- bool ProcessState::InitCalled() const {
- return process_state_ >= ProcessStateInternal::INIT_CALLED;
- }
- bool ProcessState::RevertedToSelf() const {
- return process_state_ >= ProcessStateInternal::REVERTED_TO_SELF;
- }
- bool ProcessState::IsCsrssConnected() const {
- return csrss_connected_;
- }
- void ProcessState::SetInitCalled() {
- if (process_state_ < ProcessStateInternal::INIT_CALLED)
- process_state_ = ProcessStateInternal::INIT_CALLED;
- }
- void ProcessState::SetRevertedToSelf() {
- if (process_state_ < ProcessStateInternal::REVERTED_TO_SELF)
- process_state_ = ProcessStateInternal::REVERTED_TO_SELF;
- }
- void ProcessState::SetCsrssConnected(bool csrss_connected) {
- csrss_connected_ = csrss_connected;
- }
- } // namespace sandbox
|