blob: 6d12d828dba90f4c25b0806f900d603a12c59643 [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 <stddef.h>
#include "sandbox/win/src/process_mitigations_win32k_interception.h"
#include "sandbox/win/src/process_mitigations_win32k_policy.h"
namespace sandbox {
namespace {
// Define GUIDs for OPM APIs
const GUID DXGKMDT_OPM_GET_CONNECTOR_TYPE = {
0x81d0bfd5,
0x6afe,
0x48c2,
{0x99, 0xc0, 0x95, 0xa0, 0x8f, 0x97, 0xc5, 0xda}};
const GUID DXGKMDT_OPM_GET_SUPPORTED_PROTECTION_TYPES = {
0x38f2a801,
0x9a6c,
0x48bb,
{0x91, 0x07, 0xb6, 0x69, 0x6e, 0x6f, 0x17, 0x97}};
const GUID DXGKMDT_OPM_GET_VIRTUAL_PROTECTION_LEVEL = {
0xb2075857,
0x3eda,
0x4d5d,
{0x88, 0xdb, 0x74, 0x8f, 0x8c, 0x1a, 0x05, 0x49}};
const GUID DXGKMDT_OPM_GET_ACTUAL_PROTECTION_LEVEL = {
0x1957210a,
0x7766,
0x452a,
{0xb9, 0x9a, 0xd2, 0x7a, 0xed, 0x54, 0xf0, 0x3a}};
const GUID DXGKMDT_OPM_SET_PROTECTION_LEVEL = {
0x9bb9327c,
0x4eb5,
0x4727,
{0x9f, 0x00, 0xb4, 0x2b, 0x09, 0x19, 0xc0, 0xda}};
void StringToUnicodeString(PUNICODE_STRING unicode_string,
const base::string16& device_name) {
static RtlInitUnicodeStringFunction RtlInitUnicodeString;
if (!RtlInitUnicodeString) {
HMODULE ntdll = ::GetModuleHandle(kNtdllName);
RtlInitUnicodeString = reinterpret_cast<RtlInitUnicodeStringFunction>(
GetProcAddress(ntdll, "RtlInitUnicodeString"));
}
RtlInitUnicodeString(unicode_string, device_name.c_str());
}
struct MonitorListState {
HMONITOR* monitor_list;
uint32_t monitor_list_size;
uint32_t monitor_list_pos;
};
BOOL CALLBACK DisplayMonitorEnumProc(HMONITOR monitor,
HDC hdc_monitor,
LPRECT rect_monitor,
LPARAM data) {
MonitorListState* state = reinterpret_cast<MonitorListState*>(data);
if (state->monitor_list_pos >= state->monitor_list_size)
return FALSE;
state->monitor_list[state->monitor_list_pos++] = monitor;
return TRUE;
}
template <typename T>
T GetExportedFunc(const wchar_t* libname, const char* name) {
OverrideForTestFunction test_override =
ProcessMitigationsWin32KLockdownPolicy::GetOverrideForTestCallback();
if (test_override)
return reinterpret_cast<T>(test_override(name));
static T func = nullptr;
if (!func) {
func =
reinterpret_cast<T>(::GetProcAddress(::GetModuleHandle(libname), name));
DCHECK(!!func);
}
return func;
}
#define GDIFUNC(name) GetExportedFunc<name##Function>(L"gdi32.dll", #name)
#define USERFUNC(name) GetExportedFunc<name##Function>(L"user32.dll", #name)
struct ValidateMonitorParams {
HMONITOR monitor;
base::string16 device_name;
bool result;
};
bool GetMonitorDeviceName(HMONITOR monitor, base::string16* device_name) {
MONITORINFOEXW monitor_info = {};
monitor_info.cbSize = sizeof(monitor_info);
if (!USERFUNC(GetMonitorInfoW)(monitor, &monitor_info))
return false;
if (monitor_info.szDevice[CCHDEVICENAME - 1] != 0)
return false;
*device_name = monitor_info.szDevice;
return true;
}
BOOL CALLBACK ValidateMonitorEnumProc(HMONITOR monitor,
HDC,
LPRECT,
LPARAM data) {
ValidateMonitorParams* valid_params =
reinterpret_cast<ValidateMonitorParams*>(data);
base::string16 device_name;
bool result = false;
if (valid_params->device_name.empty()) {
result = monitor == valid_params->monitor;
} else if (GetMonitorDeviceName(monitor, &device_name)) {
result = device_name == valid_params->device_name;
}
valid_params->result = result;
if (!result)
return TRUE;
return FALSE;
}
bool IsValidMonitorOrDeviceName(HMONITOR monitor, const wchar_t* device_name) {
ValidateMonitorParams params = {};
params.monitor = monitor;
if (device_name)
params.device_name = device_name;
USERFUNC(EnumDisplayMonitors)
(nullptr, nullptr, ValidateMonitorEnumProc,
reinterpret_cast<LPARAM>(&params));
return params.result;
}
} // namespace
OverrideForTestFunction
ProcessMitigationsWin32KLockdownPolicy::override_callback_;
bool ProcessMitigationsWin32KLockdownPolicy::GenerateRules(
const wchar_t* name,
TargetPolicy::Semantics semantics,
LowLevelPolicy* policy) {
PolicyRule rule(FAKE_SUCCESS);
if (!policy->AddRule(IPC_GDI_GDIDLLINITIALIZE_TAG, &rule))
return false;
if (!policy->AddRule(IPC_GDI_GETSTOCKOBJECT_TAG, &rule))
return false;
if (!policy->AddRule(IPC_USER_REGISTERCLASSW_TAG, &rule))
return false;
if (semantics != TargetPolicy::IMPLEMENT_OPM_APIS)
return true;
if (!policy->AddRule(IPC_USER_ENUMDISPLAYMONITORS_TAG, &rule))
return false;
if (!policy->AddRule(IPC_USER_ENUMDISPLAYDEVICES_TAG, &rule))
return false;
if (!policy->AddRule(IPC_USER_GETMONITORINFO_TAG, &rule))
return false;
if (!policy->AddRule(IPC_GDI_CREATEOPMPROTECTEDOUTPUTS_TAG, &rule))
return false;
if (!policy->AddRule(IPC_GDI_GETCERTIFICATE_TAG, &rule))
return false;
if (!policy->AddRule(IPC_GDI_GETCERTIFICATESIZE_TAG, &rule))
return false;
if (!policy->AddRule(IPC_GDI_DESTROYOPMPROTECTEDOUTPUT_TAG, &rule))
return false;
if (!policy->AddRule(IPC_GDI_CONFIGUREOPMPROTECTEDOUTPUT_TAG, &rule))
return false;
if (!policy->AddRule(IPC_GDI_GETOPMINFORMATION_TAG, &rule))
return false;
if (!policy->AddRule(IPC_GDI_GETOPMRANDOMNUMBER_TAG, &rule))
return false;
if (!policy->AddRule(IPC_GDI_GETSUGGESTEDOPMPROTECTEDOUTPUTARRAYSIZE_TAG,
&rule))
return false;
if (!policy->AddRule(IPC_GDI_SETOPMSIGNINGKEYANDSEQUENCENUMBERS_TAG, &rule))
return false;
return true;
}
uint32_t ProcessMitigationsWin32KLockdownPolicy::EnumDisplayMonitorsAction(
const ClientInfo& client_info,
HMONITOR* monitor_list,
uint32_t monitor_list_size) {
MonitorListState state = {monitor_list, monitor_list_size, 0};
USERFUNC(EnumDisplayMonitors)
(nullptr, nullptr, DisplayMonitorEnumProc, reinterpret_cast<LPARAM>(&state));
return state.monitor_list_pos;
}
BOOL ProcessMitigationsWin32KLockdownPolicy::GetMonitorInfoAction(
const ClientInfo& client_info,
HMONITOR monitor,
MONITORINFO* monitor_info_ptr) {
if (!IsValidMonitorOrDeviceName(monitor, nullptr))
return FALSE;
MONITORINFOEXW monitor_info = {};
monitor_info.cbSize = sizeof(MONITORINFOEXW);
BOOL success = USERFUNC(GetMonitorInfoW)(
monitor, reinterpret_cast<MONITORINFO*>(&monitor_info));
if (success)
memcpy(monitor_info_ptr, &monitor_info, sizeof(monitor_info));
return success;
}
NTSTATUS ProcessMitigationsWin32KLockdownPolicy::
GetSuggestedOPMProtectedOutputArraySizeAction(
const ClientInfo& client_info,
const base::string16& device_name,
uint32_t* suggested_array_size) {
if (!IsValidMonitorOrDeviceName(nullptr, device_name.c_str())) {
return STATUS_ACCESS_DENIED;
}
UNICODE_STRING unicode_device_name;
StringToUnicodeString(&unicode_device_name, device_name);
DWORD suggested_array_size_dword = 0;
NTSTATUS status = GDIFUNC(GetSuggestedOPMProtectedOutputArraySize)(
&unicode_device_name, &suggested_array_size_dword);
if (!status)
*suggested_array_size = suggested_array_size_dword;
return status;
}
NTSTATUS
ProcessMitigationsWin32KLockdownPolicy::CreateOPMProtectedOutputsAction(
const ClientInfo& client_info,
const base::string16& device_name,
HANDLE* protected_outputs,
uint32_t array_input_size,
uint32_t* array_output_size) {
if (!IsValidMonitorOrDeviceName(nullptr, device_name.c_str())) {
return STATUS_ACCESS_DENIED;
}
UNICODE_STRING unicode_device_name;
StringToUnicodeString(&unicode_device_name, device_name);
DWORD output_size = 0;
NTSTATUS status = GDIFUNC(CreateOPMProtectedOutputs)(
&unicode_device_name, DXGKMDT_OPM_VOS_OPM_SEMANTICS, array_input_size,
&output_size,
reinterpret_cast<OPM_PROTECTED_OUTPUT_HANDLE*>(protected_outputs));
if (!status)
*array_output_size = output_size;
return status;
}
NTSTATUS ProcessMitigationsWin32KLockdownPolicy::GetCertificateSizeAction(
const ClientInfo& client_info,
const base::string16& device_name,
uint32_t* cert_size) {
if (!IsValidMonitorOrDeviceName(nullptr, device_name.c_str())) {
return STATUS_ACCESS_DENIED;
}
UNICODE_STRING unicode_device_name;
StringToUnicodeString(&unicode_device_name, device_name);
return GDIFUNC(GetCertificateSize)(&unicode_device_name,
DXGKMDT_OPM_CERTIFICATE,
reinterpret_cast<DWORD*>(cert_size));
}
NTSTATUS ProcessMitigationsWin32KLockdownPolicy::GetCertificateAction(
const ClientInfo& client_info,
const base::string16& device_name,
BYTE* cert_data,
uint32_t cert_size) {
if (!IsValidMonitorOrDeviceName(nullptr, device_name.c_str())) {
return STATUS_ACCESS_DENIED;
}
UNICODE_STRING unicode_device_name;
StringToUnicodeString(&unicode_device_name, device_name);
return GDIFUNC(GetCertificate)(&unicode_device_name, DXGKMDT_OPM_CERTIFICATE,
cert_data, cert_size);
}
NTSTATUS
ProcessMitigationsWin32KLockdownPolicy::GetCertificateSizeByHandleAction(
const ClientInfo& client_info,
HANDLE protected_output,
uint32_t* cert_size) {
auto get_certificate_size_func = GDIFUNC(GetCertificateSizeByHandle);
if (get_certificate_size_func) {
return get_certificate_size_func(protected_output, DXGKMDT_OPM_CERTIFICATE,
reinterpret_cast<DWORD*>(cert_size));
}
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS ProcessMitigationsWin32KLockdownPolicy::GetCertificateByHandleAction(
const ClientInfo& client_info,
HANDLE protected_output,
BYTE* cert_data,
uint32_t cert_size) {
auto get_certificate_func = GDIFUNC(GetCertificateByHandle);
if (get_certificate_func) {
return get_certificate_func(protected_output, DXGKMDT_OPM_CERTIFICATE,
cert_data, cert_size);
}
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS ProcessMitigationsWin32KLockdownPolicy::GetOPMRandomNumberAction(
const ClientInfo& client_info,
HANDLE protected_output,
void* random_number) {
return GDIFUNC(GetOPMRandomNumber)(
protected_output, static_cast<DXGKMDT_OPM_RANDOM_NUMBER*>(random_number));
}
NTSTATUS ProcessMitigationsWin32KLockdownPolicy::
SetOPMSigningKeyAndSequenceNumbersAction(const ClientInfo& client_info,
HANDLE protected_output,
void* parameters) {
return GDIFUNC(SetOPMSigningKeyAndSequenceNumbers)(
protected_output,
static_cast<DXGKMDT_OPM_ENCRYPTED_PARAMETERS*>(parameters));
}
NTSTATUS
ProcessMitigationsWin32KLockdownPolicy::ConfigureOPMProtectedOutputAction(
const ClientInfo& client_info,
HANDLE protected_output,
void* parameters_ptr) {
DXGKMDT_OPM_CONFIGURE_PARAMETERS parameters;
memcpy(&parameters, parameters_ptr, sizeof(parameters));
if (parameters.guidSetting != DXGKMDT_OPM_SET_PROTECTION_LEVEL ||
parameters.cbParametersSize !=
sizeof(DXGKMDT_OPM_SET_PROTECTION_LEVEL_PARAMETERS)) {
return STATUS_INVALID_PARAMETER;
}
DXGKMDT_OPM_SET_PROTECTION_LEVEL_PARAMETERS prot_level;
memcpy(&prot_level, parameters.abParameters, sizeof(prot_level));
if (prot_level.Reserved || prot_level.Reserved2)
return STATUS_INVALID_PARAMETER;
if (prot_level.ulProtectionType != DXGKMDT_OPM_PROTECTION_TYPE_HDCP &&
prot_level.ulProtectionType != DXGKMDT_OPM_PROTECTION_TYPE_DPCP) {
return STATUS_INVALID_PARAMETER;
}
// Protection levels are same for HDCP and DPCP.
if (prot_level.ulProtectionLevel != DXGKMDT_OPM_HDCP_OFF &&
prot_level.ulProtectionLevel != DXGKMDT_OPM_HDCP_ON) {
return STATUS_INVALID_PARAMETER;
}
return GDIFUNC(ConfigureOPMProtectedOutput)(protected_output, &parameters, 0,
nullptr);
}
NTSTATUS ProcessMitigationsWin32KLockdownPolicy::GetOPMInformationAction(
const ClientInfo& client_info,
HANDLE protected_output,
void* parameters_ptr,
void* requested_info_ptr) {
DXGKMDT_OPM_GET_INFO_PARAMETERS parameters;
memcpy(&parameters, parameters_ptr, sizeof(parameters));
bool valid_parameters = false;
// Validate sizes based on the type being requested.
if ((parameters.guidInformation == DXGKMDT_OPM_GET_CONNECTOR_TYPE ||
parameters.guidInformation ==
DXGKMDT_OPM_GET_SUPPORTED_PROTECTION_TYPES) &&
parameters.cbParametersSize == 0) {
valid_parameters = true;
} else if ((parameters.guidInformation ==
DXGKMDT_OPM_GET_VIRTUAL_PROTECTION_LEVEL ||
parameters.guidInformation ==
DXGKMDT_OPM_GET_ACTUAL_PROTECTION_LEVEL) &&
parameters.cbParametersSize == sizeof(uint32_t)) {
uint32_t param_value;
memcpy(&param_value, parameters.abParameters, sizeof(param_value));
if (param_value == DXGKMDT_OPM_PROTECTION_TYPE_HDCP ||
param_value == DXGKMDT_OPM_PROTECTION_TYPE_DPCP) {
valid_parameters = true;
}
}
if (!valid_parameters)
return STATUS_INVALID_PARAMETER;
DXGKMDT_OPM_REQUESTED_INFORMATION requested_info = {};
NTSTATUS status = GDIFUNC(GetOPMInformation)(protected_output, &parameters,
&requested_info);
if (!status)
memcpy(requested_info_ptr, &requested_info, sizeof(requested_info));
return status;
}
NTSTATUS
ProcessMitigationsWin32KLockdownPolicy::DestroyOPMProtectedOutputAction(
HANDLE protected_output) {
return GDIFUNC(DestroyOPMProtectedOutput)(protected_output);
}
void ProcessMitigationsWin32KLockdownPolicy::SetOverrideForTestCallback(
OverrideForTestFunction callback) {
override_callback_ = callback;
}
OverrideForTestFunction
ProcessMitigationsWin32KLockdownPolicy::GetOverrideForTestCallback() {
return override_callback_;
}
} // namespace sandbox