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 |