• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9 
10 #include "base/win/win_util.h"
11 
12 #include <objbase.h>
13 
14 #include <initguid.h>
15 #include <shobjidl.h>
16 #include <tchar.h>
17 
18 #include <aclapi.h>
19 #include <cfgmgr32.h>
20 #include <inspectable.h>
21 #include <lm.h>
22 #include <mdmregistration.h>
23 #include <powrprof.h>
24 #include <propkey.h>
25 #include <psapi.h>
26 #include <roapi.h>
27 #include <sddl.h>
28 #include <setupapi.h>
29 #include <shellscalingapi.h>
30 #include <signal.h>
31 #include <stddef.h>
32 #include <stdlib.h>
33 #include <strsafe.h>
34 #include <tpcshrd.h>
35 #include <uiviewsettingsinterop.h>
36 #include <wbemidl.h>
37 #include <windows.ui.viewmanagement.h>
38 #include <winstring.h>
39 #include <wrl/client.h>
40 #include <wrl/wrappers/corewrappers.h>
41 
42 #include <limits>
43 #include <memory>
44 #include <optional>
45 #include <string_view>
46 #include <utility>
47 
48 #include "base/base_switches.h"
49 #include "base/command_line.h"
50 #include "base/files/file_path.h"
51 #include "base/logging.h"
52 #include "base/metrics/histogram_functions.h"
53 #include "base/notreached.h"
54 #include "base/scoped_native_library.h"
55 #include "base/strings/string_util.h"
56 #include "base/strings/string_util_win.h"
57 #include "base/strings/utf_string_conversions.h"
58 #include "base/threading/scoped_blocking_call.h"
59 #include "base/threading/scoped_thread_priority.h"
60 #include "base/threading/thread_restrictions.h"
61 #include "base/timer/elapsed_timer.h"
62 #include "base/win/access_token.h"
63 #include "base/win/com_init_util.h"
64 #include "base/win/core_winrt_util.h"
65 #include "base/win/propvarutil.h"
66 #include "base/win/registry.h"
67 #include "base/win/scoped_bstr.h"
68 #include "base/win/scoped_co_mem.h"
69 #include "base/win/scoped_handle.h"
70 #include "base/win/scoped_hstring.h"
71 #include "base/win/scoped_propvariant.h"
72 #include "base/win/scoped_safearray.h"
73 #include "base/win/scoped_variant.h"
74 #include "base/win/shlwapi.h"
75 #include "base/win/static_constants.h"
76 #include "base/win/windows_version.h"
77 #include "base/win/wmi.h"
78 
79 namespace base {
80 namespace win {
81 
82 namespace {
83 
84 using QueryKeyFunction =
85     ScopedDeviceConvertibilityStateForTesting::QueryFunction;
86 
87 // Sets the value of |property_key| to |property_value| in |property_store|.
SetPropVariantValueForPropertyStore(IPropertyStore * property_store,const PROPERTYKEY & property_key,const ScopedPropVariant & property_value)88 bool SetPropVariantValueForPropertyStore(
89     IPropertyStore* property_store,
90     const PROPERTYKEY& property_key,
91     const ScopedPropVariant& property_value) {
92   DCHECK(property_store);
93 
94   HRESULT result = property_store->SetValue(property_key, property_value.get());
95   if (result == S_OK)
96     result = property_store->Commit();
97   if (SUCCEEDED(result))
98     return true;
99 #if DCHECK_IS_ON()
100   if (HRESULT_FACILITY(result) == FACILITY_WIN32)
101     ::SetLastError(HRESULT_CODE(result));
102   // See third_party/perl/c/i686-w64-mingw32/include/propkey.h for GUID and
103   // PID definitions.
104   DPLOG(ERROR) << "Failed to set property with GUID "
105                << WStringFromGUID(property_key.fmtid) << " PID "
106                << property_key.pid;
107 #endif
108   return false;
109 }
110 
ForceCrashOnSigAbort(int)111 void __cdecl ForceCrashOnSigAbort(int) {
112   *((volatile int*)nullptr) = 0x1337;
113 }
114 
115 // Returns the current platform role. We use the PowerDeterminePlatformRoleEx
116 // API for that.
GetPlatformRole()117 POWER_PLATFORM_ROLE GetPlatformRole() {
118   return PowerDeterminePlatformRoleEx(POWER_PLATFORM_ROLE_V2);
119 }
120 
121 // Enable V2 per-monitor high-DPI support for the process. This will cause
122 // Windows to scale dialogs, comctl32 controls, context menus, and non-client
123 // area owned by this process on a per-monitor basis. If per-monitor V2 is not
124 // available (i.e., prior to Windows 10 1703) or fails, returns false.
125 // https://docs.microsoft.com/en-us/windows/desktop/hidpi/dpi-awareness-context
EnablePerMonitorV2()126 bool EnablePerMonitorV2() {
127   if (!IsUser32AndGdi32Available())
128     return false;
129 
130   static const auto set_process_dpi_awareness_context_func =
131       reinterpret_cast<decltype(&::SetProcessDpiAwarenessContext)>(
132           GetUser32FunctionPointer("SetProcessDpiAwarenessContext"));
133   if (set_process_dpi_awareness_context_func) {
134     return set_process_dpi_awareness_context_func(
135         DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
136   }
137 
138   DCHECK_LT(GetVersion(), Version::WIN10_RS2)
139       << "SetProcessDpiAwarenessContext should be available on all platforms"
140          " >= Windows 10 Redstone 2";
141 
142   return false;
143 }
144 
GetDomainEnrollmentStateStorage()145 bool* GetDomainEnrollmentStateStorage() {
146   static bool state = IsOS(OS_DOMAINMEMBER);
147   return &state;
148 }
149 
GetRegisteredWithManagementStateStorage()150 bool* GetRegisteredWithManagementStateStorage() {
151   static bool state = [] {
152     // Mitigate the issues caused by loading DLLs on a background thread
153     // (http://crbug/973868).
154     SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
155 
156     ScopedNativeLibrary library(
157         FilePath(FILE_PATH_LITERAL("MDMRegistration.dll")));
158     if (!library.is_valid())
159       return false;
160 
161     using IsDeviceRegisteredWithManagementFunction =
162         decltype(&::IsDeviceRegisteredWithManagement);
163     IsDeviceRegisteredWithManagementFunction
164         is_device_registered_with_management_function =
165             reinterpret_cast<IsDeviceRegisteredWithManagementFunction>(
166                 library.GetFunctionPointer("IsDeviceRegisteredWithManagement"));
167     if (!is_device_registered_with_management_function)
168       return false;
169 
170     BOOL is_managed = FALSE;
171     HRESULT hr =
172         is_device_registered_with_management_function(&is_managed, 0, nullptr);
173     return SUCCEEDED(hr) && is_managed;
174   }();
175 
176   return &state;
177 }
178 
179 // TODO (crbug/1300219): return a DSREG_JOIN_TYPE* instead of bool*.
GetAzureADJoinStateStorage()180 bool* GetAzureADJoinStateStorage() {
181   static bool state = [] {
182     base::ElapsedTimer timer;
183 
184     // Mitigate the issues caused by loading DLLs on a background thread
185     // (http://crbug/973868).
186     SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
187 
188     ScopedNativeLibrary netapi32(
189         base::LoadSystemLibrary(FILE_PATH_LITERAL("netapi32.dll")));
190     if (!netapi32.is_valid())
191       return false;
192 
193     const auto net_get_aad_join_information_function =
194         reinterpret_cast<decltype(&::NetGetAadJoinInformation)>(
195             netapi32.GetFunctionPointer("NetGetAadJoinInformation"));
196     if (!net_get_aad_join_information_function)
197       return false;
198 
199     const auto net_free_aad_join_information_function =
200         reinterpret_cast<decltype(&::NetFreeAadJoinInformation)>(
201             netapi32.GetFunctionPointer("NetFreeAadJoinInformation"));
202     DPCHECK(net_free_aad_join_information_function);
203 
204     DSREG_JOIN_INFO* join_info = nullptr;
205     HRESULT hr = net_get_aad_join_information_function(/*pcszTenantId=*/nullptr,
206                                                        &join_info);
207     const bool is_aad_joined = SUCCEEDED(hr) && join_info;
208     if (join_info) {
209       net_free_aad_join_information_function(join_info);
210     }
211 
212     base::UmaHistogramTimes("EnterpriseCheck.AzureADJoinStatusCheckTime",
213                             timer.Elapsed());
214     return is_aad_joined;
215   }();
216   return &state;
217 }
218 
PinUser32Internal(NativeLibraryLoadError * error)219 NativeLibrary PinUser32Internal(NativeLibraryLoadError* error) {
220   static NativeLibraryLoadError load_error;
221   static const NativeLibrary user32_module =
222       PinSystemLibrary(FILE_PATH_LITERAL("user32.dll"), &load_error);
223   if (!user32_module && error)
224     error->code = load_error.code;
225   return user32_module;
226 }
227 
228 }  // namespace
229 
230 // The device convertibility functions below return references to cached data
231 // to allow for complete test scenarios. See:
232 // ScopedDeviceConvertibilityStateForTesting.
233 //
234 // Returns a reference to a cached value computed on first-use that is true only
235 // if the device is a tablet, convertible, or detachable according to
236 // RtlGetDeviceFamilyInfoEnum. Looks for the following values: Tablet(2),
237 // Convertible(5), or Detachable(6).
238 // https://learn.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-deployment-deviceform
IsDeviceFormConvertible()239 bool& IsDeviceFormConvertible() {
240   static bool is_convertible = [] {
241     DWORD deviceForm = DEVICEFAMILYDEVICEFORM_UNKNOWN;
242     using lpfnRtlGetDeviceFamilyInfo =
243         VOID(WINAPI*)(ULONGLONG*, DWORD*, DWORD*);
244     static const lpfnRtlGetDeviceFamilyInfo get_device_family_info_fn =
245         reinterpret_cast<lpfnRtlGetDeviceFamilyInfo>(GetProcAddress(
246             ::GetModuleHandle(L"ntdll.dll"), "RtlGetDeviceFamilyInfoEnum"));
247     PCHECK(get_device_family_info_fn);
248     get_device_family_info_fn(/*pullUAPInfo=*/nullptr,
249                               /*pulDeviceFamily=*/nullptr, &deviceForm);
250 
251     // Is not reliable for all devices. Surface Book 3 for instance has Chassis
252     // Type 9 (Laptop) and DeviceForm 0 (Unknown).
253     return (deviceForm == DEVICEFAMILYDEVICEFORM_TABLET) ||
254            (deviceForm == DEVICEFAMILYDEVICEFORM_CONVERTIBLE) ||
255            (deviceForm == DEVICEFAMILYDEVICEFORM_DETACHABLE);
256   }();
257   return is_convertible;
258 }
259 
260 // Returns a reference to a cached boolean that is true if the device hardware
261 // is convertible. The value is determined via a WMI query for
262 // Win32_SystemEnclosure. This should only be executed for a small amount of
263 // devices that don't have ConvertibleChassis or ConvertibilityEnabled keys set.
264 // https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/win32-systemenclosure
IsChassisConvertible()265 bool& IsChassisConvertible() {
266   static bool chassis_convertible = [] {
267     ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
268     AssertComApartmentType(ComApartmentType::STA);
269 
270     constexpr std::wstring_view kQuery =
271         L"select ChassisTypes from Win32_SystemEnclosure";
272     Microsoft::WRL::ComPtr<IEnumWbemClassObject> enumerator;
273     if (RunWmiQuery(kCimV2ServerName, std::wstring(kQuery), &enumerator)
274             .has_value()) {
275       return false;
276     }
277 
278     ULONG obj_count = 0;
279     Microsoft::WRL::ComPtr<IWbemClassObject> info;
280     HRESULT hr = E_FAIL;
281     hr = enumerator->Next(25, 1, &info, &obj_count);
282     if (FAILED(hr)) {
283       return false;
284     }
285 
286     // The accepted values for a convertible device are Tablet (30), Convertible
287     // (31), and Detachable (32).
288     enum ChassisType : int32_t {
289       kUnknownChassisType = 2,
290       kTabletChassisType = 30,
291       kConvertibleChassisType = 31,
292       kDetachableChassisType = 32,
293     };
294     int32_t chassis_type_id = kUnknownChassisType;
295 
296     if (obj_count >= 1) {
297       ScopedVariant chassisTypeVariant;
298       hr = info->Get(L"ChassisTypes", 0, chassisTypeVariant.Receive(), nullptr,
299                      nullptr);
300       if (FAILED(hr)) {
301         return false;
302       }
303 
304       // Although chassisType is documented as uint16[], the type in reality is
305       // a 32bit integer array.
306       if (chassisTypeVariant.type() == (VT_ARRAY | VT_I4)) {
307         ScopedSafearray safearray(chassisTypeVariant.Release().parray);
308         auto lock_scope = safearray.CreateLockScope<VT_I4>();
309         if (!lock_scope) {
310           return false;
311         }
312         chassis_type_id = (*lock_scope)[0];
313       }
314     }
315 
316     return (chassis_type_id == kTabletChassisType) ||
317            (chassis_type_id == kConvertibleChassisType) ||
318            (chassis_type_id == kDetachableChassisType);
319   }();
320   return chassis_convertible;
321 }
322 
323 // Returns a reference to a cached boolean optional. If a value exists, it means
324 // that the queried registry key, ConvertibilityEnabled, exists. Used by Surface
325 // for devices that can't set deviceForm or ChassisType. The RegKey need not
326 // exist, but if it does it will override other checks.
GetConvertibilityEnabledOverride()327 std::optional<bool>& GetConvertibilityEnabledOverride() {
328   static std::optional<bool> convertibility_enabled =
329       []() -> std::optional<bool> {
330     DWORD data;
331     base::win::RegKey key(
332         HKEY_LOCAL_MACHINE,
333         L"System\\CurrentControlSet\\Control\\PriorityControl",
334         KEY_QUERY_VALUE);
335     return key.ReadValueDW(L"ConvertibilityEnabled", &data) == ERROR_SUCCESS
336                ? std::make_optional(data != 0)
337                : std::nullopt;
338   }();
339   return convertibility_enabled;
340 }
341 
342 // Returns a reference to a cached boolean optional. If a value exists, it means
343 // that the queried registry key, ConvertibleChassis, exists. Windows may cache
344 // the results of convertible chassis queries, preventing the need for running
345 // the expensive WMI query. This should always be checked prior to running
346 // `IsChassisConvertible()`.
GetConvertibleChassisKeyValue()347 std::optional<bool>& GetConvertibleChassisKeyValue() {
348   static std::optional<bool> convertible_chassis = []() -> std::optional<bool> {
349     DWORD data;
350     base::win::RegKey key(HKEY_CURRENT_USER,
351                           L"SOFTWARE\\Microsoft\\TabletTip\\ConvertibleChassis",
352                           KEY_QUERY_VALUE);
353     return key.ReadValueDW(L"ConvertibleChassis", &data) == ERROR_SUCCESS
354                ? std::make_optional(data != 0)
355                : std::nullopt;
356   }();
357   return convertible_chassis;
358 }
359 
360 // Returns a function pointer that points to the lambda function that
361 // tracks if the device's convertible slate mode state has ever changed, which
362 // would indicate that proper GPIO drivers are available for a convertible
363 // machine. A pointer is used so that the function at the address can be
364 // replaced for testing purposes.
HasCSMStateChanged()365 QueryKeyFunction& HasCSMStateChanged() {
366   static QueryKeyFunction state = []() {
367     DWORD data;
368     base::win::RegKey key(
369         HKEY_CURRENT_USER,
370         L"SOFTWARE\\Microsoft\\TabletTip\\ConvertibleSlateModeChanged",
371         KEY_QUERY_VALUE);
372     bool value_exists =
373         key.ReadValueDW(L"ConvertibleSlateModeChanged", &data) == ERROR_SUCCESS;
374     return (value_exists && data != 0);
375   };
376   return state;
377 }
378 
379 // Uses the Windows 10 WRL API's to query the current system state. The API's
380 // we are using in the function below are supported in Win32 apps as per msdn.
381 // It looks like the API implementation is buggy at least on Surface 4 causing
382 // it to always return UserInteractionMode_Touch which as per documentation
383 // indicates tablet mode.
IsWindows10OrGreaterTabletMode(HWND hwnd)384 bool IsWindows10OrGreaterTabletMode(HWND hwnd) {
385   if (GetVersion() >= Version::WIN11) {
386     // Only Win10 supports explicit tablet mode. On Win11,
387     // get_UserInteractionMode always returns UserInteractionMode_Mouse, so
388     // instead we check if we're in slate mode or not - 0 value means slate
389     // mode. See
390     // https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-gpiobuttons-convertibleslatemode
391 
392     constexpr int kKeyboardPresent = 1;
393     base::win::RegKey registry_key(
394         HKEY_LOCAL_MACHINE,
395         L"System\\CurrentControlSet\\Control\\PriorityControl",
396         KEY_QUERY_VALUE);
397     DWORD slate_mode = 0;
398     bool value_exists = registry_key.ReadValueDW(L"ConvertibleSlateMode",
399                                                  &slate_mode) == ERROR_SUCCESS;
400     // Some devices don't set the reg key to 1 for keyboard-only devices, so
401     // also check if the device is used as a tablet if it is not 1. Some devices
402     // don't set the registry key at all; fall back to checking if the device
403     // is used as a tablet for them as well.
404     return !(value_exists && slate_mode == kKeyboardPresent) &&
405            IsDeviceUsedAsATablet(/*reason=*/nullptr);
406   }
407 
408   ScopedHString view_settings_guid = ScopedHString::Create(
409       RuntimeClass_Windows_UI_ViewManagement_UIViewSettings);
410   Microsoft::WRL::ComPtr<IUIViewSettingsInterop> view_settings_interop;
411   HRESULT hr = ::RoGetActivationFactory(view_settings_guid.get(),
412                                         IID_PPV_ARGS(&view_settings_interop));
413   if (FAILED(hr))
414     return false;
415 
416   Microsoft::WRL::ComPtr<ABI::Windows::UI::ViewManagement::IUIViewSettings>
417       view_settings;
418   hr = view_settings_interop->GetForWindow(hwnd, IID_PPV_ARGS(&view_settings));
419   if (FAILED(hr))
420     return false;
421 
422   ABI::Windows::UI::ViewManagement::UserInteractionMode mode =
423       ABI::Windows::UI::ViewManagement::UserInteractionMode_Mouse;
424   view_settings->get_UserInteractionMode(&mode);
425   return mode == ABI::Windows::UI::ViewManagement::UserInteractionMode_Touch;
426 }
427 
QueryDeviceConvertibility()428 bool QueryDeviceConvertibility() {
429   // Ensure this function runs on a thread that allows blocking in the event
430   // that the WMI query in the chassis convertibility check is executed.
431   AssertBlockingAllowed();
432 
433   if (const auto& convertibility_enabled = GetConvertibilityEnabledOverride()) {
434     return *convertibility_enabled;
435   }
436 
437   if (const auto& convertible_chassis_key = GetConvertibleChassisKeyValue()) {
438     return *convertible_chassis_key;
439   }
440 
441   if (IsDeviceFormConvertible() || IsChassisConvertible()) {
442     return true;
443   }
444 
445   return (HasCSMStateChanged())();
446 }
447 
448 // Returns true if a physical keyboard is detected on Windows 8 and up.
449 // Uses the Setup APIs to enumerate the attached keyboards and returns true
450 // if the keyboard count is 1 or more.. While this will work in most cases
451 // it won't work if there are devices which expose keyboard interfaces which
452 // are attached to the machine.
IsKeyboardPresentOnSlate(HWND hwnd,std::string * reason)453 bool IsKeyboardPresentOnSlate(HWND hwnd, std::string* reason) {
454   bool result = false;
455 
456   if (CommandLine::ForCurrentProcess()->HasSwitch(
457           switches::kDisableUsbKeyboardDetect)) {
458     if (reason) {
459       *reason = "Detection disabled";
460     }
461     return false;
462   }
463 
464   // This function should be only invoked for machines with touch screens.
465   if ((GetSystemMetrics(SM_DIGITIZER) & NID_INTEGRATED_TOUCH) !=
466       NID_INTEGRATED_TOUCH) {
467     if (!reason) {
468       return true;
469     }
470 
471     *reason += "NID_INTEGRATED_TOUCH\n";
472     result = true;
473   }
474 
475   // If it is a tablet device we assume that there is no keyboard attached.
476   if (IsTabletDevice(reason, hwnd)) {
477     if (reason) {
478       *reason += "Tablet device.\n";
479     }
480     return false;
481   }
482 
483   if (!reason) {
484     return true;
485   }
486 
487   *reason += "Not a tablet device";
488   result = true;
489 
490   // To determine whether a keyboard is present on the device, we do the
491   // following:-
492   // 1. Check whether the device supports auto rotation. If it does then
493   //    it possibly supports flipping from laptop to slate mode. If it
494   //    does not support auto rotation, then we assume it is a desktop
495   //    or a normal laptop and assume that there is a keyboard.
496 
497   // 2. If the device supports auto rotation, then we get its platform role
498   //    and check the system metric SM_CONVERTIBLESLATEMODE to see if it is
499   //    being used in slate mode. If yes then we return false here to ensure
500   //    that the OSK is displayed.
501 
502   // 3. If step 1 and 2 fail then we check attached keyboards and return true
503   //    if we find ACPI\* or HID\VID* keyboards.
504 
505   using GetAutoRotationState = decltype(&::GetAutoRotationState);
506   static const auto get_rotation_state = reinterpret_cast<GetAutoRotationState>(
507       GetUser32FunctionPointer("GetAutoRotationState"));
508   if (get_rotation_state) {
509     AR_STATE auto_rotation_state = AR_ENABLED;
510     get_rotation_state(&auto_rotation_state);
511     if ((auto_rotation_state & AR_NOSENSOR) ||
512         (auto_rotation_state & AR_NOT_SUPPORTED)) {
513       // If there is no auto rotation sensor or rotation is not supported in
514       // the current configuration, then we can assume that this is a desktop
515       // or a traditional laptop.
516       if (!reason) {
517         return true;
518       }
519 
520       *reason += (auto_rotation_state & AR_NOSENSOR) ? "AR_NOSENSOR\n"
521                                                      : "AR_NOT_SUPPORTED\n";
522       result = true;
523     }
524   }
525 
526   const GUID KEYBOARD_CLASS_GUID = {
527       0x4D36E96B,
528       0xE325,
529       0x11CE,
530       {0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18}};
531 
532   // Query for all the keyboard devices.
533   HDEVINFO device_info = SetupDiGetClassDevs(&KEYBOARD_CLASS_GUID, nullptr,
534                                              nullptr, DIGCF_PRESENT);
535   if (device_info == INVALID_HANDLE_VALUE) {
536     if (reason) {
537       *reason += "No keyboard info\n";
538     }
539     return result;
540   }
541 
542   // Enumerate all keyboards and look for ACPI\PNP and HID\VID devices. If
543   // the count is more than 1 we assume that a keyboard is present. This is
544   // under the assumption that there will always be one keyboard device.
545   for (DWORD i = 0;; ++i) {
546     SP_DEVINFO_DATA device_info_data = {0};
547     device_info_data.cbSize = sizeof(device_info_data);
548     if (!SetupDiEnumDeviceInfo(device_info, i, &device_info_data))
549       break;
550 
551     // Get the device ID.
552     wchar_t device_id[MAX_DEVICE_ID_LEN];
553     CONFIGRET status = CM_Get_Device_ID(device_info_data.DevInst, device_id,
554                                         MAX_DEVICE_ID_LEN, 0);
555     if (status == CR_SUCCESS) {
556       // To reduce the scope of the hack we only look for ACPI and HID\\VID
557       // prefixes in the keyboard device ids.
558       if (StartsWith(device_id, L"ACPI", CompareCase::INSENSITIVE_ASCII) ||
559           StartsWith(device_id, L"HID\\VID", CompareCase::INSENSITIVE_ASCII)) {
560         if (reason) {
561           *reason += "device: ";
562           *reason += WideToUTF8(device_id);
563           *reason += '\n';
564         }
565         // The heuristic we are using is to check the count of keyboards and
566         // return true if the API's report one or more keyboards. Please note
567         // that this will break for non keyboard devices which expose a
568         // keyboard PDO.
569         result = true;
570       }
571     }
572   }
573   return result;
574 }
575 
576 static bool g_crash_on_process_detach = false;
577 
GetUserSidString(std::wstring * user_sid)578 bool GetUserSidString(std::wstring* user_sid) {
579   std::optional<AccessToken> token = AccessToken::FromCurrentProcess();
580   if (!token)
581     return false;
582   std::optional<std::wstring> sid_string = token->User().ToSddlString();
583   if (!sid_string)
584     return false;
585   *user_sid = *sid_string;
586   return true;
587 }
588 
589 class ScopedAllowBlockingForUserAccountControl : public ScopedAllowBlocking {};
590 
UserAccountControlIsEnabled()591 bool UserAccountControlIsEnabled() {
592   // This can be slow if Windows ends up going to disk.  Should watch this key
593   // for changes and only read it once, preferably on the file thread.
594   //   http://code.google.com/p/chromium/issues/detail?id=61644
595   ScopedAllowBlockingForUserAccountControl allow_blocking;
596 
597   RegKey key(HKEY_LOCAL_MACHINE,
598              L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System",
599              KEY_READ);
600   DWORD uac_enabled;
601   if (key.ReadValueDW(L"EnableLUA", &uac_enabled) != ERROR_SUCCESS) {
602     return true;
603   }
604   // Users can set the EnableLUA value to something arbitrary, like 2, which
605   // Vista will treat as UAC enabled, so we make sure it is not set to 0.
606   return (uac_enabled != 0);
607 }
608 
SetBooleanValueForPropertyStore(IPropertyStore * property_store,const PROPERTYKEY & property_key,bool property_bool_value)609 bool SetBooleanValueForPropertyStore(IPropertyStore* property_store,
610                                      const PROPERTYKEY& property_key,
611                                      bool property_bool_value) {
612   ScopedPropVariant property_value;
613   if (FAILED(InitPropVariantFromBoolean(property_bool_value,
614                                         property_value.Receive()))) {
615     return false;
616   }
617 
618   return SetPropVariantValueForPropertyStore(property_store, property_key,
619                                              property_value);
620 }
621 
SetStringValueForPropertyStore(IPropertyStore * property_store,const PROPERTYKEY & property_key,const wchar_t * property_string_value)622 bool SetStringValueForPropertyStore(IPropertyStore* property_store,
623                                     const PROPERTYKEY& property_key,
624                                     const wchar_t* property_string_value) {
625   ScopedPropVariant property_value;
626   if (FAILED(InitPropVariantFromString(property_string_value,
627                                        property_value.Receive()))) {
628     return false;
629   }
630 
631   return SetPropVariantValueForPropertyStore(property_store, property_key,
632                                              property_value);
633 }
634 
SetClsidForPropertyStore(IPropertyStore * property_store,const PROPERTYKEY & property_key,const CLSID & property_clsid_value)635 bool SetClsidForPropertyStore(IPropertyStore* property_store,
636                               const PROPERTYKEY& property_key,
637                               const CLSID& property_clsid_value) {
638   ScopedPropVariant property_value;
639   if (FAILED(InitPropVariantFromCLSID(property_clsid_value,
640                                       property_value.Receive()))) {
641     return false;
642   }
643 
644   return SetPropVariantValueForPropertyStore(property_store, property_key,
645                                              property_value);
646 }
647 
SetAppIdForPropertyStore(IPropertyStore * property_store,const wchar_t * app_id)648 bool SetAppIdForPropertyStore(IPropertyStore* property_store,
649                               const wchar_t* app_id) {
650   // App id should be less than 128 chars and contain no space. And recommended
651   // format is CompanyName.ProductName[.SubProduct.ProductNumber].
652   // See
653   // https://docs.microsoft.com/en-us/windows/win32/shell/appids#how-to-form-an-application-defined-appusermodelid
654   DCHECK_LT(lstrlen(app_id), 128);
655   DCHECK_EQ(wcschr(app_id, L' '), nullptr);
656 
657   return SetStringValueForPropertyStore(property_store, PKEY_AppUserModel_ID,
658                                         app_id);
659 }
660 
661 static const wchar_t kAutoRunKeyPath[] =
662     L"Software\\Microsoft\\Windows\\CurrentVersion\\Run";
663 
AddCommandToAutoRun(HKEY root_key,const std::wstring & name,const std::wstring & command)664 bool AddCommandToAutoRun(HKEY root_key,
665                          const std::wstring& name,
666                          const std::wstring& command) {
667   RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_SET_VALUE);
668   return (autorun_key.WriteValue(name.c_str(), command.c_str()) ==
669           ERROR_SUCCESS);
670 }
671 
RemoveCommandFromAutoRun(HKEY root_key,const std::wstring & name)672 bool RemoveCommandFromAutoRun(HKEY root_key, const std::wstring& name) {
673   RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_SET_VALUE);
674   return (autorun_key.DeleteValue(name.c_str()) == ERROR_SUCCESS);
675 }
676 
ReadCommandFromAutoRun(HKEY root_key,const std::wstring & name,std::wstring * command)677 bool ReadCommandFromAutoRun(HKEY root_key,
678                             const std::wstring& name,
679                             std::wstring* command) {
680   RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_QUERY_VALUE);
681   return (autorun_key.ReadValue(name.c_str(), command) == ERROR_SUCCESS);
682 }
683 
SetShouldCrashOnProcessDetach(bool crash)684 void SetShouldCrashOnProcessDetach(bool crash) {
685   g_crash_on_process_detach = crash;
686 }
687 
ShouldCrashOnProcessDetach()688 bool ShouldCrashOnProcessDetach() {
689   return g_crash_on_process_detach;
690 }
691 
SetAbortBehaviorForCrashReporting()692 void SetAbortBehaviorForCrashReporting() {
693   // Prevent CRT's abort code from prompting a dialog or trying to "report" it.
694   // Disabling the _CALL_REPORTFAULT behavior is important since otherwise it
695   // has the sideffect of clearing our exception filter, which means we
696   // don't get any crash.
697   _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
698 
699   // Set a SIGABRT handler for good measure. We will crash even if the default
700   // is left in place, however this allows us to crash earlier. And it also
701   // lets us crash in response to code which might directly call raise(SIGABRT)
702   signal(SIGABRT, ForceCrashOnSigAbort);
703 }
704 
IsTabletDevice(std::string * reason,HWND hwnd)705 bool IsTabletDevice(std::string* reason, HWND hwnd) {
706   if (IsWindows10OrGreaterTabletMode(hwnd))
707     return true;
708 
709   return IsDeviceUsedAsATablet(reason);
710 }
711 
712 // This method is used to set the right interactions media queries,
713 // see https://drafts.csswg.org/mediaqueries-4/#mf-interaction. It doesn't
714 // check the Windows 10 tablet mode because it doesn't reflect the actual
715 // input configuration of the device and can be manually triggered by the user
716 // independently from the hardware state.
IsDeviceUsedAsATablet(std::string * reason)717 bool IsDeviceUsedAsATablet(std::string* reason) {
718   // Once this is set, it shouldn't be overridden, and it should be the ultimate
719   // return value, so that this method returns the same result whether or not
720   // reason is NULL.
721   std::optional<bool> ret;
722 
723   if (GetSystemMetrics(SM_MAXIMUMTOUCHES) == 0) {
724     if (!reason) {
725       return false;
726     }
727 
728     *reason += "Device does not support touch.\n";
729     ret = false;
730   }
731 
732   // If the device is docked, the user is treating the device as a PC.
733   if (GetSystemMetrics(SM_SYSTEMDOCKED) != 0) {
734     if (!reason) {
735       return false;
736     }
737 
738     *reason += "SM_SYSTEMDOCKED\n";
739     if (!ret.has_value()) {
740       ret = false;
741     }
742   }
743 
744   // If the device is not supporting rotation, it's unlikely to be a tablet,
745   // a convertible or a detachable.
746   // See
747   // https://msdn.microsoft.com/en-us/library/windows/desktop/dn629263(v=vs.85).aspx
748   using GetAutoRotationStateType = decltype(GetAutoRotationState)*;
749   static const auto get_auto_rotation_state_func =
750       reinterpret_cast<GetAutoRotationStateType>(
751           GetUser32FunctionPointer("GetAutoRotationState"));
752   if (get_auto_rotation_state_func) {
753     AR_STATE rotation_state = AR_ENABLED;
754     if (get_auto_rotation_state_func(&rotation_state) &&
755         (rotation_state & (AR_NOT_SUPPORTED | AR_LAPTOP | AR_NOSENSOR)) != 0) {
756       return ret.value_or(false);
757     }
758   }
759 
760   // PlatformRoleSlate was added in Windows 8+.
761   POWER_PLATFORM_ROLE role = GetPlatformRole();
762   bool is_tablet = false;
763   if (role == PlatformRoleMobile || role == PlatformRoleSlate) {
764     is_tablet = !GetSystemMetrics(SM_CONVERTIBLESLATEMODE);
765     if (!is_tablet) {
766       if (!reason) {
767         return false;
768       }
769 
770       *reason += "Not in slate mode.\n";
771       if (!ret.has_value()) {
772         ret = false;
773       }
774     } else if (reason) {
775       *reason += (role == PlatformRoleMobile) ? "PlatformRoleMobile\n"
776                                               : "PlatformRoleSlate\n";
777     }
778   } else if (reason) {
779     *reason += "Device role is not mobile or slate.\n";
780   }
781   return ret.value_or(is_tablet);
782 }
783 
IsEnrolledToDomain()784 bool IsEnrolledToDomain() {
785   return *GetDomainEnrollmentStateStorage();
786 }
787 
IsDeviceRegisteredWithManagement()788 bool IsDeviceRegisteredWithManagement() {
789   // GetRegisteredWithManagementStateStorage() can be true for devices running
790   // the Home sku, however the Home sku does not allow for management of the web
791   // browser. As such, we automatically exclude devices running the Home sku.
792   if (OSInfo::GetInstance()->version_type() == SUITE_HOME)
793     return false;
794   return *GetRegisteredWithManagementStateStorage();
795 }
796 
IsJoinedToAzureAD()797 bool IsJoinedToAzureAD() {
798   return *GetAzureADJoinStateStorage();
799 }
800 
IsUser32AndGdi32Available()801 bool IsUser32AndGdi32Available() {
802   static const bool is_user32_and_gdi32_available = [] {
803     // If win32k syscalls aren't disabled, then user32 and gdi32 are available.
804     PROCESS_MITIGATION_SYSTEM_CALL_DISABLE_POLICY policy = {};
805     if (::GetProcessMitigationPolicy(GetCurrentProcess(),
806                                      ProcessSystemCallDisablePolicy, &policy,
807                                      sizeof(policy))) {
808       return policy.DisallowWin32kSystemCalls == 0;
809     }
810 
811     return true;
812   }();
813   return is_user32_and_gdi32_available;
814 }
815 
GetLoadedModulesSnapshot(HANDLE process,std::vector<HMODULE> * snapshot)816 bool GetLoadedModulesSnapshot(HANDLE process, std::vector<HMODULE>* snapshot) {
817   DCHECK(snapshot);
818   DCHECK_EQ(0u, snapshot->size());
819   snapshot->resize(128);
820 
821   // We will retry at least once after first determining |bytes_required|. If
822   // the list of modules changes after we receive |bytes_required| we may retry
823   // more than once.
824   int retries_remaining = 5;
825   do {
826     DWORD bytes_required = 0;
827     // EnumProcessModules returns 'success' even if the buffer size is too
828     // small.
829     DCHECK_GE(std::numeric_limits<DWORD>::max(),
830               snapshot->size() * sizeof(HMODULE));
831     if (!::EnumProcessModules(
832             process, &(*snapshot)[0],
833             static_cast<DWORD>(snapshot->size() * sizeof(HMODULE)),
834             &bytes_required)) {
835       DPLOG(ERROR) << "::EnumProcessModules failed.";
836       return false;
837     }
838 
839     DCHECK_EQ(0u, bytes_required % sizeof(HMODULE));
840     size_t num_modules = bytes_required / sizeof(HMODULE);
841     if (num_modules <= snapshot->size()) {
842       // Buffer size was too big, presumably because a module was unloaded.
843       snapshot->erase(snapshot->begin() + static_cast<ptrdiff_t>(num_modules),
844                       snapshot->end());
845       return true;
846     }
847 
848     if (num_modules == 0) {
849       DLOG(ERROR) << "Can't determine the module list size.";
850       return false;
851     }
852 
853     // Buffer size was too small. Try again with a larger buffer. A little
854     // more room is given to avoid multiple expensive calls to
855     // ::EnumProcessModules() just because one module has been added.
856     snapshot->resize(num_modules + 8, nullptr);
857   } while (--retries_remaining);
858 
859   DLOG(ERROR) << "Failed to enumerate modules.";
860   return false;
861 }
862 
EnableFlicks(HWND hwnd)863 void EnableFlicks(HWND hwnd) {
864   ::RemoveProp(hwnd, MICROSOFT_TABLETPENSERVICE_PROPERTY);
865 }
866 
DisableFlicks(HWND hwnd)867 void DisableFlicks(HWND hwnd) {
868   ::SetProp(hwnd, MICROSOFT_TABLETPENSERVICE_PROPERTY,
869             reinterpret_cast<HANDLE>(TABLET_DISABLE_FLICKS |
870                                      TABLET_DISABLE_FLICKFALLBACKKEYS));
871 }
872 
EnableHighDPISupport()873 void EnableHighDPISupport() {
874   if (!IsUser32AndGdi32Available())
875     return;
876 
877   // Enable per-monitor V2 if it is available (Win10 1703 or later).
878   if (EnablePerMonitorV2())
879     return;
880 
881   // Fall back to per-monitor DPI for older versions of Win10.
882   PROCESS_DPI_AWARENESS process_dpi_awareness = PROCESS_PER_MONITOR_DPI_AWARE;
883   if (!::SetProcessDpiAwareness(process_dpi_awareness)) {
884     // For windows versions where SetProcessDpiAwareness fails, try its
885     // predecessor.
886     BOOL result = ::SetProcessDPIAware();
887     DCHECK(result) << "SetProcessDPIAware failed.";
888   }
889 }
890 
WStringFromGUID(const::GUID & rguid)891 std::wstring WStringFromGUID(const ::GUID& rguid) {
892   // This constant counts the number of characters in the formatted string,
893   // including the null termination character.
894   constexpr int kGuidStringCharacters =
895       1 + 8 + 1 + 4 + 1 + 4 + 1 + 4 + 1 + 12 + 1 + 1;
896   wchar_t guid_string[kGuidStringCharacters];
897   CHECK(SUCCEEDED(StringCchPrintfW(
898       guid_string, kGuidStringCharacters,
899       L"{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", rguid.Data1,
900       rguid.Data2, rguid.Data3, rguid.Data4[0], rguid.Data4[1], rguid.Data4[2],
901       rguid.Data4[3], rguid.Data4[4], rguid.Data4[5], rguid.Data4[6],
902       rguid.Data4[7])));
903   return std::wstring(guid_string, kGuidStringCharacters - 1);
904 }
905 
PinUser32(NativeLibraryLoadError * error)906 bool PinUser32(NativeLibraryLoadError* error) {
907   return PinUser32Internal(error) != nullptr;
908 }
909 
GetUser32FunctionPointer(const char * function_name,NativeLibraryLoadError * error)910 void* GetUser32FunctionPointer(const char* function_name,
911                                NativeLibraryLoadError* error) {
912   NativeLibrary user32_module = PinUser32Internal(error);
913   if (user32_module)
914     return GetFunctionPointerFromNativeLibrary(user32_module, function_name);
915   return nullptr;
916 }
917 
GetWindowObjectName(HANDLE handle)918 std::wstring GetWindowObjectName(HANDLE handle) {
919   // Get the size of the name.
920   std::wstring object_name;
921 
922   DWORD size = 0;
923   ::GetUserObjectInformation(handle, UOI_NAME, nullptr, 0, &size);
924   if (!size) {
925     DPCHECK(false);
926     return object_name;
927   }
928 
929   LOG_ASSERT(size % sizeof(wchar_t) == 0u);
930 
931   // Query the name of the object.
932   if (!::GetUserObjectInformation(
933           handle, UOI_NAME, WriteInto(&object_name, size / sizeof(wchar_t)),
934           size, &size)) {
935     DPCHECK(false);
936   }
937 
938   return object_name;
939 }
940 
GetPointerDevice(HANDLE device,POINTER_DEVICE_INFO & result)941 bool GetPointerDevice(HANDLE device, POINTER_DEVICE_INFO& result) {
942   return ::GetPointerDevice(device, &result);
943 }
944 
GetPointerDevices()945 std::optional<std::vector<POINTER_DEVICE_INFO>> GetPointerDevices() {
946   uint32_t device_count;
947   if (!::GetPointerDevices(&device_count, nullptr)) {
948     return std::nullopt;
949   }
950 
951   std::vector<POINTER_DEVICE_INFO> pointer_devices(device_count);
952   if (!::GetPointerDevices(&device_count, pointer_devices.data())) {
953     return std::nullopt;
954   }
955   return pointer_devices;
956 }
957 
RegisterPointerDeviceNotifications(HWND hwnd,bool notify_proximity_changes)958 bool RegisterPointerDeviceNotifications(HWND hwnd,
959                                         bool notify_proximity_changes) {
960   return ::RegisterPointerDeviceNotifications(hwnd, notify_proximity_changes);
961 }
962 
IsRunningUnderDesktopName(std::wstring_view desktop_name)963 bool IsRunningUnderDesktopName(std::wstring_view desktop_name) {
964   HDESK thread_desktop = ::GetThreadDesktop(::GetCurrentThreadId());
965   if (!thread_desktop)
966     return false;
967 
968   std::wstring current_desktop_name = GetWindowObjectName(thread_desktop);
969   return EqualsCaseInsensitiveASCII(AsStringPiece16(current_desktop_name),
970                                     AsStringPiece16(desktop_name));
971 }
972 
973 // This method is used to detect whether current session is a remote session.
974 // See:
975 // https://docs.microsoft.com/en-us/windows/desktop/TermServ/detecting-the-terminal-services-environment
IsCurrentSessionRemote()976 bool IsCurrentSessionRemote() {
977   if (::GetSystemMetrics(SM_REMOTESESSION))
978     return true;
979 
980   DWORD current_session_id = 0;
981 
982   if (!::ProcessIdToSessionId(::GetCurrentProcessId(), &current_session_id))
983     return false;
984 
985   static constexpr wchar_t kRdpSettingsKeyName[] =
986       L"SYSTEM\\CurrentControlSet\\Control\\Terminal Server";
987   RegKey key(HKEY_LOCAL_MACHINE, kRdpSettingsKeyName, KEY_READ);
988   if (!key.Valid())
989     return false;
990 
991   static constexpr wchar_t kGlassSessionIdValueName[] = L"GlassSessionId";
992   DWORD glass_session_id = 0;
993   if (key.ReadValueDW(kGlassSessionIdValueName, &glass_session_id) !=
994       ERROR_SUCCESS) {
995     return false;
996   }
997 
998   return current_session_id != glass_session_id;
999 }
1000 
IsAppVerifierLoaded()1001 bool IsAppVerifierLoaded() {
1002   return GetModuleHandleA(kApplicationVerifierDllName);
1003 }
1004 
ExpandEnvironmentVariables(wcstring_view str)1005 std::optional<std::wstring> ExpandEnvironmentVariables(wcstring_view str) {
1006   std::wstring path_expanded;
1007   DWORD path_len = MAX_PATH;
1008   for (int iterations = 0; iterations < 5; iterations++) {
1009     DWORD result = ::ExpandEnvironmentStringsW(
1010         str.c_str(), base::WriteInto(&path_expanded, path_len), path_len);
1011     if (!result) {
1012       // Failed to expand variables.
1013       break;
1014     }
1015     if (result <= path_len) {
1016       return path_expanded.substr(0, result - 1);
1017     }
1018     path_len = result;
1019   }
1020 
1021   return std::nullopt;
1022 }
1023 
ScopedDomainStateForTesting(bool state)1024 ScopedDomainStateForTesting::ScopedDomainStateForTesting(bool state)
1025     : initial_state_(IsEnrolledToDomain()) {
1026   *GetDomainEnrollmentStateStorage() = state;
1027 }
1028 
~ScopedDomainStateForTesting()1029 ScopedDomainStateForTesting::~ScopedDomainStateForTesting() {
1030   *GetDomainEnrollmentStateStorage() = initial_state_;
1031 }
1032 
1033 ScopedDeviceRegisteredWithManagementForTesting::
ScopedDeviceRegisteredWithManagementForTesting(bool state)1034     ScopedDeviceRegisteredWithManagementForTesting(bool state)
1035     : initial_state_(IsDeviceRegisteredWithManagement()) {
1036   *GetRegisteredWithManagementStateStorage() = state;
1037 }
1038 
1039 ScopedDeviceRegisteredWithManagementForTesting::
~ScopedDeviceRegisteredWithManagementForTesting()1040     ~ScopedDeviceRegisteredWithManagementForTesting() {
1041   *GetRegisteredWithManagementStateStorage() = initial_state_;
1042 }
1043 
ScopedAzureADJoinStateForTesting(bool state)1044 ScopedAzureADJoinStateForTesting::ScopedAzureADJoinStateForTesting(bool state)
1045     : initial_state_(std::exchange(*GetAzureADJoinStateStorage(), state)) {}
1046 
~ScopedAzureADJoinStateForTesting()1047 ScopedAzureADJoinStateForTesting::~ScopedAzureADJoinStateForTesting() {
1048   *GetAzureADJoinStateStorage() = initial_state_;
1049 }
1050 
1051 ScopedDeviceConvertibilityStateForTesting::
ScopedDeviceConvertibilityStateForTesting(bool form_convertible,bool chassis_convertible,QueryKeyFunction csm_changed,std::optional<bool> convertible_chassis_key,std::optional<bool> convertibility_enabled)1052     ScopedDeviceConvertibilityStateForTesting(
1053         bool form_convertible,
1054         bool chassis_convertible,
1055         QueryKeyFunction csm_changed,
1056         std::optional<bool> convertible_chassis_key,
1057         std::optional<bool> convertibility_enabled)
1058     : initial_form_convertible_(&IsDeviceFormConvertible(), form_convertible),
1059       initial_chassis_convertible_(&IsChassisConvertible(),
1060                                    chassis_convertible),
1061       initial_csm_changed_(&HasCSMStateChanged(), csm_changed),
1062       initial_convertible_chassis_key_(&GetConvertibleChassisKeyValue(),
1063                                        convertible_chassis_key),
1064       initial_convertibility_enabled_(&GetConvertibilityEnabledOverride(),
1065                                       convertibility_enabled) {}
1066 
1067 ScopedDeviceConvertibilityStateForTesting::
1068     ~ScopedDeviceConvertibilityStateForTesting() = default;
1069 
1070 }  // namespace win
1071 }  // namespace base
1072