1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/sandbox/win/src/process_thread_interception.cc Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,447 @@ 1.4 +// Copyright (c) 2006-2011 The Chromium Authors. All rights reserved. 1.5 +// Use of this source code is governed by a BSD-style license that can be 1.6 +// found in the LICENSE file. 1.7 + 1.8 +#include "sandbox/win/src/process_thread_interception.h" 1.9 + 1.10 +#include "sandbox/win/src/crosscall_client.h" 1.11 +#include "sandbox/win/src/ipc_tags.h" 1.12 +#include "sandbox/win/src/policy_params.h" 1.13 +#include "sandbox/win/src/policy_target.h" 1.14 +#include "sandbox/win/src/sandbox_factory.h" 1.15 +#include "sandbox/win/src/sandbox_nt_util.h" 1.16 +#include "sandbox/win/src/sharedmem_ipc_client.h" 1.17 +#include "sandbox/win/src/target_services.h" 1.18 + 1.19 +namespace sandbox { 1.20 + 1.21 +SANDBOX_INTERCEPT NtExports g_nt; 1.22 + 1.23 +// Hooks NtOpenThread and proxy the call to the broker if it's trying to 1.24 +// open a thread in the same process. 1.25 +NTSTATUS WINAPI TargetNtOpenThread(NtOpenThreadFunction orig_OpenThread, 1.26 + PHANDLE thread, ACCESS_MASK desired_access, 1.27 + POBJECT_ATTRIBUTES object_attributes, 1.28 + PCLIENT_ID client_id) { 1.29 + NTSTATUS status = orig_OpenThread(thread, desired_access, object_attributes, 1.30 + client_id); 1.31 + if (NT_SUCCESS(status)) 1.32 + return status; 1.33 + 1.34 + do { 1.35 + if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) 1.36 + break; 1.37 + if (!client_id) 1.38 + break; 1.39 + 1.40 + uint32 thread_id = 0; 1.41 + bool should_break = false; 1.42 + __try { 1.43 + // We support only the calls for the current process 1.44 + if (NULL != client_id->UniqueProcess) 1.45 + should_break = true; 1.46 + 1.47 + // Object attributes should be NULL or empty. 1.48 + if (!should_break && NULL != object_attributes) { 1.49 + if (0 != object_attributes->Attributes || 1.50 + NULL != object_attributes->ObjectName || 1.51 + NULL != object_attributes->RootDirectory || 1.52 + NULL != object_attributes->SecurityDescriptor || 1.53 + NULL != object_attributes->SecurityQualityOfService) { 1.54 + should_break = true; 1.55 + } 1.56 + } 1.57 + 1.58 + thread_id = static_cast<uint32>( 1.59 + reinterpret_cast<ULONG_PTR>(client_id->UniqueThread)); 1.60 + } __except(EXCEPTION_EXECUTE_HANDLER) { 1.61 + break; 1.62 + } 1.63 + 1.64 + if (should_break) 1.65 + break; 1.66 + 1.67 + if (!ValidParameter(thread, sizeof(HANDLE), WRITE)) 1.68 + break; 1.69 + 1.70 + void* memory = GetGlobalIPCMemory(); 1.71 + if (NULL == memory) 1.72 + break; 1.73 + 1.74 + SharedMemIPCClient ipc(memory); 1.75 + CrossCallReturn answer = {0}; 1.76 + ResultCode code = CrossCall(ipc, IPC_NTOPENTHREAD_TAG, desired_access, 1.77 + thread_id, &answer); 1.78 + if (SBOX_ALL_OK != code) 1.79 + break; 1.80 + 1.81 + if (!NT_SUCCESS(answer.nt_status)) 1.82 + // The nt_status here is most likely STATUS_INVALID_CID because 1.83 + // in the broker we set the process id in the CID (client ID) param 1.84 + // to be the current process. If you try to open a thread from another 1.85 + // process you will get this INVALID_CID error. On the other hand, if you 1.86 + // try to open a thread in your own process, it should return success. 1.87 + // We don't want to return STATUS_INVALID_CID here, so we return the 1.88 + // return of the original open thread status, which is most likely 1.89 + // STATUS_ACCESS_DENIED. 1.90 + break; 1.91 + 1.92 + __try { 1.93 + // Write the output parameters. 1.94 + *thread = answer.handle; 1.95 + } __except(EXCEPTION_EXECUTE_HANDLER) { 1.96 + break; 1.97 + } 1.98 + 1.99 + return answer.nt_status; 1.100 + } while (false); 1.101 + 1.102 + return status; 1.103 +} 1.104 + 1.105 +// Hooks NtOpenProcess and proxy the call to the broker if it's trying to 1.106 +// open the current process. 1.107 +NTSTATUS WINAPI TargetNtOpenProcess(NtOpenProcessFunction orig_OpenProcess, 1.108 + PHANDLE process, ACCESS_MASK desired_access, 1.109 + POBJECT_ATTRIBUTES object_attributes, 1.110 + PCLIENT_ID client_id) { 1.111 + NTSTATUS status = orig_OpenProcess(process, desired_access, object_attributes, 1.112 + client_id); 1.113 + if (NT_SUCCESS(status)) 1.114 + return status; 1.115 + 1.116 + do { 1.117 + if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) 1.118 + break; 1.119 + if (!client_id) 1.120 + break; 1.121 + 1.122 + uint32 process_id = 0; 1.123 + bool should_break = false; 1.124 + __try { 1.125 + // Object attributes should be NULL or empty. 1.126 + if (!should_break && NULL != object_attributes) { 1.127 + if (0 != object_attributes->Attributes || 1.128 + NULL != object_attributes->ObjectName || 1.129 + NULL != object_attributes->RootDirectory || 1.130 + NULL != object_attributes->SecurityDescriptor || 1.131 + NULL != object_attributes->SecurityQualityOfService) { 1.132 + should_break = true; 1.133 + } 1.134 + } 1.135 + 1.136 + process_id = static_cast<uint32>( 1.137 + reinterpret_cast<ULONG_PTR>(client_id->UniqueProcess)); 1.138 + } __except(EXCEPTION_EXECUTE_HANDLER) { 1.139 + break; 1.140 + } 1.141 + 1.142 + if (should_break) 1.143 + break; 1.144 + 1.145 + if (!ValidParameter(process, sizeof(HANDLE), WRITE)) 1.146 + break; 1.147 + 1.148 + void* memory = GetGlobalIPCMemory(); 1.149 + if (NULL == memory) 1.150 + break; 1.151 + 1.152 + SharedMemIPCClient ipc(memory); 1.153 + CrossCallReturn answer = {0}; 1.154 + ResultCode code = CrossCall(ipc, IPC_NTOPENPROCESS_TAG, desired_access, 1.155 + process_id, &answer); 1.156 + if (SBOX_ALL_OK != code) 1.157 + break; 1.158 + 1.159 + if (!NT_SUCCESS(answer.nt_status)) 1.160 + return answer.nt_status; 1.161 + 1.162 + __try { 1.163 + // Write the output parameters. 1.164 + *process = answer.handle; 1.165 + } __except(EXCEPTION_EXECUTE_HANDLER) { 1.166 + break; 1.167 + } 1.168 + 1.169 + return answer.nt_status; 1.170 + } while (false); 1.171 + 1.172 + return status; 1.173 +} 1.174 + 1.175 + 1.176 +NTSTATUS WINAPI TargetNtOpenProcessToken( 1.177 + NtOpenProcessTokenFunction orig_OpenProcessToken, HANDLE process, 1.178 + ACCESS_MASK desired_access, PHANDLE token) { 1.179 + NTSTATUS status = orig_OpenProcessToken(process, desired_access, token); 1.180 + if (NT_SUCCESS(status)) 1.181 + return status; 1.182 + 1.183 + do { 1.184 + if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) 1.185 + break; 1.186 + 1.187 + if (CURRENT_PROCESS != process) 1.188 + break; 1.189 + 1.190 + if (!ValidParameter(token, sizeof(HANDLE), WRITE)) 1.191 + break; 1.192 + 1.193 + void* memory = GetGlobalIPCMemory(); 1.194 + if (NULL == memory) 1.195 + break; 1.196 + 1.197 + SharedMemIPCClient ipc(memory); 1.198 + CrossCallReturn answer = {0}; 1.199 + ResultCode code = CrossCall(ipc, IPC_NTOPENPROCESSTOKEN_TAG, process, 1.200 + desired_access, &answer); 1.201 + if (SBOX_ALL_OK != code) 1.202 + break; 1.203 + 1.204 + if (!NT_SUCCESS(answer.nt_status)) 1.205 + return answer.nt_status; 1.206 + 1.207 + __try { 1.208 + // Write the output parameters. 1.209 + *token = answer.handle; 1.210 + } __except(EXCEPTION_EXECUTE_HANDLER) { 1.211 + break; 1.212 + } 1.213 + 1.214 + return answer.nt_status; 1.215 + } while (false); 1.216 + 1.217 + return status; 1.218 +} 1.219 + 1.220 +NTSTATUS WINAPI TargetNtOpenProcessTokenEx( 1.221 + NtOpenProcessTokenExFunction orig_OpenProcessTokenEx, HANDLE process, 1.222 + ACCESS_MASK desired_access, ULONG handle_attributes, PHANDLE token) { 1.223 + NTSTATUS status = orig_OpenProcessTokenEx(process, desired_access, 1.224 + handle_attributes, token); 1.225 + if (NT_SUCCESS(status)) 1.226 + return status; 1.227 + 1.228 + do { 1.229 + if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) 1.230 + break; 1.231 + 1.232 + if (CURRENT_PROCESS != process) 1.233 + break; 1.234 + 1.235 + if (!ValidParameter(token, sizeof(HANDLE), WRITE)) 1.236 + break; 1.237 + 1.238 + void* memory = GetGlobalIPCMemory(); 1.239 + if (NULL == memory) 1.240 + break; 1.241 + 1.242 + SharedMemIPCClient ipc(memory); 1.243 + CrossCallReturn answer = {0}; 1.244 + ResultCode code = CrossCall(ipc, IPC_NTOPENPROCESSTOKENEX_TAG, process, 1.245 + desired_access, handle_attributes, &answer); 1.246 + if (SBOX_ALL_OK != code) 1.247 + break; 1.248 + 1.249 + if (!NT_SUCCESS(answer.nt_status)) 1.250 + return answer.nt_status; 1.251 + 1.252 + __try { 1.253 + // Write the output parameters. 1.254 + *token = answer.handle; 1.255 + } __except(EXCEPTION_EXECUTE_HANDLER) { 1.256 + break; 1.257 + } 1.258 + 1.259 + return answer.nt_status; 1.260 + } while (false); 1.261 + 1.262 + return status; 1.263 +} 1.264 + 1.265 +BOOL WINAPI TargetCreateProcessW(CreateProcessWFunction orig_CreateProcessW, 1.266 + LPCWSTR application_name, LPWSTR command_line, 1.267 + LPSECURITY_ATTRIBUTES process_attributes, 1.268 + LPSECURITY_ATTRIBUTES thread_attributes, 1.269 + BOOL inherit_handles, DWORD flags, 1.270 + LPVOID environment, LPCWSTR current_directory, 1.271 + LPSTARTUPINFOW startup_info, 1.272 + LPPROCESS_INFORMATION process_information) { 1.273 + if (orig_CreateProcessW(application_name, command_line, process_attributes, 1.274 + thread_attributes, inherit_handles, flags, 1.275 + environment, current_directory, startup_info, 1.276 + process_information)) { 1.277 + return TRUE; 1.278 + } 1.279 + DWORD original_error = ::GetLastError(); 1.280 + 1.281 + // We don't trust that the IPC can work this early. 1.282 + if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) 1.283 + return FALSE; 1.284 + 1.285 + do { 1.286 + if (!ValidParameter(process_information, sizeof(PROCESS_INFORMATION), 1.287 + WRITE)) 1.288 + break; 1.289 + 1.290 + void* memory = GetGlobalIPCMemory(); 1.291 + if (NULL == memory) 1.292 + break; 1.293 + 1.294 + const wchar_t* cur_dir = NULL; 1.295 + 1.296 + wchar_t current_directory[MAX_PATH]; 1.297 + DWORD result = ::GetCurrentDirectory(MAX_PATH, current_directory); 1.298 + if (0 != result && result < MAX_PATH) 1.299 + cur_dir = current_directory; 1.300 + 1.301 + SharedMemIPCClient ipc(memory); 1.302 + CrossCallReturn answer = {0}; 1.303 + 1.304 + InOutCountedBuffer proc_info(process_information, 1.305 + sizeof(PROCESS_INFORMATION)); 1.306 + 1.307 + ResultCode code = CrossCall(ipc, IPC_CREATEPROCESSW_TAG, application_name, 1.308 + command_line, cur_dir, proc_info, &answer); 1.309 + if (SBOX_ALL_OK != code) 1.310 + break; 1.311 + 1.312 + ::SetLastError(answer.win32_result); 1.313 + if (ERROR_SUCCESS != answer.win32_result) 1.314 + return FALSE; 1.315 + 1.316 + return TRUE; 1.317 + } while (false); 1.318 + 1.319 + ::SetLastError(original_error); 1.320 + return FALSE; 1.321 +} 1.322 + 1.323 +BOOL WINAPI TargetCreateProcessA(CreateProcessAFunction orig_CreateProcessA, 1.324 + LPCSTR application_name, LPSTR command_line, 1.325 + LPSECURITY_ATTRIBUTES process_attributes, 1.326 + LPSECURITY_ATTRIBUTES thread_attributes, 1.327 + BOOL inherit_handles, DWORD flags, 1.328 + LPVOID environment, LPCSTR current_directory, 1.329 + LPSTARTUPINFOA startup_info, 1.330 + LPPROCESS_INFORMATION process_information) { 1.331 + if (orig_CreateProcessA(application_name, command_line, process_attributes, 1.332 + thread_attributes, inherit_handles, flags, 1.333 + environment, current_directory, startup_info, 1.334 + process_information)) { 1.335 + return TRUE; 1.336 + } 1.337 + DWORD original_error = ::GetLastError(); 1.338 + 1.339 + // We don't trust that the IPC can work this early. 1.340 + if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) 1.341 + return FALSE; 1.342 + 1.343 + do { 1.344 + if (!ValidParameter(process_information, sizeof(PROCESS_INFORMATION), 1.345 + WRITE)) 1.346 + break; 1.347 + 1.348 + void* memory = GetGlobalIPCMemory(); 1.349 + if (NULL == memory) 1.350 + break; 1.351 + 1.352 + // Convert the input params to unicode. 1.353 + UNICODE_STRING *cmd_unicode = NULL; 1.354 + UNICODE_STRING *app_unicode = NULL; 1.355 + if (command_line) { 1.356 + cmd_unicode = AnsiToUnicode(command_line); 1.357 + if (!cmd_unicode) 1.358 + break; 1.359 + } 1.360 + 1.361 + if (application_name) { 1.362 + app_unicode = AnsiToUnicode(application_name); 1.363 + if (!app_unicode) { 1.364 + operator delete(cmd_unicode, NT_ALLOC); 1.365 + break; 1.366 + } 1.367 + } 1.368 + 1.369 + const wchar_t* cmd_line = cmd_unicode ? cmd_unicode->Buffer : NULL; 1.370 + const wchar_t* app_name = app_unicode ? app_unicode->Buffer : NULL; 1.371 + const wchar_t* cur_dir = NULL; 1.372 + 1.373 + wchar_t current_directory[MAX_PATH]; 1.374 + DWORD result = ::GetCurrentDirectory(MAX_PATH, current_directory); 1.375 + if (0 != result && result < MAX_PATH) 1.376 + cur_dir = current_directory; 1.377 + 1.378 + SharedMemIPCClient ipc(memory); 1.379 + CrossCallReturn answer = {0}; 1.380 + 1.381 + InOutCountedBuffer proc_info(process_information, 1.382 + sizeof(PROCESS_INFORMATION)); 1.383 + 1.384 + ResultCode code = CrossCall(ipc, IPC_CREATEPROCESSW_TAG, app_name, 1.385 + cmd_line, cur_dir, proc_info, &answer); 1.386 + 1.387 + operator delete(cmd_unicode, NT_ALLOC); 1.388 + operator delete(app_unicode, NT_ALLOC); 1.389 + 1.390 + if (SBOX_ALL_OK != code) 1.391 + break; 1.392 + 1.393 + ::SetLastError(answer.win32_result); 1.394 + if (ERROR_SUCCESS != answer.win32_result) 1.395 + return FALSE; 1.396 + 1.397 + return TRUE; 1.398 + } while (false); 1.399 + 1.400 + ::SetLastError(original_error); 1.401 + return FALSE; 1.402 +} 1.403 + 1.404 +// Creates a thread without registering with CSRSS. This is required if we 1.405 +// closed the CSRSS ALPC port after lockdown. 1.406 +HANDLE WINAPI TargetCreateThread(CreateThreadFunction orig_CreateThread, 1.407 + LPSECURITY_ATTRIBUTES thread_attributes, 1.408 + SIZE_T stack_size, 1.409 + LPTHREAD_START_ROUTINE start_address, 1.410 + PVOID parameter, 1.411 + DWORD creation_flags, 1.412 + LPDWORD thread_id) { 1.413 +// Try the normal CreateThread; switch to RtlCreateUserThread if needed. 1.414 + static bool use_create_thread = true; 1.415 + HANDLE thread; 1.416 + if (use_create_thread) { 1.417 + thread = orig_CreateThread(thread_attributes, stack_size, start_address, 1.418 + parameter, creation_flags, thread_id); 1.419 + if (thread) 1.420 + return thread; 1.421 + } 1.422 + 1.423 + PSECURITY_DESCRIPTOR sd = 1.424 + thread_attributes ? thread_attributes->lpSecurityDescriptor : NULL; 1.425 + CLIENT_ID client_id; 1.426 + 1.427 + NTSTATUS result = g_nt.RtlCreateUserThread(NtCurrentProcess, sd, 1.428 + creation_flags & CREATE_SUSPENDED, 1.429 + 0, stack_size, 0, start_address, 1.430 + parameter, &thread, &client_id); 1.431 + if (!NT_SUCCESS(result)) 1.432 + return 0; 1.433 + 1.434 + // CSRSS is closed if we got here, so use RtlCreateUserThread from here on. 1.435 + use_create_thread = false; 1.436 + if (thread_id) 1.437 + *thread_id = HandleToUlong(client_id.UniqueThread); 1.438 + return thread; 1.439 +} 1.440 + 1.441 +// Cache the default LCID to avoid pinging CSRSS after lockdown. 1.442 +// TODO(jschuh): This approach will miss a default locale changes after 1.443 +// lockdown. In the future we may want to have the broker check instead. 1.444 +LCID WINAPI TargetGetUserDefaultLCID( 1.445 + GetUserDefaultLCIDFunction orig_GetUserDefaultLCID) { 1.446 + static LCID default_lcid = orig_GetUserDefaultLCID(); 1.447 + return default_lcid; 1.448 +} 1.449 + 1.450 +} // namespace sandbox