blob: 26744a51e30548a7500569c048411dd3a7f9c28f [file] [log] [blame]
// Copyright 2014 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_mitigations_win32k_interception.h"
#include <algorithm>
#include "base/numerics/safe_conversions.h"
#include "base/numerics/safe_math.h"
#include "base/win/scoped_handle.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"
namespace sandbox {
namespace {
// Implement a simple shared memory class as we can't use the base one.
class ScopedSharedMemory {
public:
ScopedSharedMemory(uint32_t size) : memory_(nullptr) {
handle_.Set(::CreateFileMapping(INVALID_HANDLE_VALUE, nullptr,
PAGE_READWRITE | SEC_COMMIT, 0, size,
nullptr));
if (handle_.IsValid()) {
memory_ = ::MapViewOfFile(handle_.Get(), FILE_MAP_READ | FILE_MAP_WRITE,
0, 0, size);
}
}
~ScopedSharedMemory() {
if (memory_)
::UnmapViewOfFile(memory_);
}
void* handle() { return handle_.Get(); }
void* memory() { return memory_; }
bool IsValid() { return handle_.IsValid() && memory_; }
private:
base::win::ScopedHandle handle_;
void* memory_;
};
void UnicodeStringToString(PUNICODE_STRING unicode_string,
base::string16* result) {
*result = base::string16(
unicode_string->Buffer,
unicode_string->Buffer +
(unicode_string->Length / sizeof(unicode_string->Buffer[0])));
}
} // namespace
BOOL WINAPI TargetGdiDllInitialize(
GdiDllInitializeFunction orig_gdi_dll_initialize,
HANDLE dll,
DWORD reason) {
return TRUE;
}
HGDIOBJ WINAPI TargetGetStockObject(
GetStockObjectFunction orig_get_stock_object,
int object) {
return reinterpret_cast<HGDIOBJ>(NULL);
}
ATOM WINAPI TargetRegisterClassW(
RegisterClassWFunction orig_register_class_function,
const WNDCLASS* wnd_class) {
return TRUE;
}
BOOL WINAPI TargetEnumDisplayMonitors(EnumDisplayMonitorsFunction,
HDC hdc,
LPCRECT lprcClip,
MONITORENUMPROC lpfnEnum,
LPARAM dwData) {
if (!lpfnEnum || hdc || lprcClip) {
return FALSE;
}
// We don't trust that the IPC can work this early.
if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
return FALSE;
void* ipc_memory = GetGlobalIPCMemory();
if (ipc_memory == NULL)
return FALSE;
CrossCallReturn answer = {0};
answer.nt_status = 0;
EnumMonitorsResult result = {};
InOutCountedBuffer result_buffer(&result, sizeof(result));
SharedMemIPCClient ipc(ipc_memory);
ResultCode code =
CrossCall(ipc, IPC_USER_ENUMDISPLAYMONITORS_TAG, result_buffer, &answer);
if (code != SBOX_ALL_OK) {
return FALSE;
}
if (answer.win32_result) {
return FALSE;
}
if (result.monitor_count > kMaxEnumMonitors) {
return FALSE;
}
for (uint32_t monitor_pos = 0; monitor_pos < result.monitor_count;
++monitor_pos) {
BOOL continue_enum =
lpfnEnum(result.monitors[monitor_pos], nullptr, nullptr, dwData);
if (!continue_enum)
return FALSE;
}
return TRUE;
}
BOOL WINAPI TargetEnumDisplayDevicesA(EnumDisplayDevicesAFunction,
LPCSTR lpDevice,
DWORD iDevNum,
PDISPLAY_DEVICEA lpDisplayDevice,
DWORD dwFlags) {
return FALSE;
}
static BOOL CallMonitorInfo(HMONITOR monitor,
MONITORINFOEXW* monitor_info_ptr) {
// We don't trust that the IPC can work this early.
if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
return FALSE;
void* ipc_memory = GetGlobalIPCMemory();
if (ipc_memory == NULL)
return FALSE;
CrossCallReturn answer = {};
SharedMemIPCClient ipc(ipc_memory);
InOutCountedBuffer buffer(monitor_info_ptr, sizeof(*monitor_info_ptr));
ResultCode code = CrossCall(ipc, IPC_USER_GETMONITORINFO_TAG,
static_cast<void*>(monitor), buffer, &answer);
if (code != SBOX_ALL_OK) {
return FALSE;
}
if (answer.win32_result != ERROR_SUCCESS)
return FALSE;
return TRUE;
}
BOOL WINAPI TargetGetMonitorInfoA(GetMonitorInfoAFunction,
HMONITOR monitor,
MONITORINFO* monitor_info_ptr) {
if (!monitor_info_ptr)
return FALSE;
DWORD size = monitor_info_ptr->cbSize;
if (size != sizeof(MONITORINFO) && size != sizeof(MONITORINFOEXA)) {
return FALSE;
}
MONITORINFOEXW monitor_info_tmp = {};
monitor_info_tmp.cbSize = sizeof(monitor_info_tmp);
BOOL success = CallMonitorInfo(monitor, &monitor_info_tmp);
if (!success)
return FALSE;
memcpy(monitor_info_ptr, &monitor_info_tmp, sizeof(*monitor_info_ptr));
if (size == sizeof(MONITORINFOEXA)) {
MONITORINFOEXA* monitor_info_exa =
reinterpret_cast<MONITORINFOEXA*>(monitor_info_ptr);
if (!::WideCharToMultiByte(CP_ACP, 0, monitor_info_tmp.szDevice, -1,
monitor_info_exa->szDevice,
sizeof(monitor_info_exa->szDevice), nullptr,
nullptr)) {
return FALSE;
}
}
return TRUE;
}
BOOL WINAPI TargetGetMonitorInfoW(GetMonitorInfoWFunction,
HMONITOR monitor,
LPMONITORINFO monitor_info_ptr) {
if (!monitor_info_ptr)
return FALSE;
DWORD size = monitor_info_ptr->cbSize;
if (size != sizeof(MONITORINFO) && size != sizeof(MONITORINFOEXW)) {
return FALSE;
}
MONITORINFOEXW monitor_info_tmp = {};
monitor_info_tmp.cbSize = sizeof(monitor_info_tmp);
BOOL success = CallMonitorInfo(monitor, &monitor_info_tmp);
if (!success)
return FALSE;
memcpy(monitor_info_ptr, &monitor_info_tmp, size);
return TRUE;
}
static NTSTATUS GetCertificateCommon(
PUNICODE_STRING device_name,
OPM_PROTECTED_OUTPUT_HANDLE protected_output,
DXGKMDT_CERTIFICATE_TYPE certificate_type,
BYTE* certificate,
ULONG certificate_size) {
// Don't support arbitrarily large certificate buffers.
if (certificate_size > kProtectedVideoOutputSectionSize)
return STATUS_INVALID_PARAMETER;
if (certificate_type != DXGKMDT_OPM_CERTIFICATE)
return STATUS_INVALID_PARAMETER;
if (device_name && device_name->Length == 0)
return STATUS_INVALID_PARAMETER;
if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
return STATUS_ACCESS_DENIED;
void* ipc_memory = GetGlobalIPCMemory();
if (ipc_memory == NULL)
return STATUS_ACCESS_DENIED;
ScopedSharedMemory buffer(certificate_size);
if (!buffer.IsValid())
return STATUS_INVALID_PARAMETER;
base::string16 device_name_str;
void* protected_output_handle = nullptr;
if (device_name) {
if (device_name->Length == 0)
return STATUS_INVALID_PARAMETER;
UnicodeStringToString(device_name, &device_name_str);
} else {
protected_output_handle = protected_output;
}
CrossCallReturn answer = {};
SharedMemIPCClient ipc(ipc_memory);
ResultCode code =
CrossCall(ipc, IPC_GDI_GETCERTIFICATE_TAG, device_name_str.c_str(),
protected_output_handle, buffer.handle(),
static_cast<uint32_t>(certificate_size), &answer);
if (code != SBOX_ALL_OK) {
return STATUS_ACCESS_DENIED;
}
if (!answer.nt_status)
memcpy(certificate, buffer.memory(), certificate_size);
return answer.nt_status;
}
NTSTATUS WINAPI TargetGetCertificate(GetCertificateFunction,
PUNICODE_STRING device_name,
DXGKMDT_CERTIFICATE_TYPE certificate_type,
BYTE* certificate,
ULONG certificate_size) {
return GetCertificateCommon(device_name, nullptr, certificate_type,
certificate, certificate_size);
}
static NTSTATUS GetCertificateSizeCommon(
PUNICODE_STRING device_name,
OPM_PROTECTED_OUTPUT_HANDLE protected_output,
DXGKMDT_CERTIFICATE_TYPE certificate_type,
ULONG* certificate_length) {
if (certificate_type != DXGKMDT_OPM_CERTIFICATE)
return STATUS_INVALID_PARAMETER;
if (device_name && device_name->Length == 0)
return STATUS_INVALID_PARAMETER;
if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
return STATUS_ACCESS_DENIED;
void* ipc_memory = GetGlobalIPCMemory();
if (ipc_memory == NULL)
return STATUS_ACCESS_DENIED;
CrossCallReturn answer = {};
SharedMemIPCClient ipc(ipc_memory);
base::string16 device_name_str;
void* protected_output_handle = nullptr;
if (device_name) {
UnicodeStringToString(device_name, &device_name_str);
} else {
protected_output_handle = protected_output;
}
ResultCode code =
CrossCall(ipc, IPC_GDI_GETCERTIFICATESIZE_TAG, device_name_str.c_str(),
protected_output_handle, &answer);
if (code != SBOX_ALL_OK) {
return STATUS_ACCESS_DENIED;
}
if (!answer.nt_status)
*certificate_length = answer.extended[0].unsigned_int;
return answer.nt_status;
}
NTSTATUS WINAPI
TargetGetCertificateSize(GetCertificateSizeFunction,
PUNICODE_STRING device_name,
DXGKMDT_CERTIFICATE_TYPE certificate_type,
ULONG* certificate_length) {
return GetCertificateSizeCommon(device_name, nullptr, certificate_type,
certificate_length);
}
SANDBOX_INTERCEPT NTSTATUS WINAPI TargetGetCertificateByHandle(
GetCertificateByHandleFunction orig_get_certificate_function,
OPM_PROTECTED_OUTPUT_HANDLE protected_output,
DXGKMDT_CERTIFICATE_TYPE certificate_type,
BYTE* certificate,
ULONG certificate_length) {
return GetCertificateCommon(nullptr, protected_output, certificate_type,
certificate, certificate_length);
}
SANDBOX_INTERCEPT NTSTATUS WINAPI TargetGetCertificateSizeByHandle(
GetCertificateSizeByHandleFunction orig_get_certificate_size_function,
OPM_PROTECTED_OUTPUT_HANDLE protected_output,
DXGKMDT_CERTIFICATE_TYPE certificate_type,
ULONG* certificate_length) {
return GetCertificateSizeCommon(nullptr, protected_output, certificate_type,
certificate_length);
}
NTSTATUS WINAPI
TargetDestroyOPMProtectedOutput(DestroyOPMProtectedOutputFunction,
OPM_PROTECTED_OUTPUT_HANDLE protected_output) {
if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
return STATUS_ACCESS_DENIED;
void* ipc_memory = GetGlobalIPCMemory();
if (ipc_memory == NULL)
return STATUS_ACCESS_DENIED;
CrossCallReturn answer = {};
SharedMemIPCClient ipc(ipc_memory);
ResultCode code = CrossCall(ipc, IPC_GDI_DESTROYOPMPROTECTEDOUTPUT_TAG,
static_cast<void*>(protected_output), &answer);
if (code != SBOX_ALL_OK) {
return STATUS_ACCESS_DENIED;
}
return answer.nt_status;
}
NTSTATUS WINAPI TargetConfigureOPMProtectedOutput(
ConfigureOPMProtectedOutputFunction,
OPM_PROTECTED_OUTPUT_HANDLE protected_output,
const DXGKMDT_OPM_CONFIGURE_PARAMETERS* parameters,
ULONG additional_parameters_size,
const BYTE* additional_parameters) {
// Don't support additional parameters.
if (additional_parameters_size > 0)
return STATUS_INVALID_PARAMETER;
if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
return STATUS_ACCESS_DENIED;
void* ipc_memory = GetGlobalIPCMemory();
if (ipc_memory == NULL)
return STATUS_ACCESS_DENIED;
ScopedSharedMemory buffer(sizeof(*parameters));
if (!buffer.IsValid())
return STATUS_INVALID_PARAMETER;
memcpy(buffer.memory(), parameters, sizeof(*parameters));
CrossCallReturn answer = {};
SharedMemIPCClient ipc(ipc_memory);
ResultCode code =
CrossCall(ipc, IPC_GDI_CONFIGUREOPMPROTECTEDOUTPUT_TAG,
static_cast<void*>(protected_output), buffer.handle(), &answer);
if (code != SBOX_ALL_OK) {
return STATUS_ACCESS_DENIED;
}
return answer.nt_status;
}
NTSTATUS WINAPI TargetGetOPMInformation(
GetOPMInformationFunction,
OPM_PROTECTED_OUTPUT_HANDLE protected_output,
const DXGKMDT_OPM_GET_INFO_PARAMETERS* parameters,
DXGKMDT_OPM_REQUESTED_INFORMATION* requested_information) {
size_t max_size =
std::max(sizeof(*parameters), sizeof(*requested_information));
if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
return STATUS_ACCESS_DENIED;
void* ipc_memory = GetGlobalIPCMemory();
if (ipc_memory == NULL)
return STATUS_ACCESS_DENIED;
ScopedSharedMemory buffer(base::checked_cast<uint32_t>(max_size));
if (!buffer.IsValid())
return STATUS_INVALID_PARAMETER;
memcpy(buffer.memory(), parameters, sizeof(*parameters));
CrossCallReturn answer = {};
SharedMemIPCClient ipc(ipc_memory);
ResultCode code =
CrossCall(ipc, IPC_GDI_GETOPMINFORMATION_TAG,
static_cast<void*>(protected_output), buffer.handle(), &answer);
if (code != SBOX_ALL_OK)
return STATUS_ACCESS_DENIED;
if (!answer.nt_status) {
memcpy(requested_information, buffer.memory(),
sizeof(*requested_information));
}
return answer.nt_status;
}
NTSTATUS WINAPI
TargetGetOPMRandomNumber(GetOPMRandomNumberFunction,
OPM_PROTECTED_OUTPUT_HANDLE protected_output,
DXGKMDT_OPM_RANDOM_NUMBER* random_number) {
if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
return STATUS_ACCESS_DENIED;
void* ipc_memory = GetGlobalIPCMemory();
if (ipc_memory == NULL)
return STATUS_ACCESS_DENIED;
CrossCallReturn answer = {};
SharedMemIPCClient ipc(ipc_memory);
InOutCountedBuffer buffer(random_number, sizeof(*random_number));
ResultCode code =
CrossCall(ipc, IPC_GDI_GETOPMRANDOMNUMBER_TAG,
static_cast<void*>(protected_output), buffer, &answer);
if (code != SBOX_ALL_OK)
return STATUS_ACCESS_DENIED;
return answer.nt_status;
}
NTSTATUS WINAPI TargetGetSuggestedOPMProtectedOutputArraySize(
GetSuggestedOPMProtectedOutputArraySizeFunction,
PUNICODE_STRING device_name,
DWORD* suggested_output_size) {
if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
return STATUS_ACCESS_DENIED;
void* ipc_memory = GetGlobalIPCMemory();
if (ipc_memory == NULL)
return STATUS_ACCESS_DENIED;
CrossCallReturn answer = {};
SharedMemIPCClient ipc(ipc_memory);
base::string16 device_name_str;
UnicodeStringToString(device_name, &device_name_str);
ResultCode code =
CrossCall(ipc, IPC_GDI_GETSUGGESTEDOPMPROTECTEDOUTPUTARRAYSIZE_TAG,
device_name_str.c_str(), &answer);
if (code != SBOX_ALL_OK)
return STATUS_ACCESS_DENIED;
if (!answer.nt_status)
*suggested_output_size = answer.extended[0].unsigned_int;
return answer.nt_status;
}
NTSTATUS WINAPI TargetSetOPMSigningKeyAndSequenceNumbers(
SetOPMSigningKeyAndSequenceNumbersFunction,
OPM_PROTECTED_OUTPUT_HANDLE protected_output,
const DXGKMDT_OPM_ENCRYPTED_PARAMETERS* parameters) {
if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
return STATUS_ACCESS_DENIED;
void* ipc_memory = GetGlobalIPCMemory();
if (ipc_memory == NULL)
return STATUS_ACCESS_DENIED;
DXGKMDT_OPM_ENCRYPTED_PARAMETERS temp_parameters = *parameters;
CrossCallReturn answer = {};
SharedMemIPCClient ipc(ipc_memory);
InOutCountedBuffer buffer(&temp_parameters, sizeof(temp_parameters));
ResultCode code =
CrossCall(ipc, IPC_GDI_SETOPMSIGNINGKEYANDSEQUENCENUMBERS_TAG,
static_cast<void*>(protected_output), buffer, &answer);
if (code != SBOX_ALL_OK)
return STATUS_ACCESS_DENIED;
return answer.nt_status;
}
NTSTATUS WINAPI
TargetCreateOPMProtectedOutputs(CreateOPMProtectedOutputsFunction,
PUNICODE_STRING device_name,
DXGKMDT_OPM_VIDEO_OUTPUT_SEMANTICS vos,
DWORD outputs_array_size,
DWORD* output_size,
OPM_PROTECTED_OUTPUT_HANDLE* outputs_array) {
if (vos != DXGKMDT_OPM_VOS_OPM_SEMANTICS)
return STATUS_INVALID_PARAMETER;
if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
return STATUS_ACCESS_DENIED;
void* ipc_memory = GetGlobalIPCMemory();
if (ipc_memory == NULL)
return STATUS_ACCESS_DENIED;
CrossCallReturn answer = {};
SharedMemIPCClient ipc(ipc_memory);
base::CheckedNumeric<uint32_t> array_size = outputs_array_size;
array_size *= sizeof(HANDLE);
if (!array_size.IsValid())
return STATUS_INVALID_PARAMETER;
InOutCountedBuffer buffer(outputs_array, array_size.ValueOrDie());
base::string16 device_name_str;
UnicodeStringToString(device_name, &device_name_str);
ResultCode code = CrossCall(ipc, IPC_GDI_CREATEOPMPROTECTEDOUTPUTS_TAG,
device_name_str.c_str(), buffer, &answer);
if (code != SBOX_ALL_OK)
return STATUS_ACCESS_DENIED;
if (!answer.nt_status)
*output_size = answer.extended[0].unsigned_int;
return answer.nt_status;
}
} // namespace sandbox