1 // Copyright (c) 2013 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.
TargetNtOpenThread(NtOpenThreadFunction orig_OpenThread,PHANDLE thread,ACCESS_MASK desired_access,POBJECT_ATTRIBUTES object_attributes,PCLIENT_ID client_id)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.
TargetNtOpenProcess(NtOpenProcessFunction orig_OpenProcess,PHANDLE process,ACCESS_MASK desired_access,POBJECT_ATTRIBUTES object_attributes,PCLIENT_ID client_id)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
TargetNtOpenProcessToken(NtOpenProcessTokenFunction orig_OpenProcessToken,HANDLE process,ACCESS_MASK desired_access,PHANDLE token)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
TargetNtOpenProcessTokenEx(NtOpenProcessTokenExFunction orig_OpenProcessTokenEx,HANDLE process,ACCESS_MASK desired_access,ULONG handle_attributes,PHANDLE token)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
TargetCreateProcessW(CreateProcessWFunction orig_CreateProcessW,LPCWSTR application_name,LPWSTR command_line,LPSECURITY_ATTRIBUTES process_attributes,LPSECURITY_ATTRIBUTES thread_attributes,BOOL inherit_handles,DWORD flags,LPVOID environment,LPCWSTR current_directory,LPSTARTUPINFOW startup_info,LPPROCESS_INFORMATION process_information)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
277 // We don't trust that the IPC can work this early.
278 if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
279 return FALSE;
280
281 DWORD original_error = ::GetLastError();
282
283 do {
284 if (!ValidParameter(process_information, sizeof(PROCESS_INFORMATION),
285 WRITE))
286 break;
287
288 void* memory = GetGlobalIPCMemory();
289 if (NULL == memory)
290 break;
291
292 const wchar_t* cur_dir = NULL;
293
294 wchar_t current_directory[MAX_PATH];
295 DWORD result = ::GetCurrentDirectory(MAX_PATH, current_directory);
296 if (0 != result && result < MAX_PATH)
297 cur_dir = current_directory;
298
299 SharedMemIPCClient ipc(memory);
300 CrossCallReturn answer = {0};
301
302 InOutCountedBuffer proc_info(process_information,
303 sizeof(PROCESS_INFORMATION));
304
305 ResultCode code = CrossCall(ipc, IPC_CREATEPROCESSW_TAG, application_name,
306 command_line, cur_dir, proc_info, &answer);
307 if (SBOX_ALL_OK != code)
308 break;
309
310 ::SetLastError(answer.win32_result);
311 if (ERROR_SUCCESS != answer.win32_result)
312 return FALSE;
313
314 return TRUE;
315 } while (false);
316
317 ::SetLastError(original_error);
318 return FALSE;
319 }
320
TargetCreateProcessA(CreateProcessAFunction orig_CreateProcessA,LPCSTR application_name,LPSTR command_line,LPSECURITY_ATTRIBUTES process_attributes,LPSECURITY_ATTRIBUTES thread_attributes,BOOL inherit_handles,DWORD flags,LPVOID environment,LPCSTR current_directory,LPSTARTUPINFOA startup_info,LPPROCESS_INFORMATION process_information)321 BOOL WINAPI TargetCreateProcessA(CreateProcessAFunction orig_CreateProcessA,
322 LPCSTR application_name, LPSTR command_line,
323 LPSECURITY_ATTRIBUTES process_attributes,
324 LPSECURITY_ATTRIBUTES thread_attributes,
325 BOOL inherit_handles, DWORD flags,
326 LPVOID environment, LPCSTR current_directory,
327 LPSTARTUPINFOA startup_info,
328 LPPROCESS_INFORMATION process_information) {
329 if (orig_CreateProcessA(application_name, command_line, process_attributes,
330 thread_attributes, inherit_handles, flags,
331 environment, current_directory, startup_info,
332 process_information)) {
333 return TRUE;
334 }
335
336 // We don't trust that the IPC can work this early.
337 if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
338 return FALSE;
339
340 DWORD original_error = ::GetLastError();
341
342 do {
343 if (!ValidParameter(process_information, sizeof(PROCESS_INFORMATION),
344 WRITE))
345 break;
346
347 void* memory = GetGlobalIPCMemory();
348 if (NULL == memory)
349 break;
350
351 // Convert the input params to unicode.
352 UNICODE_STRING *cmd_unicode = NULL;
353 UNICODE_STRING *app_unicode = NULL;
354 if (command_line) {
355 cmd_unicode = AnsiToUnicode(command_line);
356 if (!cmd_unicode)
357 break;
358 }
359
360 if (application_name) {
361 app_unicode = AnsiToUnicode(application_name);
362 if (!app_unicode) {
363 operator delete(cmd_unicode, NT_ALLOC);
364 break;
365 }
366 }
367
368 const wchar_t* cmd_line = cmd_unicode ? cmd_unicode->Buffer : NULL;
369 const wchar_t* app_name = app_unicode ? app_unicode->Buffer : NULL;
370 const wchar_t* cur_dir = NULL;
371
372 wchar_t current_directory[MAX_PATH];
373 DWORD result = ::GetCurrentDirectory(MAX_PATH, current_directory);
374 if (0 != result && result < MAX_PATH)
375 cur_dir = current_directory;
376
377 SharedMemIPCClient ipc(memory);
378 CrossCallReturn answer = {0};
379
380 InOutCountedBuffer proc_info(process_information,
381 sizeof(PROCESS_INFORMATION));
382
383 ResultCode code = CrossCall(ipc, IPC_CREATEPROCESSW_TAG, app_name,
384 cmd_line, cur_dir, proc_info, &answer);
385
386 operator delete(cmd_unicode, NT_ALLOC);
387 operator delete(app_unicode, NT_ALLOC);
388
389 if (SBOX_ALL_OK != code)
390 break;
391
392 ::SetLastError(answer.win32_result);
393 if (ERROR_SUCCESS != answer.win32_result)
394 return FALSE;
395
396 return TRUE;
397 } while (false);
398
399 ::SetLastError(original_error);
400 return FALSE;
401 }
402
403 } // namespace sandbox
404