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(&param_variant);
804     IWbemClassObject *ndis_object_header = *p_ndis_object_header;
805 
806     /* set parameters */
807     V_VT(&param_variant) = VT_UI1;
808     V_UI1(&param_variant) = type;
809     hr = ndis_object_header->lpVtbl->Put(ndis_object_header, L"Type", 0,
810                                          &param_variant, 0);
811     VariantClear(&param_variant);
812     if (hr != WBEM_S_NO_ERROR) {
813         WbemLogDebug(hr);
814         goto release;
815     }
816 
817     V_VT(&param_variant) = VT_UI1;
818     V_UI1(&param_variant) = revision;
819     hr = ndis_object_header->lpVtbl->Put(ndis_object_header, L"Revision", 0,
820                                          &param_variant, 0);
821     VariantClear(&param_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(&param_variant) = VT_I4;
830     V_I4(&param_variant) = size;
831     hr = ndis_object_header->lpVtbl->Put(ndis_object_header, L"Size", 0,
832                                          &param_variant, 0);
833     VariantClear(&param_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(&param_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(&param_variant) = VT_UNKNOWN;
878     V_UNKNOWN(&param_variant) = NULL;
879     hr = GetIUnknown(ndis_object_header, &V_UNKNOWN(&param_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                                          &param_variant, 0);
889     VariantClear(&param_variant);
890     if (hr != WBEM_S_NO_ERROR) {
891         WbemLogDebug(hr);
892         goto release;
893     }
894 
895     V_VT(&param_variant) = VT_BSTR;
896     V_BSTR(&param_variant) = utob(net_luid);
897     hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"NetLuid", 0,
898                                          &param_variant, 0);
899     VariantClear(&param_variant);
900     if (hr != WBEM_S_NO_ERROR) {
901         WbemLogDebug(hr);
902         goto release;
903     }
904 
905     V_VT(&param_variant) = VT_BSTR;
906     V_BSTR(&param_variant) = utob((uint64_t)port_number);
907     hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"PortNumber", 0,
908                                          &param_variant, 0);
909     VariantClear(&param_variant);
910     if (hr != WBEM_S_NO_ERROR) {
911         WbemLogDebug(hr);
912         goto release;
913     }
914 
915     V_VT(&param_variant) = VT_BSTR;
916     V_BSTR(&param_variant) = utob(request_id);
917     hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"RequestId", 0,
918                                          &param_variant, 0);
919     VariantClear(&param_variant);
920     if (hr != WBEM_S_NO_ERROR) {
921         WbemLogDebug(hr);
922         goto release;
923     }
924 
925     V_VT(&param_variant) = VT_BSTR;
926     V_BSTR(&param_variant) = utob((uint64_t)timeout);
927     hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"Timeout", 0,
928                                          &param_variant, 0);
929     VariantClear(&param_variant);
930     if (hr != WBEM_S_NO_ERROR) {
931         WbemLogDebug(hr);
932         goto release;
933     }
934 
935     V_VT(&param_variant) = VT_BSTR;
936     V_BSTR(&param_variant) = utob((uint64_t)0);
937     hr = ndis_method_header->lpVtbl->Put(ndis_method_header, L"Padding", 0,
938                                          &param_variant, 0);
939     VariantClear(&param_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(&param_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(&param_variant) = VT_UNKNOWN;
1023     V_UNKNOWN(&param_variant) = NULL;
1024     hr = GetIUnknown(ndis_method_header, &V_UNKNOWN(&param_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                                      &param_variant, 0);
1032     VariantClear(&param_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(&param_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(&param_variant) = VT_UNKNOWN;
1259     V_UNKNOWN(&param_variant) = NULL;
1260     hr = GetIUnknown(ndis_object_header, &V_UNKNOWN(&param_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, &param_variant, 0);
1271     VariantClear(&param_variant);
1272     if (hr != WBEM_S_NO_ERROR) {
1273         Win32HResultLogDebug(hr);
1274         goto release;
1275     }
1276 
1277     /* IPv4 csum */
1278     V_VT(&param_variant) = VT_BSTR;
1279     V_BSTR(&param_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(&param_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(&param_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(&param_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(&param_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, &param_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                                                   &param_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                                                   &param_variant, 0);
1312     if (hr != WBEM_S_NO_ERROR) {
1313         WbemLogDebug(hr);
1314         goto release;
1315     }
1316     VariantClear(&param_variant);
1317 
1318     /* IPv6 csum */
1319     V_VT(&param_variant) = VT_BSTR;
1320     V_BSTR(&param_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(&param_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(&param_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(&param_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(&param_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                                                   &param_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                                                   &param_variant, 0);
1347     if (hr != WBEM_S_NO_ERROR) {
1348         WbemLogDebug(hr);
1349         goto release;
1350     }
1351     VariantClear(&param_variant);
1352 
1353     /* LSOv1 */
1354     V_VT(&param_variant) = VT_BSTR;
1355     V_BSTR(&param_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE);
1356     if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV1_IP4) != 0) {
1357         if (enable) {
1358             V_BSTR(&param_variant) =
1359                     utob(NDIS_OFFLOAD_PARAMETERS_LSOV1_ENABLED);
1360         } else {
1361             V_BSTR(&param_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, &param_variant, 0);
1367     if (hr != WBEM_S_NO_ERROR) {
1368         WbemLogDebug(hr);
1369         goto release;
1370     }
1371     VariantClear(&param_variant);
1372 
1373     /* LSOv2 IPv4 */
1374     V_VT(&param_variant) = VT_BSTR;
1375     V_BSTR(&param_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE);
1376     if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP4) != 0) {
1377         if (enable) {
1378             V_BSTR(&param_variant) =
1379                     utob(NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED);
1380         } else {
1381             V_BSTR(&param_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, &param_variant, 0);
1387     if (hr != WBEM_S_NO_ERROR) {
1388         WbemLogDebug(hr);
1389         goto release;
1390     }
1391     VariantClear(&param_variant);
1392 
1393     /* LSOv2 IPv4 */
1394     V_VT(&param_variant) = VT_BSTR;
1395     V_BSTR(&param_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE);
1396     if ((offload_flags & WIN32_TCP_OFFLOAD_FLAG_LSOV2_IP6) != 0) {
1397         if (enable) {
1398             V_BSTR(&param_variant) =
1399                     utob(NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED);
1400         } else {
1401             V_BSTR(&param_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, &param_variant, 0);
1407     if (hr != WBEM_S_NO_ERROR) {
1408         WbemLogDebug(hr);
1409         goto release;
1410     }
1411     VariantClear(&param_variant);
1412 
1413     /* currently unused fields */
1414     V_VT(&param_variant) = VT_BSTR;
1415     V_BSTR(&param_variant) = utob(NDIS_OFFLOAD_PARAMETERS_NO_CHANGE);
1416     hr = ndis_tcp_offload_parameters->lpVtbl->Put(
1417             ndis_tcp_offload_parameters, L"IPSec", 0, &param_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                                                   &param_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                                                   &param_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, &param_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(&param_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(&param_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(&param_variant) = VT_UNKNOWN;
1522     V_UNKNOWN(&param_variant) = NULL;
1523     hr = GetIUnknown(ndis_method_header, &V_UNKNOWN(&param_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                                      &param_variant, 0);
1529     VariantClear(&param_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(&param_variant) = VT_UNKNOWN;
1543     V_UNKNOWN(&param_variant) = NULL;
1544     hr = GetIUnknown(ndis_tcp_offload_parameters, &V_UNKNOWN(&param_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                                      &param_variant, 0);
1550     VariantClear(&param_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