michael@0: // Copyright (c) 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_policy.h" michael@0: michael@0: #include michael@0: michael@0: #include "base/memory/scoped_ptr.h" michael@0: #include "sandbox/win/src/ipc_tags.h" michael@0: #include "sandbox/win/src/nt_internals.h" michael@0: #include "sandbox/win/src/policy_engine_opcodes.h" michael@0: #include "sandbox/win/src/policy_params.h" michael@0: #include "sandbox/win/src/sandbox_types.h" michael@0: #include "sandbox/win/src/win_utils.h" michael@0: michael@0: namespace { michael@0: michael@0: // These are the only safe rights that can be given to a sandboxed michael@0: // process for the process created by the broker. All others are potential michael@0: // vectors of privilege elevation. michael@0: const DWORD kProcessRights = SYNCHRONIZE | michael@0: PROCESS_QUERY_INFORMATION | michael@0: PROCESS_QUERY_LIMITED_INFORMATION | michael@0: PROCESS_TERMINATE | michael@0: PROCESS_SUSPEND_RESUME; michael@0: michael@0: const DWORD kThreadRights = SYNCHRONIZE | michael@0: THREAD_TERMINATE | michael@0: THREAD_SUSPEND_RESUME | michael@0: THREAD_QUERY_INFORMATION | michael@0: THREAD_QUERY_LIMITED_INFORMATION | michael@0: THREAD_SET_LIMITED_INFORMATION; michael@0: michael@0: // Creates a child process and duplicates the handles to 'target_process'. The michael@0: // remaining parameters are the same as CreateProcess(). michael@0: BOOL CreateProcessExWHelper(HANDLE target_process, BOOL give_full_access, michael@0: LPCWSTR lpApplicationName, LPWSTR lpCommandLine, michael@0: LPSECURITY_ATTRIBUTES lpProcessAttributes, michael@0: LPSECURITY_ATTRIBUTES lpThreadAttributes, michael@0: BOOL bInheritHandles, DWORD dwCreationFlags, michael@0: LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, michael@0: LPSTARTUPINFOW lpStartupInfo, michael@0: LPPROCESS_INFORMATION lpProcessInformation) { michael@0: if (!::CreateProcessW(lpApplicationName, lpCommandLine, lpProcessAttributes, michael@0: lpThreadAttributes, bInheritHandles, dwCreationFlags, michael@0: lpEnvironment, lpCurrentDirectory, lpStartupInfo, michael@0: lpProcessInformation)) { michael@0: return FALSE; michael@0: } michael@0: michael@0: DWORD process_access = kProcessRights; michael@0: DWORD thread_access = kThreadRights; michael@0: if (give_full_access) { michael@0: process_access = PROCESS_ALL_ACCESS; michael@0: thread_access = THREAD_ALL_ACCESS; michael@0: } michael@0: if (!::DuplicateHandle(::GetCurrentProcess(), lpProcessInformation->hProcess, michael@0: target_process, &lpProcessInformation->hProcess, michael@0: process_access, FALSE, DUPLICATE_CLOSE_SOURCE)) { michael@0: ::CloseHandle(lpProcessInformation->hThread); michael@0: return FALSE; michael@0: } michael@0: if (!::DuplicateHandle(::GetCurrentProcess(), lpProcessInformation->hThread, michael@0: target_process, &lpProcessInformation->hThread, michael@0: thread_access, FALSE, DUPLICATE_CLOSE_SOURCE)) { michael@0: return FALSE; michael@0: } michael@0: return TRUE; michael@0: } michael@0: michael@0: } michael@0: michael@0: namespace sandbox { michael@0: michael@0: bool ProcessPolicy::GenerateRules(const wchar_t* name, michael@0: TargetPolicy::Semantics semantics, michael@0: LowLevelPolicy* policy) { michael@0: scoped_ptr process; michael@0: switch (semantics) { michael@0: case TargetPolicy::PROCESS_MIN_EXEC: { michael@0: process.reset(new PolicyRule(GIVE_READONLY)); michael@0: break; michael@0: }; michael@0: case TargetPolicy::PROCESS_ALL_EXEC: { michael@0: process.reset(new PolicyRule(GIVE_ALLACCESS)); michael@0: break; michael@0: }; michael@0: default: { michael@0: return false; michael@0: }; michael@0: } michael@0: michael@0: if (!process->AddStringMatch(IF, NameBased::NAME, name, CASE_INSENSITIVE)) { michael@0: return false; michael@0: } michael@0: if (!policy->AddRule(IPC_CREATEPROCESSW_TAG, process.get())) { michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: NTSTATUS ProcessPolicy::OpenThreadAction(const ClientInfo& client_info, michael@0: uint32 desired_access, michael@0: uint32 thread_id, michael@0: HANDLE* handle) { michael@0: *handle = NULL; michael@0: michael@0: NtOpenThreadFunction NtOpenThread = NULL; michael@0: ResolveNTFunctionPtr("NtOpenThread", &NtOpenThread); michael@0: michael@0: OBJECT_ATTRIBUTES attributes = {0}; michael@0: attributes.Length = sizeof(attributes); michael@0: CLIENT_ID client_id = {0}; michael@0: client_id.UniqueProcess = reinterpret_cast( michael@0: static_cast(client_info.process_id)); michael@0: client_id.UniqueThread = michael@0: reinterpret_cast(static_cast(thread_id)); michael@0: michael@0: HANDLE local_handle; michael@0: NTSTATUS status = NtOpenThread(&local_handle, desired_access, &attributes, michael@0: &client_id); michael@0: if (NT_SUCCESS(status)) { michael@0: if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, michael@0: client_info.process, handle, 0, FALSE, michael@0: DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { michael@0: ::CloseHandle(local_handle); michael@0: return STATUS_ACCESS_DENIED; michael@0: } michael@0: } michael@0: michael@0: return status; michael@0: } michael@0: michael@0: NTSTATUS ProcessPolicy::OpenProcessAction(const ClientInfo& client_info, michael@0: uint32 desired_access, michael@0: uint32 process_id, michael@0: HANDLE* handle) { michael@0: *handle = NULL; michael@0: michael@0: NtOpenProcessFunction NtOpenProcess = NULL; michael@0: ResolveNTFunctionPtr("NtOpenProcess", &NtOpenProcess); michael@0: michael@0: if (client_info.process_id != process_id) michael@0: return STATUS_ACCESS_DENIED; michael@0: michael@0: OBJECT_ATTRIBUTES attributes = {0}; michael@0: attributes.Length = sizeof(attributes); michael@0: CLIENT_ID client_id = {0}; michael@0: client_id.UniqueProcess = reinterpret_cast( michael@0: static_cast(client_info.process_id)); michael@0: HANDLE local_handle; michael@0: NTSTATUS status = NtOpenProcess(&local_handle, desired_access, &attributes, michael@0: &client_id); michael@0: if (NT_SUCCESS(status)) { michael@0: if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, michael@0: client_info.process, handle, 0, FALSE, michael@0: DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { michael@0: ::CloseHandle(local_handle); michael@0: return STATUS_ACCESS_DENIED; michael@0: } michael@0: } michael@0: michael@0: return status; michael@0: } michael@0: michael@0: NTSTATUS ProcessPolicy::OpenProcessTokenAction(const ClientInfo& client_info, michael@0: HANDLE process, michael@0: uint32 desired_access, michael@0: HANDLE* handle) { michael@0: *handle = NULL; michael@0: NtOpenProcessTokenFunction NtOpenProcessToken = NULL; michael@0: ResolveNTFunctionPtr("NtOpenProcessToken", &NtOpenProcessToken); michael@0: michael@0: if (CURRENT_PROCESS != process) michael@0: return STATUS_ACCESS_DENIED; michael@0: michael@0: HANDLE local_handle; michael@0: NTSTATUS status = NtOpenProcessToken(client_info.process, desired_access, michael@0: &local_handle); michael@0: if (NT_SUCCESS(status)) { michael@0: if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, michael@0: client_info.process, handle, 0, FALSE, michael@0: DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { michael@0: ::CloseHandle(local_handle); michael@0: return STATUS_ACCESS_DENIED; michael@0: } michael@0: } michael@0: return status; michael@0: } michael@0: michael@0: NTSTATUS ProcessPolicy::OpenProcessTokenExAction(const ClientInfo& client_info, michael@0: HANDLE process, michael@0: uint32 desired_access, michael@0: uint32 attributes, michael@0: HANDLE* handle) { michael@0: *handle = NULL; michael@0: NtOpenProcessTokenExFunction NtOpenProcessTokenEx = NULL; michael@0: ResolveNTFunctionPtr("NtOpenProcessTokenEx", &NtOpenProcessTokenEx); michael@0: michael@0: if (CURRENT_PROCESS != process) michael@0: return STATUS_ACCESS_DENIED; michael@0: michael@0: HANDLE local_handle; michael@0: NTSTATUS status = NtOpenProcessTokenEx(client_info.process, desired_access, michael@0: attributes, &local_handle); michael@0: if (NT_SUCCESS(status)) { michael@0: if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, michael@0: client_info.process, handle, 0, FALSE, michael@0: DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { michael@0: ::CloseHandle(local_handle); michael@0: return STATUS_ACCESS_DENIED; michael@0: } michael@0: } michael@0: return status; michael@0: } michael@0: michael@0: DWORD ProcessPolicy::CreateProcessWAction(EvalResult eval_result, michael@0: const ClientInfo& client_info, michael@0: const std::wstring &app_name, michael@0: const std::wstring &command_line, michael@0: PROCESS_INFORMATION* process_info) { michael@0: // The only action supported is ASK_BROKER which means create the process. michael@0: if (GIVE_ALLACCESS != eval_result && GIVE_READONLY != eval_result) { michael@0: return ERROR_ACCESS_DENIED; michael@0: } michael@0: michael@0: STARTUPINFO startup_info = {0}; michael@0: startup_info.cb = sizeof(startup_info); michael@0: scoped_ptr_malloc cmd_line(_wcsdup(command_line.c_str())); michael@0: michael@0: BOOL should_give_full_access = (GIVE_ALLACCESS == eval_result); michael@0: if (!CreateProcessExWHelper(client_info.process, should_give_full_access, michael@0: app_name.c_str(), cmd_line.get(), NULL, NULL, michael@0: FALSE, 0, NULL, NULL, &startup_info, michael@0: process_info)) { michael@0: return ERROR_ACCESS_DENIED; michael@0: } michael@0: return ERROR_SUCCESS; michael@0: } michael@0: michael@0: } // namespace sandbox