diff options
Diffstat (limited to 'security/sandbox/chromium/sandbox/win/src/process_thread_interception.cc')
-rw-r--r-- | security/sandbox/chromium/sandbox/win/src/process_thread_interception.cc | 520 |
1 files changed, 520 insertions, 0 deletions
diff --git a/security/sandbox/chromium/sandbox/win/src/process_thread_interception.cc b/security/sandbox/chromium/sandbox/win/src/process_thread_interception.cc new file mode 100644 index 0000000000..0bd3d5a8d2 --- /dev/null +++ b/security/sandbox/chromium/sandbox/win/src/process_thread_interception.cc @@ -0,0 +1,520 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/win/src/process_thread_interception.h" + +#include <stdint.h> + +#include "base/win/windows_version.h" +#include "sandbox/win/src/crosscall_client.h" +#include "sandbox/win/src/ipc_tags.h" +#include "sandbox/win/src/policy_params.h" +#include "sandbox/win/src/policy_target.h" +#include "sandbox/win/src/sandbox_factory.h" +#include "sandbox/win/src/sandbox_nt_util.h" +#include "sandbox/win/src/sharedmem_ipc_client.h" +#include "sandbox/win/src/target_services.h" +#include "mozilla/sandboxing/sandboxLogging.h" + +namespace sandbox { + +SANDBOX_INTERCEPT NtExports g_nt; + +// Hooks NtOpenThread and proxy the call to the broker if it's trying to +// open a thread in the same process. +NTSTATUS WINAPI TargetNtOpenThread(NtOpenThreadFunction orig_OpenThread, + PHANDLE thread, + ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, + PCLIENT_ID client_id) { + NTSTATUS status = + orig_OpenThread(thread, desired_access, object_attributes, client_id); + if (NT_SUCCESS(status)) + return status; + + mozilla::sandboxing::LogBlocked("NtOpenThread"); + do { + if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) + break; + if (!client_id) + break; + + uint32_t thread_id = 0; + bool should_break = false; + __try { + // We support only the calls for the current process + if (client_id->UniqueProcess) + should_break = true; + + // Object attributes should be nullptr or empty. + if (!should_break && object_attributes) { + if (object_attributes->Attributes || object_attributes->ObjectName || + object_attributes->RootDirectory || + object_attributes->SecurityDescriptor || + object_attributes->SecurityQualityOfService) { + should_break = true; + } + } + + thread_id = static_cast<uint32_t>( + reinterpret_cast<ULONG_PTR>(client_id->UniqueThread)); + } __except (EXCEPTION_EXECUTE_HANDLER) { + break; + } + + if (should_break) + break; + + if (!ValidParameter(thread, sizeof(HANDLE), WRITE)) + break; + + void* memory = GetGlobalIPCMemory(); + if (!memory) + break; + + SharedMemIPCClient ipc(memory); + CrossCallReturn answer = {0}; + ResultCode code = CrossCall(ipc, IpcTag::NTOPENTHREAD, desired_access, + thread_id, &answer); + if (SBOX_ALL_OK != code) + break; + + if (!NT_SUCCESS(answer.nt_status)) + // The nt_status here is most likely STATUS_INVALID_CID because + // in the broker we set the process id in the CID (client ID) param + // to be the current process. If you try to open a thread from another + // process you will get this INVALID_CID error. On the other hand, if you + // try to open a thread in your own process, it should return success. + // We don't want to return STATUS_INVALID_CID here, so we return the + // return of the original open thread status, which is most likely + // STATUS_ACCESS_DENIED. + break; + + __try { + // Write the output parameters. + *thread = answer.handle; + } __except (EXCEPTION_EXECUTE_HANDLER) { + break; + } + + mozilla::sandboxing::LogAllowed("NtOpenThread"); + return answer.nt_status; + } while (false); + + return status; +} + +// Hooks NtOpenProcess and proxy the call to the broker if it's trying to +// open the current process. +NTSTATUS WINAPI TargetNtOpenProcess(NtOpenProcessFunction orig_OpenProcess, + PHANDLE process, + ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, + PCLIENT_ID client_id) { + NTSTATUS status = + orig_OpenProcess(process, desired_access, object_attributes, client_id); + if (NT_SUCCESS(status)) + return status; + + do { + if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) + break; + if (!client_id) + break; + + uint32_t process_id = 0; + bool should_break = false; + __try { + // Object attributes should be nullptr or empty. + if (!should_break && object_attributes) { + if (object_attributes->Attributes || object_attributes->ObjectName || + object_attributes->RootDirectory || + object_attributes->SecurityDescriptor || + object_attributes->SecurityQualityOfService) { + should_break = true; + } + } + + process_id = static_cast<uint32_t>( + reinterpret_cast<ULONG_PTR>(client_id->UniqueProcess)); + } __except (EXCEPTION_EXECUTE_HANDLER) { + break; + } + + if (should_break) + break; + + if (!ValidParameter(process, sizeof(HANDLE), WRITE)) + break; + + void* memory = GetGlobalIPCMemory(); + if (!memory) + break; + + SharedMemIPCClient ipc(memory); + CrossCallReturn answer = {0}; + ResultCode code = CrossCall(ipc, IpcTag::NTOPENPROCESS, desired_access, + process_id, &answer); + if (SBOX_ALL_OK != code) + break; + + if (!NT_SUCCESS(answer.nt_status)) + return answer.nt_status; + + __try { + // Write the output parameters. + *process = answer.handle; + } __except (EXCEPTION_EXECUTE_HANDLER) { + break; + } + + return answer.nt_status; + } while (false); + + return status; +} + +NTSTATUS WINAPI +TargetNtOpenProcessToken(NtOpenProcessTokenFunction orig_OpenProcessToken, + HANDLE process, + ACCESS_MASK desired_access, + PHANDLE token) { + NTSTATUS status = orig_OpenProcessToken(process, desired_access, token); + if (NT_SUCCESS(status)) + return status; + + mozilla::sandboxing::LogBlocked("NtOpenProcessToken"); + do { + if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) + break; + + if (CURRENT_PROCESS != process) + break; + + if (!ValidParameter(token, sizeof(HANDLE), WRITE)) + break; + + void* memory = GetGlobalIPCMemory(); + if (!memory) + break; + + SharedMemIPCClient ipc(memory); + CrossCallReturn answer = {0}; + ResultCode code = CrossCall(ipc, IpcTag::NTOPENPROCESSTOKEN, process, + desired_access, &answer); + if (SBOX_ALL_OK != code) + break; + + if (!NT_SUCCESS(answer.nt_status)) + return answer.nt_status; + + __try { + // Write the output parameters. + *token = answer.handle; + } __except (EXCEPTION_EXECUTE_HANDLER) { + break; + } + + mozilla::sandboxing::LogAllowed("NtOpenProcessToken"); + return answer.nt_status; + } while (false); + + return status; +} + +NTSTATUS WINAPI +TargetNtOpenProcessTokenEx(NtOpenProcessTokenExFunction orig_OpenProcessTokenEx, + HANDLE process, + ACCESS_MASK desired_access, + ULONG handle_attributes, + PHANDLE token) { + NTSTATUS status = orig_OpenProcessTokenEx(process, desired_access, + handle_attributes, token); + if (NT_SUCCESS(status)) + return status; + + mozilla::sandboxing::LogBlocked("NtOpenProcessTokenEx"); + do { + if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) + break; + + if (CURRENT_PROCESS != process) + break; + + if (!ValidParameter(token, sizeof(HANDLE), WRITE)) + break; + + void* memory = GetGlobalIPCMemory(); + if (!memory) + break; + + SharedMemIPCClient ipc(memory); + CrossCallReturn answer = {0}; + ResultCode code = CrossCall(ipc, IpcTag::NTOPENPROCESSTOKENEX, process, + desired_access, handle_attributes, &answer); + if (SBOX_ALL_OK != code) + break; + + if (!NT_SUCCESS(answer.nt_status)) + return answer.nt_status; + + __try { + // Write the output parameters. + *token = answer.handle; + } __except (EXCEPTION_EXECUTE_HANDLER) { + break; + } + + mozilla::sandboxing::LogAllowed("NtOpenProcessTokenEx"); + return answer.nt_status; + } while (false); + + return status; +} + +BOOL WINAPI 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) { + if (SandboxFactory::GetTargetServices()->GetState()->IsCsrssConnected() && + orig_CreateProcessW(application_name, command_line, process_attributes, + thread_attributes, inherit_handles, flags, + environment, current_directory, startup_info, + process_information)) { + return true; + } + + mozilla::sandboxing::LogBlocked("CreateProcessW", application_name); + + // We don't trust that the IPC can work this early. + if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) + return false; + + // Don't call GetLastError before InitCalled() succeeds because kernel32 may + // not be mapped yet. + DWORD original_error = ::GetLastError(); + + do { + if (!ValidParameter(process_information, sizeof(PROCESS_INFORMATION), + WRITE)) + break; + + void* memory = GetGlobalIPCMemory(); + if (!memory) + break; + + const wchar_t* cur_dir = nullptr; + + wchar_t this_current_directory[MAX_PATH]; + DWORD result = ::GetCurrentDirectory(MAX_PATH, this_current_directory); + if (0 != result && result < MAX_PATH) + cur_dir = this_current_directory; + + SharedMemIPCClient ipc(memory); + CrossCallReturn answer = {0}; + + InOutCountedBuffer proc_info(process_information, + sizeof(PROCESS_INFORMATION)); + + ResultCode code = + CrossCall(ipc, IpcTag::CREATEPROCESSW, application_name, command_line, + cur_dir, current_directory, proc_info, &answer); + if (SBOX_ALL_OK != code) + break; + + ::SetLastError(answer.win32_result); + if (ERROR_SUCCESS != answer.win32_result) + return false; + + mozilla::sandboxing::LogAllowed("CreateProcessW", application_name); + return true; + } while (false); + + ::SetLastError(original_error); + return false; +} + +BOOL WINAPI 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) { + if (SandboxFactory::GetTargetServices()->GetState()->IsCsrssConnected() && + orig_CreateProcessA(application_name, command_line, process_attributes, + thread_attributes, inherit_handles, flags, + environment, current_directory, startup_info, + process_information)) { + return true; + } + + mozilla::sandboxing::LogBlocked("CreateProcessA", application_name); + + // We don't trust that the IPC can work this early. + if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) + return false; + + // Don't call GetLastError before InitCalled() succeeds because kernel32 may + // not be mapped yet. + DWORD original_error = ::GetLastError(); + + do { + if (!ValidParameter(process_information, sizeof(PROCESS_INFORMATION), + WRITE)) + break; + + void* memory = GetGlobalIPCMemory(); + if (!memory) + break; + + // Convert the input params to unicode. + UNICODE_STRING* cmd_unicode = nullptr; + UNICODE_STRING* app_unicode = nullptr; + UNICODE_STRING* cwd_unicode = nullptr; + if (command_line) { + cmd_unicode = AnsiToUnicode(command_line); + if (!cmd_unicode) + break; + } + + if (application_name) { + app_unicode = AnsiToUnicode(application_name); + if (!app_unicode) { + operator delete(cmd_unicode, NT_ALLOC); + break; + } + } + + if (current_directory) { + cwd_unicode = AnsiToUnicode(current_directory); + if (!cwd_unicode) { + operator delete(cmd_unicode, NT_ALLOC); + operator delete(app_unicode, NT_ALLOC); + break; + } + } + + const wchar_t* cmd_line = cmd_unicode ? cmd_unicode->Buffer : nullptr; + const wchar_t* app_name = app_unicode ? app_unicode->Buffer : nullptr; + const wchar_t* cwd = cwd_unicode ? cwd_unicode->Buffer : nullptr; + const wchar_t* cur_dir = nullptr; + + wchar_t target_current_directory[MAX_PATH]; + DWORD result = ::GetCurrentDirectory(MAX_PATH, target_current_directory); + if (0 != result && result < MAX_PATH) + cur_dir = target_current_directory; + + SharedMemIPCClient ipc(memory); + CrossCallReturn answer = {0}; + + InOutCountedBuffer proc_info(process_information, + sizeof(PROCESS_INFORMATION)); + + ResultCode code = CrossCall(ipc, IpcTag::CREATEPROCESSW, app_name, cmd_line, + cur_dir, cwd, proc_info, &answer); + + operator delete(cmd_unicode, NT_ALLOC); + operator delete(app_unicode, NT_ALLOC); + operator delete(cwd_unicode, NT_ALLOC); + + if (SBOX_ALL_OK != code) + break; + + ::SetLastError(answer.win32_result); + if (ERROR_SUCCESS != answer.win32_result) + return false; + + mozilla::sandboxing::LogAllowed("CreateProcessA", application_name); + return true; + } while (false); + + ::SetLastError(original_error); + return false; +} + +HANDLE WINAPI TargetCreateThread(CreateThreadFunction orig_CreateThread, + LPSECURITY_ATTRIBUTES thread_attributes, + SIZE_T stack_size, + LPTHREAD_START_ROUTINE start_address, + LPVOID parameter, + DWORD creation_flags, + LPDWORD thread_id) { + HANDLE hThread = nullptr; + + TargetServices* target_services = SandboxFactory::GetTargetServices(); + if (!target_services || target_services->GetState()->IsCsrssConnected()) { + hThread = orig_CreateThread(thread_attributes, stack_size, start_address, + parameter, creation_flags, thread_id); + if (hThread) + return hThread; + } + + DWORD original_error = ::GetLastError(); + do { + if (!target_services) + break; + + // We don't trust that the IPC can work this early. + if (!target_services->GetState()->InitCalled()) + break; + + __try { + if (thread_id && !ValidParameter(thread_id, sizeof(*thread_id), WRITE)) + break; + + if (!start_address) + break; + // We don't support thread_attributes not being null. + if (thread_attributes) + break; + } __except (EXCEPTION_EXECUTE_HANDLER) { + break; + } + + void* memory = GetGlobalIPCMemory(); + if (!memory) + break; + + SharedMemIPCClient ipc(memory); + CrossCallReturn answer = {0}; + + // NOTE: we don't pass the thread_attributes through. This matches the + // approach in CreateProcess and in CreateThreadInternal(). + ResultCode code = CrossCall(ipc, IpcTag::CREATETHREAD, + reinterpret_cast<LPVOID>(stack_size), + reinterpret_cast<LPVOID>(start_address), + parameter, creation_flags, &answer); + if (SBOX_ALL_OK != code) + break; + + ::SetLastError(answer.win32_result); + if (ERROR_SUCCESS != answer.win32_result) + return nullptr; + + __try { + if (thread_id) + *thread_id = ::GetThreadId(answer.handle); + return answer.handle; + } __except (EXCEPTION_EXECUTE_HANDLER) { + break; + } + } while (false); + + ::SetLastError(original_error); + return nullptr; +} + +} // namespace sandbox |