1 /* Copyright (C) 2018 Open Information Security Foundation
2 *
3 * You can copy, redistribute or modify this Program under the terms of
4 * the GNU General Public License version 2 as published by the Free
5 * Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * version 2 along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301, USA.
16 */
17
18 /**
19 * \file
20 *
21 * \author Jacob Masen-Smith <jacob@evengx.com>
22 *
23 * Isolation for WMI/COM functionality
24 *
25 * References:
26 * https://msdn.microsoft.com/en-us/library/aa390421(v=vs.85).aspx
27 * https://blogs.msdn.microsoft.com/ndis/2015/03/21/mapping-from-ndis-oids-to-wmi-classes/
28 * https://stackoverflow.com/questions/1431103/how-to-obtain-data-from-wmi-using-a-c-application
29 * https://docs.microsoft.com/en-us/windows-hardware/drivers/network/oid-tcp-offload-parameters
30 * https://wutils.com/wmi/root/wmi/ms_409/msndis_tcpoffloadcurrentconfig/
31 * https://docs.microsoft.com/en-us/windows-hardware/drivers/network/oid-tcp-offload-current-config
32 * https://wutils.com/wmi/root/wmi/msndis_tcpoffloadparameters/
33 */
34
35 #ifdef OS_WIN32
36
37 #include <inttypes.h>
38 #include <stdbool.h>
39
40 // clang-format off
41 #include <winsock2.h>
42 #include <windows.h>
43 #include <wbemidl.h>
44 #include <strsafe.h>
45 #include <ntddndis.h>
46 #include <ws2ipdef.h>
47 #include <iphlpapi.h>
48 // clang-format on
49
50 /* Windows strsafe.h defines _snprintf as an undefined warning type */
51 #undef _snprintf
52 #define _snprintf StringCbPrintfA
53
54 #include "suricata-common.h"
55 #include "util-debug.h"
56 #include "util-device.h"
57 #include "util-mem.h"
58 #include "util-unittest.h"
59
60 #include "suricata.h"
61
62 #include "win32-syscall.h"
63
64 /**
65 * \brief return only the GUID portion of the name
66 */
StripPcapPrefix(const char * pcap_dev)67 static const char *StripPcapPrefix(const char *pcap_dev)
68 {
69 return strchr(pcap_dev, '{');
70 }
71
72 /**
73 * \brief get the adapter address list, which includes IP status/details
74 *
75 * Clients MUST FREE the returned list to avoid memory leaks.
76 */
Win32GetAdaptersAddresses(IP_ADAPTER_ADDRESSES ** pif_info_list)77 uint32_t Win32GetAdaptersAddresses(IP_ADAPTER_ADDRESSES **pif_info_list)
78 {
79 DWORD err = NO_ERROR;
80 IP_ADAPTER_ADDRESSES *if_info_list;
81
82 ULONG size = 0;
83 err = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &size);
84 if (err != ERROR_BUFFER_OVERFLOW) {
85 return err;
86 }
87 if_info_list = SCMalloc((size_t)size);
88 if (if_info_list == NULL) {
89 return ERROR_NOT_ENOUGH_MEMORY;
90 }
91 err = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, if_info_list, &size);
92 if (err != NO_ERROR) {
93 SCFree(if_info_list);
94 return err;
95 }
96
97 *pif_info_list = if_info_list;
98 return NO_ERROR;
99 }
100
Win32FindAdapterAddresses(IP_ADAPTER_ADDRESSES * if_info_list,const char * adapter_name,IP_ADAPTER_ADDRESSES ** pif_info)101 uint32_t Win32FindAdapterAddresses(IP_ADAPTER_ADDRESSES *if_info_list,
102 const char *adapter_name,
103 IP_ADAPTER_ADDRESSES **pif_info)
104 {
105 DWORD ret = NO_ERROR;
106 adapter_name = StripPcapPrefix(adapter_name);
107 *pif_info = NULL;
108
109 for (IP_ADAPTER_ADDRESSES *current = if_info_list; current != NULL;
110 current = current->Next) {
111
112 /* if we find the adapter, return that data */
113 if (strncmp(adapter_name, current->AdapterName, strlen(adapter_name)) ==
114 0) {
115
116 *pif_info = current;
117 break;
118 }
119 }
120
121 if (*pif_info == NULL) {
122 ret = ERROR_NOT_FOUND;
123 }
124
125 return ret;
126 }
127
128 #if NTDDI_VERSION < NTDDI_VISTA
129
GetIfaceMTUWin32(const char * pcap_dev)130 int GetIfaceMTUWin32(const char *pcap_dev) { return 0; }
GetGlobalMTUWin32(void)131 int GetGlobalMTUWin32(void) { return 0; }
132
GetIfaceOffloadingWin32(const char * ifname,int csum,int other)133 int GetIfaceOffloadingWin32(const char *ifname, int csum, int other)
134 {
135 SCLogWarning(SC_ERR_SYSCALL, "Suricata not targeted for Windows Vista or "
136 "higher. Network offload interrogation not "
137 "available.");
138 return -1;
139 }
DisableIfaceOffloadingWin32(LiveDevice * ldev,int csum,int other)140 int DisableIfaceOffloadingWin32(LiveDevice *ldev, int csum, int other)
141 {
142 SCLogWarning(SC_ERR_SYSCALL, "Suricata not targeted for Windows Vista or "
143 "higher. Network offload interrogation not "
144 "available.");
145 return -1;
146 }
RestoreIfaceOffloadingWin32(LiveDevice * ldev)147 int RestoreIfaceOffloadingWin32(LiveDevice *ldev)
148 {
149 SCLogWarning(SC_ERR_SYSCALL, "Suricata not targeted for Windows Vista or "
150 "higher. Network offload interrogation not "
151 "available.");
152 return -1;
153 }
154
155 #else /* NTDDI_VERSION >= NTDDI_VISTA */
156
157 static HMODULE wmiutils_dll = NULL;
158
159 /**
160 * \brief obtain the WMI utilities DLL
161 */
WmiUtils(void)162 static HMODULE WmiUtils(void)
163 {
164 if (wmiutils_dll == NULL) {
165 wmiutils_dll =
166 LoadLibraryA("C:\\Windows\\System32\\wbem\\wmiutils.dll");
167 }
168
169 return wmiutils_dll;
170 }
171
172 /**
173 * \brief allocate a BSTR from a converted unsigned integer
174 */
utob(uint64_t ui)175 static BSTR utob(uint64_t ui)
176 {
177 wchar_t buf[20];
178 _ui64tow(ui, buf, 10);
179 return SysAllocString(buf);
180 }
181
182 /**
183 * \brief Get the win32/wmi error string
184 *
185 * The caller should use the LocalFree function on the returned pointer to free
186 * the buffer when it is no longer needed.
187 */
Win32GetErrorString(DWORD error_code,HMODULE ext_module)188 const char *Win32GetErrorString(DWORD error_code, HMODULE ext_module)
189 {
190 char *error_string = NULL;
191
192 DWORD flags =
193 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS;
194 if (ext_module != NULL) {
195 flags |= FORMAT_MESSAGE_FROM_HMODULE;
196 } else {
197 flags |= FORMAT_MESSAGE_FROM_SYSTEM;
198 }
199
200 FormatMessageA(flags, ext_module, error_code, 0, (LPTSTR)&error_string, 0,
201 NULL);
202
203 if (error_string == NULL) {
204 return "";
205 }
206
207 error_string[strlen(error_string) - 2] = 0; // remove line breaks
208
209 return error_string;
210 }
211
212 #ifdef DEBUG
213 /**
214 * \brief log an HRESULT
215 */
_Win32HResultLog(SCLogLevel level,HRESULT hr,const char * file,const char * function,const int line)216 static void _Win32HResultLog(SCLogLevel level, HRESULT hr, const char *file,
217 const char *function, const int line)
218 {
219 const char *err_str = Win32GetErrorString(hr, WmiUtils());
220 SCLog(level, file, function, line, "HRESULT: %s (0x%08" PRIx32 ")", err_str,
221 (uint32_t)(hr));
222 LocalFree((LPVOID)err_str);
223 }
224
225 #define Win32HResultLogDebug(hr) \
226 _Win32HResultLog(SC_LOG_DEBUG, (hr), __FILE__, __FUNCTION__, __LINE__)
227 #else
228 #define Win32HResultLogDebug(hr)
229 #endif /* DEBUG */
230
231 /**
232 * \brief log a WBEM error
233 */
234 #define WbemLogDebug(hr) (_WbemLogDebug)((hr), __FILE__, __FUNCTION__, __LINE__)
235
_WbemLogDebug(HRESULT hr,const char * file,const char * function,const int line)236 static void _WbemLogDebug(HRESULT hr, const char *file, const char *function,
237 const int line)
238 {
239 #ifdef DEBUG
240 IErrorInfo *err_info;
241 BSTR err_description;
242 char *err_description_mb = NULL;
243
244 _Win32HResultLog(SC_LOG_DEBUG, hr, file, function, line);
245
246 GetErrorInfo(0, &err_info);
247 if (!SUCCEEDED(
248 err_info->lpVtbl->GetDescription(err_info, &err_description))) {
249 // not much to do when your error log errors out...
250 goto release;
251 }
252
253 err_description_mb = SCMalloc(SysStringLen(err_description) + 1);
254
255 if (err_description_mb == NULL) {
256 // not much to do when your error log errors out...
257 goto release;
258 }
259
260 // do the actual multibyte conversion
261 err_description_mb[SysStringLen(err_description)] = 0;
262 wcstombs(err_description_mb, err_description,
263 SysStringLen(err_description));
264
265 // log the description
266 SCLog(SC_LOG_DEBUG, file, function, line, "WBEM error: %s",
267 err_description_mb);
268
269 release:
270 SCFree(err_description_mb);
271 SysFreeString(err_description);
272 #endif /* DEBUG */
273 }
274
275 /**
276 * \brief get the maximum transmissible unit for the specified pcap device name
277 */
GetIfaceMTUWin32(const char * pcap_dev)278 int GetIfaceMTUWin32(const char *pcap_dev)
279 {
280 DWORD err = NO_ERROR;
281
282 int mtu = 0;
283
284 IP_ADAPTER_ADDRESSES *if_info_list = NULL, *if_info = NULL;
285 err = Win32GetAdaptersAddresses(&if_info_list);
286 if (err != NO_ERROR) {
287 mtu = -1;
288 goto release;
289 }
290 err = Win32FindAdapterAddresses(if_info_list, pcap_dev, &if_info);
291 if (err != NO_ERROR) {
292 mtu = -1;
293 goto release;
294 }
295
296 mtu = if_info->Mtu;
297
298 release:
299 SCFree(if_info_list);
300
301 if (err != S_OK) {
302 const char *errbuf = Win32GetErrorString(err, WmiUtils());
303 SCLogWarning(SC_ERR_SYSCALL,
304 "Failure when trying to get MTU via syscall for '%s': %s "
305 "(0x%08" PRIx32 ")",
306 pcap_dev, errbuf, (uint32_t)err);
307 LocalFree((LPVOID)errbuf);
308 } else {
309 SCLogInfo("Found an MTU of %d for '%s'", mtu, pcap_dev);
310 }
311
312 return mtu;
313 }
314
315 /**
316 * \brief get the maximum transmissible unit for all devices on the system
317 */
GetGlobalMTUWin32()318 int GetGlobalMTUWin32()
319 {
320 uint32_t mtu = 0;
321
322 DWORD err = NO_ERROR;
323 IP_ADAPTER_ADDRESSES *if_info_list = NULL;
324
325 /* get a list of all adapters' data */
326 err = Win32GetAdaptersAddresses(&if_info_list);
327 if (err != NO_ERROR) {
328 goto fail;
329 }
330
331 /* now search for the right adapter in the list */
332 IP_ADAPTER_ADDRESSES *if_info = NULL;
333 for (if_info = if_info_list; if_info != NULL; if_info = if_info->Next) {
334 /* -1 (uint) is an invalid value */
335 if (if_info->Mtu == (uint32_t)-1) {
336 continue;
337 }
338
339 /* we want to return the largest MTU value so we allocate enough */
340 mtu = max(mtu, if_info->Mtu);
341 }
342
343 SCFree(if_info_list);
344
345 SCLogInfo("Found a global MTU of %" PRIu32, mtu);
346 return (int)mtu;
347
348 fail:
349 SCFree(if_info_list);
350
351 const char *errbuf = NULL;
352 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
353 FORMAT_MESSAGE_IGNORE_INSERTS,
354 NULL, err, 0, (LPTSTR)&errbuf, 0, NULL);
355
356 SCLogWarning(
357 SC_ERR_SYSCALL,
358 "Failure when trying to get global MTU via syscall: %s (%" PRId32
359 ")",
360 errbuf, (uint32_t)err);
361
362 return -1;
363 }
364
365 #define ReleaseObject(objptr) \
366 do { \
367 if ((objptr) != NULL) { \
368 (objptr)->lpVtbl->Release(objptr); \
369 (objptr) = NULL; \
370 } \
371 } while (0);
372
373 typedef enum Win32TcpOffloadFlags_ {
374 WIN32_TCP_OFFLOAD_FLAG_NONE = 0,
375 WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4RX = 1,
376 WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4TX = 1 << 1,
377 WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6RX = 1 << 2,
378 WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6TX = 1 << 3,
379 WIN32_TCP_OFFLOAD_FLAG_LSOV1_IP4 = 1 << 4,
380 WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP4 = 1 << 5,
381 WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP6 = 1 << 6,
382
383 /* aggregates */
384 WIN32_TCP_OFFLOAD_FLAG_CSUM = WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4RX |
385 WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4TX |
386 WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6RX |
387 WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6TX,
388 WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4 = WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4RX |
389 WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4TX,
390 WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6 = WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6RX |
391 WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6TX,
392 WIN32_TCP_OFFLOAD_FLAG_LSO = WIN32_TCP_OFFLOAD_FLAG_LSOV1_IP4 |
393 WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP4 |
394 WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP6,
395 } Win32TcpOffloadFlags;
396
397 typedef struct ComInstance_ {
398 IWbemLocator *locator;
399 IWbemServices *services;
400 } ComInstance;
401
402 /**
403 * \brief Creates a COM instance connected to the specified resource
404 */
ComInstanceInit(ComInstance * instance,LPCWSTR resource)405 static HRESULT ComInstanceInit(ComInstance *instance, LPCWSTR resource)
406 {
407 HRESULT hr = S_OK;
408
409 instance->locator = NULL;
410 instance->services = NULL;
411
412 BSTR resource_bstr = SysAllocString(resource);
413 if (resource_bstr == NULL) {
414 hr = HRESULT_FROM_WIN32(E_OUTOFMEMORY);
415 SCLogWarning(SC_ERR_SYSCALL, "Failed to allocate BSTR");
416 goto release;
417 }
418
419 /* connect to COM */
420 hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
421 if (hr == S_FALSE) {
422 /* already initialized */
423 hr = S_OK;
424 } else {
425 if (hr != S_OK) {
426 SCLogWarning(SC_ERR_SYSCALL,
427 "COM CoInitializeEx failed: 0x%" PRIx32, (uint32_t)hr);
428 goto release;
429 }
430 hr = CoInitializeSecurity(
431 NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT,
432 RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
433 if (hr != S_OK) {
434 SCLogWarning(SC_ERR_SYSCALL,
435 "COM CoInitializeSecurity failed: 0x%" PRIx32,
436 (uint32_t)hr);
437 goto release;
438 }
439 }
440
441 /* connect to WMI */
442 hr = CoCreateInstance(&CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER,
443 &IID_IWbemLocator, (LPVOID *)&instance->locator);
444 if (hr != S_OK) {
445 SCLogWarning(SC_ERR_SYSCALL, "COM CoCreateInstance failed: 0x%" PRIx32,
446 (uint32_t)hr);
447 goto release;
448 }
449 hr = instance->locator->lpVtbl->ConnectServer(
450 instance->locator, resource_bstr, NULL, NULL, NULL, 0, NULL, NULL,
451 &instance->services);
452 if (hr != S_OK) {
453 SCLogWarning(SC_ERR_SYSCALL, "COM ConnectServer failed: 0x%" PRIx32,
454 (uint32_t)hr);
455 goto release;
456 }
457
458 release:
459 SysFreeString(resource_bstr);
460
461 return hr;
462 }
463
464 /**
465 * \brief Releases resources for a COM instance.
466 */
ComInstanceRelease(ComInstance * instance)467 static void ComInstanceRelease(ComInstance *instance)
468 {
469 if (instance == NULL) {
470 return;
471 }
472 ReleaseObject(instance->services);
473 ReleaseObject(instance->locator);
474 }
475
476 /**
477 * \brief obtains a class definition from COM services
478 */
GetWbemClass(ComInstance * instance,LPCWSTR name,IWbemClassObject ** p_class)479 static HRESULT GetWbemClass(ComInstance *instance, LPCWSTR name,
480 IWbemClassObject **p_class)
481 {
482 HRESULT hr = WBEM_S_NO_ERROR;
483 BSTR name_bstr = NULL;
484
485 if (instance == NULL || name == NULL || p_class == NULL ||
486 *p_class != NULL) {
487 hr = HRESULT_FROM_WIN32(E_INVALIDARG);
488 Win32HResultLogDebug(hr);
489 goto release;
490 }
491
492 /* allocate name string */
493 name_bstr = SysAllocString(name);
494 if (name_bstr == NULL) {
495 hr = HRESULT_FROM_WIN32(E_OUTOFMEMORY);
496 SCLogWarning(SC_ERR_SYSCALL, "Failed to allocate BSTR");
497 goto release;
498 }
499
500 /* obtain object */
501 hr = instance->services->lpVtbl->GetObject(instance->services, name_bstr,
502 WBEM_FLAG_RETURN_WBEM_COMPLETE,
503 NULL, p_class, NULL);
504 if (hr != S_OK) {
505 WbemLogDebug(hr);
506 SCLogWarning(SC_ERR_SYSCALL, "WMI GetObject failed: 0x%" PRIx32,
507 (uint32_t)hr);
508 goto release;
509 }
510
511 release:
512 SysFreeString(name_bstr);
513
514 return hr;
515 }
516
517 /**
518 * \brief spawns an empty class instance of the specified type
519 */
GetWbemClassInstance(ComInstance * instance,LPCWSTR name,IWbemClassObject ** p_instance)520 static HRESULT GetWbemClassInstance(ComInstance *instance, LPCWSTR name,
521 IWbemClassObject **p_instance)
522 {
523 HRESULT hr = WBEM_S_NO_ERROR;
524
525 IWbemClassObject *class = NULL;
526
527 hr = GetWbemClass(instance, name, &class);
528 if (hr != WBEM_S_NO_ERROR) {
529 goto release;
530 }
531
532 hr = class->lpVtbl->SpawnInstance(class, 0, p_instance);
533 if (hr != WBEM_S_NO_ERROR) {
534 WbemLogDebug(hr);
535 SCLogWarning(SC_ERR_SYSCALL, "WMI SpawnInstance failed: 0x%" PRIx32,
536 (uint32_t)hr);
537 goto release;
538 }
539
540 release:
541 return hr;
542 }
543
544 typedef struct WbemMethod_ {
545 ComInstance *com_instance;
546
547 BSTR method_name;
548
549 IWbemClassObject *in_params, *out_params;
550 } WbemMethod;
551
552 /**
553 * \brief initializes resources for a WMI method handle
554 */
GetWbemMethod(ComInstance * com_instance,LPCWSTR class_name,LPCWSTR method_name,WbemMethod * method)555 static HRESULT GetWbemMethod(ComInstance *com_instance, LPCWSTR class_name,
556 LPCWSTR method_name, WbemMethod *method)
557 {
558 HRESULT hr = S_OK;
559 IWbemClassObject *class = NULL;
560
561 method->com_instance = com_instance;
562
563 BSTR class_name_bstr = SysAllocString(class_name);
564 if (class_name_bstr == NULL) {
565 hr = HRESULT_FROM_WIN32(E_OUTOFMEMORY);
566 SCLogWarning(SC_ERR_SYSCALL, "Failed to allocate BSTR");
567 goto release;
568 }
569 method->method_name = SysAllocString(method_name);
570 if (method->method_name == NULL) {
571 hr = HRESULT_FROM_WIN32(E_OUTOFMEMORY);
572 SCLogWarning(SC_ERR_SYSCALL, "Failed to allocate BSTR");
573 goto release;
574 }
575
576 /* find our class definition to retrieve parameters */
577 hr = GetWbemClass(com_instance, class_name, &class);
578 if (hr != WBEM_S_NO_ERROR) {
579 goto release;
580 }
581
582 /* find the method on the retrieved class */
583 hr = class->lpVtbl->GetMethod(class, method_name, 0, &method->in_params,
584 &method->out_params);
585 if (hr != WBEM_S_NO_ERROR) {
586 WbemLogDebug(hr);
587 SCLogWarning(SC_ERR_SYSCALL, "WMI GetMethod failed: 0x%" PRIx32,
588 (uint32_t)hr);
589 goto release;
590 }
591
592 release:
593 ReleaseObject(class);
594
595 SysFreeString(class_name_bstr);
596
597 return hr;
598 }
599
600 /**
601 * \brief Releases resources for a WMI method handle
602 */
WbemMethodRelease(WbemMethod * method)603 static void WbemMethodRelease(WbemMethod *method)
604 {
605 if (method == NULL) {
606 return;
607 }
608 ReleaseObject(method->in_params);
609 ReleaseObject(method->out_params);
610
611 SysFreeString(method->method_name);
612 }
613
614 typedef struct WbemMethodCall_ {
615 WbemMethod *method;
616
617 BSTR instance_path;
618
619 IWbemClassObject *in_params;
620 } WbemMethodCall;
621
622 /**
623 * \brief generates a single-use WMI method call
624 */
GetWbemMethodCall(WbemMethod * method,LPCWSTR instance_path,WbemMethodCall * call)625 static HRESULT GetWbemMethodCall(WbemMethod *method, LPCWSTR instance_path,
626 WbemMethodCall *call)
627 {
628 HRESULT hr = S_OK;
629
630 call->method = method;
631 call->instance_path = SysAllocString(instance_path);
632 if (call->instance_path == NULL) {
633 hr = HRESULT_FROM_WIN32(E_OUTOFMEMORY);
634 SCLogWarning(SC_ERR_SYSCALL, "Failed to allocate BSTR: 0x%" PRIx32,
635 (uint32_t)hr);
636 goto release;
637 }
638
639 /* make an instance of the in/out params */
640 hr = method->in_params->lpVtbl->SpawnInstance(method->in_params, 0,
641 &call->in_params);
642 if (hr != S_OK) {
643 WbemLogDebug(hr);
644 SCLogWarning(SC_ERR_SYSCALL,
645 "WMI SpawnInstance failed on in_params: 0x%" PRIx32,
646 (uint32_t)hr);
647 goto release;
648 }
649
650 release:
651 return hr;
652 }
653
654 /**
655 * \brief releases the WMI method call resources
656 */
WbemMethodCallRelease(WbemMethodCall * call)657 static void WbemMethodCallRelease(WbemMethodCall *call)
658 {
659 if (call == NULL) {
660 return;
661 }
662 ReleaseObject(call->in_params);
663
664 SysFreeString(call->instance_path);
665 }
666
667 /**
668 * \brief executes the method after the client has set applicable parameters.
669 */
WbemMethodCallExec(WbemMethodCall * call,IWbemClassObject ** p_out_params)670 static HRESULT WbemMethodCallExec(WbemMethodCall *call,
671 IWbemClassObject **p_out_params)
672 {
673 HRESULT hr = S_OK;
674
675 hr = call->method->com_instance->services->lpVtbl->ExecMethod(
676 call->method->com_instance->services, call->instance_path,
677 call->method->method_name, 0, NULL, call->in_params, p_out_params,
678 NULL);
679 if (hr != WBEM_S_NO_ERROR) {
680 WbemLogDebug(hr);
681 SCLogDebug("WMI ExecMethod failed: 0x%" PRIx32, (uint32_t)hr);
682 goto release;
683 }
684
685 release:
686 return hr;
687 }
688
689 /**
690 * Obtains an IWbemClassObject named property of a parent IWbemClassObject
691 */
WbemGetSubObject(IWbemClassObject * object,LPCWSTR property_name,IWbemClassObject ** sub_object)692 static HRESULT WbemGetSubObject(IWbemClassObject *object, LPCWSTR property_name,
693 IWbemClassObject **sub_object)
694 {
695 HRESULT hr = S_OK;
696
697 VARIANT out_var;
698 VariantInit(&out_var);
699 hr = object->lpVtbl->Get(object, property_name, 0, &out_var, NULL, NULL);
700 if (hr != WBEM_S_NO_ERROR) {
701 goto release;
702 }
703
704 IUnknown *unknown = V_UNKNOWN(&out_var);
705 hr = unknown->lpVtbl->QueryInterface(unknown, &IID_IWbemClassObject,
706 (void **)sub_object);
707 if (hr != S_OK) {
708 SCLogWarning(SC_ERR_SYSCALL,
709 "WMI QueryInterface (IWbemClassObject) failed: 0x%" PRIx32,
710 (uint32_t)hr);
711 goto release;
712 }
713
714 release:
715 VariantClear(&out_var);
716 return hr;
717 }
718
719 /**
720 * Obtains an Encapsulation value from an MSNdis_WmiOffload property
721 */
GetEncapsulation(IWbemClassObject * object,LPCWSTR category,LPCWSTR subcategory,ULONG * encapsulation)722 static HRESULT GetEncapsulation(IWbemClassObject *object, LPCWSTR category,
723 LPCWSTR subcategory, ULONG *encapsulation)
724 {
725 HRESULT hr = WBEM_S_NO_ERROR;
726
727 IWbemClassObject *category_object = NULL;
728 IWbemClassObject *subcategory_object = NULL;
729
730 VARIANT out_var;
731 VariantInit(&out_var);
732
733 /* get category object */
734 hr = WbemGetSubObject(object, category, &category_object);
735 if (hr != WBEM_S_NO_ERROR) {
736 goto release;
737 }
738
739 /* get sub-category object */
740 hr = WbemGetSubObject(category_object, subcategory, &subcategory_object);
741 if (hr != WBEM_S_NO_ERROR) {
742 goto release;
743 }
744 hr = subcategory_object->lpVtbl->Get(subcategory_object, L"Encapsulation",
745 0, &out_var, NULL, NULL);
746 if (hr != WBEM_S_NO_ERROR) {
747 goto release;
748 }
749 *encapsulation = V_UI4(&out_var);
750
751 release:
752 VariantClear(&out_var);
753 ReleaseObject(subcategory_object);
754 ReleaseObject(category_object);
755 return hr;
756 }
757
GetIUnknown(IWbemClassObject * object,IUnknown ** p_unknown)758 static HRESULT GetIUnknown(IWbemClassObject *object, IUnknown **p_unknown)
759 {
760 HRESULT hr = WBEM_S_NO_ERROR;
761
762 if (object == NULL || p_unknown == NULL || *p_unknown != NULL) {
763 hr = HRESULT_FROM_WIN32(E_INVALIDARG);
764 Win32HResultLogDebug(hr);
765 goto release;
766 }
767
768 hr = object->lpVtbl->QueryInterface(object, &IID_IUnknown,
769 (void **)p_unknown);
770 if (hr != S_OK) {
771 SCLogWarning(SC_ERR_SYSCALL,
772 "WMI QueryInterface (IUnknown) failed: 0x%" PRIx32,
773 (uint32_t)hr);
774 goto release;
775 }
776
777 release:
778 return hr;
779 }
780
BuildNdisObjectHeader(ComInstance * instance,uint8_t type,uint8_t revision,uint16_t size,IWbemClassObject ** p_ndis_object_header)781 static HRESULT BuildNdisObjectHeader(ComInstance *instance, uint8_t type,
782 uint8_t revision, uint16_t size,
783 IWbemClassObject **p_ndis_object_header)
784 {
785 HRESULT hr = WBEM_S_NO_ERROR;
786
787 if (instance == NULL || p_ndis_object_header == NULL ||
788 *p_ndis_object_header != NULL) {
789
790 hr = HRESULT_FROM_WIN32(E_INVALIDARG);
791 Win32HResultLogDebug(hr);
792 goto release;
793 }
794
795 /* obtain object */
796 hr = GetWbemClassInstance(instance, L"MSNdis_ObjectHeader",
797 p_ndis_object_header);
798 if (hr != WBEM_S_NO_ERROR) {
799 goto release;
800 }
801
802 VARIANT param_variant;
803 VariantInit(¶m_variant);
804 IWbemClassObject *ndis_object_header = *p_ndis_object_header;
805
806 /* set parameters */
807 V_VT(¶m_variant) = VT_UI1;
808 V_UI1(¶m_variant) = type;
809 hr = ndis_object_header->lpVtbl->Put(ndis_object_header, L"Type", 0,
810 ¶m_variant, 0);
811 VariantClear(¶m_variant);
812 if (hr != WBEM_S_NO_ERROR) {
813 WbemLogDebug(hr);
814 goto release;
815 }
816
817 V_VT(¶m_variant) = VT_UI1;
818 V_UI1(¶m_variant) = revision;
819 hr = ndis_object_header->lpVtbl->Put(ndis_object_header, L"Revision", 0,
820 ¶m_variant, 0);
821 VariantClear(¶m_variant);
822 if (hr != WBEM_S_NO_ERROR) {
823 WbemLogDebug(hr);
824 goto release;
825 }
826
827 /* https://docs.microsoft.com/en-us/windows-hardware/drivers/network/ndis-object-version-issues-for-wmi
828 */
829 V_VT(¶m_variant) = VT_I4;
830 V_I4(¶m_variant) = size;
831 hr = ndis_object_header->lpVtbl->Put(ndis_object_header, L"Size", 0,
832 ¶m_variant, 0);
833 VariantClear(¶m_variant);
834 if (hr != WBEM_S_NO_ERROR) {
835 WbemLogDebug(hr);
836 goto release;
837 }
838
839 release:
840 return hr;
841 }
842
BuildNdisWmiMethodHeader(ComInstance * instance,uint64_t net_luid,uint32_t port_number,uint64_t request_id,uint32_t timeout,IWbemClassObject ** p_ndis_method_header)843 static HRESULT BuildNdisWmiMethodHeader(ComInstance *instance,
844 uint64_t net_luid, uint32_t port_number,
845 uint64_t request_id, uint32_t timeout,
846 IWbemClassObject **p_ndis_method_header)
847 {
848 HRESULT hr = WBEM_S_NO_ERROR;
849
850 IWbemClassObject *ndis_object_header = NULL;
851
852 if (instance == NULL || p_ndis_method_header == NULL ||
853 *p_ndis_method_header != NULL) {
854
855 hr = HRESULT_FROM_WIN32(E_INVALIDARG);
856 Win32HResultLogDebug(hr);
857 goto release;
858 }
859
860 /* obtain object */
861 hr = GetWbemClassInstance(instance, L"MSNdis_WmiMethodHeader",
862 p_ndis_method_header);
863 if (hr != WBEM_S_NO_ERROR) {
864 goto release;
865 }
866
867 VARIANT param_variant;
868 VariantInit(¶m_variant);
869
870 /* get embedded MSNdis_ObjectHeader */
871 hr = BuildNdisObjectHeader(instance, NDIS_WMI_OBJECT_TYPE_METHOD,
872 NDIS_WMI_METHOD_HEADER_REVISION_1, 0xFFFF,
873 &ndis_object_header);
874 if (hr != WBEM_S_NO_ERROR) {
875 goto release;
876 }
877 V_VT(¶m_variant) = VT_UNKNOWN;
878 V_UNKNOWN(¶m_variant) = NULL;
879 hr = GetIUnknown(ndis_object_header, &V_UNKNOWN(¶m_variant));
880 if (hr != WBEM_S_NO_ERROR) {
881 goto release;
882 }
883
884 IWbemClassObject *ndis_method_header = *p_ndis_method_header;
885
886 /* set parameters */
887 hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"Header", 0,
888 ¶m_variant, 0);
889 VariantClear(¶m_variant);
890 if (hr != WBEM_S_NO_ERROR) {
891 WbemLogDebug(hr);
892 goto release;
893 }
894
895 V_VT(¶m_variant) = VT_BSTR;
896 V_BSTR(¶m_variant) = utob(net_luid);
897 hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"NetLuid", 0,
898 ¶m_variant, 0);
899 VariantClear(¶m_variant);
900 if (hr != WBEM_S_NO_ERROR) {
901 WbemLogDebug(hr);
902 goto release;
903 }
904
905 V_VT(¶m_variant) = VT_BSTR;
906 V_BSTR(¶m_variant) = utob((uint64_t)port_number);
907 hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"PortNumber", 0,
908 ¶m_variant, 0);
909 VariantClear(¶m_variant);
910 if (hr != WBEM_S_NO_ERROR) {
911 WbemLogDebug(hr);
912 goto release;
913 }
914
915 V_VT(¶m_variant) = VT_BSTR;
916 V_BSTR(¶m_variant) = utob(request_id);
917 hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"RequestId", 0,
918 ¶m_variant, 0);
919 VariantClear(¶m_variant);
920 if (hr != WBEM_S_NO_ERROR) {
921 WbemLogDebug(hr);
922 goto release;
923 }
924
925 V_VT(¶m_variant) = VT_BSTR;
926 V_BSTR(¶m_variant) = utob((uint64_t)timeout);
927 hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"Timeout", 0,
928 ¶m_variant, 0);
929 VariantClear(¶m_variant);
930 if (hr != WBEM_S_NO_ERROR) {
931 WbemLogDebug(hr);
932 goto release;
933 }
934
935 V_VT(¶m_variant) = VT_BSTR;
936 V_BSTR(¶m_variant) = utob((uint64_t)0);
937 hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"Padding", 0,
938 ¶m_variant, 0);
939 VariantClear(¶m_variant);
940 if (hr != WBEM_S_NO_ERROR) {
941 WbemLogDebug(hr);
942 goto release;
943 }
944
945 release:
946 ReleaseObject(ndis_object_header);
947
948 return hr;
949 }
950
951 /**
952 * \brief polls the NDIS TCP offloading status, namely LSOv1/v2
953 */
GetNdisOffload(LPCWSTR if_description,uint32_t * offload_flags)954 static HRESULT GetNdisOffload(LPCWSTR if_description, uint32_t *offload_flags)
955 {
956 HRESULT hr = S_OK;
957
958 ComInstance instance = {};
959 WbemMethod method = {};
960 WbemMethodCall call = {};
961
962 IWbemClassObject *ndis_method_header = NULL;
963 IWbemClassObject *out_params = NULL;
964 IWbemClassObject *ndis_offload = NULL;
965
966 if (if_description == NULL) {
967 SCLogWarning(SC_ERR_SYSCALL, "No description specified for device");
968 hr = HRESULT_FROM_WIN32(E_INVALIDARG);
969 goto release;
970 }
971
972 LPCWSTR class_name = L"MSNdis_TcpOffloadCurrentConfig";
973 LPCWSTR instance_name_fmt = L"%s=\"%s\"";
974 size_t n_chars = wcslen(class_name) + wcslen(if_description) +
975 wcslen(instance_name_fmt);
976 LPWSTR instance_name = SCMalloc((n_chars + 1) * sizeof(wchar_t));
977 if (instance_name == NULL) {
978 SCLogWarning(SC_ERR_SYSCALL,
979 "Failed to allocate buffer for instance path");
980 goto release;
981 }
982 instance_name[n_chars] = 0; /* defensively null-terminate */
983 hr = StringCchPrintfW(instance_name, n_chars, instance_name_fmt, class_name,
984 if_description);
985 if (hr != S_OK) {
986 SCLogWarning(SC_ERR_SYSCALL,
987 "Failed to format WMI class instance name: 0x%" PRIx32,
988 (uint32_t)hr);
989 goto release;
990 }
991 /* method name */
992 LPCWSTR method_name = L"WmiQueryCurrentOffloadConfig";
993
994 /* connect to COM/WMI */
995 hr = ComInstanceInit(&instance, L"ROOT\\WMI");
996 if (hr != S_OK) {
997 goto release;
998 }
999
1000 /* obtain method */
1001 hr = GetWbemMethod(&instance, class_name, method_name, &method);
1002 if (hr != S_OK) {
1003 goto release;
1004 }
1005
1006 /* make parameter instances */
1007 hr = GetWbemMethodCall(&method, instance_name, &call);
1008 if (hr != S_OK) {
1009 goto release;
1010 }
1011
1012 /* build parameters */
1013
1014 VARIANT param_variant;
1015 VariantInit(¶m_variant);
1016
1017 /* Make MSNdis_WmiMethodHeader */
1018 hr = BuildNdisWmiMethodHeader(&instance, 0, 0, 0, 5, &ndis_method_header);
1019 if (hr != WBEM_S_NO_ERROR) {
1020 goto release;
1021 }
1022 V_VT(¶m_variant) = VT_UNKNOWN;
1023 V_UNKNOWN(¶m_variant) = NULL;
1024 hr = GetIUnknown(ndis_method_header, &V_UNKNOWN(¶m_variant));
1025 if (hr != WBEM_S_NO_ERROR) {
1026 goto release;
1027 }
1028
1029 /* Set in_params */
1030 hr = call.in_params->lpVtbl->Put(call.in_params, L"Header", 0,
1031 ¶m_variant, 0);
1032 VariantClear(¶m_variant);
1033 if (hr != WBEM_S_NO_ERROR) {
1034 WbemLogDebug(hr);
1035 goto release;
1036 }
1037
1038 /* execute the method */
1039 hr = WbemMethodCallExec(&call, &out_params);
1040 if (hr != S_OK) {
1041 size_t if_description_len = wcslen(if_description);
1042 char *if_description_ansi = SCMalloc(if_description_len + 1);
1043 if (if_description_ansi == NULL) {
1044 SCLogWarning(SC_ERR_SYSCALL,
1045 "Failed to allocate buffer for interface description");
1046 goto release;
1047 }
1048 if_description_ansi[if_description_len] = 0;
1049 wcstombs(if_description_ansi, if_description, if_description_len);
1050 SCLogInfo("Obtaining offload state failed, device \"%s\" may not "
1051 "support offload. Error: 0x%" PRIx32,
1052 if_description_ansi, (uint32_t)hr);
1053 SCFree(if_description_ansi);
1054 Win32HResultLogDebug(hr);
1055 goto release;
1056 }
1057
1058 /* inspect the result */
1059 hr = WbemGetSubObject(out_params, L"Offload", &ndis_offload);
1060 if (hr != WBEM_S_NO_ERROR) {
1061 goto release;
1062 }
1063 ULONG encapsulation = 0;
1064
1065 /* Checksum */
1066 hr = GetEncapsulation(ndis_offload, L"Checksum", L"IPv4Receive",
1067 &encapsulation);
1068 if (hr != WBEM_S_NO_ERROR) {
1069 WbemLogDebug(hr);
1070 goto release;
1071 }
1072 if (encapsulation != 0) {
1073 *offload_flags |= WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4RX;
1074 }
1075 hr = GetEncapsulation(ndis_offload, L"Checksum", L"IPv4Transmit",
1076 &encapsulation);
1077 if (hr != WBEM_S_NO_ERROR) {
1078 WbemLogDebug(hr);
1079 goto release;
1080 }
1081 if (encapsulation != 0) {
1082 *offload_flags |= WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4TX;
1083 }
1084 hr = GetEncapsulation(ndis_offload, L"Checksum", L"IPv6Receive",
1085 &encapsulation);
1086 if (hr != WBEM_S_NO_ERROR) {
1087 WbemLogDebug(hr);
1088 goto release;
1089 }
1090 if (encapsulation != 0) {
1091 *offload_flags |= WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6RX;
1092 }
1093 hr = GetEncapsulation(ndis_offload, L"Checksum", L"IPv6Transmit",
1094 &encapsulation);
1095 if (hr != WBEM_S_NO_ERROR) {
1096 WbemLogDebug(hr);
1097 goto release;
1098 }
1099 if (encapsulation != 0) {
1100 *offload_flags |= WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6TX;
1101 }
1102
1103 /* LsoV1 */
1104 hr = GetEncapsulation(ndis_offload, L"LsoV1", L"WmiIPv4", &encapsulation);
1105 if (hr != WBEM_S_NO_ERROR) {
1106 WbemLogDebug(hr);
1107 goto release;
1108 }
1109 if (encapsulation != 0) {
1110 *offload_flags |= WIN32_TCP_OFFLOAD_FLAG_LSOV1_IP4;
1111 }
1112
1113 /* LsoV2 */
1114 hr = GetEncapsulation(ndis_offload, L"LsoV2", L"WmiIPv4", &encapsulation);
1115 if (hr != WBEM_S_NO_ERROR) {
1116 WbemLogDebug(hr);
1117 goto release;
1118 }
1119 if (encapsulation != 0) {
1120 *offload_flags |= WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP4;
1121 }
1122 hr = GetEncapsulation(ndis_offload, L"LsoV2", L"WmiIPv6", &encapsulation);
1123 if (hr != WBEM_S_NO_ERROR) {
1124 WbemLogDebug(hr);
1125 goto release;
1126 }
1127 if (encapsulation != 0) {
1128 *offload_flags |= WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP6;
1129 }
1130
1131 release:
1132 ReleaseObject(ndis_method_header);
1133 ReleaseObject(ndis_offload);
1134 ReleaseObject(out_params);
1135
1136 WbemMethodCallRelease(&call);
1137 WbemMethodRelease(&method);
1138 ComInstanceRelease(&instance);
1139
1140 return hr;
1141 }
1142
GetIfaceOffloadingWin32(const char * pcap_dev,int csum,int other)1143 int GetIfaceOffloadingWin32(const char *pcap_dev, int csum, int other)
1144 {
1145 SCLogDebug("Querying offloading for device %s", pcap_dev);
1146
1147 DWORD err = NO_ERROR;
1148 int ret = 0;
1149 uint32_t offload_flags = 0;
1150
1151 /* WMI uses the description as an identifier... */
1152 IP_ADAPTER_ADDRESSES *if_info_list = NULL, *if_info = NULL;
1153 err = Win32GetAdaptersAddresses(&if_info_list);
1154 if (err != NO_ERROR) {
1155 ret = -1;
1156 goto release;
1157 }
1158 err = Win32FindAdapterAddresses(if_info_list, pcap_dev, &if_info);
1159 if (err != NO_ERROR) {
1160 ret = -1;
1161 goto release;
1162 }
1163 LPWSTR if_description = if_info->Description;
1164
1165 /* now query WMI for the offload info */
1166 err = GetNdisOffload(if_description, &offload_flags);
1167 if (err != S_OK) {
1168 ret = -1;
1169 goto release;
1170 } else if (offload_flags != 0) {
1171 if (csum == 1) {
1172 if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM) != 0) {
1173 ret = 1;
1174 }
1175 }
1176 if (other == 1) {
1177 if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSO) != 0) {
1178 ret = 1;
1179 }
1180 }
1181 }
1182
1183 if (ret == 0) {
1184 SCLogPerf("NIC offloading on %s: Checksum IPv4 Rx: %d Tx: %d IPv6 "
1185 "Rx: %d Tx: %d LSOv1 IPv4: %d LSOv2 IPv4: %d IPv6: %d",
1186 pcap_dev,
1187 (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4RX) != 0,
1188 (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4TX) != 0,
1189 (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6RX) != 0,
1190 (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6TX) != 0,
1191 (offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV1_IP4) != 0,
1192 (offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP4) != 0,
1193 (offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP6) != 0);
1194 } else {
1195 SCLogWarning(SC_ERR_NIC_OFFLOADING,
1196 "NIC offloading on %s: Checksum IPv4 Rx: %d Tx: %d IPv6 "
1197 "Rx: %d Tx: %d LSOv1 IPv4: %d LSOv2 IPv4: %d IPv6: %d",
1198 pcap_dev,
1199 (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4RX) != 0,
1200 (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4TX) != 0,
1201 (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6RX) != 0,
1202 (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6TX) != 0,
1203 (offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV1_IP4) != 0,
1204 (offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP4) != 0,
1205 (offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP6) != 0);
1206 }
1207
1208 release:
1209 if (ret == -1) {
1210 const char *err_str = Win32GetErrorString(err, WmiUtils());
1211 SCLogWarning(SC_ERR_SYSCALL,
1212 "Failure when trying to get feature via syscall for '%s': "
1213 "%s (0x%08" PRIx32 ")",
1214 pcap_dev, err_str, (uint32_t)err);
1215 LocalFree((LPVOID)err_str);
1216 }
1217
1218 SCFree(if_info_list);
1219
1220 return ret;
1221 }
1222
1223 static HRESULT
BuildNdisTcpOffloadParameters(ComInstance * instance,uint32_t offload_flags,bool enable,IWbemClassObject ** p_ndis_tcp_offload_parameters)1224 BuildNdisTcpOffloadParameters(ComInstance *instance, uint32_t offload_flags,
1225 bool enable,
1226 IWbemClassObject **p_ndis_tcp_offload_parameters)
1227 {
1228 HRESULT hr = WBEM_S_NO_ERROR;
1229
1230 IWbemClassObject *ndis_object_header = NULL;
1231
1232 if (instance == NULL || p_ndis_tcp_offload_parameters == NULL ||
1233 *p_ndis_tcp_offload_parameters != NULL) {
1234
1235 hr = HRESULT_FROM_WIN32(E_INVALIDARG);
1236 Win32HResultLogDebug(hr);
1237 goto release;
1238 }
1239
1240 /* obtain object */
1241 hr = GetWbemClassInstance(instance, L"MSNdis_TcpOffloadParameters",
1242 p_ndis_tcp_offload_parameters);
1243 if (hr != WBEM_S_NO_ERROR) {
1244 goto release;
1245 }
1246
1247 VARIANT param_variant;
1248 VariantInit(¶m_variant);
1249
1250 /* get embedded MSNdis_ObjectHeader */
1251 hr = BuildNdisObjectHeader(instance, NDIS_OBJECT_TYPE_DEFAULT,
1252 NDIS_OFFLOAD_PARAMETERS_REVISION_1,
1253 NDIS_SIZEOF_OFFLOAD_PARAMETERS_REVISION_1,
1254 &ndis_object_header);
1255 if (hr != WBEM_S_NO_ERROR) {
1256 goto release;
1257 }
1258 V_VT(¶m_variant) = VT_UNKNOWN;
1259 V_UNKNOWN(¶m_variant) = NULL;
1260 hr = GetIUnknown(ndis_object_header, &V_UNKNOWN(¶m_variant));
1261 if (hr != WBEM_S_NO_ERROR) {
1262 goto release;
1263 }
1264
1265 IWbemClassObject *ndis_tcp_offload_parameters =
1266 *p_ndis_tcp_offload_parameters;
1267
1268 /* set parameters */
1269 hr = ndis_tcp_offload_parameters->lpVtbl->Put(
1270 ndis_tcp_offload_parameters, L"Header", 0, ¶m_variant, 0);
1271 VariantClear(¶m_variant);
1272 if (hr != WBEM_S_NO_ERROR) {
1273 Win32HResultLogDebug(hr);
1274 goto release;
1275 }
1276
1277 /* IPv4 csum */
1278 V_VT(¶m_variant) = VT_BSTR;
1279 V_BSTR(¶m_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE);
1280 if (!enable && (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4) != 0) {
1281 /* this is basically all disabled cases */
1282 V_BSTR(¶m_variant) = utob(NDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED);
1283 } else if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4) ==
1284 WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4) {
1285 /* implied enable */
1286 V_BSTR(¶m_variant) = utob(NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED);
1287 } else if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4RX) != 0) {
1288 /* implied enable */
1289 V_BSTR(¶m_variant) =
1290 utob(NDIS_OFFLOAD_PARAMETERS_RX_ENABLED_TX_DISABLED);
1291 } else if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP4TX) != 0) {
1292 /* implied enable */
1293 V_BSTR(¶m_variant) =
1294 utob(NDIS_OFFLOAD_PARAMETERS_TX_ENABLED_RX_DISABLED);
1295 }
1296 hr = ndis_tcp_offload_parameters->lpVtbl->Put(
1297 ndis_tcp_offload_parameters, L"IPv4Checksum", 0, ¶m_variant, 0);
1298 if (hr != WBEM_S_NO_ERROR) {
1299 WbemLogDebug(hr);
1300 goto release;
1301 }
1302 hr = ndis_tcp_offload_parameters->lpVtbl->Put(ndis_tcp_offload_parameters,
1303 L"TCPIPv4Checksum", 0,
1304 ¶m_variant, 0);
1305 if (hr != WBEM_S_NO_ERROR) {
1306 WbemLogDebug(hr);
1307 goto release;
1308 }
1309 hr = ndis_tcp_offload_parameters->lpVtbl->Put(ndis_tcp_offload_parameters,
1310 L"UDPIPv4Checksum", 0,
1311 ¶m_variant, 0);
1312 if (hr != WBEM_S_NO_ERROR) {
1313 WbemLogDebug(hr);
1314 goto release;
1315 }
1316 VariantClear(¶m_variant);
1317
1318 /* IPv6 csum */
1319 V_VT(¶m_variant) = VT_BSTR;
1320 V_BSTR(¶m_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE);
1321 if (!enable && (offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6) != 0) {
1322 /* this is basically all disabled cases */
1323 V_BSTR(¶m_variant) = utob(NDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED);
1324 } else if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6) ==
1325 WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6) {
1326 /* implied enable */
1327 V_BSTR(¶m_variant) = utob(NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED);
1328 } else if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6RX) != 0) {
1329 /* implied enable */
1330 V_BSTR(¶m_variant) =
1331 utob(NDIS_OFFLOAD_PARAMETERS_RX_ENABLED_TX_DISABLED);
1332 } else if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_CSUM_IP6TX) != 0) {
1333 /* implied enable */
1334 V_BSTR(¶m_variant) =
1335 utob(NDIS_OFFLOAD_PARAMETERS_TX_ENABLED_RX_DISABLED);
1336 }
1337 hr = ndis_tcp_offload_parameters->lpVtbl->Put(ndis_tcp_offload_parameters,
1338 L"TCPIPv6Checksum", 0,
1339 ¶m_variant, 0);
1340 if (hr != WBEM_S_NO_ERROR) {
1341 WbemLogDebug(hr);
1342 goto release;
1343 }
1344 hr = ndis_tcp_offload_parameters->lpVtbl->Put(ndis_tcp_offload_parameters,
1345 L"UDPIPv6Checksum", 0,
1346 ¶m_variant, 0);
1347 if (hr != WBEM_S_NO_ERROR) {
1348 WbemLogDebug(hr);
1349 goto release;
1350 }
1351 VariantClear(¶m_variant);
1352
1353 /* LSOv1 */
1354 V_VT(¶m_variant) = VT_BSTR;
1355 V_BSTR(¶m_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE);
1356 if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV1_IP4) != 0) {
1357 if (enable) {
1358 V_BSTR(¶m_variant) =
1359 utob(NDIS_OFFLOAD_PARAMETERS_LSOV1_ENABLED);
1360 } else {
1361 V_BSTR(¶m_variant) =
1362 utob(NDIS_OFFLOAD_PARAMETERS_LSOV1_DISABLED);
1363 }
1364 }
1365 hr = ndis_tcp_offload_parameters->lpVtbl->Put(
1366 ndis_tcp_offload_parameters, L"LsoV1", 0, ¶m_variant, 0);
1367 if (hr != WBEM_S_NO_ERROR) {
1368 WbemLogDebug(hr);
1369 goto release;
1370 }
1371 VariantClear(¶m_variant);
1372
1373 /* LSOv2 IPv4 */
1374 V_VT(¶m_variant) = VT_BSTR;
1375 V_BSTR(¶m_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE);
1376 if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP4) != 0) {
1377 if (enable) {
1378 V_BSTR(¶m_variant) =
1379 utob(NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED);
1380 } else {
1381 V_BSTR(¶m_variant) =
1382 utob(NDIS_OFFLOAD_PARAMETERS_LSOV2_DISABLED);
1383 }
1384 }
1385 hr = ndis_tcp_offload_parameters->lpVtbl->Put(
1386 ndis_tcp_offload_parameters, L"LsoV2IPv4", 0, ¶m_variant, 0);
1387 if (hr != WBEM_S_NO_ERROR) {
1388 WbemLogDebug(hr);
1389 goto release;
1390 }
1391 VariantClear(¶m_variant);
1392
1393 /* LSOv2 IPv4 */
1394 V_VT(¶m_variant) = VT_BSTR;
1395 V_BSTR(¶m_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE);
1396 if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP6) != 0) {
1397 if (enable) {
1398 V_BSTR(¶m_variant) =
1399 utob(NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED);
1400 } else {
1401 V_BSTR(¶m_variant) =
1402 utob(NDIS_OFFLOAD_PARAMETERS_LSOV2_DISABLED);
1403 }
1404 }
1405 hr = ndis_tcp_offload_parameters->lpVtbl->Put(
1406 ndis_tcp_offload_parameters, L"LsoV2IPv6", 0, ¶m_variant, 0);
1407 if (hr != WBEM_S_NO_ERROR) {
1408 WbemLogDebug(hr);
1409 goto release;
1410 }
1411 VariantClear(¶m_variant);
1412
1413 /* currently unused fields */
1414 V_VT(¶m_variant) = VT_BSTR;
1415 V_BSTR(¶m_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE);
1416 hr = ndis_tcp_offload_parameters->lpVtbl->Put(
1417 ndis_tcp_offload_parameters, L"IPSec", 0, ¶m_variant, 0);
1418 if (hr != WBEM_S_NO_ERROR) {
1419 WbemLogDebug(hr);
1420 goto release;
1421 }
1422 hr = ndis_tcp_offload_parameters->lpVtbl->Put(ndis_tcp_offload_parameters,
1423 L"TcpConnectionIPv4", 0,
1424 ¶m_variant, 0);
1425 if (hr != WBEM_S_NO_ERROR) {
1426 WbemLogDebug(hr);
1427 goto release;
1428 }
1429 hr = ndis_tcp_offload_parameters->lpVtbl->Put(ndis_tcp_offload_parameters,
1430 L"TcpConnectionIPv6", 0,
1431 ¶m_variant, 0);
1432 if (hr != WBEM_S_NO_ERROR) {
1433 WbemLogDebug(hr);
1434 goto release;
1435 }
1436 hr = ndis_tcp_offload_parameters->lpVtbl->Put(
1437 ndis_tcp_offload_parameters, L"Flags", 0, ¶m_variant, 0);
1438 if (hr != WBEM_S_NO_ERROR) {
1439 WbemLogDebug(hr);
1440 goto release;
1441 }
1442 /* further fields are for NDIS 6.1+ */
1443
1444 release:
1445 VariantClear(¶m_variant);
1446
1447 return hr;
1448 }
1449
SetNdisOffload(LPCWSTR if_description,uint32_t offload_flags,bool enable)1450 static HRESULT SetNdisOffload(LPCWSTR if_description, uint32_t offload_flags,
1451 bool enable)
1452 {
1453 HRESULT hr = S_OK;
1454
1455 ComInstance instance = {};
1456 WbemMethod method = {};
1457 WbemMethodCall call = {};
1458
1459 /* param 0 */
1460 IWbemClassObject *ndis_method_header = NULL;
1461 /* param 1 */
1462 IWbemClassObject *ndis_tcp_offload_parameters = NULL;
1463
1464 if (if_description == NULL) {
1465 SCLogWarning(SC_ERR_SYSCALL, "No description specified for device");
1466 return E_INVALIDARG;
1467 }
1468
1469 LPCWSTR class_name = L"MSNdis_SetTcpOffloadParameters";
1470 LPCWSTR instance_name_fmt = L"%s=\"%s\"";
1471 size_t n_chars = wcslen(class_name) + wcslen(if_description) +
1472 wcslen(instance_name_fmt);
1473 LPWSTR instance_name = SCMalloc((n_chars + 1) * sizeof(wchar_t));
1474 if (instance_name == NULL) {
1475 SCLogWarning(SC_ERR_SYSCALL,
1476 "Failed to allocate buffer for instance path");
1477 goto release;
1478 }
1479 instance_name[n_chars] = 0; /* defensively null-terminate */
1480 hr = StringCchPrintfW(instance_name, n_chars, instance_name_fmt, class_name,
1481 if_description);
1482 if (hr != S_OK) {
1483 SCLogWarning(SC_ERR_SYSCALL,
1484 "Failed to format WMI class instance name: 0x%" PRIx32,
1485 (uint32_t)hr);
1486 goto release;
1487 }
1488
1489 /* method name */
1490 LPCWSTR method_name = L"WmiSetTcpOffloadParameters";
1491
1492 /* connect to COM/WMI */
1493 hr = ComInstanceInit(&instance, L"ROOT\\WMI");
1494 if (hr != S_OK) {
1495 goto release;
1496 }
1497
1498 /* obtain method */
1499 hr = GetWbemMethod(&instance, class_name, method_name, &method);
1500 if (hr != S_OK) {
1501 goto release;
1502 }
1503
1504 /* make parameter instances */
1505 hr = GetWbemMethodCall(&method, instance_name, &call);
1506 if (hr != S_OK) {
1507 goto release;
1508 }
1509
1510 /* build parameters */
1511
1512 VARIANT param_variant;
1513 VariantInit(¶m_variant);
1514
1515 /* Make MSNdis_WmiMethodHeader */
1516 hr = BuildNdisWmiMethodHeader(&instance, 0, 0, 0, 5, &ndis_method_header);
1517 if (hr != WBEM_S_NO_ERROR) {
1518 goto release;
1519 }
1520
1521 V_VT(¶m_variant) = VT_UNKNOWN;
1522 V_UNKNOWN(¶m_variant) = NULL;
1523 hr = GetIUnknown(ndis_method_header, &V_UNKNOWN(¶m_variant));
1524 if (hr != WBEM_S_NO_ERROR) {
1525 goto release;
1526 }
1527 hr = call.in_params->lpVtbl->Put(call.in_params, L"MethodHeader", 0,
1528 ¶m_variant, 0);
1529 VariantClear(¶m_variant);
1530 if (hr != WBEM_S_NO_ERROR) {
1531 Win32HResultLogDebug(hr);
1532 goto release;
1533 }
1534
1535 /* Make MSNdis_TcpOffloadParameters */
1536 hr = BuildNdisTcpOffloadParameters(&instance, offload_flags, enable,
1537 &ndis_tcp_offload_parameters);
1538 if (hr != WBEM_S_NO_ERROR) {
1539 goto release;
1540 }
1541
1542 V_VT(¶m_variant) = VT_UNKNOWN;
1543 V_UNKNOWN(¶m_variant) = NULL;
1544 hr = GetIUnknown(ndis_tcp_offload_parameters, &V_UNKNOWN(¶m_variant));
1545 if (hr != WBEM_S_NO_ERROR) {
1546 goto release;
1547 }
1548 hr = call.in_params->lpVtbl->Put(call.in_params, L"TcpOffloadParameters", 0,
1549 ¶m_variant, 0);
1550 VariantClear(¶m_variant);
1551 if (hr != WBEM_S_NO_ERROR) {
1552 Win32HResultLogDebug(hr);
1553 goto release;
1554 }
1555
1556 /* execute the method */
1557 hr = WbemMethodCallExec(&call, NULL);
1558 if (hr != S_OK) {
1559 Win32HResultLogDebug(hr);
1560 goto release;
1561 }
1562
1563 release:
1564 ReleaseObject(ndis_tcp_offload_parameters);
1565 ReleaseObject(ndis_method_header);
1566
1567 WbemMethodCallRelease(&call);
1568 WbemMethodRelease(&method);
1569 ComInstanceRelease(&instance);
1570
1571 return hr;
1572 }
1573
DisableIfaceOffloadingWin32(LiveDevice * ldev,int csum,int other)1574 int DisableIfaceOffloadingWin32(LiveDevice *ldev, int csum, int other)
1575 {
1576 SCLogDebug("Disabling offloading for device %s", ldev->dev);
1577
1578 int ret = 0;
1579 DWORD err = NO_ERROR;
1580 uint32_t offload_flags = 0;
1581
1582 if (ldev == NULL) {
1583 return -1;
1584 }
1585
1586 /* WMI uses the description as an identifier... */
1587 IP_ADAPTER_ADDRESSES *if_info_list = NULL, *if_info = NULL;
1588 err = Win32GetAdaptersAddresses(&if_info_list);
1589 if (err != NO_ERROR) {
1590 ret = -1;
1591 goto release;
1592 }
1593 err = Win32FindAdapterAddresses(if_info_list, ldev->dev, &if_info);
1594 if (err != NO_ERROR) {
1595 ret = -1;
1596 goto release;
1597 }
1598 LPWSTR if_description = if_info->Description;
1599
1600 err = GetNdisOffload(if_description, &offload_flags);
1601 if (err != S_OK) {
1602 ret = -1;
1603 goto release;
1604 }
1605
1606 if (!csum) {
1607 offload_flags &= ~WIN32_TCP_OFFLOAD_FLAG_CSUM;
1608 }
1609 if (!other) {
1610 offload_flags &= ~WIN32_TCP_OFFLOAD_FLAG_LSO;
1611 }
1612
1613 err = SetNdisOffload(if_description, offload_flags, 0);
1614 if (err != S_OK) {
1615 ret = -1;
1616 goto release;
1617 }
1618
1619 release:
1620 SCFree(if_info_list);
1621
1622 return ret;
1623 }
1624
RestoreIfaceOffloadingWin32(LiveDevice * ldev)1625 int RestoreIfaceOffloadingWin32(LiveDevice *ldev)
1626 {
1627 SCLogDebug("Enabling offloading for device %s", ldev->dev);
1628
1629 int ret = 0;
1630 DWORD err = NO_ERROR;
1631
1632 if (ldev == NULL) {
1633 return -1;
1634 }
1635
1636 /* WMI uses the description as an identifier... */
1637 IP_ADAPTER_ADDRESSES *if_info_list = NULL, *if_info = NULL;
1638 err = Win32GetAdaptersAddresses(&if_info_list);
1639 if (err != NO_ERROR) {
1640 ret = -1;
1641 goto release;
1642 }
1643 err = Win32FindAdapterAddresses(if_info_list, ldev->dev, &if_info);
1644 if (err != NO_ERROR) {
1645 ret = -1;
1646 goto release;
1647 }
1648 LPWSTR if_description = if_info->Description;
1649
1650 err = SetNdisOffload(if_description, ldev->offload_orig, 1);
1651 if (err != S_OK) {
1652 ret = -1;
1653 goto release;
1654 }
1655
1656 release:
1657 SCFree(if_info_list);
1658
1659 return ret;
1660 }
1661
1662 #endif /* NTDDI_VERSION >= NTDDI_VISTA */
1663
1664 #ifdef UNITTESTS
Win32TestStripPcapPrefix(void)1665 static int Win32TestStripPcapPrefix(void)
1666 {
1667 int result = 1;
1668
1669 const char *name1 = "\\Device\\NPF_{D4A32435-1BA7-4008-93A6-1518AA4BBD9B}";
1670 const char *expect_name1 = "{D4A32435-1BA7-4008-93A6-1518AA4BBD9B}";
1671
1672 const char *name2 = "{D4A32435-1BA7-4008-93A6-1518AA4BBD9B}";
1673 const char *expect_name2 = "{D4A32435-1BA7-4008-93A6-1518AA4BBD9B}";
1674
1675 result &= (strncmp(expect_name1, StripPcapPrefix(name1),
1676 strlen(expect_name1)) == 0);
1677
1678 result &= (strncmp(expect_name2, StripPcapPrefix(name2),
1679 strlen(expect_name2)) == 0);
1680
1681 return result;
1682 }
1683 #endif /* UNITTESTS */
1684
Win32SyscallRegisterTests()1685 void Win32SyscallRegisterTests()
1686 {
1687 #ifdef UNITTESTS
1688 UtRegisterTest("Win32TestStripPcapPrefix", Win32TestStripPcapPrefix);
1689 #endif
1690 }
1691
1692 #endif /* OS_WIN32 */
1693