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.
5 #include "sandbox/win/src/process_thread_interception.h"
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"
18 SANDBOX_INTERCEPT NtExports g_nt
;
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
,
28 if (NT_SUCCESS(status
))
32 if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
38 bool should_break
= false;
40 // We support only the calls for the current process
41 if (NULL
!= client_id
->UniqueProcess
)
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
) {
55 thread_id
= static_cast<uint32
>(
56 reinterpret_cast<ULONG_PTR
>(client_id
->UniqueThread
));
57 } __except(EXCEPTION_EXECUTE_HANDLER
) {
64 if (!ValidParameter(thread
, sizeof(HANDLE
), WRITE
))
67 void* memory
= GetGlobalIPCMemory();
71 SharedMemIPCClient
ipc(memory
);
72 CrossCallReturn answer
= {0};
73 ResultCode code
= CrossCall(ipc
, IPC_NTOPENTHREAD_TAG
, desired_access
,
75 if (SBOX_ALL_OK
!= code
)
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.
90 // Write the output parameters.
91 *thread
= answer
.handle
;
92 } __except(EXCEPTION_EXECUTE_HANDLER
) {
96 return answer
.nt_status
;
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
,
110 if (NT_SUCCESS(status
))
114 if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
119 uint32 process_id
= 0;
120 bool should_break
= false;
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
) {
133 process_id
= static_cast<uint32
>(
134 reinterpret_cast<ULONG_PTR
>(client_id
->UniqueProcess
));
135 } __except(EXCEPTION_EXECUTE_HANDLER
) {
142 if (!ValidParameter(process
, sizeof(HANDLE
), WRITE
))
145 void* memory
= GetGlobalIPCMemory();
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
)
156 if (!NT_SUCCESS(answer
.nt_status
))
157 return answer
.nt_status
;
160 // Write the output parameters.
161 *process
= answer
.handle
;
162 } __except(EXCEPTION_EXECUTE_HANDLER
) {
166 return answer
.nt_status
;
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
))
181 if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
184 if (CURRENT_PROCESS
!= process
)
187 if (!ValidParameter(token
, sizeof(HANDLE
), WRITE
))
190 void* memory
= GetGlobalIPCMemory();
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
)
201 if (!NT_SUCCESS(answer
.nt_status
))
202 return answer
.nt_status
;
205 // Write the output parameters.
206 *token
= answer
.handle
;
207 } __except(EXCEPTION_EXECUTE_HANDLER
) {
211 return answer
.nt_status
;
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
))
226 if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
229 if (CURRENT_PROCESS
!= process
)
232 if (!ValidParameter(token
, sizeof(HANDLE
), WRITE
))
235 void* memory
= GetGlobalIPCMemory();
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
)
246 if (!NT_SUCCESS(answer
.nt_status
))
247 return answer
.nt_status
;
250 // Write the output parameters.
251 *token
= answer
.handle
;
252 } __except(EXCEPTION_EXECUTE_HANDLER
) {
256 return answer
.nt_status
;
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
)) {
277 // We don't trust that the IPC can work this early.
278 if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
281 DWORD original_error
= ::GetLastError();
284 if (!ValidParameter(process_information
, sizeof(PROCESS_INFORMATION
),
288 void* memory
= GetGlobalIPCMemory();
292 const wchar_t* cur_dir
= NULL
;
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
;
299 SharedMemIPCClient
ipc(memory
);
300 CrossCallReturn answer
= {0};
302 InOutCountedBuffer
proc_info(process_information
,
303 sizeof(PROCESS_INFORMATION
));
305 ResultCode code
= CrossCall(ipc
, IPC_CREATEPROCESSW_TAG
, application_name
,
306 command_line
, cur_dir
, proc_info
, &answer
);
307 if (SBOX_ALL_OK
!= code
)
310 ::SetLastError(answer
.win32_result
);
311 if (ERROR_SUCCESS
!= answer
.win32_result
)
317 ::SetLastError(original_error
);
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
)) {
336 // We don't trust that the IPC can work this early.
337 if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
340 DWORD original_error
= ::GetLastError();
343 if (!ValidParameter(process_information
, sizeof(PROCESS_INFORMATION
),
347 void* memory
= GetGlobalIPCMemory();
351 // Convert the input params to unicode.
352 UNICODE_STRING
*cmd_unicode
= NULL
;
353 UNICODE_STRING
*app_unicode
= NULL
;
355 cmd_unicode
= AnsiToUnicode(command_line
);
360 if (application_name
) {
361 app_unicode
= AnsiToUnicode(application_name
);
363 operator delete(cmd_unicode
, NT_ALLOC
);
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
;
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
;
377 SharedMemIPCClient
ipc(memory
);
378 CrossCallReturn answer
= {0};
380 InOutCountedBuffer
proc_info(process_information
,
381 sizeof(PROCESS_INFORMATION
));
383 ResultCode code
= CrossCall(ipc
, IPC_CREATEPROCESSW_TAG
, app_name
,
384 cmd_line
, cur_dir
, proc_info
, &answer
);
386 operator delete(cmd_unicode
, NT_ALLOC
);
387 operator delete(app_unicode
, NT_ALLOC
);
389 if (SBOX_ALL_OK
!= code
)
392 ::SetLastError(answer
.win32_result
);
393 if (ERROR_SUCCESS
!= answer
.win32_result
)
399 ::SetLastError(original_error
);
403 // Creates a thread without registering with CSRSS. This is required if we
404 // closed the CSRSS ALPC port after lockdown.
405 HANDLE WINAPI
TargetCreateThread(CreateThreadFunction orig_CreateThread
,
406 LPSECURITY_ATTRIBUTES thread_attributes
,
408 LPTHREAD_START_ROUTINE start_address
,
410 DWORD creation_flags
,
412 // Try the normal CreateThread; switch to RtlCreateUserThread if needed.
413 static bool use_create_thread
= true;
415 if (use_create_thread
) {
416 thread
= orig_CreateThread(thread_attributes
, stack_size
, start_address
,
417 parameter
, creation_flags
, thread_id
);
422 PSECURITY_DESCRIPTOR sd
=
423 thread_attributes
? thread_attributes
->lpSecurityDescriptor
: NULL
;
426 NTSTATUS result
= g_nt
.RtlCreateUserThread(NtCurrentProcess
, sd
,
427 creation_flags
& CREATE_SUSPENDED
,
428 0, stack_size
, 0, start_address
,
429 parameter
, &thread
, &client_id
);
430 if (!NT_SUCCESS(result
))
433 // CSRSS is closed if we got here, so use RtlCreateUserThread from here on.
434 use_create_thread
= false;
436 *thread_id
= HandleToUlong(client_id
.UniqueThread
);
440 // Cache the default LCID to avoid pinging CSRSS after lockdown.
441 // TODO(jschuh): This approach will miss a default locale changes after
442 // lockdown. In the future we may want to have the broker check instead.
443 LCID WINAPI
TargetGetUserDefaultLCID(
444 GetUserDefaultLCIDFunction orig_GetUserDefaultLCID
) {
445 static LCID default_lcid
= orig_GetUserDefaultLCID();
449 } // namespace sandbox