1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 **/
19
20 #include "sysinc.h"
21
22 extern "C"
23 {
24 # include "common.h"
25 # include "sysinfo.h"
26 # include "log.h"
27 # include "cfg.h"
28 }
29
30 #include <comdef.h>
31 #include <Wbemidl.h>
32
33 #pragma comment(lib, "wbemuuid.lib")
34
35 ZBX_THREAD_LOCAL static int com_initialized = 0;
36
zbx_co_initialize()37 extern "C" int zbx_co_initialize()
38 {
39 if (0 == com_initialized)
40 {
41 HRESULT hres;
42
43 /* must be called once per each thread */
44 hres = CoInitializeEx(0, COINIT_MULTITHREADED);
45
46 if (FAILED(hres))
47 {
48 zabbix_log(LOG_LEVEL_DEBUG, "cannot initialized COM library");
49 return FAIL;
50 }
51
52 /* must be called once per process, subsequent calls return RPC_E_TOO_LATE */
53 hres = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT,
54 RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
55
56 if (FAILED(hres) && RPC_E_TOO_LATE != hres)
57 {
58 zabbix_log(LOG_LEVEL_DEBUG, "cannot set default security levels for COM library");
59 CoUninitialize();
60 return FAIL;
61 }
62
63 com_initialized = 1;
64 }
65
66 return SUCCEED;
67 }
68
zbx_co_uninitialize()69 extern "C" void zbx_co_uninitialize()
70 {
71 if (1 == com_initialized)
72 CoUninitialize();
73 }
74
75 /******************************************************************************
76 * *
77 * Function: zbx_wmi_get_variant *
78 * *
79 * Purpose: retrieves WMI value and stores it in the provided memory location *
80 * *
81 * Parameters: wmi_namespace [IN] - object path of the WMI namespace (UTF-8) *
82 * wmi_query [IN] - WQL query (UTF-8) *
83 * timeout [IN] - query timeout in seconds *
84 * vtProp [OUT] - pointer to memory for the queried value *
85 * *
86 * Return value: SYSINFO_RET_OK - *vtProp contains the retrieved WMI value *
87 * SYSINFO_RET_FAIL - retrieving WMI value failed *
88 * *
89 * Comments: *vtProp must be initialized with VariantInit(), *
90 * wmi_* must not be NULL. The callers must convert value to the *
91 * intended format using VariantChangeType() *
92 * *
93 ******************************************************************************/
zbx_wmi_get_variant(const char * wmi_namespace,const char * wmi_query,double timeout,VARIANT * vtProp)94 extern "C" int zbx_wmi_get_variant(const char *wmi_namespace, const char *wmi_query, double timeout, VARIANT *vtProp)
95 {
96 IWbemLocator *pLoc = 0;
97 IWbemServices *pService = 0;
98 IEnumWbemClassObject *pEnumerator = 0;
99 int ret = SYSINFO_RET_FAIL;
100 HRESULT hres;
101 wchar_t *wmi_namespace_wide;
102 wchar_t *wmi_query_wide;
103 ULONG obj_num = 0;
104
105 /* obtain the initial locator to Windows Management on a particular host computer */
106 hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pLoc);
107
108 if (FAILED(hres))
109 {
110 zabbix_log(LOG_LEVEL_DEBUG, "cannot obtain WMI locator service");
111 goto exit;
112 }
113
114 wmi_namespace_wide = zbx_utf8_to_unicode(wmi_namespace);
115 hres = pLoc->ConnectServer(_bstr_t(wmi_namespace_wide), NULL, NULL, 0, NULL, 0, 0, &pService);
116 zbx_free(wmi_namespace_wide);
117
118 if (FAILED(hres))
119 {
120 zabbix_log(LOG_LEVEL_DEBUG, "cannot obtain %s WMI service", wmi_namespace);
121 goto exit;
122 }
123
124 /* set the IWbemServices proxy so that impersonation of the user (client) occurs */
125 hres = CoSetProxyBlanket(pService, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL,
126 RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
127
128 if (FAILED(hres))
129 {
130 zabbix_log(LOG_LEVEL_DEBUG, "cannot set IWbemServices proxy");
131 goto exit;
132 }
133
134 wmi_query_wide = zbx_utf8_to_unicode(wmi_query);
135 hres = pService->ExecQuery(_bstr_t("WQL"), _bstr_t(wmi_query_wide),
136 WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator);
137 zbx_free(wmi_query_wide);
138
139 if (FAILED(hres))
140 {
141 zabbix_log(LOG_LEVEL_DEBUG, "failed to execute WMI query %s", wmi_query);
142 goto exit;
143 }
144
145 while (pEnumerator)
146 {
147 IWbemClassObject *pclsObj = 0;
148 ULONG uReturn = 0;
149
150 hres = pEnumerator->Next((long)(1000 * timeout), 1, &pclsObj, &uReturn);
151
152 if (WBEM_S_TIMEDOUT == hres)
153 {
154 ret = SYSINFO_RET_FAIL;
155 zabbix_log(LOG_LEVEL_DEBUG, "WMI query timeout");
156 goto exit;
157 }
158
159 if (0 == uReturn)
160 goto exit;
161
162 obj_num += uReturn;
163
164 if (1 == obj_num)
165 {
166 hres = pclsObj->BeginEnumeration(WBEM_FLAG_NONSYSTEM_ONLY);
167
168 if (FAILED(hres))
169 {
170 zabbix_log(LOG_LEVEL_DEBUG, "cannot start WMI query result enumeration");
171 goto out;
172 }
173
174 hres = pclsObj->Next(0, NULL, vtProp, 0, 0);
175
176 if (FAILED(hres))
177 {
178 zabbix_log(LOG_LEVEL_DEBUG, "cannot convert WMI result of type %d to VT_BSTR",
179 vtProp->vt);
180 goto out;
181 }
182
183 pclsObj->EndEnumeration();
184
185 if (FAILED(hres) || hres == WBEM_S_NO_MORE_DATA)
186 goto out;
187 else
188 ret = SYSINFO_RET_OK;
189 }
190
191 out:
192 if (0 != pclsObj)
193 pclsObj->Release();
194 }
195
196 exit:
197 if (0 != pEnumerator)
198 pEnumerator->Release();
199
200 if (0 != pService)
201 pService->Release();
202
203 if (0 != pLoc)
204 pLoc->Release();
205
206 return ret;
207 }
208
209 /******************************************************************************
210 * *
211 * Function: zbx_wmi_get *
212 * *
213 * Purpose: wrapper function for zbx_wmi_get_variant(), stores the retrieved *
214 * WMI value as UTF-8 encoded string *
215 * *
216 * Parameters: wmi_namespace [IN] - object path of the WMI namespace (UTF-8) *
217 * wmi_query [IN] - WQL query (UTF-8) *
218 * utf8_value [OUT] - address of the pointer to the retrieved *
219 * value (dynamically allocated) *
220 * *
221 * Comments: if either retrieval or type conversion failed then *utf8_value *
222 * remains unchanged (set it to NULL before calling this function *
223 * to check for this condition). Callers must free *utf8_value. *
224 * *
225 ******************************************************************************/
zbx_wmi_get(const char * wmi_namespace,const char * wmi_query,double timeout,char ** utf8_value)226 extern "C" void zbx_wmi_get(const char *wmi_namespace, const char *wmi_query, double timeout, char **utf8_value)
227 {
228 VARIANT vtProp;
229 HRESULT hres;
230
231 VariantInit(&vtProp);
232
233 if (SUCCEED != zbx_co_initialize())
234 {
235 zabbix_log(LOG_LEVEL_DEBUG, "cannot initialize COM library for querying WMI");
236 goto out;
237 }
238
239 if (SYSINFO_RET_FAIL == zbx_wmi_get_variant(wmi_namespace, wmi_query, timeout, &vtProp))
240 {
241 zabbix_log(LOG_LEVEL_DEBUG, "cannot get WMI result");
242 goto out;
243 }
244
245 hres = VariantChangeType(&vtProp, &vtProp, VARIANT_ALPHABOOL, VT_BSTR);
246
247 if (FAILED(hres))
248 {
249 zabbix_log(LOG_LEVEL_DEBUG, "cannot convert WMI result of type %d to VT_BSTR", vtProp.vt);
250 goto out;
251 }
252
253 *utf8_value = zbx_unicode_to_utf8((wchar_t *)_bstr_t(vtProp.bstrVal));
254 out:
255 VariantClear(&vtProp);
256 }
257
WMI_GET(AGENT_REQUEST * request,AGENT_RESULT * result)258 extern "C" int WMI_GET(AGENT_REQUEST *request, AGENT_RESULT *result)
259 {
260 char *wmi_namespace, *wmi_query;
261 VARIANT vtProp;
262 HRESULT hres;
263 int ret = SYSINFO_RET_FAIL;
264
265 if (2 != request->nparam)
266 {
267 SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid number of parameters."));
268 return SYSINFO_RET_FAIL;
269 }
270
271 wmi_namespace = get_rparam(request, 0);
272 wmi_query = get_rparam(request, 1);
273
274 VariantInit(&vtProp);
275
276 if (SUCCEED != zbx_co_initialize())
277 {
278 SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot initialize COM library."));
279 return SYSINFO_RET_FAIL;
280 }
281
282 if (SYSINFO_RET_FAIL == zbx_wmi_get_variant(wmi_namespace, wmi_query, CONFIG_TIMEOUT, &vtProp))
283 {
284 zabbix_log(LOG_LEVEL_DEBUG, "cannot get WMI result");
285 goto out;
286 }
287
288 if (0 != (vtProp.vt & VT_ARRAY))
289 {
290 zabbix_log(LOG_LEVEL_DEBUG, "cannot convert WMI array result");
291 goto out;
292 }
293
294 switch (vtProp.vt)
295 {
296 case VT_EMPTY:
297 case VT_NULL:
298 goto out;
299 case VT_I8:
300 case VT_I4:
301 case VT_UI1:
302 case VT_I2:
303 case VT_I1:
304 case VT_UI2:
305 case VT_UI4:
306 case VT_UI8:
307 case VT_INT:
308 case VT_UINT:
309 hres = VariantChangeType(&vtProp, &vtProp, 0, VT_I8);
310
311 if (FAILED(hres))
312 {
313 zabbix_log(LOG_LEVEL_DEBUG, "cannot convert WMI result of type %d to VT_I8", vtProp.vt);
314 goto out;
315 }
316
317 SET_UI64_RESULT(result, vtProp.llVal);
318 ret = SYSINFO_RET_OK;
319
320 break;
321 case VT_R4:
322 case VT_R8:
323 hres = VariantChangeType(&vtProp, &vtProp, 0, VT_R8);
324
325 if (FAILED(hres))
326 {
327 zabbix_log(LOG_LEVEL_DEBUG, "cannot convert WMI result of type %d to VT_R8", vtProp.vt);
328 goto out;
329 }
330
331 SET_DBL_RESULT(result, vtProp.dblVal);
332 ret = SYSINFO_RET_OK;
333
334 break;
335 default:
336 hres = VariantChangeType(&vtProp, &vtProp, VARIANT_ALPHABOOL, VT_BSTR);
337
338 if (FAILED(hres))
339 {
340 zabbix_log(LOG_LEVEL_DEBUG, "cannot convert WMI result of type %d to VT_BSTR", vtProp.vt);
341 goto out;
342 }
343
344 SET_TEXT_RESULT(result, zbx_unicode_to_utf8((wchar_t *)_bstr_t(vtProp.bstrVal)));
345 ret = SYSINFO_RET_OK;
346
347 break;
348 }
349 out:
350 VariantClear(&vtProp);
351
352 if (SYSINFO_RET_FAIL == ret)
353 SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot obtain WMI information."));
354
355 return ret;
356 }
357