1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "sandbox/win/src/process_mitigations_win32k_policy.h"
6 
7 #include <stddef.h>
8 
9 #include "sandbox/win/src/process_mitigations_win32k_interception.h"
10 
11 namespace sandbox {
12 
13 namespace {
14 
15 // Define GUIDs for OPM APIs
16 const GUID DXGKMDT_OPM_GET_CONNECTOR_TYPE = {
17     0x81d0bfd5,
18     0x6afe,
19     0x48c2,
20     {0x99, 0xc0, 0x95, 0xa0, 0x8f, 0x97, 0xc5, 0xda}};
21 const GUID DXGKMDT_OPM_GET_SUPPORTED_PROTECTION_TYPES = {
22     0x38f2a801,
23     0x9a6c,
24     0x48bb,
25     {0x91, 0x07, 0xb6, 0x69, 0x6e, 0x6f, 0x17, 0x97}};
26 const GUID DXGKMDT_OPM_GET_VIRTUAL_PROTECTION_LEVEL = {
27     0xb2075857,
28     0x3eda,
29     0x4d5d,
30     {0x88, 0xdb, 0x74, 0x8f, 0x8c, 0x1a, 0x05, 0x49}};
31 const GUID DXGKMDT_OPM_GET_ACTUAL_PROTECTION_LEVEL = {
32     0x1957210a,
33     0x7766,
34     0x452a,
35     {0xb9, 0x9a, 0xd2, 0x7a, 0xed, 0x54, 0xf0, 0x3a}};
36 const GUID DXGKMDT_OPM_SET_PROTECTION_LEVEL = {
37     0x9bb9327c,
38     0x4eb5,
39     0x4727,
40     {0x9f, 0x00, 0xb4, 0x2b, 0x09, 0x19, 0xc0, 0xda}};
41 
StringToUnicodeString(PUNICODE_STRING unicode_string,const std::wstring & device_name)42 void StringToUnicodeString(PUNICODE_STRING unicode_string,
43                            const std::wstring& device_name) {
44   static RtlInitUnicodeStringFunction RtlInitUnicodeString;
45   if (!RtlInitUnicodeString) {
46     HMODULE ntdll = ::GetModuleHandle(kNtdllName);
47     RtlInitUnicodeString = reinterpret_cast<RtlInitUnicodeStringFunction>(
48         GetProcAddress(ntdll, "RtlInitUnicodeString"));
49   }
50   RtlInitUnicodeString(unicode_string, device_name.c_str());
51 }
52 
53 struct MonitorListState {
54   HMONITOR* monitor_list;
55   uint32_t monitor_list_size;
56   uint32_t monitor_list_pos;
57 };
58 
DisplayMonitorEnumProc(HMONITOR monitor,HDC hdc_monitor,LPRECT rect_monitor,LPARAM data)59 BOOL CALLBACK DisplayMonitorEnumProc(HMONITOR monitor,
60                                      HDC hdc_monitor,
61                                      LPRECT rect_monitor,
62                                      LPARAM data) {
63   MonitorListState* state = reinterpret_cast<MonitorListState*>(data);
64   if (state->monitor_list_pos >= state->monitor_list_size)
65     return false;
66   state->monitor_list[state->monitor_list_pos++] = monitor;
67   return true;
68 }
69 
70 template <typename T>
GetExportedFunc(const wchar_t * libname,const char * name)71 T GetExportedFunc(const wchar_t* libname, const char* name) {
72   OverrideForTestFunction test_override =
73       ProcessMitigationsWin32KLockdownPolicy::GetOverrideForTestCallback();
74   if (test_override)
75     return reinterpret_cast<T>(test_override(name));
76 
77   static T func = nullptr;
78   if (!func) {
79     func =
80         reinterpret_cast<T>(::GetProcAddress(::GetModuleHandle(libname), name));
81     DCHECK(!!func);
82   }
83   return func;
84 }
85 
86 #define GDIFUNC(name) GetExportedFunc<name##Function>(L"gdi32.dll", #name)
87 #define USERFUNC(name) GetExportedFunc<name##Function>(L"user32.dll", #name)
88 
89 struct ValidateMonitorParams {
90   HMONITOR monitor;
91   std::wstring device_name;
92   bool result;
93 };
94 
GetMonitorDeviceName(HMONITOR monitor,std::wstring * device_name)95 bool GetMonitorDeviceName(HMONITOR monitor, std::wstring* device_name) {
96   MONITORINFOEXW monitor_info = {};
97   monitor_info.cbSize = sizeof(monitor_info);
98   if (!USERFUNC(GetMonitorInfoW)(monitor, &monitor_info))
99     return false;
100   if (monitor_info.szDevice[CCHDEVICENAME - 1] != 0)
101     return false;
102   *device_name = monitor_info.szDevice;
103   return true;
104 }
105 
ValidateMonitorEnumProc(HMONITOR monitor,HDC,LPRECT,LPARAM data)106 BOOL CALLBACK ValidateMonitorEnumProc(HMONITOR monitor,
107                                       HDC,
108                                       LPRECT,
109                                       LPARAM data) {
110   ValidateMonitorParams* valid_params =
111       reinterpret_cast<ValidateMonitorParams*>(data);
112   std::wstring device_name;
113   bool result = false;
114   if (valid_params->device_name.empty()) {
115     result = monitor == valid_params->monitor;
116   } else if (GetMonitorDeviceName(monitor, &device_name)) {
117     result = device_name == valid_params->device_name;
118   }
119   valid_params->result = result;
120   if (!result)
121     return true;
122   return false;
123 }
124 
IsValidMonitorOrDeviceName(HMONITOR monitor,const wchar_t * device_name)125 bool IsValidMonitorOrDeviceName(HMONITOR monitor, const wchar_t* device_name) {
126   ValidateMonitorParams params = {};
127   params.monitor = monitor;
128   if (device_name)
129     params.device_name = device_name;
130   USERFUNC(EnumDisplayMonitors)
131   (nullptr, nullptr, ValidateMonitorEnumProc,
132    reinterpret_cast<LPARAM>(&params));
133   return params.result;
134 }
135 
136 }  // namespace
137 
138 OverrideForTestFunction
139     ProcessMitigationsWin32KLockdownPolicy::override_callback_;
140 
GenerateRules(const wchar_t * name,TargetPolicy::Semantics semantics,LowLevelPolicy * policy)141 bool ProcessMitigationsWin32KLockdownPolicy::GenerateRules(
142     const wchar_t* name,
143     TargetPolicy::Semantics semantics,
144     LowLevelPolicy* policy) {
145   PolicyRule rule(FAKE_SUCCESS);
146   if (!policy->AddRule(IpcTag::GDI_GDIDLLINITIALIZE, &rule))
147     return false;
148   if (!policy->AddRule(IpcTag::GDI_GETSTOCKOBJECT, &rule))
149     return false;
150   if (!policy->AddRule(IpcTag::USER_REGISTERCLASSW, &rule))
151     return false;
152   if (semantics != TargetPolicy::IMPLEMENT_OPM_APIS)
153     return true;
154   if (!policy->AddRule(IpcTag::USER_ENUMDISPLAYMONITORS, &rule))
155     return false;
156   if (!policy->AddRule(IpcTag::USER_ENUMDISPLAYDEVICES, &rule))
157     return false;
158   if (!policy->AddRule(IpcTag::USER_GETMONITORINFO, &rule))
159     return false;
160   if (!policy->AddRule(IpcTag::GDI_CREATEOPMPROTECTEDOUTPUTS, &rule))
161     return false;
162   if (!policy->AddRule(IpcTag::GDI_GETCERTIFICATE, &rule))
163     return false;
164   if (!policy->AddRule(IpcTag::GDI_GETCERTIFICATESIZE, &rule))
165     return false;
166   if (!policy->AddRule(IpcTag::GDI_DESTROYOPMPROTECTEDOUTPUT, &rule))
167     return false;
168   if (!policy->AddRule(IpcTag::GDI_CONFIGUREOPMPROTECTEDOUTPUT, &rule))
169     return false;
170   if (!policy->AddRule(IpcTag::GDI_GETOPMINFORMATION, &rule))
171     return false;
172   if (!policy->AddRule(IpcTag::GDI_GETOPMRANDOMNUMBER, &rule))
173     return false;
174   if (!policy->AddRule(IpcTag::GDI_GETSUGGESTEDOPMPROTECTEDOUTPUTARRAYSIZE,
175                        &rule))
176     return false;
177   if (!policy->AddRule(IpcTag::GDI_SETOPMSIGNINGKEYANDSEQUENCENUMBERS, &rule))
178     return false;
179   return true;
180 }
181 
EnumDisplayMonitorsAction(const ClientInfo & client_info,HMONITOR * monitor_list,uint32_t monitor_list_size)182 uint32_t ProcessMitigationsWin32KLockdownPolicy::EnumDisplayMonitorsAction(
183     const ClientInfo& client_info,
184     HMONITOR* monitor_list,
185     uint32_t monitor_list_size) {
186   MonitorListState state = {monitor_list, monitor_list_size, 0};
187   USERFUNC(EnumDisplayMonitors)
188   (nullptr, nullptr, DisplayMonitorEnumProc, reinterpret_cast<LPARAM>(&state));
189   return state.monitor_list_pos;
190 }
191 
GetMonitorInfoAction(const ClientInfo & client_info,HMONITOR monitor,MONITORINFO * monitor_info_ptr)192 bool ProcessMitigationsWin32KLockdownPolicy::GetMonitorInfoAction(
193     const ClientInfo& client_info,
194     HMONITOR monitor,
195     MONITORINFO* monitor_info_ptr) {
196   if (!IsValidMonitorOrDeviceName(monitor, nullptr))
197     return false;
198   MONITORINFOEXW monitor_info = {};
199   monitor_info.cbSize = sizeof(MONITORINFOEXW);
200 
201   bool success = USERFUNC(GetMonitorInfoW)(
202       monitor, reinterpret_cast<MONITORINFO*>(&monitor_info));
203   if (success)
204     memcpy(monitor_info_ptr, &monitor_info, sizeof(monitor_info));
205   return success;
206 }
207 
208 NTSTATUS ProcessMitigationsWin32KLockdownPolicy::
GetSuggestedOPMProtectedOutputArraySizeAction(const ClientInfo & client_info,const std::wstring & device_name,uint32_t * suggested_array_size)209     GetSuggestedOPMProtectedOutputArraySizeAction(
210         const ClientInfo& client_info,
211         const std::wstring& device_name,
212         uint32_t* suggested_array_size) {
213   if (!IsValidMonitorOrDeviceName(nullptr, device_name.c_str())) {
214     return STATUS_ACCESS_DENIED;
215   }
216   UNICODE_STRING unicode_device_name;
217   StringToUnicodeString(&unicode_device_name, device_name);
218   DWORD suggested_array_size_dword = 0;
219   NTSTATUS status = GDIFUNC(GetSuggestedOPMProtectedOutputArraySize)(
220       &unicode_device_name, &suggested_array_size_dword);
221   if (!status)
222     *suggested_array_size = suggested_array_size_dword;
223   return status;
224 }
225 
226 NTSTATUS
CreateOPMProtectedOutputsAction(const ClientInfo & client_info,const std::wstring & device_name,HANDLE * protected_outputs,uint32_t array_input_size,uint32_t * array_output_size)227 ProcessMitigationsWin32KLockdownPolicy::CreateOPMProtectedOutputsAction(
228     const ClientInfo& client_info,
229     const std::wstring& device_name,
230     HANDLE* protected_outputs,
231     uint32_t array_input_size,
232     uint32_t* array_output_size) {
233   if (!IsValidMonitorOrDeviceName(nullptr, device_name.c_str())) {
234     return STATUS_ACCESS_DENIED;
235   }
236 
237   UNICODE_STRING unicode_device_name;
238   StringToUnicodeString(&unicode_device_name, device_name);
239   DWORD output_size = 0;
240 
241   NTSTATUS status = GDIFUNC(CreateOPMProtectedOutputs)(
242       &unicode_device_name, DXGKMDT_OPM_VOS_OPM_SEMANTICS, array_input_size,
243       &output_size,
244       reinterpret_cast<OPM_PROTECTED_OUTPUT_HANDLE*>(protected_outputs));
245   if (!status)
246     *array_output_size = output_size;
247   return status;
248 }
249 
GetCertificateSizeAction(const ClientInfo & client_info,const std::wstring & device_name,uint32_t * cert_size)250 NTSTATUS ProcessMitigationsWin32KLockdownPolicy::GetCertificateSizeAction(
251     const ClientInfo& client_info,
252     const std::wstring& device_name,
253     uint32_t* cert_size) {
254   if (!IsValidMonitorOrDeviceName(nullptr, device_name.c_str())) {
255     return STATUS_ACCESS_DENIED;
256   }
257   UNICODE_STRING unicode_device_name;
258   StringToUnicodeString(&unicode_device_name, device_name);
259 
260   return GDIFUNC(GetCertificateSize)(&unicode_device_name,
261                                      DXGKMDT_OPM_CERTIFICATE,
262                                      reinterpret_cast<DWORD*>(cert_size));
263 }
264 
GetCertificateAction(const ClientInfo & client_info,const std::wstring & device_name,BYTE * cert_data,uint32_t cert_size)265 NTSTATUS ProcessMitigationsWin32KLockdownPolicy::GetCertificateAction(
266     const ClientInfo& client_info,
267     const std::wstring& device_name,
268     BYTE* cert_data,
269     uint32_t cert_size) {
270   if (!IsValidMonitorOrDeviceName(nullptr, device_name.c_str())) {
271     return STATUS_ACCESS_DENIED;
272   }
273   UNICODE_STRING unicode_device_name;
274   StringToUnicodeString(&unicode_device_name, device_name);
275 
276   return GDIFUNC(GetCertificate)(&unicode_device_name, DXGKMDT_OPM_CERTIFICATE,
277                                  cert_data, cert_size);
278 }
279 
280 NTSTATUS
GetCertificateSizeByHandleAction(const ClientInfo & client_info,HANDLE protected_output,uint32_t * cert_size)281 ProcessMitigationsWin32KLockdownPolicy::GetCertificateSizeByHandleAction(
282     const ClientInfo& client_info,
283     HANDLE protected_output,
284     uint32_t* cert_size) {
285   auto get_certificate_size_func = GDIFUNC(GetCertificateSizeByHandle);
286   if (get_certificate_size_func) {
287     return get_certificate_size_func(protected_output, DXGKMDT_OPM_CERTIFICATE,
288                                      reinterpret_cast<DWORD*>(cert_size));
289   }
290   return STATUS_NOT_IMPLEMENTED;
291 }
292 
GetCertificateByHandleAction(const ClientInfo & client_info,HANDLE protected_output,BYTE * cert_data,uint32_t cert_size)293 NTSTATUS ProcessMitigationsWin32KLockdownPolicy::GetCertificateByHandleAction(
294     const ClientInfo& client_info,
295     HANDLE protected_output,
296     BYTE* cert_data,
297     uint32_t cert_size) {
298   auto get_certificate_func = GDIFUNC(GetCertificateByHandle);
299   if (get_certificate_func) {
300     return get_certificate_func(protected_output, DXGKMDT_OPM_CERTIFICATE,
301                                 cert_data, cert_size);
302   }
303   return STATUS_NOT_IMPLEMENTED;
304 }
305 
GetOPMRandomNumberAction(const ClientInfo & client_info,HANDLE protected_output,void * random_number)306 NTSTATUS ProcessMitigationsWin32KLockdownPolicy::GetOPMRandomNumberAction(
307     const ClientInfo& client_info,
308     HANDLE protected_output,
309     void* random_number) {
310   return GDIFUNC(GetOPMRandomNumber)(
311       protected_output, static_cast<DXGKMDT_OPM_RANDOM_NUMBER*>(random_number));
312 }
313 
314 NTSTATUS ProcessMitigationsWin32KLockdownPolicy::
SetOPMSigningKeyAndSequenceNumbersAction(const ClientInfo & client_info,HANDLE protected_output,void * parameters)315     SetOPMSigningKeyAndSequenceNumbersAction(const ClientInfo& client_info,
316                                              HANDLE protected_output,
317                                              void* parameters) {
318   return GDIFUNC(SetOPMSigningKeyAndSequenceNumbers)(
319       protected_output,
320       static_cast<DXGKMDT_OPM_ENCRYPTED_PARAMETERS*>(parameters));
321 }
322 
323 NTSTATUS
ConfigureOPMProtectedOutputAction(const ClientInfo & client_info,HANDLE protected_output,void * parameters_ptr)324 ProcessMitigationsWin32KLockdownPolicy::ConfigureOPMProtectedOutputAction(
325     const ClientInfo& client_info,
326     HANDLE protected_output,
327     void* parameters_ptr) {
328   DXGKMDT_OPM_CONFIGURE_PARAMETERS parameters;
329   memcpy(&parameters, parameters_ptr, sizeof(parameters));
330   if (parameters.guidSetting != DXGKMDT_OPM_SET_PROTECTION_LEVEL ||
331       parameters.cbParametersSize !=
332           sizeof(DXGKMDT_OPM_SET_PROTECTION_LEVEL_PARAMETERS)) {
333     return STATUS_INVALID_PARAMETER;
334   }
335 
336   DXGKMDT_OPM_SET_PROTECTION_LEVEL_PARAMETERS prot_level;
337   memcpy(&prot_level, parameters.abParameters, sizeof(prot_level));
338   if (prot_level.Reserved || prot_level.Reserved2)
339     return STATUS_INVALID_PARAMETER;
340 
341   if (prot_level.ulProtectionType != DXGKMDT_OPM_PROTECTION_TYPE_HDCP &&
342       prot_level.ulProtectionType != DXGKMDT_OPM_PROTECTION_TYPE_DPCP) {
343     return STATUS_INVALID_PARAMETER;
344   }
345 
346   // Protection levels are same for HDCP and DPCP.
347   if (prot_level.ulProtectionLevel != DXGKMDT_OPM_HDCP_OFF &&
348       prot_level.ulProtectionLevel != DXGKMDT_OPM_HDCP_ON) {
349     return STATUS_INVALID_PARAMETER;
350   }
351 
352   return GDIFUNC(ConfigureOPMProtectedOutput)(protected_output, &parameters, 0,
353                                               nullptr);
354 }
355 
GetOPMInformationAction(const ClientInfo & client_info,HANDLE protected_output,void * parameters_ptr,void * requested_info_ptr)356 NTSTATUS ProcessMitigationsWin32KLockdownPolicy::GetOPMInformationAction(
357     const ClientInfo& client_info,
358     HANDLE protected_output,
359     void* parameters_ptr,
360     void* requested_info_ptr) {
361   DXGKMDT_OPM_GET_INFO_PARAMETERS parameters;
362   memcpy(&parameters, parameters_ptr, sizeof(parameters));
363 
364   bool valid_parameters = false;
365   // Validate sizes based on the type being requested.
366   if ((parameters.guidInformation == DXGKMDT_OPM_GET_CONNECTOR_TYPE ||
367        parameters.guidInformation ==
368            DXGKMDT_OPM_GET_SUPPORTED_PROTECTION_TYPES) &&
369       parameters.cbParametersSize == 0) {
370     valid_parameters = true;
371   } else if ((parameters.guidInformation ==
372                   DXGKMDT_OPM_GET_VIRTUAL_PROTECTION_LEVEL ||
373               parameters.guidInformation ==
374                   DXGKMDT_OPM_GET_ACTUAL_PROTECTION_LEVEL) &&
375              parameters.cbParametersSize == sizeof(uint32_t)) {
376     uint32_t param_value;
377     memcpy(&param_value, parameters.abParameters, sizeof(param_value));
378     if (param_value == DXGKMDT_OPM_PROTECTION_TYPE_HDCP ||
379         param_value == DXGKMDT_OPM_PROTECTION_TYPE_DPCP) {
380       valid_parameters = true;
381     }
382   }
383   if (!valid_parameters)
384     return STATUS_INVALID_PARAMETER;
385   DXGKMDT_OPM_REQUESTED_INFORMATION requested_info = {};
386   NTSTATUS status = GDIFUNC(GetOPMInformation)(protected_output, &parameters,
387                                                &requested_info);
388   if (!status)
389     memcpy(requested_info_ptr, &requested_info, sizeof(requested_info));
390 
391   return status;
392 }
393 
394 NTSTATUS
DestroyOPMProtectedOutputAction(HANDLE protected_output)395 ProcessMitigationsWin32KLockdownPolicy::DestroyOPMProtectedOutputAction(
396     HANDLE protected_output) {
397   return GDIFUNC(DestroyOPMProtectedOutput)(protected_output);
398 }
399 
SetOverrideForTestCallback(OverrideForTestFunction callback)400 void ProcessMitigationsWin32KLockdownPolicy::SetOverrideForTestCallback(
401     OverrideForTestFunction callback) {
402   override_callback_ = callback;
403 }
404 
405 OverrideForTestFunction
GetOverrideForTestCallback()406 ProcessMitigationsWin32KLockdownPolicy::GetOverrideForTestCallback() {
407   return override_callback_;
408 }
409 
410 }  // namespace sandbox
411