michael@0: // Copyright (c) 2006-2011 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: #include "sandbox/win/src/process_thread_interception.h" michael@0: michael@0: #include "sandbox/win/src/crosscall_client.h" michael@0: #include "sandbox/win/src/ipc_tags.h" michael@0: #include "sandbox/win/src/policy_params.h" michael@0: #include "sandbox/win/src/policy_target.h" michael@0: #include "sandbox/win/src/sandbox_factory.h" michael@0: #include "sandbox/win/src/sandbox_nt_util.h" michael@0: #include "sandbox/win/src/sharedmem_ipc_client.h" michael@0: #include "sandbox/win/src/target_services.h" michael@0: michael@0: namespace sandbox { michael@0: michael@0: SANDBOX_INTERCEPT NtExports g_nt; michael@0: michael@0: // Hooks NtOpenThread and proxy the call to the broker if it's trying to michael@0: // open a thread in the same process. michael@0: NTSTATUS WINAPI TargetNtOpenThread(NtOpenThreadFunction orig_OpenThread, michael@0: PHANDLE thread, ACCESS_MASK desired_access, michael@0: POBJECT_ATTRIBUTES object_attributes, michael@0: PCLIENT_ID client_id) { michael@0: NTSTATUS status = orig_OpenThread(thread, desired_access, object_attributes, michael@0: client_id); michael@0: if (NT_SUCCESS(status)) michael@0: return status; michael@0: michael@0: do { michael@0: if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) michael@0: break; michael@0: if (!client_id) michael@0: break; michael@0: michael@0: uint32 thread_id = 0; michael@0: bool should_break = false; michael@0: __try { michael@0: // We support only the calls for the current process michael@0: if (NULL != client_id->UniqueProcess) michael@0: should_break = true; michael@0: michael@0: // Object attributes should be NULL or empty. michael@0: if (!should_break && NULL != object_attributes) { michael@0: if (0 != object_attributes->Attributes || michael@0: NULL != object_attributes->ObjectName || michael@0: NULL != object_attributes->RootDirectory || michael@0: NULL != object_attributes->SecurityDescriptor || michael@0: NULL != object_attributes->SecurityQualityOfService) { michael@0: should_break = true; michael@0: } michael@0: } michael@0: michael@0: thread_id = static_cast( michael@0: reinterpret_cast(client_id->UniqueThread)); michael@0: } __except(EXCEPTION_EXECUTE_HANDLER) { michael@0: break; michael@0: } michael@0: michael@0: if (should_break) michael@0: break; michael@0: michael@0: if (!ValidParameter(thread, sizeof(HANDLE), WRITE)) michael@0: break; michael@0: michael@0: void* memory = GetGlobalIPCMemory(); michael@0: if (NULL == memory) michael@0: break; michael@0: michael@0: SharedMemIPCClient ipc(memory); michael@0: CrossCallReturn answer = {0}; michael@0: ResultCode code = CrossCall(ipc, IPC_NTOPENTHREAD_TAG, desired_access, michael@0: thread_id, &answer); michael@0: if (SBOX_ALL_OK != code) michael@0: break; michael@0: michael@0: if (!NT_SUCCESS(answer.nt_status)) michael@0: // The nt_status here is most likely STATUS_INVALID_CID because michael@0: // in the broker we set the process id in the CID (client ID) param michael@0: // to be the current process. If you try to open a thread from another michael@0: // process you will get this INVALID_CID error. On the other hand, if you michael@0: // try to open a thread in your own process, it should return success. michael@0: // We don't want to return STATUS_INVALID_CID here, so we return the michael@0: // return of the original open thread status, which is most likely michael@0: // STATUS_ACCESS_DENIED. michael@0: break; michael@0: michael@0: __try { michael@0: // Write the output parameters. michael@0: *thread = answer.handle; michael@0: } __except(EXCEPTION_EXECUTE_HANDLER) { michael@0: break; michael@0: } michael@0: michael@0: return answer.nt_status; michael@0: } while (false); michael@0: michael@0: return status; michael@0: } michael@0: michael@0: // Hooks NtOpenProcess and proxy the call to the broker if it's trying to michael@0: // open the current process. michael@0: NTSTATUS WINAPI TargetNtOpenProcess(NtOpenProcessFunction orig_OpenProcess, michael@0: PHANDLE process, ACCESS_MASK desired_access, michael@0: POBJECT_ATTRIBUTES object_attributes, michael@0: PCLIENT_ID client_id) { michael@0: NTSTATUS status = orig_OpenProcess(process, desired_access, object_attributes, michael@0: client_id); michael@0: if (NT_SUCCESS(status)) michael@0: return status; michael@0: michael@0: do { michael@0: if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) michael@0: break; michael@0: if (!client_id) michael@0: break; michael@0: michael@0: uint32 process_id = 0; michael@0: bool should_break = false; michael@0: __try { michael@0: // Object attributes should be NULL or empty. michael@0: if (!should_break && NULL != object_attributes) { michael@0: if (0 != object_attributes->Attributes || michael@0: NULL != object_attributes->ObjectName || michael@0: NULL != object_attributes->RootDirectory || michael@0: NULL != object_attributes->SecurityDescriptor || michael@0: NULL != object_attributes->SecurityQualityOfService) { michael@0: should_break = true; michael@0: } michael@0: } michael@0: michael@0: process_id = static_cast( michael@0: reinterpret_cast(client_id->UniqueProcess)); michael@0: } __except(EXCEPTION_EXECUTE_HANDLER) { michael@0: break; michael@0: } michael@0: michael@0: if (should_break) michael@0: break; michael@0: michael@0: if (!ValidParameter(process, sizeof(HANDLE), WRITE)) michael@0: break; michael@0: michael@0: void* memory = GetGlobalIPCMemory(); michael@0: if (NULL == memory) michael@0: break; michael@0: michael@0: SharedMemIPCClient ipc(memory); michael@0: CrossCallReturn answer = {0}; michael@0: ResultCode code = CrossCall(ipc, IPC_NTOPENPROCESS_TAG, desired_access, michael@0: process_id, &answer); michael@0: if (SBOX_ALL_OK != code) michael@0: break; michael@0: michael@0: if (!NT_SUCCESS(answer.nt_status)) michael@0: return answer.nt_status; michael@0: michael@0: __try { michael@0: // Write the output parameters. michael@0: *process = answer.handle; michael@0: } __except(EXCEPTION_EXECUTE_HANDLER) { michael@0: break; michael@0: } michael@0: michael@0: return answer.nt_status; michael@0: } while (false); michael@0: michael@0: return status; michael@0: } michael@0: michael@0: michael@0: NTSTATUS WINAPI TargetNtOpenProcessToken( michael@0: NtOpenProcessTokenFunction orig_OpenProcessToken, HANDLE process, michael@0: ACCESS_MASK desired_access, PHANDLE token) { michael@0: NTSTATUS status = orig_OpenProcessToken(process, desired_access, token); michael@0: if (NT_SUCCESS(status)) michael@0: return status; michael@0: michael@0: do { michael@0: if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) michael@0: break; michael@0: michael@0: if (CURRENT_PROCESS != process) michael@0: break; michael@0: michael@0: if (!ValidParameter(token, sizeof(HANDLE), WRITE)) michael@0: break; michael@0: michael@0: void* memory = GetGlobalIPCMemory(); michael@0: if (NULL == memory) michael@0: break; michael@0: michael@0: SharedMemIPCClient ipc(memory); michael@0: CrossCallReturn answer = {0}; michael@0: ResultCode code = CrossCall(ipc, IPC_NTOPENPROCESSTOKEN_TAG, process, michael@0: desired_access, &answer); michael@0: if (SBOX_ALL_OK != code) michael@0: break; michael@0: michael@0: if (!NT_SUCCESS(answer.nt_status)) michael@0: return answer.nt_status; michael@0: michael@0: __try { michael@0: // Write the output parameters. michael@0: *token = answer.handle; michael@0: } __except(EXCEPTION_EXECUTE_HANDLER) { michael@0: break; michael@0: } michael@0: michael@0: return answer.nt_status; michael@0: } while (false); michael@0: michael@0: return status; michael@0: } michael@0: michael@0: NTSTATUS WINAPI TargetNtOpenProcessTokenEx( michael@0: NtOpenProcessTokenExFunction orig_OpenProcessTokenEx, HANDLE process, michael@0: ACCESS_MASK desired_access, ULONG handle_attributes, PHANDLE token) { michael@0: NTSTATUS status = orig_OpenProcessTokenEx(process, desired_access, michael@0: handle_attributes, token); michael@0: if (NT_SUCCESS(status)) michael@0: return status; michael@0: michael@0: do { michael@0: if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) michael@0: break; michael@0: michael@0: if (CURRENT_PROCESS != process) michael@0: break; michael@0: michael@0: if (!ValidParameter(token, sizeof(HANDLE), WRITE)) michael@0: break; michael@0: michael@0: void* memory = GetGlobalIPCMemory(); michael@0: if (NULL == memory) michael@0: break; michael@0: michael@0: SharedMemIPCClient ipc(memory); michael@0: CrossCallReturn answer = {0}; michael@0: ResultCode code = CrossCall(ipc, IPC_NTOPENPROCESSTOKENEX_TAG, process, michael@0: desired_access, handle_attributes, &answer); michael@0: if (SBOX_ALL_OK != code) michael@0: break; michael@0: michael@0: if (!NT_SUCCESS(answer.nt_status)) michael@0: return answer.nt_status; michael@0: michael@0: __try { michael@0: // Write the output parameters. michael@0: *token = answer.handle; michael@0: } __except(EXCEPTION_EXECUTE_HANDLER) { michael@0: break; michael@0: } michael@0: michael@0: return answer.nt_status; michael@0: } while (false); michael@0: michael@0: return status; michael@0: } michael@0: michael@0: BOOL WINAPI TargetCreateProcessW(CreateProcessWFunction orig_CreateProcessW, michael@0: LPCWSTR application_name, LPWSTR command_line, michael@0: LPSECURITY_ATTRIBUTES process_attributes, michael@0: LPSECURITY_ATTRIBUTES thread_attributes, michael@0: BOOL inherit_handles, DWORD flags, michael@0: LPVOID environment, LPCWSTR current_directory, michael@0: LPSTARTUPINFOW startup_info, michael@0: LPPROCESS_INFORMATION process_information) { michael@0: if (orig_CreateProcessW(application_name, command_line, process_attributes, michael@0: thread_attributes, inherit_handles, flags, michael@0: environment, current_directory, startup_info, michael@0: process_information)) { michael@0: return TRUE; michael@0: } michael@0: DWORD original_error = ::GetLastError(); michael@0: michael@0: // We don't trust that the IPC can work this early. michael@0: if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) michael@0: return FALSE; michael@0: michael@0: do { michael@0: if (!ValidParameter(process_information, sizeof(PROCESS_INFORMATION), michael@0: WRITE)) michael@0: break; michael@0: michael@0: void* memory = GetGlobalIPCMemory(); michael@0: if (NULL == memory) michael@0: break; michael@0: michael@0: const wchar_t* cur_dir = NULL; michael@0: michael@0: wchar_t current_directory[MAX_PATH]; michael@0: DWORD result = ::GetCurrentDirectory(MAX_PATH, current_directory); michael@0: if (0 != result && result < MAX_PATH) michael@0: cur_dir = current_directory; michael@0: michael@0: SharedMemIPCClient ipc(memory); michael@0: CrossCallReturn answer = {0}; michael@0: michael@0: InOutCountedBuffer proc_info(process_information, michael@0: sizeof(PROCESS_INFORMATION)); michael@0: michael@0: ResultCode code = CrossCall(ipc, IPC_CREATEPROCESSW_TAG, application_name, michael@0: command_line, cur_dir, proc_info, &answer); michael@0: if (SBOX_ALL_OK != code) michael@0: break; michael@0: michael@0: ::SetLastError(answer.win32_result); michael@0: if (ERROR_SUCCESS != answer.win32_result) michael@0: return FALSE; michael@0: michael@0: return TRUE; michael@0: } while (false); michael@0: michael@0: ::SetLastError(original_error); michael@0: return FALSE; michael@0: } michael@0: michael@0: BOOL WINAPI TargetCreateProcessA(CreateProcessAFunction orig_CreateProcessA, michael@0: LPCSTR application_name, LPSTR command_line, michael@0: LPSECURITY_ATTRIBUTES process_attributes, michael@0: LPSECURITY_ATTRIBUTES thread_attributes, michael@0: BOOL inherit_handles, DWORD flags, michael@0: LPVOID environment, LPCSTR current_directory, michael@0: LPSTARTUPINFOA startup_info, michael@0: LPPROCESS_INFORMATION process_information) { michael@0: if (orig_CreateProcessA(application_name, command_line, process_attributes, michael@0: thread_attributes, inherit_handles, flags, michael@0: environment, current_directory, startup_info, michael@0: process_information)) { michael@0: return TRUE; michael@0: } michael@0: DWORD original_error = ::GetLastError(); michael@0: michael@0: // We don't trust that the IPC can work this early. michael@0: if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) michael@0: return FALSE; michael@0: michael@0: do { michael@0: if (!ValidParameter(process_information, sizeof(PROCESS_INFORMATION), michael@0: WRITE)) michael@0: break; michael@0: michael@0: void* memory = GetGlobalIPCMemory(); michael@0: if (NULL == memory) michael@0: break; michael@0: michael@0: // Convert the input params to unicode. michael@0: UNICODE_STRING *cmd_unicode = NULL; michael@0: UNICODE_STRING *app_unicode = NULL; michael@0: if (command_line) { michael@0: cmd_unicode = AnsiToUnicode(command_line); michael@0: if (!cmd_unicode) michael@0: break; michael@0: } michael@0: michael@0: if (application_name) { michael@0: app_unicode = AnsiToUnicode(application_name); michael@0: if (!app_unicode) { michael@0: operator delete(cmd_unicode, NT_ALLOC); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: const wchar_t* cmd_line = cmd_unicode ? cmd_unicode->Buffer : NULL; michael@0: const wchar_t* app_name = app_unicode ? app_unicode->Buffer : NULL; michael@0: const wchar_t* cur_dir = NULL; michael@0: michael@0: wchar_t current_directory[MAX_PATH]; michael@0: DWORD result = ::GetCurrentDirectory(MAX_PATH, current_directory); michael@0: if (0 != result && result < MAX_PATH) michael@0: cur_dir = current_directory; michael@0: michael@0: SharedMemIPCClient ipc(memory); michael@0: CrossCallReturn answer = {0}; michael@0: michael@0: InOutCountedBuffer proc_info(process_information, michael@0: sizeof(PROCESS_INFORMATION)); michael@0: michael@0: ResultCode code = CrossCall(ipc, IPC_CREATEPROCESSW_TAG, app_name, michael@0: cmd_line, cur_dir, proc_info, &answer); michael@0: michael@0: operator delete(cmd_unicode, NT_ALLOC); michael@0: operator delete(app_unicode, NT_ALLOC); michael@0: michael@0: if (SBOX_ALL_OK != code) michael@0: break; michael@0: michael@0: ::SetLastError(answer.win32_result); michael@0: if (ERROR_SUCCESS != answer.win32_result) michael@0: return FALSE; michael@0: michael@0: return TRUE; michael@0: } while (false); michael@0: michael@0: ::SetLastError(original_error); michael@0: return FALSE; michael@0: } michael@0: michael@0: // Creates a thread without registering with CSRSS. This is required if we michael@0: // closed the CSRSS ALPC port after lockdown. michael@0: HANDLE WINAPI TargetCreateThread(CreateThreadFunction orig_CreateThread, michael@0: LPSECURITY_ATTRIBUTES thread_attributes, michael@0: SIZE_T stack_size, michael@0: LPTHREAD_START_ROUTINE start_address, michael@0: PVOID parameter, michael@0: DWORD creation_flags, michael@0: LPDWORD thread_id) { michael@0: // Try the normal CreateThread; switch to RtlCreateUserThread if needed. michael@0: static bool use_create_thread = true; michael@0: HANDLE thread; michael@0: if (use_create_thread) { michael@0: thread = orig_CreateThread(thread_attributes, stack_size, start_address, michael@0: parameter, creation_flags, thread_id); michael@0: if (thread) michael@0: return thread; michael@0: } michael@0: michael@0: PSECURITY_DESCRIPTOR sd = michael@0: thread_attributes ? thread_attributes->lpSecurityDescriptor : NULL; michael@0: CLIENT_ID client_id; michael@0: michael@0: NTSTATUS result = g_nt.RtlCreateUserThread(NtCurrentProcess, sd, michael@0: creation_flags & CREATE_SUSPENDED, michael@0: 0, stack_size, 0, start_address, michael@0: parameter, &thread, &client_id); michael@0: if (!NT_SUCCESS(result)) michael@0: return 0; michael@0: michael@0: // CSRSS is closed if we got here, so use RtlCreateUserThread from here on. michael@0: use_create_thread = false; michael@0: if (thread_id) michael@0: *thread_id = HandleToUlong(client_id.UniqueThread); michael@0: return thread; michael@0: } michael@0: michael@0: // Cache the default LCID to avoid pinging CSRSS after lockdown. michael@0: // TODO(jschuh): This approach will miss a default locale changes after michael@0: // lockdown. In the future we may want to have the broker check instead. michael@0: LCID WINAPI TargetGetUserDefaultLCID( michael@0: GetUserDefaultLCIDFunction orig_GetUserDefaultLCID) { michael@0: static LCID default_lcid = orig_GetUserDefaultLCID(); michael@0: return default_lcid; michael@0: } michael@0: michael@0: } // namespace sandbox