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