|
1 // Copyright (c) 2006-2011 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/process_thread_interception.h" |
|
6 |
|
7 #include "sandbox/win/src/crosscall_client.h" |
|
8 #include "sandbox/win/src/ipc_tags.h" |
|
9 #include "sandbox/win/src/policy_params.h" |
|
10 #include "sandbox/win/src/policy_target.h" |
|
11 #include "sandbox/win/src/sandbox_factory.h" |
|
12 #include "sandbox/win/src/sandbox_nt_util.h" |
|
13 #include "sandbox/win/src/sharedmem_ipc_client.h" |
|
14 #include "sandbox/win/src/target_services.h" |
|
15 |
|
16 namespace sandbox { |
|
17 |
|
18 SANDBOX_INTERCEPT NtExports g_nt; |
|
19 |
|
20 // Hooks NtOpenThread and proxy the call to the broker if it's trying to |
|
21 // open a thread in the same process. |
|
22 NTSTATUS WINAPI TargetNtOpenThread(NtOpenThreadFunction orig_OpenThread, |
|
23 PHANDLE thread, ACCESS_MASK desired_access, |
|
24 POBJECT_ATTRIBUTES object_attributes, |
|
25 PCLIENT_ID client_id) { |
|
26 NTSTATUS status = orig_OpenThread(thread, desired_access, object_attributes, |
|
27 client_id); |
|
28 if (NT_SUCCESS(status)) |
|
29 return status; |
|
30 |
|
31 do { |
|
32 if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) |
|
33 break; |
|
34 if (!client_id) |
|
35 break; |
|
36 |
|
37 uint32 thread_id = 0; |
|
38 bool should_break = false; |
|
39 __try { |
|
40 // We support only the calls for the current process |
|
41 if (NULL != client_id->UniqueProcess) |
|
42 should_break = true; |
|
43 |
|
44 // Object attributes should be NULL or empty. |
|
45 if (!should_break && NULL != object_attributes) { |
|
46 if (0 != object_attributes->Attributes || |
|
47 NULL != object_attributes->ObjectName || |
|
48 NULL != object_attributes->RootDirectory || |
|
49 NULL != object_attributes->SecurityDescriptor || |
|
50 NULL != object_attributes->SecurityQualityOfService) { |
|
51 should_break = true; |
|
52 } |
|
53 } |
|
54 |
|
55 thread_id = static_cast<uint32>( |
|
56 reinterpret_cast<ULONG_PTR>(client_id->UniqueThread)); |
|
57 } __except(EXCEPTION_EXECUTE_HANDLER) { |
|
58 break; |
|
59 } |
|
60 |
|
61 if (should_break) |
|
62 break; |
|
63 |
|
64 if (!ValidParameter(thread, sizeof(HANDLE), WRITE)) |
|
65 break; |
|
66 |
|
67 void* memory = GetGlobalIPCMemory(); |
|
68 if (NULL == memory) |
|
69 break; |
|
70 |
|
71 SharedMemIPCClient ipc(memory); |
|
72 CrossCallReturn answer = {0}; |
|
73 ResultCode code = CrossCall(ipc, IPC_NTOPENTHREAD_TAG, desired_access, |
|
74 thread_id, &answer); |
|
75 if (SBOX_ALL_OK != code) |
|
76 break; |
|
77 |
|
78 if (!NT_SUCCESS(answer.nt_status)) |
|
79 // The nt_status here is most likely STATUS_INVALID_CID because |
|
80 // in the broker we set the process id in the CID (client ID) param |
|
81 // to be the current process. If you try to open a thread from another |
|
82 // process you will get this INVALID_CID error. On the other hand, if you |
|
83 // try to open a thread in your own process, it should return success. |
|
84 // We don't want to return STATUS_INVALID_CID here, so we return the |
|
85 // return of the original open thread status, which is most likely |
|
86 // STATUS_ACCESS_DENIED. |
|
87 break; |
|
88 |
|
89 __try { |
|
90 // Write the output parameters. |
|
91 *thread = answer.handle; |
|
92 } __except(EXCEPTION_EXECUTE_HANDLER) { |
|
93 break; |
|
94 } |
|
95 |
|
96 return answer.nt_status; |
|
97 } while (false); |
|
98 |
|
99 return status; |
|
100 } |
|
101 |
|
102 // Hooks NtOpenProcess and proxy the call to the broker if it's trying to |
|
103 // open the current process. |
|
104 NTSTATUS WINAPI TargetNtOpenProcess(NtOpenProcessFunction orig_OpenProcess, |
|
105 PHANDLE process, ACCESS_MASK desired_access, |
|
106 POBJECT_ATTRIBUTES object_attributes, |
|
107 PCLIENT_ID client_id) { |
|
108 NTSTATUS status = orig_OpenProcess(process, desired_access, object_attributes, |
|
109 client_id); |
|
110 if (NT_SUCCESS(status)) |
|
111 return status; |
|
112 |
|
113 do { |
|
114 if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) |
|
115 break; |
|
116 if (!client_id) |
|
117 break; |
|
118 |
|
119 uint32 process_id = 0; |
|
120 bool should_break = false; |
|
121 __try { |
|
122 // Object attributes should be NULL or empty. |
|
123 if (!should_break && NULL != object_attributes) { |
|
124 if (0 != object_attributes->Attributes || |
|
125 NULL != object_attributes->ObjectName || |
|
126 NULL != object_attributes->RootDirectory || |
|
127 NULL != object_attributes->SecurityDescriptor || |
|
128 NULL != object_attributes->SecurityQualityOfService) { |
|
129 should_break = true; |
|
130 } |
|
131 } |
|
132 |
|
133 process_id = static_cast<uint32>( |
|
134 reinterpret_cast<ULONG_PTR>(client_id->UniqueProcess)); |
|
135 } __except(EXCEPTION_EXECUTE_HANDLER) { |
|
136 break; |
|
137 } |
|
138 |
|
139 if (should_break) |
|
140 break; |
|
141 |
|
142 if (!ValidParameter(process, sizeof(HANDLE), WRITE)) |
|
143 break; |
|
144 |
|
145 void* memory = GetGlobalIPCMemory(); |
|
146 if (NULL == memory) |
|
147 break; |
|
148 |
|
149 SharedMemIPCClient ipc(memory); |
|
150 CrossCallReturn answer = {0}; |
|
151 ResultCode code = CrossCall(ipc, IPC_NTOPENPROCESS_TAG, desired_access, |
|
152 process_id, &answer); |
|
153 if (SBOX_ALL_OK != code) |
|
154 break; |
|
155 |
|
156 if (!NT_SUCCESS(answer.nt_status)) |
|
157 return answer.nt_status; |
|
158 |
|
159 __try { |
|
160 // Write the output parameters. |
|
161 *process = answer.handle; |
|
162 } __except(EXCEPTION_EXECUTE_HANDLER) { |
|
163 break; |
|
164 } |
|
165 |
|
166 return answer.nt_status; |
|
167 } while (false); |
|
168 |
|
169 return status; |
|
170 } |
|
171 |
|
172 |
|
173 NTSTATUS WINAPI TargetNtOpenProcessToken( |
|
174 NtOpenProcessTokenFunction orig_OpenProcessToken, HANDLE process, |
|
175 ACCESS_MASK desired_access, PHANDLE token) { |
|
176 NTSTATUS status = orig_OpenProcessToken(process, desired_access, token); |
|
177 if (NT_SUCCESS(status)) |
|
178 return status; |
|
179 |
|
180 do { |
|
181 if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) |
|
182 break; |
|
183 |
|
184 if (CURRENT_PROCESS != process) |
|
185 break; |
|
186 |
|
187 if (!ValidParameter(token, sizeof(HANDLE), WRITE)) |
|
188 break; |
|
189 |
|
190 void* memory = GetGlobalIPCMemory(); |
|
191 if (NULL == memory) |
|
192 break; |
|
193 |
|
194 SharedMemIPCClient ipc(memory); |
|
195 CrossCallReturn answer = {0}; |
|
196 ResultCode code = CrossCall(ipc, IPC_NTOPENPROCESSTOKEN_TAG, process, |
|
197 desired_access, &answer); |
|
198 if (SBOX_ALL_OK != code) |
|
199 break; |
|
200 |
|
201 if (!NT_SUCCESS(answer.nt_status)) |
|
202 return answer.nt_status; |
|
203 |
|
204 __try { |
|
205 // Write the output parameters. |
|
206 *token = answer.handle; |
|
207 } __except(EXCEPTION_EXECUTE_HANDLER) { |
|
208 break; |
|
209 } |
|
210 |
|
211 return answer.nt_status; |
|
212 } while (false); |
|
213 |
|
214 return status; |
|
215 } |
|
216 |
|
217 NTSTATUS WINAPI TargetNtOpenProcessTokenEx( |
|
218 NtOpenProcessTokenExFunction orig_OpenProcessTokenEx, HANDLE process, |
|
219 ACCESS_MASK desired_access, ULONG handle_attributes, PHANDLE token) { |
|
220 NTSTATUS status = orig_OpenProcessTokenEx(process, desired_access, |
|
221 handle_attributes, token); |
|
222 if (NT_SUCCESS(status)) |
|
223 return status; |
|
224 |
|
225 do { |
|
226 if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) |
|
227 break; |
|
228 |
|
229 if (CURRENT_PROCESS != process) |
|
230 break; |
|
231 |
|
232 if (!ValidParameter(token, sizeof(HANDLE), WRITE)) |
|
233 break; |
|
234 |
|
235 void* memory = GetGlobalIPCMemory(); |
|
236 if (NULL == memory) |
|
237 break; |
|
238 |
|
239 SharedMemIPCClient ipc(memory); |
|
240 CrossCallReturn answer = {0}; |
|
241 ResultCode code = CrossCall(ipc, IPC_NTOPENPROCESSTOKENEX_TAG, process, |
|
242 desired_access, handle_attributes, &answer); |
|
243 if (SBOX_ALL_OK != code) |
|
244 break; |
|
245 |
|
246 if (!NT_SUCCESS(answer.nt_status)) |
|
247 return answer.nt_status; |
|
248 |
|
249 __try { |
|
250 // Write the output parameters. |
|
251 *token = answer.handle; |
|
252 } __except(EXCEPTION_EXECUTE_HANDLER) { |
|
253 break; |
|
254 } |
|
255 |
|
256 return answer.nt_status; |
|
257 } while (false); |
|
258 |
|
259 return status; |
|
260 } |
|
261 |
|
262 BOOL WINAPI TargetCreateProcessW(CreateProcessWFunction orig_CreateProcessW, |
|
263 LPCWSTR application_name, LPWSTR command_line, |
|
264 LPSECURITY_ATTRIBUTES process_attributes, |
|
265 LPSECURITY_ATTRIBUTES thread_attributes, |
|
266 BOOL inherit_handles, DWORD flags, |
|
267 LPVOID environment, LPCWSTR current_directory, |
|
268 LPSTARTUPINFOW startup_info, |
|
269 LPPROCESS_INFORMATION process_information) { |
|
270 if (orig_CreateProcessW(application_name, command_line, process_attributes, |
|
271 thread_attributes, inherit_handles, flags, |
|
272 environment, current_directory, startup_info, |
|
273 process_information)) { |
|
274 return TRUE; |
|
275 } |
|
276 DWORD original_error = ::GetLastError(); |
|
277 |
|
278 // We don't trust that the IPC can work this early. |
|
279 if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) |
|
280 return FALSE; |
|
281 |
|
282 do { |
|
283 if (!ValidParameter(process_information, sizeof(PROCESS_INFORMATION), |
|
284 WRITE)) |
|
285 break; |
|
286 |
|
287 void* memory = GetGlobalIPCMemory(); |
|
288 if (NULL == memory) |
|
289 break; |
|
290 |
|
291 const wchar_t* cur_dir = NULL; |
|
292 |
|
293 wchar_t current_directory[MAX_PATH]; |
|
294 DWORD result = ::GetCurrentDirectory(MAX_PATH, current_directory); |
|
295 if (0 != result && result < MAX_PATH) |
|
296 cur_dir = current_directory; |
|
297 |
|
298 SharedMemIPCClient ipc(memory); |
|
299 CrossCallReturn answer = {0}; |
|
300 |
|
301 InOutCountedBuffer proc_info(process_information, |
|
302 sizeof(PROCESS_INFORMATION)); |
|
303 |
|
304 ResultCode code = CrossCall(ipc, IPC_CREATEPROCESSW_TAG, application_name, |
|
305 command_line, cur_dir, proc_info, &answer); |
|
306 if (SBOX_ALL_OK != code) |
|
307 break; |
|
308 |
|
309 ::SetLastError(answer.win32_result); |
|
310 if (ERROR_SUCCESS != answer.win32_result) |
|
311 return FALSE; |
|
312 |
|
313 return TRUE; |
|
314 } while (false); |
|
315 |
|
316 ::SetLastError(original_error); |
|
317 return FALSE; |
|
318 } |
|
319 |
|
320 BOOL WINAPI TargetCreateProcessA(CreateProcessAFunction orig_CreateProcessA, |
|
321 LPCSTR application_name, LPSTR command_line, |
|
322 LPSECURITY_ATTRIBUTES process_attributes, |
|
323 LPSECURITY_ATTRIBUTES thread_attributes, |
|
324 BOOL inherit_handles, DWORD flags, |
|
325 LPVOID environment, LPCSTR current_directory, |
|
326 LPSTARTUPINFOA startup_info, |
|
327 LPPROCESS_INFORMATION process_information) { |
|
328 if (orig_CreateProcessA(application_name, command_line, process_attributes, |
|
329 thread_attributes, inherit_handles, flags, |
|
330 environment, current_directory, startup_info, |
|
331 process_information)) { |
|
332 return TRUE; |
|
333 } |
|
334 DWORD original_error = ::GetLastError(); |
|
335 |
|
336 // We don't trust that the IPC can work this early. |
|
337 if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) |
|
338 return FALSE; |
|
339 |
|
340 do { |
|
341 if (!ValidParameter(process_information, sizeof(PROCESS_INFORMATION), |
|
342 WRITE)) |
|
343 break; |
|
344 |
|
345 void* memory = GetGlobalIPCMemory(); |
|
346 if (NULL == memory) |
|
347 break; |
|
348 |
|
349 // Convert the input params to unicode. |
|
350 UNICODE_STRING *cmd_unicode = NULL; |
|
351 UNICODE_STRING *app_unicode = NULL; |
|
352 if (command_line) { |
|
353 cmd_unicode = AnsiToUnicode(command_line); |
|
354 if (!cmd_unicode) |
|
355 break; |
|
356 } |
|
357 |
|
358 if (application_name) { |
|
359 app_unicode = AnsiToUnicode(application_name); |
|
360 if (!app_unicode) { |
|
361 operator delete(cmd_unicode, NT_ALLOC); |
|
362 break; |
|
363 } |
|
364 } |
|
365 |
|
366 const wchar_t* cmd_line = cmd_unicode ? cmd_unicode->Buffer : NULL; |
|
367 const wchar_t* app_name = app_unicode ? app_unicode->Buffer : NULL; |
|
368 const wchar_t* cur_dir = NULL; |
|
369 |
|
370 wchar_t current_directory[MAX_PATH]; |
|
371 DWORD result = ::GetCurrentDirectory(MAX_PATH, current_directory); |
|
372 if (0 != result && result < MAX_PATH) |
|
373 cur_dir = current_directory; |
|
374 |
|
375 SharedMemIPCClient ipc(memory); |
|
376 CrossCallReturn answer = {0}; |
|
377 |
|
378 InOutCountedBuffer proc_info(process_information, |
|
379 sizeof(PROCESS_INFORMATION)); |
|
380 |
|
381 ResultCode code = CrossCall(ipc, IPC_CREATEPROCESSW_TAG, app_name, |
|
382 cmd_line, cur_dir, proc_info, &answer); |
|
383 |
|
384 operator delete(cmd_unicode, NT_ALLOC); |
|
385 operator delete(app_unicode, NT_ALLOC); |
|
386 |
|
387 if (SBOX_ALL_OK != code) |
|
388 break; |
|
389 |
|
390 ::SetLastError(answer.win32_result); |
|
391 if (ERROR_SUCCESS != answer.win32_result) |
|
392 return FALSE; |
|
393 |
|
394 return TRUE; |
|
395 } while (false); |
|
396 |
|
397 ::SetLastError(original_error); |
|
398 return FALSE; |
|
399 } |
|
400 |
|
401 // Creates a thread without registering with CSRSS. This is required if we |
|
402 // closed the CSRSS ALPC port after lockdown. |
|
403 HANDLE WINAPI TargetCreateThread(CreateThreadFunction orig_CreateThread, |
|
404 LPSECURITY_ATTRIBUTES thread_attributes, |
|
405 SIZE_T stack_size, |
|
406 LPTHREAD_START_ROUTINE start_address, |
|
407 PVOID parameter, |
|
408 DWORD creation_flags, |
|
409 LPDWORD thread_id) { |
|
410 // Try the normal CreateThread; switch to RtlCreateUserThread if needed. |
|
411 static bool use_create_thread = true; |
|
412 HANDLE thread; |
|
413 if (use_create_thread) { |
|
414 thread = orig_CreateThread(thread_attributes, stack_size, start_address, |
|
415 parameter, creation_flags, thread_id); |
|
416 if (thread) |
|
417 return thread; |
|
418 } |
|
419 |
|
420 PSECURITY_DESCRIPTOR sd = |
|
421 thread_attributes ? thread_attributes->lpSecurityDescriptor : NULL; |
|
422 CLIENT_ID client_id; |
|
423 |
|
424 NTSTATUS result = g_nt.RtlCreateUserThread(NtCurrentProcess, sd, |
|
425 creation_flags & CREATE_SUSPENDED, |
|
426 0, stack_size, 0, start_address, |
|
427 parameter, &thread, &client_id); |
|
428 if (!NT_SUCCESS(result)) |
|
429 return 0; |
|
430 |
|
431 // CSRSS is closed if we got here, so use RtlCreateUserThread from here on. |
|
432 use_create_thread = false; |
|
433 if (thread_id) |
|
434 *thread_id = HandleToUlong(client_id.UniqueThread); |
|
435 return thread; |
|
436 } |
|
437 |
|
438 // Cache the default LCID to avoid pinging CSRSS after lockdown. |
|
439 // TODO(jschuh): This approach will miss a default locale changes after |
|
440 // lockdown. In the future we may want to have the broker check instead. |
|
441 LCID WINAPI TargetGetUserDefaultLCID( |
|
442 GetUserDefaultLCIDFunction orig_GetUserDefaultLCID) { |
|
443 static LCID default_lcid = orig_GetUserDefaultLCID(); |
|
444 return default_lcid; |
|
445 } |
|
446 |
|
447 } // namespace sandbox |