| // Copyright (c) 2011 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 <initguid.h> |
| #include <d3d9.h> |
| #include <Opmapi.h> |
| #include <windows.h> |
| |
| #include <map> |
| #include <string> |
| |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/path_service.h" |
| #include "base/process/launch.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/win/scoped_handle.h" |
| #include "base/win/windows_version.h" |
| #include "sandbox/win/src/nt_internals.h" |
| #include "sandbox/win/src/process_mitigations.h" |
| #include "sandbox/win/src/process_mitigations_win32k_policy.h" |
| #include "sandbox/win/src/sandbox.h" |
| #include "sandbox/win/src/sandbox_factory.h" |
| #include "sandbox/win/src/target_services.h" |
| #include "sandbox/win/src/win_utils.h" |
| #include "sandbox/win/tests/common/controller.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace { |
| |
| // API defined in winbase.h. |
| typedef decltype(GetProcessDEPPolicy)* GetProcessDEPPolicyFunction; |
| |
| // API defined in processthreadsapi.h. |
| typedef decltype( |
| GetProcessMitigationPolicy)* GetProcessMitigationPolicyFunction; |
| GetProcessMitigationPolicyFunction get_process_mitigation_policy; |
| |
| // APIs defined in wingdi.h. |
| typedef decltype(AddFontMemResourceEx)* AddFontMemResourceExFunction; |
| typedef decltype(RemoveFontMemResourceEx)* RemoveFontMemResourceExFunction; |
| |
| #if !defined(_WIN64) |
| bool CheckWin8DepPolicy() { |
| PROCESS_MITIGATION_DEP_POLICY policy = {}; |
| if (!get_process_mitigation_policy(::GetCurrentProcess(), ProcessDEPPolicy, |
| &policy, sizeof(policy))) { |
| return false; |
| } |
| return policy.Enable && policy.Permanent; |
| } |
| #endif // !defined(_WIN64) |
| |
| #if defined(NDEBUG) |
| bool CheckWin8AslrPolicy() { |
| PROCESS_MITIGATION_ASLR_POLICY policy = {}; |
| if (!get_process_mitigation_policy(::GetCurrentProcess(), ProcessASLRPolicy, |
| &policy, sizeof(policy))) { |
| return false; |
| } |
| return policy.EnableForceRelocateImages && policy.DisallowStrippedImages; |
| } |
| #endif // defined(NDEBUG) |
| |
| bool CheckWin8StrictHandlePolicy() { |
| PROCESS_MITIGATION_STRICT_HANDLE_CHECK_POLICY policy = {}; |
| if (!get_process_mitigation_policy(::GetCurrentProcess(), |
| ProcessStrictHandleCheckPolicy, |
| &policy, sizeof(policy))) { |
| return false; |
| } |
| return policy.RaiseExceptionOnInvalidHandleReference && |
| policy.HandleExceptionsPermanentlyEnabled; |
| } |
| |
| bool CheckWin8Win32CallPolicy() { |
| PROCESS_MITIGATION_SYSTEM_CALL_DISABLE_POLICY policy = {}; |
| if (!get_process_mitigation_policy(::GetCurrentProcess(), |
| ProcessSystemCallDisablePolicy, |
| &policy, sizeof(policy))) { |
| return false; |
| } |
| return policy.DisallowWin32kSystemCalls; |
| } |
| |
| bool CheckWin8DllExtensionPolicy() { |
| PROCESS_MITIGATION_EXTENSION_POINT_DISABLE_POLICY policy = {}; |
| if (!get_process_mitigation_policy(::GetCurrentProcess(), |
| ProcessExtensionPointDisablePolicy, |
| &policy, sizeof(policy))) { |
| return false; |
| } |
| return policy.DisableExtensionPoints; |
| } |
| |
| bool CheckWin10FontPolicy() { |
| PROCESS_MITIGATION_FONT_DISABLE_POLICY policy = {}; |
| if (!get_process_mitigation_policy(::GetCurrentProcess(), |
| ProcessFontDisablePolicy, &policy, |
| sizeof(policy))) { |
| return false; |
| } |
| return policy.DisableNonSystemFonts; |
| } |
| |
| bool CheckWin10ImageLoadNoRemotePolicy() { |
| PROCESS_MITIGATION_IMAGE_LOAD_POLICY policy = {}; |
| if (!get_process_mitigation_policy(::GetCurrentProcess(), |
| ProcessImageLoadPolicy, &policy, |
| sizeof(policy))) { |
| return false; |
| } |
| return policy.NoRemoteImages; |
| } |
| |
| void TestWin10ImageLoadRemote(bool is_success_test) { |
| // ***Insert your manual testing share UNC path here! |
| // E.g.: \\\\hostname\\sharename\\calc.exe |
| std::wstring unc = L"\"\\\\hostname\\sharename\\calc.exe\""; |
| |
| sandbox::TestRunner runner; |
| sandbox::TargetPolicy* policy = runner.GetPolicy(); |
| |
| // Set a policy that would normally allow for process creation. |
| policy->SetJobLevel(sandbox::JOB_NONE, 0); |
| policy->SetTokenLevel(sandbox::USER_UNPROTECTED, sandbox::USER_UNPROTECTED); |
| runner.SetDisableCsrss(false); |
| |
| if (!is_success_test) { |
| // Enable the NoRemote mitigation. |
| EXPECT_EQ(policy->SetDelayedProcessMitigations( |
| sandbox::MITIGATION_IMAGE_LOAD_NO_REMOTE), |
| sandbox::SBOX_ALL_OK); |
| } |
| |
| std::wstring test = L"TestChildProcess "; |
| test += unc.c_str(); |
| EXPECT_EQ((is_success_test ? sandbox::SBOX_TEST_SUCCEEDED |
| : sandbox::SBOX_TEST_FAILED), |
| runner.RunTest(test.c_str())); |
| } |
| |
| bool CheckWin10ImageLoadNoLowLabelPolicy() { |
| PROCESS_MITIGATION_IMAGE_LOAD_POLICY policy = {}; |
| if (!get_process_mitigation_policy(::GetCurrentProcess(), |
| ProcessImageLoadPolicy, &policy, |
| sizeof(policy))) { |
| return false; |
| } |
| return policy.NoLowMandatoryLabelImages; |
| } |
| |
| void TestWin10ImageLoadLowLabel(bool is_success_test) { |
| // Setup a mandatory low executable for this test (calc.exe). |
| // If anything fails during setup, ASSERT to end test. |
| base::FilePath orig_path; |
| ASSERT_TRUE(base::PathService::Get(base::DIR_SYSTEM, &orig_path)); |
| orig_path = orig_path.Append(L"calc.exe"); |
| |
| base::ScopedTempDir temp_dir; |
| ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| base::FilePath new_path = temp_dir.path(); |
| new_path = new_path.Append(L"lowIL_calc.exe"); |
| |
| // Test file will be cleaned up by the ScopedTempDir. |
| ASSERT_TRUE(base::CopyFileW(orig_path, new_path)); |
| |
| std::wstring cmd_line = L"icacls \""; |
| cmd_line += new_path.value().c_str(); |
| cmd_line += L"\" /setintegritylevel Low"; |
| |
| base::LaunchOptions options = base::LaunchOptionsForTest(); |
| base::Process setup_proc = base::LaunchProcess(cmd_line.c_str(), options); |
| ASSERT_TRUE(setup_proc.IsValid()); |
| |
| int exit_code = 1; |
| if (!setup_proc.WaitForExitWithTimeout(base::TimeDelta::FromSeconds(10), |
| &exit_code)) { |
| // Might have timed out, or might have failed. |
| // Terminate to make sure we clean up any mess. |
| setup_proc.Terminate(0, false); |
| ASSERT_TRUE(false); |
| } |
| // Make sure icacls was successful. |
| ASSERT_EQ(0, exit_code); |
| |
| sandbox::TestRunner runner; |
| sandbox::TargetPolicy* policy = runner.GetPolicy(); |
| |
| // Set a policy that would normally allow for process creation. |
| policy->SetJobLevel(sandbox::JOB_NONE, 0); |
| policy->SetTokenLevel(sandbox::USER_UNPROTECTED, sandbox::USER_UNPROTECTED); |
| runner.SetDisableCsrss(false); |
| |
| if (!is_success_test) { |
| // Enable the NoLowLabel mitigation. |
| EXPECT_EQ(policy->SetDelayedProcessMitigations( |
| sandbox::MITIGATION_IMAGE_LOAD_NO_LOW_LABEL), |
| sandbox::SBOX_ALL_OK); |
| } |
| |
| std::wstring test = L"TestChildProcess "; |
| test += new_path.value().c_str(); |
| |
| EXPECT_EQ((is_success_test ? sandbox::SBOX_TEST_SUCCEEDED |
| : sandbox::SBOX_TEST_FAILED), |
| runner.RunTest(test.c_str())); |
| } |
| |
| BOOL CALLBACK MonitorEnumCallback(HMONITOR monitor, |
| HDC hdc_monitor, |
| LPRECT rect_monitor, |
| LPARAM data) { |
| std::map<HMONITOR, base::string16>& monitors = |
| *reinterpret_cast<std::map<HMONITOR, base::string16>*>(data); |
| MONITORINFOEXW monitor_info = {}; |
| monitor_info.cbSize = sizeof(monitor_info); |
| |
| if (!::GetMonitorInfoW(monitor, |
| reinterpret_cast<MONITORINFO*>(&monitor_info))) |
| return FALSE; |
| monitors[monitor] = monitor_info.szDevice; |
| return TRUE; |
| } |
| |
| std::map<HMONITOR, std::wstring> EnumerateMonitors() { |
| std::map<HMONITOR, std::wstring> result; |
| ::EnumDisplayMonitors(nullptr, nullptr, MonitorEnumCallback, |
| reinterpret_cast<LPARAM>(&result)); |
| return result; |
| } |
| |
| #define HMONITOR_ENTRY(monitor) \ |
| result[reinterpret_cast<HMONITOR>(monitor)] = \ |
| base::StringPrintf(L"\\\\.\\DISPLAY%X", monitor) |
| |
| std::map<HMONITOR, std::wstring> GetTestMonitors() { |
| std::map<HMONITOR, std::wstring> result; |
| |
| HMONITOR_ENTRY(0x11111111); |
| HMONITOR_ENTRY(0x22222222); |
| HMONITOR_ENTRY(0x44444444); |
| HMONITOR_ENTRY(0x88888888); |
| return result; |
| } |
| |
| std::wstring UnicodeStringToString(PUNICODE_STRING name) { |
| return std::wstring(name->Buffer, |
| name->Buffer + (name->Length / sizeof(name->Buffer[0]))); |
| } |
| |
| // Returns an index 1, 2, 4 or 8 depening on the device. 0 on error. |
| DWORD GetTestDeviceMonitorIndex(PUNICODE_STRING device_name) { |
| std::wstring name = UnicodeStringToString(device_name); |
| std::map<HMONITOR, std::wstring> monitors = GetTestMonitors(); |
| for (const auto& monitor : monitors) { |
| if (name == monitor.second) |
| return static_cast<DWORD>(reinterpret_cast<uintptr_t>(monitor.first)) & |
| 0xF; |
| } |
| return 0; |
| } |
| |
| NTSTATUS WINAPI GetSuggestedOPMProtectedOutputArraySizeTest( |
| PUNICODE_STRING device_name, |
| DWORD* suggested_output_array_size) { |
| DWORD monitor = GetTestDeviceMonitorIndex(device_name); |
| if (!monitor) |
| return STATUS_OBJECT_NAME_NOT_FOUND; |
| *suggested_output_array_size = monitor; |
| return STATUS_SUCCESS; |
| } |
| |
| NTSTATUS WINAPI |
| CreateOPMProtectedOutputsTest(PUNICODE_STRING device_name, |
| DXGKMDT_OPM_VIDEO_OUTPUT_SEMANTICS vos, |
| DWORD output_array_size, |
| DWORD* num_in_output_array, |
| OPM_PROTECTED_OUTPUT_HANDLE* output_array) { |
| DWORD monitor = GetTestDeviceMonitorIndex(device_name); |
| if (!monitor) |
| return STATUS_OBJECT_NAME_NOT_FOUND; |
| if (vos != DXGKMDT_OPM_VOS_OPM_SEMANTICS) |
| return STATUS_INVALID_PARAMETER; |
| if (output_array_size != monitor) |
| return STATUS_INVALID_PARAMETER; |
| *num_in_output_array = monitor - 1; |
| for (DWORD index = 0; index < monitor - 1; ++index) { |
| output_array[index] = |
| reinterpret_cast<OPM_PROTECTED_OUTPUT_HANDLE>((monitor << 4) + index); |
| } |
| return STATUS_SUCCESS; |
| } |
| |
| ULONG CalculateCertLength(ULONG monitor) { |
| return (monitor * 0x800) + 0xabc; |
| } |
| |
| NTSTATUS WINAPI GetCertificateTest(PUNICODE_STRING device_name, |
| DXGKMDT_CERTIFICATE_TYPE certificate_type, |
| BYTE* certificate, |
| ULONG certificate_length) { |
| DWORD monitor = GetTestDeviceMonitorIndex(device_name); |
| if (!monitor) |
| return STATUS_OBJECT_NAME_NOT_FOUND; |
| if (certificate_type != DXGKMDT_OPM_CERTIFICATE) |
| return STATUS_INVALID_PARAMETER; |
| if (certificate_length != CalculateCertLength(monitor)) |
| return STATUS_INVALID_PARAMETER; |
| memset(certificate, 'A' + monitor, certificate_length); |
| return STATUS_SUCCESS; |
| } |
| |
| NTSTATUS WINAPI |
| GetCertificateSizeTest(PUNICODE_STRING device_name, |
| DXGKMDT_CERTIFICATE_TYPE certificate_type, |
| ULONG* certificate_length) { |
| DWORD monitor = GetTestDeviceMonitorIndex(device_name); |
| if (!monitor) |
| return STATUS_OBJECT_NAME_NOT_FOUND; |
| if (certificate_type != DXGKMDT_OPM_CERTIFICATE) |
| return STATUS_INVALID_PARAMETER; |
| *certificate_length = CalculateCertLength(monitor); |
| return STATUS_SUCCESS; |
| } |
| |
| // Check for valid output handle and return the monitor index. |
| DWORD IsValidProtectedOutput(OPM_PROTECTED_OUTPUT_HANDLE protected_output) { |
| uintptr_t handle = reinterpret_cast<uintptr_t>(protected_output); |
| uintptr_t monitor = handle >> 4; |
| uintptr_t index = handle & 0xF; |
| switch (monitor) { |
| case 1: |
| case 2: |
| case 4: |
| case 8: |
| break; |
| default: |
| return 0; |
| } |
| if (index >= (monitor - 1)) |
| return 0; |
| return static_cast<DWORD>(monitor); |
| } |
| |
| NTSTATUS WINAPI |
| GetCertificateByHandleTest(OPM_PROTECTED_OUTPUT_HANDLE protected_output, |
| DXGKMDT_CERTIFICATE_TYPE certificate_type, |
| BYTE* certificate, |
| ULONG certificate_length) { |
| DWORD monitor = IsValidProtectedOutput(protected_output); |
| if (!monitor) |
| return STATUS_INVALID_HANDLE; |
| if (certificate_type != DXGKMDT_OPM_CERTIFICATE) |
| return STATUS_INVALID_PARAMETER; |
| if (certificate_length != CalculateCertLength(monitor)) |
| return STATUS_INVALID_PARAMETER; |
| memset(certificate, 'A' + monitor, certificate_length); |
| return STATUS_SUCCESS; |
| } |
| |
| NTSTATUS WINAPI |
| GetCertificateSizeByHandleTest(OPM_PROTECTED_OUTPUT_HANDLE protected_output, |
| DXGKMDT_CERTIFICATE_TYPE certificate_type, |
| ULONG* certificate_length) { |
| DWORD monitor = IsValidProtectedOutput(protected_output); |
| if (!monitor) |
| return STATUS_INVALID_HANDLE; |
| if (certificate_type != DXGKMDT_OPM_CERTIFICATE) |
| return STATUS_INVALID_PARAMETER; |
| *certificate_length = CalculateCertLength(monitor); |
| return STATUS_SUCCESS; |
| } |
| |
| NTSTATUS WINAPI |
| DestroyOPMProtectedOutputTest(OPM_PROTECTED_OUTPUT_HANDLE protected_output) { |
| if (!IsValidProtectedOutput(protected_output)) |
| return STATUS_INVALID_HANDLE; |
| return STATUS_SUCCESS; |
| } |
| |
| NTSTATUS WINAPI ConfigureOPMProtectedOutputTest( |
| OPM_PROTECTED_OUTPUT_HANDLE protected_output, |
| const DXGKMDT_OPM_CONFIGURE_PARAMETERS* parameters, |
| ULONG additional_parameters_size, |
| const BYTE* additional_parameters) { |
| if (!IsValidProtectedOutput(protected_output)) |
| return STATUS_INVALID_HANDLE; |
| if (additional_parameters && additional_parameters_size) |
| return STATUS_INVALID_PARAMETER; |
| return STATUS_SUCCESS; |
| } |
| |
| NTSTATUS WINAPI GetOPMInformationTest( |
| OPM_PROTECTED_OUTPUT_HANDLE protected_output, |
| const DXGKMDT_OPM_GET_INFO_PARAMETERS* parameters, |
| DXGKMDT_OPM_REQUESTED_INFORMATION* requested_information) { |
| DWORD monitor = IsValidProtectedOutput(protected_output); |
| if (!monitor) |
| return STATUS_INVALID_HANDLE; |
| memset(requested_information, '0' + monitor, |
| sizeof(DXGKMDT_OPM_REQUESTED_INFORMATION)); |
| return STATUS_SUCCESS; |
| } |
| |
| NTSTATUS WINAPI |
| GetOPMRandomNumberTest(OPM_PROTECTED_OUTPUT_HANDLE protected_output, |
| DXGKMDT_OPM_RANDOM_NUMBER* random_number) { |
| DWORD monitor = IsValidProtectedOutput(protected_output); |
| if (!monitor) |
| return STATUS_INVALID_HANDLE; |
| memset(random_number->abRandomNumber, '!' + monitor, |
| sizeof(random_number->abRandomNumber)); |
| return STATUS_SUCCESS; |
| } |
| |
| NTSTATUS WINAPI SetOPMSigningKeyAndSequenceNumbersTest( |
| OPM_PROTECTED_OUTPUT_HANDLE protected_output, |
| const DXGKMDT_OPM_ENCRYPTED_PARAMETERS* parameters) { |
| DWORD monitor = IsValidProtectedOutput(protected_output); |
| if (!monitor) |
| return STATUS_INVALID_HANDLE; |
| DXGKMDT_OPM_ENCRYPTED_PARAMETERS test_params = {}; |
| memset(test_params.abEncryptedParameters, 'a' + monitor, |
| sizeof(test_params.abEncryptedParameters)); |
| if (memcmp(test_params.abEncryptedParameters, |
| parameters->abEncryptedParameters, |
| sizeof(test_params.abEncryptedParameters)) != 0) |
| return STATUS_INVALID_PARAMETER; |
| return STATUS_SUCCESS; |
| } |
| |
| BOOL WINAPI EnumDisplayMonitorsTest(HDC hdc, |
| LPCRECT clip_rect, |
| MONITORENUMPROC enum_function, |
| LPARAM data) { |
| RECT rc = {}; |
| for (const auto& monitor : GetTestMonitors()) { |
| if (!enum_function(monitor.first, hdc, &rc, data)) |
| return FALSE; |
| } |
| return TRUE; |
| } |
| |
| BOOL WINAPI GetMonitorInfoWTest(HMONITOR monitor, LPMONITORINFO monitor_info) { |
| std::map<HMONITOR, std::wstring> monitors = GetTestMonitors(); |
| if (monitor_info->cbSize != sizeof(MONITORINFO) && |
| monitor_info->cbSize != sizeof(MONITORINFOEXW)) |
| return FALSE; |
| auto it = monitors.find(monitor); |
| if (it == monitors.end()) |
| return FALSE; |
| if (monitor_info->cbSize == sizeof(MONITORINFOEXW)) { |
| MONITORINFOEXW* monitor_info_ex = |
| reinterpret_cast<MONITORINFOEXW*>(monitor_info); |
| size_t copy_size = (it->second.size() + 1) * sizeof(WCHAR); |
| if (copy_size > sizeof(monitor_info_ex->szDevice) - sizeof(WCHAR)) |
| copy_size = sizeof(monitor_info_ex->szDevice) - sizeof(WCHAR); |
| memset(monitor_info_ex->szDevice, 0, sizeof(monitor_info_ex->szDevice)); |
| memcpy(monitor_info_ex->szDevice, it->second.c_str(), copy_size); |
| } |
| return TRUE; |
| } |
| |
| #define RETURN_TEST_FUNC(n) \ |
| if (strcmp(name, #n) == 0) { \ |
| return n##Test; \ |
| } |
| |
| void* FunctionOverrideForTest(const char* name) { |
| RETURN_TEST_FUNC(GetSuggestedOPMProtectedOutputArraySize); |
| RETURN_TEST_FUNC(CreateOPMProtectedOutputs); |
| RETURN_TEST_FUNC(GetCertificate); |
| RETURN_TEST_FUNC(GetCertificateSize); |
| RETURN_TEST_FUNC(DestroyOPMProtectedOutput); |
| RETURN_TEST_FUNC(ConfigureOPMProtectedOutput); |
| RETURN_TEST_FUNC(GetOPMInformation); |
| RETURN_TEST_FUNC(GetOPMRandomNumber); |
| RETURN_TEST_FUNC(SetOPMSigningKeyAndSequenceNumbers); |
| RETURN_TEST_FUNC(EnumDisplayMonitors); |
| RETURN_TEST_FUNC(GetMonitorInfoW); |
| RETURN_TEST_FUNC(GetCertificateByHandle); |
| RETURN_TEST_FUNC(GetCertificateSizeByHandle); |
| NOTREACHED(); |
| return nullptr; |
| } |
| |
| } // namespace |
| |
| namespace sandbox { |
| |
| // A shared helper test command that will attempt to CreateProcess with a given |
| // command line. The second optional parameter will cause the child process to |
| // return that as an exit code on termination. |
| // |
| // ***Make sure you've enabled basic process creation in the |
| // test sandbox settings via: |
| // sandbox::TargetPolicy::SetJobLevel(), |
| // sandbox::TargetPolicy::SetTokenLevel(), |
| // and TestRunner::SetDisableCsrss(). |
| SBOX_TESTS_COMMAND int TestChildProcess(int argc, wchar_t** argv) { |
| if (argc < 1) |
| return SBOX_TEST_INVALID_PARAMETER; |
| |
| int desired_exit_code = 0; |
| |
| if (argc == 2) { |
| desired_exit_code = wcstoul(argv[1], nullptr, 0); |
| } |
| |
| std::wstring cmd = argv[0]; |
| base::LaunchOptions options = base::LaunchOptionsForTest(); |
| base::Process setup_proc = base::LaunchProcess(cmd.c_str(), options); |
| |
| if (setup_proc.IsValid()) { |
| setup_proc.Terminate(desired_exit_code, false); |
| return SBOX_TEST_SUCCEEDED; |
| } |
| // Note: GetLastError from CreateProcess returns 5, "ERROR_ACCESS_DENIED". |
| return SBOX_TEST_FAILED; |
| } |
| |
| //------------------------------------------------------------------------------ |
| // Win8 Checks: |
| // MITIGATION_DEP(_NO_ATL_THUNK) |
| // MITIGATION_EXTENSION_DLL_DISABLE |
| // MITIGATION_RELOCATE_IMAGE(_REQUIRED) - ASLR, release only |
| // MITIGATION_STRICT_HANDLE_CHECKS |
| // >= Win8 |
| //------------------------------------------------------------------------------ |
| |
| SBOX_TESTS_COMMAND int CheckWin8(int argc, wchar_t **argv) { |
| get_process_mitigation_policy = |
| reinterpret_cast<GetProcessMitigationPolicyFunction>( |
| ::GetProcAddress(::GetModuleHandleW(L"kernel32.dll"), |
| "GetProcessMitigationPolicy")); |
| if (!get_process_mitigation_policy) |
| return SBOX_TEST_NOT_FOUND; |
| |
| #if !defined(_WIN64) // DEP is always enabled on 64-bit. |
| if (!CheckWin8DepPolicy()) |
| return SBOX_TEST_FIRST_ERROR; |
| #endif |
| |
| #if defined(NDEBUG) // ASLR cannot be forced in debug builds. |
| if (!CheckWin8AslrPolicy()) |
| return SBOX_TEST_SECOND_ERROR; |
| #endif |
| |
| if (!CheckWin8StrictHandlePolicy()) |
| return SBOX_TEST_THIRD_ERROR; |
| |
| if (!CheckWin8DllExtensionPolicy()) |
| return SBOX_TEST_FIFTH_ERROR; |
| |
| return SBOX_TEST_SUCCEEDED; |
| } |
| |
| TEST(ProcessMitigationsTest, CheckWin8) { |
| if (base::win::GetVersion() < base::win::VERSION_WIN8) |
| return; |
| |
| TestRunner runner; |
| sandbox::TargetPolicy* policy = runner.GetPolicy(); |
| |
| sandbox::MitigationFlags mitigations = MITIGATION_DEP | |
| MITIGATION_DEP_NO_ATL_THUNK | |
| MITIGATION_EXTENSION_DLL_DISABLE; |
| #if defined(NDEBUG) // ASLR cannot be forced in debug builds. |
| mitigations |= MITIGATION_RELOCATE_IMAGE | |
| MITIGATION_RELOCATE_IMAGE_REQUIRED; |
| #endif |
| |
| EXPECT_EQ(policy->SetProcessMitigations(mitigations), SBOX_ALL_OK); |
| |
| mitigations |= MITIGATION_STRICT_HANDLE_CHECKS; |
| |
| EXPECT_EQ(policy->SetDelayedProcessMitigations(mitigations), SBOX_ALL_OK); |
| |
| EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckWin8")); |
| } |
| |
| //------------------------------------------------------------------------------ |
| // DEP (MITIGATION_DEP) |
| // < Win8 x86 |
| //------------------------------------------------------------------------------ |
| |
| SBOX_TESTS_COMMAND int CheckDep(int argc, wchar_t **argv) { |
| GetProcessDEPPolicyFunction get_process_dep_policy = |
| reinterpret_cast<GetProcessDEPPolicyFunction>( |
| ::GetProcAddress(::GetModuleHandleW(L"kernel32.dll"), |
| "GetProcessDEPPolicy")); |
| if (get_process_dep_policy) { |
| BOOL is_permanent = FALSE; |
| DWORD dep_flags = 0; |
| |
| if (!get_process_dep_policy(::GetCurrentProcess(), &dep_flags, |
| &is_permanent)) { |
| return SBOX_TEST_FIRST_ERROR; |
| } |
| |
| if (!(dep_flags & PROCESS_DEP_ENABLE) || !is_permanent) |
| return SBOX_TEST_SECOND_ERROR; |
| |
| } else { |
| NtQueryInformationProcessFunction query_information_process = NULL; |
| ResolveNTFunctionPtr("NtQueryInformationProcess", |
| &query_information_process); |
| if (!query_information_process) |
| return SBOX_TEST_NOT_FOUND; |
| |
| ULONG size = 0; |
| ULONG dep_flags = 0; |
| if (!SUCCEEDED(query_information_process(::GetCurrentProcess(), |
| ProcessExecuteFlags, &dep_flags, |
| sizeof(dep_flags), &size))) { |
| return SBOX_TEST_THIRD_ERROR; |
| } |
| |
| static const int MEM_EXECUTE_OPTION_DISABLE = 2; |
| static const int MEM_EXECUTE_OPTION_PERMANENT = 8; |
| dep_flags &= 0xff; |
| |
| if (dep_flags != (MEM_EXECUTE_OPTION_DISABLE | |
| MEM_EXECUTE_OPTION_PERMANENT)) { |
| return SBOX_TEST_FOURTH_ERROR; |
| } |
| } |
| |
| return SBOX_TEST_SUCCEEDED; |
| } |
| |
| #if !defined(_WIN64) // DEP is always enabled on 64-bit. |
| TEST(ProcessMitigationsTest, CheckDep) { |
| if (base::win::GetVersion() > base::win::VERSION_WIN7) |
| return; |
| |
| TestRunner runner; |
| sandbox::TargetPolicy* policy = runner.GetPolicy(); |
| |
| EXPECT_EQ(policy->SetProcessMitigations( |
| MITIGATION_DEP | |
| MITIGATION_DEP_NO_ATL_THUNK | |
| MITIGATION_SEHOP), |
| SBOX_ALL_OK); |
| EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckDep")); |
| } |
| #endif |
| |
| //------------------------------------------------------------------------------ |
| // Win32k Lockdown (MITIGATION_WIN32K_DISABLE) |
| // >= Win8 |
| //------------------------------------------------------------------------------ |
| |
| SBOX_TESTS_COMMAND int CheckWin8Lockdown(int argc, wchar_t **argv) { |
| get_process_mitigation_policy = |
| reinterpret_cast<GetProcessMitigationPolicyFunction>( |
| ::GetProcAddress(::GetModuleHandleW(L"kernel32.dll"), |
| "GetProcessMitigationPolicy")); |
| if (!get_process_mitigation_policy) |
| return SBOX_TEST_NOT_FOUND; |
| |
| if (!CheckWin8Win32CallPolicy()) |
| return SBOX_TEST_FIRST_ERROR; |
| return SBOX_TEST_SUCCEEDED; |
| } |
| |
| SBOX_TESTS_COMMAND int CheckWin8MonitorsRedirection(int argc, wchar_t** argv) { |
| std::map<HMONITOR, base::string16> monitors = EnumerateMonitors(); |
| std::map<HMONITOR, base::string16> monitors_to_test = GetTestMonitors(); |
| if (monitors.size() != monitors_to_test.size()) |
| return SBOX_TEST_FIRST_ERROR; |
| |
| for (const auto& monitor : monitors) { |
| auto result = monitors_to_test.find(monitor.first); |
| if (result == monitors_to_test.end()) |
| return SBOX_TEST_SECOND_ERROR; |
| if (result->second != monitor.second) |
| return SBOX_TEST_THIRD_ERROR; |
| } |
| return SBOX_TEST_SUCCEEDED; |
| } |
| |
| SBOX_TESTS_COMMAND int CheckWin8MonitorInfo(int argc, wchar_t** argv) { |
| std::map<HMONITOR, base::string16> monitors_to_test = GetTestMonitors(); |
| MONITORINFO monitor_info = {}; |
| MONITORINFOEXW monitor_info_exw = {}; |
| MONITORINFOEXA monitor_info_exa = {}; |
| HMONITOR valid_monitor = monitors_to_test.begin()->first; |
| std::wstring valid_device = monitors_to_test.begin()->second; |
| monitor_info.cbSize = sizeof(MONITORINFO); |
| if (!::GetMonitorInfoW(valid_monitor, &monitor_info)) |
| return SBOX_TEST_FIRST_ERROR; |
| monitor_info.cbSize = sizeof(MONITORINFO); |
| if (!::GetMonitorInfoA(valid_monitor, &monitor_info)) |
| return SBOX_TEST_SECOND_ERROR; |
| monitor_info_exw.cbSize = sizeof(MONITORINFOEXW); |
| if (!::GetMonitorInfoW(valid_monitor, |
| reinterpret_cast<MONITORINFO*>(&monitor_info_exw)) || |
| valid_device != monitor_info_exw.szDevice) { |
| return SBOX_TEST_THIRD_ERROR; |
| } |
| monitor_info_exa.cbSize = sizeof(MONITORINFOEXA); |
| if (!::GetMonitorInfoA(valid_monitor, |
| reinterpret_cast<MONITORINFO*>(&monitor_info_exa)) || |
| valid_device != base::ASCIIToUTF16(monitor_info_exa.szDevice)) { |
| return SBOX_TEST_FOURTH_ERROR; |
| } |
| |
| // Invalid size checks. |
| monitor_info.cbSize = 0; |
| if (::GetMonitorInfoW(valid_monitor, &monitor_info)) |
| return SBOX_TEST_FIFTH_ERROR; |
| monitor_info.cbSize = 0x10000; |
| if (::GetMonitorInfoW(valid_monitor, &monitor_info)) |
| return SBOX_TEST_SIXTH_ERROR; |
| |
| // Check that an invalid handle isn't accepted. |
| HMONITOR invalid_monitor = reinterpret_cast<HMONITOR>(-1); |
| monitor_info.cbSize = sizeof(MONITORINFO); |
| if (::GetMonitorInfoW(invalid_monitor, &monitor_info)) |
| return SBOX_TEST_SEVENTH_ERROR; |
| |
| return SBOX_TEST_SUCCEEDED; |
| } |
| |
| bool RunTestsOnVideoOutputConfigure(uintptr_t monitor_index, |
| IOPMVideoOutput* video_output) { |
| OPM_CONFIGURE_PARAMETERS config_params = {}; |
| OPM_SET_PROTECTION_LEVEL_PARAMETERS* protection_level = |
| reinterpret_cast<OPM_SET_PROTECTION_LEVEL_PARAMETERS*>( |
| config_params.abParameters); |
| protection_level->ulProtectionType = OPM_PROTECTION_TYPE_HDCP; |
| protection_level->ulProtectionLevel = OPM_HDCP_ON; |
| config_params.guidSetting = OPM_SET_PROTECTION_LEVEL; |
| config_params.cbParametersSize = sizeof(OPM_SET_PROTECTION_LEVEL_PARAMETERS); |
| HRESULT hr = video_output->Configure(&config_params, 0, nullptr); |
| if (FAILED(hr)) |
| return false; |
| protection_level->ulProtectionType = OPM_PROTECTION_TYPE_DPCP; |
| hr = video_output->Configure(&config_params, 0, nullptr); |
| if (FAILED(hr)) |
| return false; |
| protection_level->ulProtectionLevel = OPM_HDCP_OFF; |
| hr = video_output->Configure(&config_params, 0, nullptr); |
| if (FAILED(hr)) |
| return false; |
| BYTE dummy_byte = 0; |
| hr = video_output->Configure(&config_params, 1, &dummy_byte); |
| if (SUCCEEDED(hr)) |
| return false; |
| protection_level->ulProtectionType = 0xFFFFFFFF; |
| hr = video_output->Configure(&config_params, 0, nullptr); |
| if (SUCCEEDED(hr)) |
| return false; |
| // Invalid protection level test. |
| protection_level->ulProtectionType = OPM_PROTECTION_TYPE_HDCP; |
| protection_level->ulProtectionLevel = OPM_HDCP_ON + 1; |
| hr = video_output->Configure(&config_params, 0, nullptr); |
| if (SUCCEEDED(hr)) |
| return false; |
| hr = video_output->Configure(&config_params, 0, nullptr); |
| if (SUCCEEDED(hr)) |
| return false; |
| config_params.guidSetting = OPM_SET_HDCP_SRM; |
| OPM_SET_HDCP_SRM_PARAMETERS* srm_parameters = |
| reinterpret_cast<OPM_SET_HDCP_SRM_PARAMETERS*>( |
| config_params.abParameters); |
| srm_parameters->ulSRMVersion = 1; |
| config_params.cbParametersSize = sizeof(OPM_SET_HDCP_SRM_PARAMETERS); |
| hr = video_output->Configure(&config_params, 0, nullptr); |
| if (SUCCEEDED(hr)) |
| return false; |
| return true; |
| } |
| |
| bool RunTestsOnVideoOutputFinishInitialization(uintptr_t monitor_index, |
| IOPMVideoOutput* video_output) { |
| OPM_ENCRYPTED_INITIALIZATION_PARAMETERS init_params = {}; |
| memset(init_params.abEncryptedInitializationParameters, 'a' + monitor_index, |
| sizeof(init_params.abEncryptedInitializationParameters)); |
| HRESULT hr = video_output->FinishInitialization(&init_params); |
| if (FAILED(hr)) |
| return false; |
| memset(init_params.abEncryptedInitializationParameters, 'Z' + monitor_index, |
| sizeof(init_params.abEncryptedInitializationParameters)); |
| hr = video_output->FinishInitialization(&init_params); |
| if (SUCCEEDED(hr)) |
| return false; |
| return true; |
| } |
| |
| bool RunTestsOnVideoOutputStartInitialization(uintptr_t monitor_index, |
| IOPMVideoOutput* video_output) { |
| OPM_RANDOM_NUMBER random_number = {}; |
| BYTE* certificate = nullptr; |
| ULONG certificate_length = 0; |
| |
| HRESULT hr = video_output->StartInitialization(&random_number, &certificate, |
| &certificate_length); |
| if (FAILED(hr)) |
| return false; |
| |
| if (certificate_length != CalculateCertLength(monitor_index)) |
| return false; |
| |
| for (ULONG i = 0; i < certificate_length; ++i) { |
| if (certificate[i] != 'A' + monitor_index) |
| return false; |
| } |
| |
| for (ULONG i = 0; i < sizeof(random_number.abRandomNumber); ++i) { |
| if (random_number.abRandomNumber[i] != '!' + monitor_index) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static bool SendSingleGetInfoRequest(uintptr_t monitor_index, |
| IOPMVideoOutput* video_output, |
| const GUID& request, |
| ULONG data_length, |
| void* data) { |
| OPM_GET_INFO_PARAMETERS params = {}; |
| OPM_REQUESTED_INFORMATION requested_information = {}; |
| BYTE* requested_information_ptr = |
| reinterpret_cast<BYTE*>(&requested_information); |
| params.guidInformation = request; |
| params.cbParametersSize = data_length; |
| memcpy(params.abParameters, data, data_length); |
| HRESULT hr = video_output->GetInformation(¶ms, &requested_information); |
| if (FAILED(hr)) |
| return false; |
| for (size_t i = 0; i < sizeof(OPM_REQUESTED_INFORMATION); ++i) { |
| if (requested_information_ptr[i] != '0' + monitor_index) |
| return false; |
| } |
| return true; |
| } |
| |
| bool RunTestsOnVideoOutputGetInformation(uintptr_t monitor_index, |
| IOPMVideoOutput* video_output) { |
| ULONG dummy = 0; |
| if (!SendSingleGetInfoRequest(monitor_index, video_output, |
| OPM_GET_CONNECTOR_TYPE, 0, nullptr)) { |
| return false; |
| } |
| if (!SendSingleGetInfoRequest(monitor_index, video_output, |
| OPM_GET_SUPPORTED_PROTECTION_TYPES, 0, |
| nullptr)) { |
| return false; |
| } |
| // These should fail due to invalid parameter sizes. |
| if (SendSingleGetInfoRequest(monitor_index, video_output, |
| OPM_GET_CONNECTOR_TYPE, sizeof(dummy), &dummy)) { |
| return false; |
| } |
| if (SendSingleGetInfoRequest(monitor_index, video_output, |
| OPM_GET_SUPPORTED_PROTECTION_TYPES, |
| sizeof(dummy), &dummy)) { |
| return false; |
| } |
| ULONG protection_type = OPM_PROTECTION_TYPE_HDCP; |
| if (!SendSingleGetInfoRequest(monitor_index, video_output, |
| OPM_GET_ACTUAL_PROTECTION_LEVEL, |
| sizeof(protection_type), &protection_type)) { |
| return false; |
| } |
| protection_type = OPM_PROTECTION_TYPE_DPCP; |
| if (!SendSingleGetInfoRequest(monitor_index, video_output, |
| OPM_GET_ACTUAL_PROTECTION_LEVEL, |
| sizeof(protection_type), &protection_type)) { |
| return false; |
| } |
| // These should fail as unsupported or invalid parameters. |
| protection_type = OPM_PROTECTION_TYPE_ACP; |
| if (SendSingleGetInfoRequest(monitor_index, video_output, |
| OPM_GET_ACTUAL_PROTECTION_LEVEL, |
| sizeof(protection_type), &protection_type)) { |
| return false; |
| } |
| if (SendSingleGetInfoRequest(monitor_index, video_output, |
| OPM_GET_ACTUAL_PROTECTION_LEVEL, 0, nullptr)) { |
| return false; |
| } |
| protection_type = OPM_PROTECTION_TYPE_HDCP; |
| if (!SendSingleGetInfoRequest(monitor_index, video_output, |
| OPM_GET_VIRTUAL_PROTECTION_LEVEL, |
| sizeof(protection_type), &protection_type)) { |
| return false; |
| } |
| protection_type = OPM_PROTECTION_TYPE_DPCP; |
| if (!SendSingleGetInfoRequest(monitor_index, video_output, |
| OPM_GET_VIRTUAL_PROTECTION_LEVEL, |
| sizeof(protection_type), &protection_type)) { |
| return false; |
| } |
| // These should fail as unsupported or invalid parameters. |
| protection_type = OPM_PROTECTION_TYPE_ACP; |
| if (SendSingleGetInfoRequest(monitor_index, video_output, |
| OPM_GET_VIRTUAL_PROTECTION_LEVEL, |
| sizeof(protection_type), &protection_type)) { |
| return false; |
| } |
| if (SendSingleGetInfoRequest(monitor_index, video_output, |
| OPM_GET_VIRTUAL_PROTECTION_LEVEL, 0, nullptr)) { |
| return false; |
| } |
| // This should fail with unsupported request. |
| if (SendSingleGetInfoRequest(monitor_index, video_output, OPM_GET_CODEC_INFO, |
| 0, nullptr)) { |
| return false; |
| } |
| return true; |
| } |
| |
| int RunTestsOnVideoOutput(uintptr_t monitor_index, |
| IOPMVideoOutput* video_output) { |
| if (!RunTestsOnVideoOutputStartInitialization(monitor_index, video_output)) |
| return SBOX_TEST_FIRST_ERROR; |
| |
| if (!RunTestsOnVideoOutputFinishInitialization(monitor_index, video_output)) |
| return SBOX_TEST_SECOND_ERROR; |
| |
| if (!RunTestsOnVideoOutputConfigure(monitor_index, video_output)) |
| return SBOX_TEST_THIRD_ERROR; |
| |
| if (!RunTestsOnVideoOutputGetInformation(monitor_index, video_output)) |
| return SBOX_TEST_FOURTH_ERROR; |
| |
| return SBOX_TEST_SUCCEEDED; |
| } |
| |
| SBOX_TESTS_COMMAND int CheckWin8OPMApis(int argc, wchar_t** argv) { |
| std::map<HMONITOR, base::string16> monitors = GetTestMonitors(); |
| for (const auto& monitor : monitors) { |
| ULONG output_count = 0; |
| IOPMVideoOutput** outputs = nullptr; |
| uintptr_t monitor_index = reinterpret_cast<uintptr_t>(monitor.first) & 0xF; |
| HRESULT hr = OPMGetVideoOutputsFromHMONITOR( |
| monitor.first, OPM_VOS_OPM_SEMANTICS, &output_count, &outputs); |
| if (monitor_index > 4) { |
| // These should fail because the certificate is too large. |
| if (SUCCEEDED(hr)) |
| return SBOX_TEST_FIRST_ERROR; |
| continue; |
| } |
| if (FAILED(hr)) |
| return SBOX_TEST_SECOND_ERROR; |
| if (output_count != monitor_index - 1) |
| return SBOX_TEST_THIRD_ERROR; |
| for (ULONG output_index = 0; output_index < output_count; ++output_index) { |
| int result = RunTestsOnVideoOutput(monitor_index, outputs[output_index]); |
| outputs[output_index]->Release(); |
| if (result != SBOX_TEST_SUCCEEDED) |
| return result; |
| } |
| ::CoTaskMemFree(outputs); |
| } |
| return SBOX_TEST_SUCCEEDED; |
| } |
| |
| // This test validates that setting the MITIGATION_WIN32K_DISABLE mitigation on |
| // the target process causes the launch to fail in process initialization. |
| // The test process itself links against user32/gdi32. |
| TEST(ProcessMitigationsTest, CheckWin8Win32KLockDownFailure) { |
| if (base::win::GetVersion() < base::win::VERSION_WIN8) |
| return; |
| |
| TestRunner runner; |
| sandbox::TargetPolicy* policy = runner.GetPolicy(); |
| |
| EXPECT_EQ(policy->SetProcessMitigations(MITIGATION_WIN32K_DISABLE), |
| SBOX_ALL_OK); |
| EXPECT_NE(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckWin8Lockdown")); |
| } |
| |
| // This test validates that setting the MITIGATION_WIN32K_DISABLE mitigation |
| // along with the policy to fake user32 and gdi32 initialization successfully |
| // launches the target process. |
| // The test process itself links against user32/gdi32. |
| TEST(ProcessMitigationsTest, CheckWin8Win32KLockDownSuccess) { |
| if (base::win::GetVersion() < base::win::VERSION_WIN8) |
| return; |
| |
| TestRunner runner; |
| sandbox::TargetPolicy* policy = runner.GetPolicy(); |
| ProcessMitigationsWin32KLockdownPolicy::SetOverrideForTestCallback( |
| FunctionOverrideForTest); |
| |
| EXPECT_EQ(policy->SetProcessMitigations(MITIGATION_WIN32K_DISABLE), |
| SBOX_ALL_OK); |
| EXPECT_EQ(policy->AddRule(sandbox::TargetPolicy::SUBSYS_WIN32K_LOCKDOWN, |
| sandbox::TargetPolicy::FAKE_USER_GDI_INIT, NULL), |
| sandbox::SBOX_ALL_OK); |
| EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckWin8Lockdown")); |
| EXPECT_NE(SBOX_TEST_SUCCEEDED, |
| runner.RunTest(L"CheckWin8MonitorsRedirection")); |
| EXPECT_NE(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckWin8MonitorInfo")); |
| EXPECT_NE(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckWin8OPMApis")); |
| } |
| |
| // This test validates the even though we're running under win32k lockdown |
| // we can use the IPC redirection to enumerate the list of monitors. |
| TEST(ProcessMitigationsTest, CheckWin8Win32KRedirection) { |
| if (base::win::GetVersion() < base::win::VERSION_WIN8) |
| return; |
| |
| TestRunner runner; |
| sandbox::TargetPolicy* policy = runner.GetPolicy(); |
| ProcessMitigationsWin32KLockdownPolicy::SetOverrideForTestCallback( |
| FunctionOverrideForTest); |
| |
| EXPECT_EQ(policy->SetProcessMitigations(MITIGATION_WIN32K_DISABLE), |
| SBOX_ALL_OK); |
| EXPECT_EQ(policy->AddRule(sandbox::TargetPolicy::SUBSYS_WIN32K_LOCKDOWN, |
| sandbox::TargetPolicy::IMPLEMENT_OPM_APIS, NULL), |
| sandbox::SBOX_ALL_OK); |
| policy->SetEnableOPMRedirection(); |
| EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckWin8Lockdown")); |
| EXPECT_EQ(SBOX_TEST_SUCCEEDED, |
| runner.RunTest(L"CheckWin8MonitorsRedirection")); |
| EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckWin8MonitorInfo")); |
| EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckWin8OPMApis")); |
| } |
| |
| //------------------------------------------------------------------------------ |
| // Disable non-system font loads (MITIGATION_NONSYSTEM_FONT_DISABLE) |
| // >= Win10 |
| //------------------------------------------------------------------------------ |
| |
| SBOX_TESTS_COMMAND int CheckWin10FontLockDown(int argc, wchar_t** argv) { |
| get_process_mitigation_policy = |
| reinterpret_cast<GetProcessMitigationPolicyFunction>(::GetProcAddress( |
| ::GetModuleHandleW(L"kernel32.dll"), "GetProcessMitigationPolicy")); |
| if (!get_process_mitigation_policy) |
| return SBOX_TEST_NOT_FOUND; |
| |
| if (!CheckWin10FontPolicy()) |
| return SBOX_TEST_FIRST_ERROR; |
| return SBOX_TEST_SUCCEEDED; |
| } |
| |
| SBOX_TESTS_COMMAND int CheckWin10FontLoad(int argc, wchar_t** argv) { |
| if (argc < 1) |
| return SBOX_TEST_INVALID_PARAMETER; |
| |
| HMODULE gdi_module = ::LoadLibraryW(L"gdi32.dll"); |
| if (!gdi_module) |
| return SBOX_TEST_NOT_FOUND; |
| |
| AddFontMemResourceExFunction add_font_mem_resource = |
| reinterpret_cast<AddFontMemResourceExFunction>( |
| ::GetProcAddress(gdi_module, "AddFontMemResourceEx")); |
| |
| RemoveFontMemResourceExFunction rem_font_mem_resource = |
| reinterpret_cast<RemoveFontMemResourceExFunction>( |
| ::GetProcAddress(gdi_module, "RemoveFontMemResourceEx")); |
| |
| if (!add_font_mem_resource || !rem_font_mem_resource) |
| return SBOX_TEST_NOT_FOUND; |
| |
| // Open font file passed in as an argument. |
| base::File file(base::FilePath(argv[0]), |
| base::File::FLAG_OPEN | base::File::FLAG_READ); |
| if (!file.IsValid()) |
| // Failed to open the font file passed in. |
| return SBOX_TEST_NOT_FOUND; |
| |
| std::vector<char> font_data; |
| int64_t len = file.GetLength(); |
| font_data.resize(len); |
| |
| int read = file.Read(0, &font_data[0], len); |
| file.Close(); |
| |
| if (read != len) |
| return SBOX_TEST_NOT_FOUND; |
| |
| DWORD font_count = 0; |
| HANDLE font_handle = add_font_mem_resource( |
| &font_data[0], static_cast<DWORD>(font_data.size()), NULL, &font_count); |
| |
| if (font_handle) { |
| rem_font_mem_resource(font_handle); |
| return SBOX_TEST_SUCCEEDED; |
| } |
| |
| return SBOX_TEST_FAILED; |
| } |
| |
| // This test validates that setting the MITIGATION_NON_SYSTEM_FONTS_DISABLE |
| // mitigation enables the setting on a process. |
| TEST(ProcessMitigationsTest, CheckWin10NonSystemFontLockDownPolicySuccess) { |
| if (base::win::GetVersion() < base::win::VERSION_WIN10) |
| return; |
| |
| TestRunner runner; |
| sandbox::TargetPolicy* policy = runner.GetPolicy(); |
| |
| EXPECT_EQ(policy->SetProcessMitigations(MITIGATION_NONSYSTEM_FONT_DISABLE), |
| SBOX_ALL_OK); |
| EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckWin10FontLockDown")); |
| } |
| |
| // This test validates that we can load a non-system font |
| // if the MITIGATION_NON_SYSTEM_FONTS_DISABLE |
| // mitigation is NOT set. |
| TEST(ProcessMitigationsTest, CheckWin10NonSystemFontLockDownLoadSuccess) { |
| if (base::win::GetVersion() < base::win::VERSION_WIN10) |
| return; |
| |
| base::FilePath font_path; |
| EXPECT_TRUE(base::PathService::Get(base::DIR_WINDOWS_FONTS, &font_path)); |
| // Arial font should always be available |
| font_path = font_path.Append(L"arial.ttf"); |
| |
| TestRunner runner; |
| EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, |
| font_path.value().c_str())); |
| |
| std::wstring test_command = L"CheckWin10FontLoad \""; |
| test_command += font_path.value().c_str(); |
| test_command += L"\""; |
| EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(test_command.c_str())); |
| } |
| |
| // This test validates that setting the MITIGATION_NON_SYSTEM_FONTS_DISABLE |
| // mitigation prevents the loading of a non-system font. |
| TEST(ProcessMitigationsTest, CheckWin10NonSystemFontLockDownLoadFailure) { |
| if (base::win::GetVersion() < base::win::VERSION_WIN10) |
| return; |
| |
| base::FilePath font_path; |
| EXPECT_TRUE(base::PathService::Get(base::DIR_WINDOWS_FONTS, &font_path)); |
| // Arial font should always be available |
| font_path = font_path.Append(L"arial.ttf"); |
| |
| TestRunner runner; |
| sandbox::TargetPolicy* policy = runner.GetPolicy(); |
| EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, |
| font_path.value().c_str())); |
| |
| // Turn on the non-system font disable mitigation. |
| EXPECT_EQ(policy->SetProcessMitigations(MITIGATION_NONSYSTEM_FONT_DISABLE), |
| SBOX_ALL_OK); |
| |
| std::wstring test_command = L"CheckWin10FontLoad \""; |
| test_command += font_path.value().c_str(); |
| test_command += L"\""; |
| |
| EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(test_command.c_str())); |
| } |
| |
| //------------------------------------------------------------------------------ |
| // Disable image load from remote devices (MITIGATION_IMAGE_LOAD_NO_REMOTE). |
| // >= Win10_TH2 |
| //------------------------------------------------------------------------------ |
| |
| SBOX_TESTS_COMMAND int CheckWin10ImageLoadNoRemote(int argc, wchar_t** argv) { |
| get_process_mitigation_policy = |
| reinterpret_cast<GetProcessMitigationPolicyFunction>(::GetProcAddress( |
| ::GetModuleHandleW(L"kernel32.dll"), "GetProcessMitigationPolicy")); |
| if (!get_process_mitigation_policy) |
| return SBOX_TEST_NOT_FOUND; |
| |
| if (!CheckWin10ImageLoadNoRemotePolicy()) |
| return SBOX_TEST_FIRST_ERROR; |
| return SBOX_TEST_SUCCEEDED; |
| } |
| |
| // This test validates that setting the MITIGATION_IMAGE_LOAD_NO_REMOTE |
| // mitigation enables the setting on a process. |
| TEST(ProcessMitigationsTest, CheckWin10ImageLoadNoRemotePolicySuccess) { |
| if (base::win::GetVersion() < base::win::VERSION_WIN10_TH2) |
| return; |
| |
| TestRunner runner; |
| sandbox::TargetPolicy* policy = runner.GetPolicy(); |
| |
| EXPECT_EQ( |
| policy->SetDelayedProcessMitigations(MITIGATION_IMAGE_LOAD_NO_REMOTE), |
| SBOX_ALL_OK); |
| EXPECT_EQ(SBOX_TEST_SUCCEEDED, |
| runner.RunTest(L"CheckWin10ImageLoadNoRemote")); |
| } |
| |
| // This test validates that we CAN create a new process from |
| // a remote UNC device, if the MITIGATION_IMAGE_LOAD_NO_REMOTE |
| // mitigation is NOT set. |
| // |
| // DISABLED for automated testing bots. Enable for manual testing. |
| TEST(ProcessMitigationsTest, DISABLED_CheckWin10ImageLoadNoRemoteSuccess) { |
| if (base::win::GetVersion() < base::win::VERSION_WIN10_TH2) |
| return; |
| |
| TestWin10ImageLoadRemote(true); |
| } |
| |
| // This test validates that setting the MITIGATION_IMAGE_LOAD_NO_REMOTE |
| // mitigation prevents creating a new process from a remote |
| // UNC device. |
| // |
| // DISABLED for automated testing bots. Enable for manual testing. |
| TEST(ProcessMitigationsTest, DISABLED_CheckWin10ImageLoadNoRemoteFailure) { |
| if (base::win::GetVersion() < base::win::VERSION_WIN10_TH2) |
| return; |
| |
| TestWin10ImageLoadRemote(false); |
| } |
| |
| //------------------------------------------------------------------------------ |
| // Disable image load when "mandatory low label" (integrity level). |
| // (MITIGATION_IMAGE_LOAD_NO_LOW_LABEL) |
| // >= Win10_TH2 |
| //------------------------------------------------------------------------------ |
| |
| SBOX_TESTS_COMMAND int CheckWin10ImageLoadNoLowLabel(int argc, wchar_t** argv) { |
| get_process_mitigation_policy = |
| reinterpret_cast<GetProcessMitigationPolicyFunction>(::GetProcAddress( |
| ::GetModuleHandleW(L"kernel32.dll"), "GetProcessMitigationPolicy")); |
| if (!get_process_mitigation_policy) |
| return SBOX_TEST_NOT_FOUND; |
| |
| if (!CheckWin10ImageLoadNoLowLabelPolicy()) |
| return SBOX_TEST_FIRST_ERROR; |
| return SBOX_TEST_SUCCEEDED; |
| } |
| |
| // This test validates that setting the MITIGATION_IMAGE_LOAD_NO_LOW_LABEL |
| // mitigation enables the setting on a process. |
| TEST(ProcessMitigationsTest, CheckWin10ImageLoadNoLowLabelPolicySuccess) { |
| if (base::win::GetVersion() < base::win::VERSION_WIN10_TH2) |
| return; |
| |
| TestRunner runner; |
| sandbox::TargetPolicy* policy = runner.GetPolicy(); |
| |
| EXPECT_EQ( |
| policy->SetDelayedProcessMitigations(MITIGATION_IMAGE_LOAD_NO_LOW_LABEL), |
| SBOX_ALL_OK); |
| EXPECT_EQ(SBOX_TEST_SUCCEEDED, |
| runner.RunTest(L"CheckWin10ImageLoadNoLowLabel")); |
| } |
| |
| // This test validates that we CAN create a new process with |
| // low mandatory label (IL), if the MITIGATION_IMAGE_LOAD_NO_LOW_LABEL |
| // mitigation is NOT set. |
| TEST(ProcessMitigationsTest, CheckWin10ImageLoadNoLowLabelSuccess) { |
| if (base::win::GetVersion() < base::win::VERSION_WIN10_TH2) |
| return; |
| |
| TestWin10ImageLoadLowLabel(true); |
| } |
| |
| // This test validates that setting the MITIGATION_IMAGE_LOAD_NO_LOW_LABEL |
| // mitigation prevents creating a new process with low mandatory label (IL). |
| TEST(ProcessMitigationsTest, CheckWin10ImageLoadNoLowLabelFailure) { |
| if (base::win::GetVersion() < base::win::VERSION_WIN10_TH2) |
| return; |
| |
| TestWin10ImageLoadLowLabel(false); |
| } |
| |
| //------------------------------------------------------------------------------ |
| // Disable child process creation. |
| // - JobLevel <= JOB_LIMITED_USER (on < WIN10_TH2). |
| // - JobLevel <= JOB_LIMITED_USER which also triggers setting |
| // PROC_THREAD_ATTRIBUTE_CHILD_PROCESS_POLICY to |
| // PROCESS_CREATION_CHILD_PROCESS_RESTRICTED in |
| // BrokerServicesBase::SpawnTarget (on >= WIN10_TH2). |
| //------------------------------------------------------------------------------ |
| |
| // This test validates that we can spawn a child process if |
| // MITIGATION_CHILD_PROCESS_CREATION_RESTRICTED mitigation is |
| // not set. |
| TEST(ProcessMitigationsTest, CheckChildProcessSuccess) { |
| TestRunner runner; |
| sandbox::TargetPolicy* policy = runner.GetPolicy(); |
| |
| // Set a policy that would normally allow for process creation. |
| policy->SetJobLevel(JOB_INTERACTIVE, 0); |
| policy->SetTokenLevel(USER_UNPROTECTED, USER_UNPROTECTED); |
| runner.SetDisableCsrss(false); |
| |
| base::FilePath cmd; |
| EXPECT_TRUE(base::PathService::Get(base::DIR_SYSTEM, &cmd)); |
| cmd = cmd.Append(L"calc.exe"); |
| |
| std::wstring test_command = L"TestChildProcess "; |
| test_command += cmd.value().c_str(); |
| |
| EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(test_command.c_str())); |
| } |
| |
| // This test validates that setting the |
| // MITIGATION_CHILD_PROCESS_CREATION_RESTRICTED mitigation prevents |
| // the spawning of child processes. |
| TEST(ProcessMitigationsTest, CheckChildProcessFailure) { |
| TestRunner runner; |
| sandbox::TargetPolicy* policy = runner.GetPolicy(); |
| |
| // Now set the job level to be <= JOB_LIMITED_USER |
| // and ensure we can no longer create a child process. |
| policy->SetJobLevel(JOB_LIMITED_USER, 0); |
| policy->SetTokenLevel(USER_UNPROTECTED, USER_UNPROTECTED); |
| runner.SetDisableCsrss(false); |
| |
| base::FilePath cmd; |
| EXPECT_TRUE(base::PathService::Get(base::DIR_SYSTEM, &cmd)); |
| cmd = cmd.Append(L"calc.exe"); |
| |
| std::wstring test_command = L"TestChildProcess "; |
| test_command += cmd.value().c_str(); |
| |
| EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(test_command.c_str())); |
| } |
| |
| // This test validates that when the sandboxed target within a job spawns a |
| // child process and the target process exits abnormally, the broker correctly |
| // handles the JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS message. |
| // Because this involves spawning a child process from the target process and is |
| // very similar to the above CheckChildProcess* tests, this test is here rather |
| // than elsewhere closer to the other Job tests. |
| TEST(ProcessMitigationsTest, CheckChildProcessAbnormalExit) { |
| TestRunner runner; |
| sandbox::TargetPolicy* policy = runner.GetPolicy(); |
| |
| // Set a policy that would normally allow for process creation. |
| policy->SetJobLevel(JOB_INTERACTIVE, 0); |
| policy->SetTokenLevel(USER_UNPROTECTED, USER_UNPROTECTED); |
| runner.SetDisableCsrss(false); |
| |
| base::FilePath cmd; |
| EXPECT_TRUE(base::PathService::Get(base::DIR_SYSTEM, &cmd)); |
| cmd = cmd.Append(L"calc.exe"); |
| |
| std::wstring test_command(base::StringPrintf(L"TestChildProcess %ls 0x%08X", |
| cmd.value().c_str(), |
| STATUS_ACCESS_VIOLATION)); |
| |
| EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(test_command.c_str())); |
| } |
| |
| } // namespace sandbox |