xref: /aosp_15_r20/external/cronet/base/win/wmi.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2010 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 #include "base/win/wmi.h"
6 
7 #include <objbase.h>
8 
9 #include <windows.h>
10 
11 #include <stdint.h>
12 
13 #include <string_view>
14 #include <utility>
15 
16 #include "base/location.h"
17 #include "base/no_destructor.h"
18 #include "base/strings/string_util.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/threading/scoped_thread_priority.h"
21 #include "base/win/scoped_bstr.h"
22 #include "base/win/scoped_variant.h"
23 
24 using Microsoft::WRL::ComPtr;
25 
26 namespace base {
27 namespace win {
28 
29 const wchar_t kCimV2ServerName[] = L"ROOT\\CIMV2";
30 
31 const wchar_t kSecurityCenter2ServerName[] = L"ROOT\\SecurityCenter2";
32 
33 namespace {
34 
35 constexpr wchar_t kSerialNumberQuery[] = L"SELECT SerialNumber FROM Win32_Bios";
36 
37 // Instantiates `wmi_services` with a connection to `server_name` in WMI. Will
38 // set a security blanket if `set_blanket` is true.
CreateLocalWmiConnection(bool set_blanket,const std::wstring & server_name,ComPtr<IWbemServices> * wmi_services)39 std::optional<WmiError> CreateLocalWmiConnection(
40     bool set_blanket,
41     const std::wstring& server_name,
42     ComPtr<IWbemServices>* wmi_services) {
43   DCHECK(wmi_services);
44   ComPtr<IWbemLocator> wmi_locator;
45   HRESULT hr =
46       ::CoCreateInstance(CLSID_WbemLocator, nullptr, CLSCTX_INPROC_SERVER,
47                          IID_PPV_ARGS(&wmi_locator));
48   if (FAILED(hr))
49     return WmiError::kFailedToCreateInstance;
50 
51   ComPtr<IWbemServices> wmi_services_r;
52   hr = wmi_locator->ConnectServer(base::win::ScopedBstr(server_name).Get(),
53                                   nullptr, nullptr, nullptr, 0, nullptr,
54                                   nullptr, &wmi_services_r);
55   if (FAILED(hr))
56     return WmiError::kFailedToConnectToWMI;
57 
58   if (set_blanket) {
59     hr = ::CoSetProxyBlanket(wmi_services_r.Get(), RPC_C_AUTHN_WINNT,
60                              RPC_C_AUTHZ_NONE, nullptr, RPC_C_AUTHN_LEVEL_CALL,
61                              RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_NONE);
62     if (FAILED(hr))
63       return WmiError::kFailedToSetSecurityBlanket;
64   }
65 
66   *wmi_services = std::move(wmi_services_r);
67   return std::nullopt;
68 }
69 
70 // Runs `query` through `wmi_services` and sets the results' `enumerator`.
TryRunQuery(const std::wstring & query,const ComPtr<IWbemServices> & wmi_services,ComPtr<IEnumWbemClassObject> * enumerator)71 bool TryRunQuery(const std::wstring& query,
72                  const ComPtr<IWbemServices>& wmi_services,
73                  ComPtr<IEnumWbemClassObject>* enumerator) {
74   DCHECK(enumerator);
75   base::win::ScopedBstr query_language(L"WQL");
76   base::win::ScopedBstr query_bstr(query);
77 
78   ComPtr<IEnumWbemClassObject> enumerator_r;
79   HRESULT hr = wmi_services->ExecQuery(
80       query_language.Get(), query_bstr.Get(),
81       WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, nullptr,
82       &enumerator_r);
83 
84   if (FAILED(hr))
85     return false;
86 
87   *enumerator = std::move(enumerator_r);
88   return true;
89 }
90 
91 }  // namespace
92 
RunWmiQuery(const std::wstring & server_name,const std::wstring & query,ComPtr<IEnumWbemClassObject> * enumerator)93 std::optional<WmiError> RunWmiQuery(const std::wstring& server_name,
94                                     const std::wstring& query,
95                                     ComPtr<IEnumWbemClassObject>* enumerator) {
96   SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
97 
98   DCHECK(enumerator);
99 
100   ComPtr<IWbemServices> wmi_services;
101   auto error = CreateLocalWmiConnection(/*set_blanket=*/true, server_name,
102                                         &wmi_services);
103 
104   if (error.has_value())
105     return error;
106 
107   if (!TryRunQuery(query, wmi_services, enumerator))
108     return WmiError::kFailedToExecWMIQuery;
109 
110   return std::nullopt;
111 }
112 
CreateLocalWmiConnection(bool set_blanket,ComPtr<IWbemServices> * wmi_services)113 bool CreateLocalWmiConnection(bool set_blanket,
114                               ComPtr<IWbemServices>* wmi_services) {
115   // Mitigate the issues caused by loading DLLs on a background thread
116   // (http://crbug/973868).
117   SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
118 
119   auto error =
120       CreateLocalWmiConnection(set_blanket, kCimV2ServerName, wmi_services);
121   return !error.has_value();
122 }
123 
CreateWmiConnection(bool set_blanket,const std::wstring & resource)124 ComPtr<IWbemServices> CreateWmiConnection(bool set_blanket,
125                                           const std::wstring& resource) {
126   // Mitigate the issues caused by loading DLLs on a background thread
127   // (http://crbug/973868).
128   SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
129 
130   ComPtr<IWbemServices> wmi_services = nullptr;
131   auto error = CreateLocalWmiConnection(set_blanket, resource, &wmi_services);
132   if (error.has_value())
133     return nullptr;
134   return wmi_services;
135 }
136 
CreateWmiClassMethodObject(IWbemServices * wmi_services,std::wstring_view class_name,std::wstring_view method_name,ComPtr<IWbemClassObject> * class_instance)137 bool CreateWmiClassMethodObject(IWbemServices* wmi_services,
138                                 std::wstring_view class_name,
139                                 std::wstring_view method_name,
140                                 ComPtr<IWbemClassObject>* class_instance) {
141   // We attempt to instantiate a COM object that represents a WMI object plus
142   // a method rolled into one entity.
143   ScopedBstr b_class_name(class_name);
144   ScopedBstr b_method_name(method_name);
145   ComPtr<IWbemClassObject> class_object;
146   HRESULT hr;
147   hr = wmi_services->GetObject(b_class_name.Get(), 0, nullptr, &class_object,
148                                nullptr);
149   if (FAILED(hr))
150     return false;
151 
152   ComPtr<IWbemClassObject> params_def;
153   hr = class_object->GetMethod(b_method_name.Get(), 0, &params_def, nullptr);
154   if (FAILED(hr))
155     return false;
156 
157   if (!params_def.Get()) {
158     // You hit this special case if the WMI class is not a CIM class. MSDN
159     // sometimes tells you this. Welcome to WMI hell.
160     return false;
161   }
162 
163   hr = params_def->SpawnInstance(0, &(*class_instance));
164   return SUCCEEDED(hr);
165 }
166 
167 // The code in Launch() basically calls the Create Method of the Win32_Process
168 // CIM class is documented here:
169 // http://msdn2.microsoft.com/en-us/library/aa389388(VS.85).aspx
170 // NOTE: The documentation for the Create method suggests that the ProcessId
171 // parameter and return value are of type uint32_t, but when we call the method
172 // the values in the returned out_params, are VT_I4, which is int32_t.
WmiLaunchProcess(const std::wstring & command_line,int * process_id)173 bool WmiLaunchProcess(const std::wstring& command_line, int* process_id) {
174   ComPtr<IWbemServices> wmi_local;
175   if (!CreateLocalWmiConnection(true, &wmi_local))
176     return false;
177 
178   static constexpr wchar_t class_name[] = L"Win32_Process";
179   static constexpr wchar_t method_name[] = L"Create";
180   ComPtr<IWbemClassObject> process_create;
181   if (!CreateWmiClassMethodObject(wmi_local.Get(), class_name, method_name,
182                                   &process_create)) {
183     return false;
184   }
185 
186   ScopedVariant b_command_line(command_line.c_str());
187 
188   if (FAILED(process_create->Put(L"CommandLine", 0, b_command_line.AsInput(),
189                                  0))) {
190     return false;
191   }
192 
193   ComPtr<IWbemClassObject> out_params;
194   HRESULT hr = wmi_local->ExecMethod(
195       ScopedBstr(class_name).Get(), ScopedBstr(method_name).Get(), 0, nullptr,
196       process_create.Get(), &out_params, nullptr);
197   if (FAILED(hr))
198     return false;
199 
200   // We're only expecting int32_t or uint32_t values, so no need for
201   // ScopedVariant.
202   VARIANT ret_value = {{{VT_EMPTY}}};
203   hr = out_params->Get(L"ReturnValue", 0, &ret_value, nullptr, nullptr);
204   if (FAILED(hr) || V_I4(&ret_value) != 0)
205     return false;
206 
207   VARIANT pid = {{{VT_EMPTY}}};
208   hr = out_params->Get(L"ProcessId", 0, &pid, nullptr, nullptr);
209   if (FAILED(hr) || V_I4(&pid) == 0)
210     return false;
211 
212   if (process_id)
213     *process_id = V_I4(&pid);
214 
215   return true;
216 }
217 
218 // static
Get()219 WmiComputerSystemInfo WmiComputerSystemInfo::Get() {
220   static const base::NoDestructor<WmiComputerSystemInfo> static_info([] {
221     WmiComputerSystemInfo info;
222     ComPtr<IEnumWbemClassObject> enumerator_bios;
223     auto error =
224         RunWmiQuery(kCimV2ServerName, kSerialNumberQuery, &enumerator_bios);
225     if (!error.has_value())
226       info.PopulateSerialNumber(enumerator_bios);
227     return info;
228   }());
229   return *static_info;
230 }
231 
PopulateSerialNumber(const ComPtr<IEnumWbemClassObject> & enumerator_bios)232 void WmiComputerSystemInfo::PopulateSerialNumber(
233     const ComPtr<IEnumWbemClassObject>& enumerator_bios) {
234   ComPtr<IWbemClassObject> class_obj;
235   ULONG items_returned = 0;
236   HRESULT hr =
237       enumerator_bios->Next(WBEM_INFINITE, 1, &class_obj, &items_returned);
238   if (FAILED(hr) || !items_returned)
239     return;
240 
241   ScopedVariant serial_number;
242   hr = class_obj->Get(L"SerialNumber", 0, serial_number.Receive(), nullptr,
243                       nullptr);
244   if (SUCCEEDED(hr) && serial_number.type() == VT_BSTR) {
245     serial_number_.assign(V_BSTR(serial_number.ptr()),
246                           ::SysStringLen(V_BSTR(serial_number.ptr())));
247   }
248 }
249 
250 }  // namespace win
251 }  // namespace base
252