| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "sandbox/win/src/target_services.h" | |
| 6 | |
| 7 #include <new> | |
| 8 | |
| 9 #include <process.h> | |
| 10 #include <stdint.h> | |
| 11 | |
| 12 #include "base/win/windows_version.h" | |
| 13 #include "sandbox/win/src/crosscall_client.h" | |
| 14 #include "sandbox/win/src/handle_closer_agent.h" | |
| 15 #include "sandbox/win/src/handle_interception.h" | |
| 16 #include "sandbox/win/src/ipc_tags.h" | |
| 17 #include "sandbox/win/src/process_mitigations.h" | |
| 18 #include "sandbox/win/src/restricted_token_utils.h" | |
| 19 #include "sandbox/win/src/sandbox.h" | |
| 20 #include "sandbox/win/src/sandbox_nt_util.h" | |
| 21 #include "sandbox/win/src/sandbox_types.h" | |
| 22 #include "sandbox/win/src/sharedmem_ipc_client.h" | |
| 23 | |
| 24 namespace { | |
| 25 | |
| 26 // Flushing a cached key is triggered by just opening the key and closing the | |
| 27 // resulting handle. RegDisablePredefinedCache() is the documented way to flush | |
| 28 // HKCU so do not use it with this function. | |
| 29 bool FlushRegKey(HKEY root) { | |
| 30 HKEY key; | |
| 31 if (ERROR_SUCCESS == ::RegOpenKeyExW(root, NULL, 0, MAXIMUM_ALLOWED, &key)) { | |
| 32 if (ERROR_SUCCESS != ::RegCloseKey(key)) | |
| 33 return false; | |
| 34 } | |
| 35 return true; | |
| 36 } | |
| 37 | |
| 38 // This function forces advapi32.dll to release some internally cached handles | |
| 39 // that were made during calls to RegOpenkey and RegOpenKeyEx if it is called | |
| 40 // with a more restrictive token. Returns true if the flushing is succesful | |
| 41 // although this behavior is undocumented and there is no guarantee that in | |
| 42 // fact this will happen in future versions of windows. | |
| 43 bool FlushCachedRegHandles() { | |
| 44 return (FlushRegKey(HKEY_LOCAL_MACHINE) && | |
| 45 FlushRegKey(HKEY_CLASSES_ROOT) && | |
| 46 FlushRegKey(HKEY_USERS)); | |
| 47 } | |
| 48 | |
| 49 // Checks if we have handle entries pending and runs the closer. | |
| 50 // Updates is_csrss_connected based on which handle types are closed. | |
| 51 bool CloseOpenHandles(bool* is_csrss_connected) { | |
| 52 if (sandbox::HandleCloserAgent::NeedsHandlesClosed()) { | |
| 53 sandbox::HandleCloserAgent handle_closer; | |
| 54 handle_closer.InitializeHandlesToClose(is_csrss_connected); | |
| 55 if (!handle_closer.CloseHandles()) | |
| 56 return false; | |
| 57 } | |
| 58 | |
| 59 return true; | |
| 60 } | |
| 61 | |
| 62 // GetUserDefaultLocaleName is not available on WIN XP. So we'll | |
| 63 // load it on-the-fly. | |
| 64 const wchar_t kKernel32DllName[] = L"kernel32.dll"; | |
| 65 typedef decltype(GetUserDefaultLocaleName)* GetUserDefaultLocaleNameFunction; | |
| 66 | |
| 67 // Warm up language subsystems before the sandbox is turned on. | |
| 68 // Tested on Win8.1 x64: | |
| 69 // This needs to happen after RevertToSelf() is called, because (at least) in | |
| 70 // the case of GetUserDefaultLCID() it checks the TEB to see if the process is | |
| 71 // impersonating (TEB!IsImpersonating). If it is, the cached locale information | |
| 72 // is not used, nor is it set. Therefore, calls after RevertToSelf() will not | |
| 73 // have warmed-up values to use. | |
| 74 bool WarmupWindowsLocales() { | |
| 75 // NOTE(liamjm): When last checked (Win 8.1 x64) it wasn't necessary to | |
| 76 // warmup all of these functions, but let's not assume that. | |
| 77 ::GetUserDefaultLangID(); | |
| 78 ::GetUserDefaultLCID(); | |
| 79 static GetUserDefaultLocaleNameFunction GetUserDefaultLocaleName_func = | |
| 80 NULL; | |
| 81 if (!GetUserDefaultLocaleName_func) { | |
| 82 HMODULE kernel32_dll = ::GetModuleHandle(kKernel32DllName); | |
| 83 if (!kernel32_dll) { | |
| 84 return false; | |
| 85 } | |
| 86 GetUserDefaultLocaleName_func = | |
| 87 reinterpret_cast<GetUserDefaultLocaleNameFunction>( | |
| 88 GetProcAddress(kernel32_dll, "GetUserDefaultLocaleName")); | |
| 89 if (!GetUserDefaultLocaleName_func) { | |
| 90 return false; | |
| 91 } | |
| 92 } | |
| 93 wchar_t localeName[LOCALE_NAME_MAX_LENGTH] = {0}; | |
| 94 return (0 != GetUserDefaultLocaleName_func( | |
| 95 localeName, LOCALE_NAME_MAX_LENGTH * sizeof(wchar_t))); | |
| 96 } | |
| 97 | |
| 98 // Used as storage for g_target_services, because other allocation facilities | |
| 99 // are not available early. We can't use a regular function static because on | |
| 100 // VS2015, because the CRT tries to acquire a lock to guard initialization, but | |
| 101 // this code runs before the CRT is initialized. | |
| 102 char g_target_services_memory[sizeof(sandbox::TargetServicesBase)]; | |
| 103 sandbox::TargetServicesBase* g_target_services = nullptr; | |
| 104 | |
| 105 } // namespace | |
| 106 | |
| 107 namespace sandbox { | |
| 108 | |
| 109 SANDBOX_INTERCEPT IntegrityLevel g_shared_delayed_integrity_level = | |
| 110 INTEGRITY_LEVEL_LAST; | |
| 111 SANDBOX_INTERCEPT MitigationFlags g_shared_delayed_mitigations = 0; | |
| 112 | |
| 113 TargetServicesBase::TargetServicesBase() { | |
| 114 } | |
| 115 | |
| 116 ResultCode TargetServicesBase::Init() { | |
| 117 process_state_.SetInitCalled(); | |
| 118 return SBOX_ALL_OK; | |
| 119 } | |
| 120 | |
| 121 // Failure here is a breach of security so the process is terminated. | |
| 122 void TargetServicesBase::LowerToken() { | |
| 123 if (ERROR_SUCCESS != | |
| 124 SetProcessIntegrityLevel(g_shared_delayed_integrity_level)) | |
| 125 ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_INTEGRITY); | |
| 126 process_state_.SetRevertedToSelf(); | |
| 127 // If the client code as called RegOpenKey, advapi32.dll has cached some | |
| 128 // handles. The following code gets rid of them. | |
| 129 if (!::RevertToSelf()) | |
| 130 ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_DROPTOKEN); | |
| 131 if (!FlushCachedRegHandles()) | |
| 132 ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_FLUSHANDLES); | |
| 133 if (ERROR_SUCCESS != ::RegDisablePredefinedCache()) | |
| 134 ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_CACHEDISABLE); | |
| 135 if (!WarmupWindowsLocales()) | |
| 136 ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_WARMUP); | |
| 137 bool is_csrss_connected = true; | |
| 138 if (!CloseOpenHandles(&is_csrss_connected)) | |
| 139 ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_CLOSEHANDLES); | |
| 140 process_state_.SetCsrssConnected(is_csrss_connected); | |
| 141 // Enabling mitigations must happen last otherwise handle closing breaks | |
| 142 if (g_shared_delayed_mitigations && | |
| 143 !ApplyProcessMitigationsToCurrentProcess(g_shared_delayed_mitigations)) | |
| 144 ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_MITIGATION); | |
| 145 } | |
| 146 | |
| 147 ProcessState* TargetServicesBase::GetState() { | |
| 148 return &process_state_; | |
| 149 } | |
| 150 | |
| 151 TargetServicesBase* TargetServicesBase::GetInstance() { | |
| 152 // Leak on purpose TargetServicesBase. | |
| 153 if (!g_target_services) | |
| 154 g_target_services = new (g_target_services_memory) TargetServicesBase; | |
| 155 return g_target_services; | |
| 156 } | |
| 157 | |
| 158 // The broker services a 'test' IPC service with the IPC_PING_TAG tag. | |
| 159 bool TargetServicesBase::TestIPCPing(int version) { | |
| 160 void* memory = GetGlobalIPCMemory(); | |
| 161 if (NULL == memory) { | |
| 162 return false; | |
| 163 } | |
| 164 SharedMemIPCClient ipc(memory); | |
| 165 CrossCallReturn answer = {0}; | |
| 166 | |
| 167 if (1 == version) { | |
| 168 uint32_t tick1 = ::GetTickCount(); | |
| 169 uint32_t cookie = 717115; | |
| 170 ResultCode code = CrossCall(ipc, IPC_PING1_TAG, cookie, &answer); | |
| 171 | |
| 172 if (SBOX_ALL_OK != code) { | |
| 173 return false; | |
| 174 } | |
| 175 // We should get two extended returns values from the IPC, one is the | |
| 176 // tick count on the broker and the other is the cookie times two. | |
| 177 if ((answer.extended_count != 2)) { | |
| 178 return false; | |
| 179 } | |
| 180 // We test the first extended answer to be within the bounds of the tick | |
| 181 // count only if there was no tick count wraparound. | |
| 182 uint32_t tick2 = ::GetTickCount(); | |
| 183 if (tick2 >= tick1) { | |
| 184 if ((answer.extended[0].unsigned_int < tick1) || | |
| 185 (answer.extended[0].unsigned_int > tick2)) { | |
| 186 return false; | |
| 187 } | |
| 188 } | |
| 189 | |
| 190 if (answer.extended[1].unsigned_int != cookie * 2) { | |
| 191 return false; | |
| 192 } | |
| 193 } else if (2 == version) { | |
| 194 uint32_t cookie = 717111; | |
| 195 InOutCountedBuffer counted_buffer(&cookie, sizeof(cookie)); | |
| 196 ResultCode code = CrossCall(ipc, IPC_PING2_TAG, counted_buffer, &answer); | |
| 197 | |
| 198 if (SBOX_ALL_OK != code) { | |
| 199 return false; | |
| 200 } | |
| 201 if (cookie != 717111 * 3) { | |
| 202 return false; | |
| 203 } | |
| 204 } else { | |
| 205 return false; | |
| 206 } | |
| 207 return true; | |
| 208 } | |
| 209 | |
| 210 ProcessState::ProcessState() : process_state_(0), csrss_connected_(true) { | |
| 211 } | |
| 212 | |
| 213 bool ProcessState::IsKernel32Loaded() const { | |
| 214 return process_state_ != 0; | |
| 215 } | |
| 216 | |
| 217 bool ProcessState::InitCalled() const { | |
| 218 return process_state_ > 1; | |
| 219 } | |
| 220 | |
| 221 bool ProcessState::RevertedToSelf() const { | |
| 222 return process_state_ > 2; | |
| 223 } | |
| 224 | |
| 225 bool ProcessState::IsCsrssConnected() const { | |
| 226 return csrss_connected_; | |
| 227 } | |
| 228 | |
| 229 void ProcessState::SetKernel32Loaded() { | |
| 230 if (!process_state_) | |
| 231 process_state_ = 1; | |
| 232 } | |
| 233 | |
| 234 void ProcessState::SetInitCalled() { | |
| 235 if (process_state_ < 2) | |
| 236 process_state_ = 2; | |
| 237 } | |
| 238 | |
| 239 void ProcessState::SetRevertedToSelf() { | |
| 240 if (process_state_ < 3) | |
| 241 process_state_ = 3; | |
| 242 } | |
| 243 | |
| 244 void ProcessState::SetCsrssConnected(bool csrss_connected) { | |
| 245 csrss_connected_ = csrss_connected; | |
| 246 } | |
| 247 | |
| 248 | |
| 249 ResultCode TargetServicesBase::DuplicateHandle(HANDLE source_handle, | |
| 250 DWORD target_process_id, | |
| 251 HANDLE* target_handle, | |
| 252 DWORD desired_access, | |
| 253 DWORD options) { | |
| 254 return sandbox::DuplicateHandleProxy(source_handle, target_process_id, | |
| 255 target_handle, desired_access, options); | |
| 256 } | |
| 257 | |
| 258 } // namespace sandbox | |
| OLD | NEW |