diff --git a/README.md b/README.md index cf6214c..9bc7813 100644 --- a/README.md +++ b/README.md @@ -11,12 +11,12 @@ Wintun is deployed as a platform-specific `wintun.dll` file. Install the `wintun Include the [`wintun.h` file](https://git.zx2c4.com/wintun/tree/api/wintun.h) in your project simply by copying it there and dynamically load the `wintun.dll` using [`LoadLibraryEx()`](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexa) and [`GetProcAddress()`](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getprocaddress) to resolve each function, using the typedefs provided in the header file. The [`InitializeWintun` function in the example.c code](https://git.zx2c4.com/wintun/tree/example/example.c) provides this in a function that you can simply copy and paste. -With the library setup, Wintun can then be used by first creating an adapter, and then starting a tunnel session on that adapter. Adapters have names (e.g. "OfficeNet"), and each one belongs to a _pool_ (e.g. "WireGuard"). So, for example, the WireGuard application app creates multiple tunnels all inside of its "WireGuard" _pool_: +With the library setup, Wintun can then be used by first creating an adapter, configuring it, and then setting its status to "up". Adapters have names (e.g. "OfficeNet") and types (e.g. "Wintun"). ```C -WINTUN_ADAPTER_HANDLE Adapter1 = WintunCreateAdapter(L"WireGuard", L"OfficeNet", &SomeFixedGUID1, NULL); -WINTUN_ADAPTER_HANDLE Adapter2 = WintunCreateAdapter(L"WireGuard", L"HomeNet", &SomeFixedGUID2, NULL); -WINTUN_ADAPTER_HANDLE Adapter3 = WintunCreateAdapter(L"WireGuard", L"Data Center", &SomeFixedGUID3, NULL); +WINTUN_ADAPTER_HANDLE Adapter1 = WintunCreateAdapter(L"OfficeNet", L"Wintun", &SomeFixedGUID1); +WINTUN_ADAPTER_HANDLE Adapter2 = WintunCreateAdapter(L"HomeNet", L"Wintun", &SomeFixedGUID2); +WINTUN_ADAPTER_HANDLE Adapter3 = WintunCreateAdapter(L"Data Center", L"Wintun", &SomeFixedGUID3); ``` After creating an adapter, we can use it by starting a session: @@ -119,13 +119,14 @@ Non-zero to continue iterating adapters; zero to stop. #### WINTUN\_LOGGER\_CALLBACK -`typedef void(* WINTUN_LOGGER_CALLBACK) (WINTUN_LOGGER_LEVEL Level, const WCHAR *Message)` +`typedef void(* WINTUN_LOGGER_CALLBACK) (WINTUN_LOGGER_LEVEL Level, DWORD64 Timestamp, const WCHAR *Message)` Called by internal logger to report diagnostic messages **Parameters** - *Level*: Message level. +- *Timestamp*: Message timestamp in in 100ns intervals since 1601-01-01 UTC. - *Message*: Message text. #### WINTUN\_SESSION\_HANDLE @@ -152,88 +153,49 @@ Enumerator #### WintunCreateAdapter() -`WINTUN_ADAPTER_HANDLE WintunCreateAdapter (const WCHAR * Pool, const WCHAR * Name, const GUID * RequestedGUID, BOOL * RebootRequired)` +`WINTUN_ADAPTER_HANDLE WintunCreateAdapter (const WCHAR * Name, const WCHAR * TunnelType, const GUID * RequestedGUID)` Creates a new Wintun adapter. **Parameters** -- *Pool*: Name of the adapter pool. Zero-terminated string of up to WINTUN\_MAX\_POOL-1 characters. - *Name*: The requested name of the adapter. Zero-terminated string of up to MAX\_ADAPTER\_NAME-1 characters. +- *Name*: Name of the adapter tunnel type. Zero-terminated string of up to MAX\_ADAPTER\_NAME-1 characters. - *RequestedGUID*: The GUID of the created network adapter, which then influences NLA generation deterministically. If it is set to NULL, the GUID is chosen by the system at random, and hence a new NLA entry is created for each new adapter. It is called "requested" GUID because the API it uses is completely undocumented, and so there could be minor interesting complications with its usage. -- *RebootRequired*: Optional pointer to a boolean flag to be set to TRUE in case SetupAPI suggests a reboot. **Returns** -If the function succeeds, the return value is the adapter handle. Must be released with WintunFreeAdapter. If the function fails, the return value is NULL. To get extended error information, call GetLastError. +If the function succeeds, the return value is the adapter handle. Must be released with WintunCloseAdapter. If the function fails, the return value is NULL. To get extended error information, call GetLastError. #### WintunOpenAdapter() -`WINTUN_ADAPTER_HANDLE WintunOpenAdapter (const WCHAR * Pool, const WCHAR * Name)` +`WINTUN_ADAPTER_HANDLE WintunOpenAdapter (const WCHAR * Name)` Opens an existing Wintun adapter. **Parameters** -- *Pool*: Name of the adapter pool. Zero-terminated string of up to WINTUN\_MAX\_POOL-1 characters. -- *Name*: Adapter name. Zero-terminated string of up to MAX\_ADAPTER\_NAME-1 characters. +- *Name*: The requested name of the adapter. Zero-terminated string of up to MAX\_ADAPTER\_NAME-1 characters. **Returns** -If the function succeeds, the return value is adapter handle. Must be released with WintunFreeAdapter. If the function fails, the return value is NULL. To get extended error information, call GetLastError. Possible errors include the following: ERROR\_FILE\_NOT\_FOUND if adapter with given name is not found; ERROR\_ALREADY\_EXISTS if adapter is found but not a Wintun-class or not a member of the pool +If the function succeeds, the return value is adapter handle. Must be released with WintunCloseAdapter. If the function fails, the return value is NULL. To get extended error information, call GetLastError. -#### WintunDeleteAdapter() +#### WintunCloseAdapter() -`BOOL WintunDeleteAdapter (WINTUN_ADAPTER_HANDLE Adapter, BOOL ForceCloseSessions, BOOL * RebootRequired)` +`void WintunCloseAdapter (WINTUN_ADAPTER_HANDLE Adapter)` -Deletes a Wintun adapter. +Releases Wintun adapter resources and, if adapter was created with WintunCreateAdapter, removes adapter. **Parameters** -- *Adapter*: Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter. -- *ForceCloseSessions*: Force close adapter handles that may be in use by other processes. Only set this to TRUE with extreme care, as this is resource intensive and may put processes into an undefined or unpredictable state. Most users should set this to FALSE. -- *RebootRequired*: Optional pointer to a boolean flag to be set to TRUE in case SetupAPI suggests a reboot. +- *Adapter*: Adapter handle obtained with WintunCreateAdapter or WintunOpenAdapter. -**Returns** +#### WintunDeleteDriver() -If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To get extended error information, call GetLastError. +`BOOL WintunDeleteDriver ()` -#### WintunEnumAdapters() - -`BOOL WintunEnumAdapters (const WCHAR * Pool, WINTUN_ENUM_CALLBACK Callback, LPARAM Param)` - -Enumerates all Wintun adapters. - -**Parameters** - -- *Pool*: Name of the adapter pool. Zero-terminated string of up to WINTUN\_MAX\_POOL-1 characters. -- *Callback*: Callback function. To continue enumeration, the callback function must return TRUE; to stop enumeration, it must return FALSE. -- *Param*: An application-defined value to be passed to the callback function. - -**Returns** - -If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To get extended error information, call GetLastError. - -#### WintunFreeAdapter() - -`void WintunFreeAdapter (WINTUN_ADAPTER_HANDLE Adapter)` - -Releases Wintun adapter resources. - -**Parameters** - -- *Adapter*: Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter. - -#### WintunDeletePoolDriver() - -`BOOL WintunDeletePoolDriver (const WCHAR * Pool, BOOL * RebootRequired)` - -Deletes all Wintun adapters in a pool and if there are no more adapters in any other pools, also removes Wintun from the driver store, usually called by uninstallers. - -**Parameters** - -- *Pool*: Name of the adapter pool. Zero-terminated string of up to WINTUN\_MAX\_POOL-1 characters. -- *RebootRequired*: Optional pointer to a boolean flag to be set to TRUE in case SetupAPI suggests a reboot. +Deletes the Wintun driver if there are no more adapters in use. **Returns** @@ -250,36 +212,6 @@ Returns the LUID of the adapter. - *Adapter*: Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter - *Luid*: Pointer to LUID to receive adapter LUID. -#### WintunGetAdapterName() - -`BOOL WintunGetAdapterName (WINTUN_ADAPTER_HANDLE Adapter, WCHAR * Name)` - -Returns the name of the Wintun adapter. - -**Parameters** - -- *Adapter*: Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter -- *Name*: Pointer to a string to receive adapter name - -**Returns** - -If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To get extended error information, call GetLastError. - -#### WintunSetAdapterName() - -`BOOL WintunSetAdapterName (WINTUN_ADAPTER_HANDLE Adapter, const WCHAR * Name)` - -Sets name of the Wintun adapter. - -**Parameters** - -- *Adapter*: Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter -- *Name*: Adapter name. Zero-terminated string of up to MAX\_ADAPTER\_NAME-1 characters. - -**Returns** - -If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To get extended error information, call GetLastError. - #### WintunGetRunningDriverVersion() `DWORD WintunGetRunningDriverVersion (void )` @@ -397,10 +329,10 @@ Sends the packet and releases internal buffer. WintunSendPacket is thread-safe, General requirements: -- [Visual Studio 2019](https://visualstudio.microsoft.com/downloads/) with Windows SDK 10.0.18362.0 -- [Windows Driver Kit for Windows 10, version 1903](https://docs.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk) +- [Visual Studio 2019](https://visualstudio.microsoft.com/downloads/) with Windows SDK +- [Windows Driver Kit](https://docs.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk) -`wintun.sln` may be opened in Visual Studio for development and building. Be sure to run `bcdedit /set testsigning on` before to enable unsigned driver loading. The default run sequence (F5) in Visual Studio will build the example project. +`wireguard.sln` may be opened in Visual Studio for development and building. Be sure to run `bcdedit /set testsigning on` and then reboot before to enable unsigned driver loading. The default run sequence (F5) in Visual Studio will build the example project and its dependencies. ## License diff --git a/api/adapter.c b/api/adapter.c index f0a90e5..b71032a 100644 --- a/api/adapter.c +++ b/api/adapter.c @@ -3,208 +3,369 @@ * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved. */ -#include #include #include #include #include -#include #include +#include #include #include #include -#include +#include #include #include /* Keep these two at bottom in this order, so that we only generate extra GUIDs for devpkey. The other keys we'll get from uuid.lib like usual. */ #include -#include + +/* We pretend we're Windows 8, and then hack around the limitation in Windows 7 below. */ +#if NTDDI_VERSION == NTDDI_WIN7 +# undef NTDDI_VERSION +# define NTDDI_VERSION NTDDI_WIN8 +# include +# include +# undef NTDDI_VERSION +# define NTDDI_VERSION NTDDI_WIN7 +#else +# include +# include +#endif #include "adapter.h" +#include "driver.h" #include "logger.h" #include "main.h" #include "namespace.h" #include "nci.h" #include "ntdll.h" -#include "registry.h" -#include "resource.h" #include "rundll32.h" -#include "wintun-inf.h" +#include "registry.h" +#include "adapter_win7.h" #pragma warning(disable : 4221) /* nonstandard: address of automatic in initializer */ -#define WAIT_FOR_REGISTRY_TIMEOUT 10000 /* ms */ -#define MAX_POOL_DEVICE_TYPE (WINTUN_MAX_POOL + 8) /* Should accommodate a pool name with " Tunnel" appended */ - -static const DEVPROPKEY DEVPKEY_Wintun_Pool = { - { 0xaba51201, 0xdf7a, 0x3a38, { 0x0a, 0xd9, 0x90, 0x64, 0x42, 0xd2, 0x71, 0xae } }, - DEVPROPID_FIRST_USABLE + 0 -}; - -static const DEVPROPKEY DEVPKEY_Wintun_Name = { +const DEVPROPKEY DEVPKEY_Wintun_Name = { { 0x3361c968, 0x2f2e, 0x4660, { 0xb4, 0x7e, 0x69, 0x9c, 0xdc, 0x4c, 0x32, 0xb9 } }, DEVPROPID_FIRST_USABLE + 1 }; -typedef struct _SP_DEVINFO_DATA_LIST -{ - SP_DEVINFO_DATA Data; - struct _SP_DEVINFO_DATA_LIST *Next; -} SP_DEVINFO_DATA_LIST; - _Must_inspect_result_ -static _Return_type_success_(return != NULL) -_Post_maybenull_ -SP_DRVINFO_DETAIL_DATA_W * -GetAdapterDrvInfoDetail( - _In_ HDEVINFO DevInfo, - _In_opt_ SP_DEVINFO_DATA *DevInfoData, - _In_ SP_DRVINFO_DATA_W *DrvInfoData) +static _Return_type_success_(return != FALSE) +BOOL +PopulateAdapterData(_Inout_ WINTUN_ADAPTER *Adapter) { - DWORD Size = sizeof(SP_DRVINFO_DETAIL_DATA_W) + 0x100; - for (;;) - { - SP_DRVINFO_DETAIL_DATA_W *DrvInfoDetailData = Alloc(Size); - if (!DrvInfoDetailData) - return NULL; - DrvInfoDetailData->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA_W); - if (SetupDiGetDriverInfoDetailW(DevInfo, DevInfoData, DrvInfoData, DrvInfoDetailData, Size, &Size)) - return DrvInfoDetailData; - DWORD LastError = GetLastError(); - Free(DrvInfoDetailData); - if (LastError != ERROR_INSUFFICIENT_BUFFER) - { - if (DevInfoData) - LOG_ERROR(LastError, L"Failed for adapter %u", DevInfoData->DevInst); - else - LOG_ERROR(LastError, L"Failed"); - SetLastError(LastError); - return NULL; - } - } -} + DWORD LastError = ERROR_SUCCESS; -_Must_inspect_result_ -static _Return_type_success_(return != NULL) -_Post_maybenull_ -_Post_writable_byte_size_(*BufLen) -VOID * -GetDeviceRegistryProperty( - _In_ HDEVINFO DevInfo, - _In_ SP_DEVINFO_DATA *DevInfoData, - _In_ DWORD Property, - _Out_opt_ DWORD *ValueType, - _Inout_ DWORD *BufLen) -{ - for (;;) + /* Open HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\\ registry key. */ + HKEY Key = + SetupDiOpenDevRegKey(Adapter->DevInfo, &Adapter->DevInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_QUERY_VALUE); + if (Key == INVALID_HANDLE_VALUE) { - BYTE *Data = Alloc(*BufLen); - if (!Data) - return NULL; - if (SetupDiGetDeviceRegistryPropertyW(DevInfo, DevInfoData, Property, ValueType, Data, *BufLen, BufLen)) - return Data; - DWORD LastError = GetLastError(); - Free(Data); - if (LastError != ERROR_INSUFFICIENT_BUFFER) - { - SetLastError( - LOG_ERROR(LastError, L"Failed to query adapter %u property 0x%x", DevInfoData->DevInst, Property)); - return NULL; - } - } -} - -_Must_inspect_result_ -static _Return_type_success_(return != NULL) -_Post_maybenull_ -LPWSTR -GetDeviceRegistryString(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData, _In_ DWORD Property) -{ - DWORD LastError, ValueType, Size = 256 * sizeof(WCHAR); - LPWSTR Buf = GetDeviceRegistryProperty(DevInfo, DevInfoData, Property, &ValueType, &Size); - if (!Buf) - return NULL; - switch (ValueType) - { - case REG_SZ: - case REG_EXPAND_SZ: - case REG_MULTI_SZ: - if (RegistryGetString(&Buf, Size / sizeof(*Buf), ValueType)) - return Buf; - LastError = GetLastError(); - break; - default: - LOG(WINTUN_LOG_ERR, - L"Adapter %u property 0x%x is not a string (type: %u)", - DevInfoData->DevInst, - Property, - ValueType); - LastError = ERROR_INVALID_DATATYPE; - } - Free(Buf); - SetLastError(LastError); - return NULL; -} - -_Must_inspect_result_ -static _Return_type_success_(return != NULL) -_Post_maybenull_ -PZZWSTR -GetDeviceRegistryMultiString(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData, _In_ DWORD Property) -{ - DWORD LastError, ValueType, Size = 256 * sizeof(WCHAR); - PZZWSTR Buf = GetDeviceRegistryProperty(DevInfo, DevInfoData, Property, &ValueType, &Size); - if (!Buf) - return NULL; - switch (ValueType) - { - case REG_SZ: - case REG_EXPAND_SZ: - case REG_MULTI_SZ: - if (RegistryGetMultiString(&Buf, Size / sizeof(*Buf), ValueType)) - return Buf; - LastError = GetLastError(); - break; - default: - LOG(WINTUN_LOG_ERR, - L"Adapter %u property 0x%x is not a string (type: %u)", - DevInfoData->DevInst, - Property, - ValueType); - LastError = ERROR_INVALID_DATATYPE; - } - Free(Buf); - SetLastError(LastError); - return NULL; -} - -static BOOL -IsOurHardwareID(_In_z_ PCZZWSTR Hwids) -{ - for (; Hwids[0]; Hwids += wcslen(Hwids) + 1) - if (!_wcsicmp(Hwids, WINTUN_HWID)) - return TRUE; - return FALSE; -} - -static BOOL -IsOurAdapter(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData) -{ - PZZWSTR Hwids = GetDeviceRegistryMultiString(DevInfo, DevInfoData, SPDRP_HARDWAREID); - if (!Hwids) - { - LOG_LAST_ERROR(L"Failed to get adapter %u hardware ID", DevInfoData->DevInst); + LOG_LAST_ERROR(L"Failed to open adapter device registry key"); return FALSE; } - BOOL IsOurs = IsOurHardwareID(Hwids); - Free(Hwids); - return IsOurs; + + LPWSTR ValueStr = RegistryQueryString(Key, L"NetCfgInstanceId", TRUE); + if (!ValueStr) + { + WCHAR RegPath[MAX_REG_PATH]; + LoggerGetRegistryKeyPath(Key, RegPath); + LastError = LOG(WINTUN_LOG_ERR, L"Failed to get %.*s\\NetCfgInstanceId", MAX_REG_PATH, RegPath); + goto cleanupKey; + } + if (FAILED(CLSIDFromString(ValueStr, &Adapter->CfgInstanceID))) + { + WCHAR RegPath[MAX_REG_PATH]; + LoggerGetRegistryKeyPath(Key, RegPath); + LastError = LOG(WINTUN_LOG_ERR, L"%.*s\\NetCfgInstanceId is not a GUID: %s", MAX_REG_PATH, RegPath, ValueStr); + Free(ValueStr); + goto cleanupKey; + } + Free(ValueStr); + + if (!RegistryQueryDWORD(Key, L"NetLuidIndex", &Adapter->LuidIndex, TRUE)) + { + WCHAR RegPath[MAX_REG_PATH]; + LoggerGetRegistryKeyPath(Key, RegPath); + LastError = LOG(WINTUN_LOG_ERR, L"Failed to get %.*s\\NetLuidIndex", MAX_REG_PATH, RegPath); + goto cleanupKey; + } + + if (!RegistryQueryDWORD(Key, L"*IfType", &Adapter->IfType, TRUE)) + { + WCHAR RegPath[MAX_REG_PATH]; + LoggerGetRegistryKeyPath(Key, RegPath); + LastError = LOG(WINTUN_LOG_ERR, L"Failed to get %.*s\\*IfType", MAX_REG_PATH, RegPath); + goto cleanupKey; + } + + Adapter->InterfaceFilename = AdapterGetDeviceObjectFileName(Adapter->DevInstanceID); + if (!Adapter->InterfaceFilename) + { + LastError = LOG_LAST_ERROR(L"Unable to determine device object file name"); + goto cleanupKey; + } + +cleanupKey: + RegCloseKey(Key); + return RET_ERROR(TRUE, LastError); +} + +static volatile LONG OrphanThreadIsWorking = FALSE; + +static DWORD +DoOrphanedDeviceCleanup(_In_opt_ LPVOID Ctx) +{ + AdapterCleanupOrphanedDevices(); + OrphanThreadIsWorking = FALSE; + return 0; +} + +static VOID QueueUpOrphanedDeviceCleanupRoutine(VOID) +{ + if (InterlockedCompareExchange(&OrphanThreadIsWorking, TRUE, FALSE) == FALSE) + QueueUserWorkItem(DoOrphanedDeviceCleanup, NULL, 0); +} + +VOID AdapterCleanupOrphanedDevices(VOID) +{ + HANDLE DeviceInstallationMutex = NamespaceTakeDeviceInstallationMutex(); + if (!DeviceInstallationMutex) + { + LOG_LAST_ERROR(L"Failed to take device installation mutex"); + return; + } + + if (IsWindows7) + { + AdapterCleanupOrphanedDevicesWin7(); + goto cleanupDeviceInstallationMutex; + } + + HDEVINFO DevInfo = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, WINTUN_ENUMERATOR, NULL, 0, NULL, NULL, NULL); + if (DevInfo == INVALID_HANDLE_VALUE) + { + LOG_LAST_ERROR(L"Failed to get adapters"); + goto cleanupDeviceInstallationMutex; + } + + SP_DEVINFO_DATA DevInfoData = { .cbSize = sizeof(DevInfoData) }; + for (DWORD EnumIndex = 0;; ++EnumIndex) + { + if (!SetupDiEnumDeviceInfo(DevInfo, EnumIndex, &DevInfoData)) + { + if (GetLastError() == ERROR_NO_MORE_ITEMS) + break; + continue; + } + ULONG Status, Code; + if (CM_Get_DevNode_Status(&Status, &Code, DevInfoData.DevInst, 0) == CR_SUCCESS && !(Status & DN_HAS_PROBLEM)) + continue; + + DEVPROPTYPE PropType; + WCHAR Name[MAX_ADAPTER_NAME] = L""; + SetupDiGetDevicePropertyW( + DevInfo, + &DevInfoData, + &DEVPKEY_Wintun_Name, + &PropType, + (PBYTE)Name, + MAX_ADAPTER_NAME * sizeof(Name[0]), + NULL, + 0); + if (!AdapterRemoveInstance(DevInfo, &DevInfoData)) + { + LOG_LAST_ERROR(L"Failed to remove orphaned adapter \"%s\"", Name); + continue; + } + LOG(WINTUN_LOG_INFO, L"Removed orphaned adapter \"%s\"", Name); + } + SetupDiDestroyDeviceInfoList(DevInfo); +cleanupDeviceInstallationMutex: + NamespaceReleaseMutex(DeviceInstallationMutex); +} + +_Use_decl_annotations_ +VOID WINAPI +WintunCloseAdapter(WINTUN_ADAPTER *Adapter) +{ + if (!Adapter) + return; + Free(Adapter->InterfaceFilename); + if (Adapter->DevInfo) + AdapterForceCloseHandles(Adapter->DevInfo, &Adapter->DevInfoData); + if (Adapter->SwDevice) + SwDeviceClose(Adapter->SwDevice); + if (Adapter->DevInfo) + { + if (!AdapterRemoveInstance(Adapter->DevInfo, &Adapter->DevInfoData)) + LOG_LAST_ERROR(L"Failed to remove adapter when closing"); + SetupDiDestroyDeviceInfoList(Adapter->DevInfo); + } + Free(Adapter); + QueueUpOrphanedDeviceCleanupRoutine(); +} + +static _Return_type_success_(return != FALSE) +BOOL +RenameByNetGUID(_In_ GUID *Guid, _In_reads_or_z_(MAX_ADAPTER_NAME) LPCWSTR Name) +{ + DWORD LastError = ERROR_NOT_FOUND; + HDEVINFO DevInfo = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, WINTUN_ENUMERATOR, NULL, 0, NULL, NULL, NULL); + if (DevInfo == INVALID_HANDLE_VALUE) + { + LastError = GetLastError(); + goto cleanup; + } + + SP_DEVINFO_DATA DevInfoData = { .cbSize = sizeof(DevInfoData) }; + for (DWORD EnumIndex = 0;; ++EnumIndex) + { + if (!SetupDiEnumDeviceInfo(DevInfo, EnumIndex, &DevInfoData)) + { + if (GetLastError() == ERROR_NO_MORE_ITEMS) + break; + continue; + } + + HKEY Key = SetupDiOpenDevRegKey(DevInfo, &DevInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_QUERY_VALUE); + if (Key == INVALID_HANDLE_VALUE) + continue; + LPWSTR ValueStr = RegistryQueryString(Key, L"NetCfgInstanceId", TRUE); + RegCloseKey(Key); + if (!ValueStr) + continue; + GUID Guid2; + HRESULT HRet = CLSIDFromString(ValueStr, &Guid2); + Free(ValueStr); + if (FAILED(HRet) || memcmp(Guid, &Guid2, sizeof(*Guid))) + continue; + LastError = SetupDiSetDevicePropertyW( + DevInfo, + &DevInfoData, + &DEVPKEY_Wintun_Name, + DEVPROP_TYPE_STRING, + (PBYTE)Name, + (DWORD)((wcslen(Name) + 1) * sizeof(Name[0])), + 0) + ? ERROR_SUCCESS + : GetLastError(); + break; + } + SetupDiDestroyDeviceInfoList(DevInfo); +cleanup: + return RET_ERROR(TRUE, LastError); } _Must_inspect_result_ -static _Return_type_success_(return != NULL) -_Post_maybenull_ +static _Return_type_success_(return != FALSE) +BOOL +ConvertInterfaceAliasToGuid(_In_z_ LPCWSTR Name, _Out_ GUID *Guid) +{ + NET_LUID Luid; + DWORD LastError = ConvertInterfaceAliasToLuid(Name, &Luid); + if (LastError != NO_ERROR) + { + SetLastError(LOG_ERROR(LastError, L"Failed convert interface %s name to the locally unique identifier", Name)); + return FALSE; + } + LastError = ConvertInterfaceLuidToGuid(&Luid, Guid); + if (LastError != NO_ERROR) + { + SetLastError(LOG_ERROR(LastError, L"Failed to convert interface %s LUID (%I64u) to GUID", Name, Luid.Value)); + return FALSE; + } + return TRUE; +} + +static _Return_type_success_(return != FALSE) +BOOL +NciSetAdapterName(_In_ GUID *Guid, _In_reads_or_z_(MAX_ADAPTER_NAME) LPCWSTR Name) +{ + const int MaxSuffix = 1000; + WCHAR AvailableName[MAX_ADAPTER_NAME]; + if (wcsncpy_s(AvailableName, _countof(AvailableName), Name, _TRUNCATE) == STRUNCATE) + { + SetLastError(ERROR_BUFFER_OVERFLOW); + return FALSE; + } + for (int i = 0;; ++i) + { + DWORD LastError = NciSetConnectionName(Guid, AvailableName); + if (LastError == ERROR_DUP_NAME) + { + GUID Guid2; + if (ConvertInterfaceAliasToGuid(AvailableName, &Guid2)) + { + for (int j = 0; j < MaxSuffix; ++j) + { + WCHAR Proposal[MAX_ADAPTER_NAME]; + if (_snwprintf_s(Proposal, _countof(Proposal), _TRUNCATE, L"%s %d", Name, j + 1) == -1) + { + SetLastError(ERROR_BUFFER_OVERFLOW); + return FALSE; + } + if (_wcsnicmp(Proposal, AvailableName, MAX_ADAPTER_NAME) == 0) + continue; + DWORD LastError2 = NciSetConnectionName(&Guid2, Proposal); + if (LastError2 == ERROR_DUP_NAME) + continue; + if (!RenameByNetGUID(&Guid2, Proposal)) + LOG_LAST_ERROR(L"Failed to set foreign adapter name to \"%s\"", Proposal); + if (LastError2 == ERROR_SUCCESS) + { + LastError = NciSetConnectionName(Guid, AvailableName); + if (LastError == ERROR_SUCCESS) + break; + } + break; + } + } + } + if (LastError == ERROR_SUCCESS) + break; + if (i >= MaxSuffix || LastError != ERROR_DUP_NAME) + { + SetLastError(LastError); + return FALSE; + } + if (_snwprintf_s(AvailableName, _countof(AvailableName), _TRUNCATE, L"%s %d", Name, i + 1) == -1) + { + SetLastError(ERROR_BUFFER_OVERFLOW); + return FALSE; + } + } + return TRUE; +} + +_Use_decl_annotations_ +VOID WINAPI +WintunGetAdapterLUID(WINTUN_ADAPTER *Adapter, NET_LUID *Luid) +{ + Luid->Info.Reserved = 0; + Luid->Info.NetLuidIndex = Adapter->LuidIndex; + Luid->Info.IfType = Adapter->IfType; +} + +_Use_decl_annotations_ +HANDLE WINAPI +AdapterOpenDeviceObject(const WINTUN_ADAPTER *Adapter) +{ + HANDLE Handle = CreateFileW( + Adapter->InterfaceFilename, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + 0, + NULL); + if (Handle == INVALID_HANDLE_VALUE) + LOG_LAST_ERROR(L"Failed to connect to adapter interface %s", Adapter->InterfaceFilename); + return Handle; +} + +_Use_decl_annotations_ LPWSTR -GetDeviceObjectFileName(_In_z_ LPCWSTR InstanceId) +AdapterGetDeviceObjectFileName(LPCWSTR InstanceId) { ULONG InterfacesLen; DWORD LastError = CM_MapCrToWin32Err( @@ -246,1499 +407,439 @@ GetDeviceObjectFileName(_In_z_ LPCWSTR InstanceId) return Interfaces; } -_Must_inspect_result_ -static _Return_type_success_(return != INVALID_HANDLE_VALUE) -HANDLE -OpenDeviceObject(_In_z_ LPCWSTR InstanceId) +typedef struct _WAIT_FOR_INTERFACE_CTX { - LPWSTR Filename = GetDeviceObjectFileName(InstanceId); - if (!Filename) - return INVALID_HANDLE_VALUE; - HANDLE Handle = CreateFileW( - Filename, - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, - OPEN_EXISTING, - 0, - NULL); - if (Handle == INVALID_HANDLE_VALUE) - LOG_LAST_ERROR(L"Failed to connect to adapter %s associated instance %s", InstanceId, Filename); - Free(Filename); - return Handle; -} + HANDLE Event; + DWORD LastError; +} WAIT_FOR_INTERFACE_CTX; -static BOOL -EnsureDeviceObject(_In_z_ LPCWSTR InstanceId) +static VOID WINAPI +WaitForInterfaceCallback( + _In_ HDEVQUERY DevQuery, + _Inout_ PVOID Context, + _In_ const DEV_QUERY_RESULT_ACTION_DATA *ActionData) { - LPWSTR Filename = GetDeviceObjectFileName(InstanceId); - if (!Filename) + WAIT_FOR_INTERFACE_CTX *Ctx = Context; + Ctx->LastError = ERROR_SUCCESS; + if (ActionData->Action == DevQueryResultStateChange) { - LOG_LAST_ERROR(L"Failed to determine adapter %s device object", InstanceId); - return FALSE; + if (ActionData->Data.State != DevQueryStateAborted) + return; + Ctx->LastError = ERROR_DEVICE_NOT_AVAILABLE; } - BOOL Exists = TRUE; - const int Attempts = 100; - for (int i = 0; i < Attempts; ++i) - { - HANDLE Handle = CreateFileW(Filename, 0, 0, NULL, OPEN_EXISTING, 0, NULL); - if (Handle != INVALID_HANDLE_VALUE) - { - CloseHandle(Handle); - goto out; - } - if (i != Attempts - 1) - Sleep(50); - } - Exists = FALSE; - LOG_LAST_ERROR(L"Failed to connect to adapter %s associated instance %s", InstanceId, Filename); -out: - Free(Filename); - return Exists; -} - -#define TUN_IOCTL_FORCE_CLOSE_HANDLES CTL_CODE(51820U, 0x971U, METHOD_NEITHER, FILE_READ_DATA | FILE_WRITE_DATA) - -static _Return_type_success_(return != FALSE) -BOOL -ForceCloseWintunAdapterHandle(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData) -{ - DWORD LastError = ERROR_SUCCESS; - DWORD RequiredBytes; - if (SetupDiGetDeviceInstanceIdW(DevInfo, DevInfoData, NULL, 0, &RequiredBytes) || - (LastError = GetLastError()) != ERROR_INSUFFICIENT_BUFFER) - { - LOG_ERROR(LastError, L"Failed to query adapter %u instance ID size", DevInfoData->DevInst); - return FALSE; - } - LastError = ERROR_SUCCESS; - LPWSTR InstanceId = ZallocArray(RequiredBytes, sizeof(*InstanceId)); - if (!InstanceId) - return FALSE; - if (!SetupDiGetDeviceInstanceIdW(DevInfo, DevInfoData, InstanceId, RequiredBytes, &RequiredBytes)) - { - LastError = LOG_LAST_ERROR(L"Failed to get adapter %u instance ID", DevInfoData->DevInst); - goto cleanupInstanceId; - } - HANDLE NdisHandle = OpenDeviceObject(InstanceId); - if (NdisHandle == INVALID_HANDLE_VALUE) - { - LastError = LOG(WINTUN_LOG_ERR, L"Failed to get adapter %u object", DevInfoData->DevInst); - goto cleanupInstanceId; - } - if (DeviceIoControl(NdisHandle, TUN_IOCTL_FORCE_CLOSE_HANDLES, NULL, 0, NULL, 0, &RequiredBytes, NULL)) - { - LastError = ERROR_SUCCESS; - Sleep(200); - } - else if (GetLastError() == ERROR_NOTHING_TO_TERMINATE) - LastError = ERROR_SUCCESS; - else - LastError = LOG_LAST_ERROR(L"Failed to perform ioctl on adapter %u", DevInfoData->DevInst); - CloseHandle(NdisHandle); -cleanupInstanceId: - Free(InstanceId); - return RET_ERROR(TRUE, LastError); -} - -static _Return_type_success_(return != FALSE) -BOOL -DisableAllOurAdapters(_In_ HDEVINFO DevInfo, _Inout_ SP_DEVINFO_DATA_LIST **DisabledAdapters) -{ - SP_PROPCHANGE_PARAMS Params = { .ClassInstallHeader = { .cbSize = sizeof(SP_CLASSINSTALL_HEADER), - .InstallFunction = DIF_PROPERTYCHANGE }, - .StateChange = DICS_DISABLE, - .Scope = DICS_FLAG_GLOBAL }; - DWORD LastError = ERROR_SUCCESS; - for (DWORD EnumIndex = 0;; ++EnumIndex) - { - SP_DEVINFO_DATA_LIST *DeviceNode = Zalloc(sizeof(*DeviceNode)); - if (!DeviceNode) - return FALSE; - DeviceNode->Data.cbSize = sizeof(SP_DEVINFO_DATA); - if (!SetupDiEnumDeviceInfo(DevInfo, EnumIndex, &DeviceNode->Data)) - { - if (GetLastError() == ERROR_NO_MORE_ITEMS) - { - Free(DeviceNode); - break; - } - goto cleanupDeviceNode; - } - if (!IsOurAdapter(DevInfo, &DeviceNode->Data)) - goto cleanupDeviceNode; - - ULONG Status, ProblemCode; - if (CM_Get_DevNode_Status(&Status, &ProblemCode, DeviceNode->Data.DevInst, 0) != CR_SUCCESS || - ((Status & DN_HAS_PROBLEM) && ProblemCode == CM_PROB_DISABLED)) - goto cleanupDeviceNode; - - LOG(WINTUN_LOG_INFO, L"Force closing all adapter %u open handles", DeviceNode->Data.DevInst); - if (!ForceCloseWintunAdapterHandle(DevInfo, &DeviceNode->Data)) - LOG(WINTUN_LOG_WARN, L"Failed to force close adapter %u handles", DeviceNode->Data.DevInst); - - LOG(WINTUN_LOG_INFO, L"Disabling adapter %u", DeviceNode->Data.DevInst); - if (!SetupDiSetClassInstallParamsW(DevInfo, &DeviceNode->Data, &Params.ClassInstallHeader, sizeof(Params)) || - !SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, DevInfo, &DeviceNode->Data)) - { - LOG_LAST_ERROR(L"Failed to disable adapter %u", DeviceNode->Data.DevInst); - LastError = LastError != ERROR_SUCCESS ? LastError : GetLastError(); - goto cleanupDeviceNode; - } - - DeviceNode->Next = *DisabledAdapters; - *DisabledAdapters = DeviceNode; - continue; - - cleanupDeviceNode: - Free(DeviceNode); - } - return RET_ERROR(TRUE, LastError); -} - -static _Return_type_success_(return != FALSE) -BOOL -EnableAllOurAdapters(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA_LIST *AdaptersToEnable) -{ - SP_PROPCHANGE_PARAMS Params = { .ClassInstallHeader = { .cbSize = sizeof(SP_CLASSINSTALL_HEADER), - .InstallFunction = DIF_PROPERTYCHANGE }, - .StateChange = DICS_ENABLE, - .Scope = DICS_FLAG_GLOBAL }; - DWORD LastError = ERROR_SUCCESS; - for (SP_DEVINFO_DATA_LIST *DeviceNode = AdaptersToEnable; DeviceNode; DeviceNode = DeviceNode->Next) - { - LOG(WINTUN_LOG_INFO, L"Enabling adapter %u", DeviceNode->Data.DevInst); - if (!SetupDiSetClassInstallParamsW(DevInfo, &DeviceNode->Data, &Params.ClassInstallHeader, sizeof(Params)) || - !SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, DevInfo, &DeviceNode->Data)) - { - LOG_LAST_ERROR(L"Failed to enable adapter %u", DeviceNode->Data.DevInst); - LastError = LastError != ERROR_SUCCESS ? LastError : GetLastError(); - } - } - return RET_ERROR(TRUE, LastError); -} - -static BOOL -CheckReboot(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData) -{ - SP_DEVINSTALL_PARAMS_W DevInstallParams = { .cbSize = sizeof(SP_DEVINSTALL_PARAMS_W) }; - if (!SetupDiGetDeviceInstallParamsW(DevInfo, DevInfoData, &DevInstallParams)) - { - LOG_LAST_ERROR(L"Failed to retrieve adapter %u device installation parameters", DevInfoData->DevInst); - return FALSE; - } - SetLastError(ERROR_SUCCESS); - return (DevInstallParams.Flags & (DI_NEEDREBOOT | DI_NEEDRESTART)) != 0; -} - -_Must_inspect_result_ -static _Return_type_success_(return != FALSE) -BOOL -GetPoolDeviceTypeName(_In_z_ LPCWSTR Pool, _Out_writes_z_(MAX_POOL_DEVICE_TYPE) LPWSTR Name) -{ - if (_snwprintf_s(Name, MAX_POOL_DEVICE_TYPE, _TRUNCATE, L"%s Tunnel", Pool) == -1) - { - LOG(WINTUN_LOG_ERR, L"Pool name too long: %s", Pool); - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - return TRUE; -} - -static BOOL -IsPoolMember(_In_z_ LPCWSTR Pool, _In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData) -{ - WCHAR PoolProp[MAX_POOL_DEVICE_TYPE]; - DEVPROPTYPE PropType; - if (!SetupDiGetDevicePropertyW( - DevInfo, DevInfoData, &DEVPKEY_Wintun_Pool, &PropType, (PBYTE)PoolProp, sizeof(PoolProp), NULL, 0)) - return FALSE; - if (PropType != DEVPROP_TYPE_STRING) - { - SetLastError(ERROR_BAD_DEVICE); - return FALSE; - } - SetLastError(ERROR_SUCCESS); - return !_wcsicmp(PoolProp, Pool); -} - -_Must_inspect_result_ -static _Return_type_success_(return != FALSE) -BOOL -PopulateAdapterData(_Inout_ WINTUN_ADAPTER *Adapter, _In_z_ LPCWSTR Pool) -{ - DWORD LastError = ERROR_SUCCESS; - - /* Open HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\\ registry key. */ - HKEY Key = - SetupDiOpenDevRegKey(Adapter->DevInfo, &Adapter->DevInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_QUERY_VALUE); - if (Key == INVALID_HANDLE_VALUE) - { - LOG_LAST_ERROR(L"Failed to open adapter %u device registry key", Adapter->DevInfoData.DevInst); - return FALSE; - } - - LPWSTR ValueStr = RegistryQueryString(Key, L"NetCfgInstanceId", TRUE); - if (!ValueStr) - { - WCHAR RegPath[MAX_REG_PATH]; - LoggerGetRegistryKeyPath(Key, RegPath); - LastError = LOG(WINTUN_LOG_ERR, L"Failed to get %.*s\\NetCfgInstanceId", MAX_REG_PATH, RegPath); - goto cleanupKey; - } - if (FAILED(CLSIDFromString(ValueStr, &Adapter->CfgInstanceID))) - { - WCHAR RegPath[MAX_REG_PATH]; - LoggerGetRegistryKeyPath(Key, RegPath); - LastError = LOG(WINTUN_LOG_ERR, L"%.*s\\NetCfgInstanceId is not a GUID: %s", MAX_REG_PATH, RegPath, ValueStr); - Free(ValueStr); - goto cleanupKey; - } - Free(ValueStr); - - if (!RegistryQueryDWORD(Key, L"NetLuidIndex", &Adapter->LuidIndex, TRUE)) - { - WCHAR RegPath[MAX_REG_PATH]; - LoggerGetRegistryKeyPath(Key, RegPath); - LastError = LOG(WINTUN_LOG_ERR, L"Failed to get %.*s\\NetLuidIndex", MAX_REG_PATH, RegPath); - goto cleanupKey; - } - - if (!RegistryQueryDWORD(Key, L"*IfType", &Adapter->IfType, TRUE)) - { - WCHAR RegPath[MAX_REG_PATH]; - LoggerGetRegistryKeyPath(Key, RegPath); - LastError = LOG(WINTUN_LOG_ERR, L"Failed to get %.*s\\*IfType", MAX_REG_PATH, RegPath); - goto cleanupKey; - } - - DWORD Size; - if (!SetupDiGetDeviceInstanceIdW( - Adapter->DevInfo, &Adapter->DevInfoData, Adapter->DevInstanceID, _countof(Adapter->DevInstanceID), &Size)) - { - LastError = LOG_LAST_ERROR(L"Failed to get adapter %u instance ID", Adapter->DevInfoData.DevInst); - goto cleanupKey; - } - - if (wcsncpy_s(Adapter->Pool, _countof(Adapter->Pool), Pool, _TRUNCATE) == STRUNCATE) - { - LOG(WINTUN_LOG_ERR, L"Pool name too long: %s", Pool); - LastError = ERROR_INVALID_PARAMETER; - goto cleanupKey; - } - -cleanupKey: - RegCloseKey(Key); - return RET_ERROR(TRUE, LastError); -} - -_Use_decl_annotations_ -VOID WINAPI -WintunFreeAdapter(WINTUN_ADAPTER *Adapter) -{ - if (!Adapter) + else if (ActionData->Action == DevQueryResultRemove) return; - if (Adapter->DevInfo) - SetupDiDestroyDeviceInfoList(Adapter->DevInfo); - Free(Adapter); -} - -_Use_decl_annotations_ -BOOL WINAPI -WintunGetAdapterName(WINTUN_ADAPTER *Adapter, LPWSTR Name) -{ - DEVPROPTYPE PropType; - if (!SetupDiGetDevicePropertyW( - Adapter->DevInfo, - &Adapter->DevInfoData, - &DEVPKEY_Wintun_Name, - &PropType, - (PBYTE)Name, - MAX_ADAPTER_NAME * sizeof(*Name), - NULL, - 0)) - return FALSE; - if (PropType != DEVPROP_TYPE_STRING || !*Name) - { - SetLastError(ERROR_BAD_DEVICE); - return FALSE; - } - return TRUE; + SetEvent(Ctx->Event); } _Must_inspect_result_ static _Return_type_success_(return != FALSE) BOOL -ConvertInterfaceAliasToGuid(_In_z_ LPCWSTR Name, _Out_ GUID *Guid) +WaitForInterface(_In_ WCHAR *InstanceId) { - NET_LUID Luid; - DWORD LastError = ConvertInterfaceAliasToLuid(Name, &Luid); - if (LastError != NO_ERROR) - { - SetLastError(LOG_ERROR(LastError, L"Failed convert interface %s name to the locally unique identifier", Name)); - return FALSE; - } - LastError = ConvertInterfaceLuidToGuid(&Luid, Guid); - if (LastError != NO_ERROR) - { - SetLastError(LOG_ERROR(LastError, L"Failed to convert interface %s LUID (%I64u) to GUID", Name, Luid.Value)); - return FALSE; - } - return TRUE; -} - -_Use_decl_annotations_ -BOOL WINAPI -WintunSetAdapterName(WINTUN_ADAPTER *Adapter, LPCWSTR Name) -{ - const int MaxSuffix = 1000; - WCHAR AvailableName[MAX_ADAPTER_NAME]; - if (wcsncpy_s(AvailableName, _countof(AvailableName), Name, _TRUNCATE) == STRUNCATE) - { - LOG(WINTUN_LOG_ERR, L"Adapter name too long: %s", Name); - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - - if (!SetupDiSetDevicePropertyW( - Adapter->DevInfo, - &Adapter->DevInfoData, - &DEVPKEY_Wintun_Name, - DEVPROP_TYPE_STRING, -#pragma warning(suppress : 4090) - (const BYTE *)Name, - (DWORD)((wcslen(Name) + 1) * sizeof(*Name)), - 0)) - { - LOG_LAST_ERROR(L"Failed to set adapter %u name", Adapter->DevInfoData.DevInst); - return FALSE; - } - - for (int i = 0;; ++i) - { - DWORD LastError = NciSetConnectionName(&Adapter->CfgInstanceID, AvailableName); - if (LastError == ERROR_DUP_NAME) - { - GUID Guid2; - if (ConvertInterfaceAliasToGuid(AvailableName, &Guid2)) - { - for (int j = 0; j < MaxSuffix; ++j) - { - WCHAR Proposal[MAX_ADAPTER_NAME]; - if (_snwprintf_s(Proposal, _countof(Proposal), _TRUNCATE, L"%s %d", Name, j + 1) == -1) - { - LOG(WINTUN_LOG_ERR, L"Adapter name too long: %s %d", Name, j + 1); - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - if (_wcsnicmp(Proposal, AvailableName, MAX_ADAPTER_NAME) == 0) - continue; - DWORD LastError2 = NciSetConnectionName(&Guid2, Proposal); - if (LastError2 == ERROR_DUP_NAME) - continue; - if (LastError2 == ERROR_SUCCESS) - { - LastError = NciSetConnectionName(&Adapter->CfgInstanceID, AvailableName); - if (LastError == ERROR_SUCCESS) - break; - } - break; - } - } - } - if (LastError == ERROR_SUCCESS) - break; - if (i >= MaxSuffix || LastError != ERROR_DUP_NAME) - { - SetLastError(LOG_ERROR(LastError, L"Failed to set adapter name")); - return FALSE; - } - if (_snwprintf_s(AvailableName, _countof(AvailableName), _TRUNCATE, L"%s %d", Name, i + 1) == -1) - { - LOG(WINTUN_LOG_ERR, L"Adapter name too long: %s %d", Name, i + 1); - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - } - - if (!SetupDiSetDevicePropertyW( - Adapter->DevInfo, - &Adapter->DevInfoData, - &DEVPKEY_Wintun_Pool, - DEVPROP_TYPE_STRING, -#pragma warning(suppress : 4090) - (const BYTE *)Adapter->Pool, - (DWORD)((wcslen(Adapter->Pool) + 1) * sizeof(*Adapter->Pool)), - 0)) - { - LOG_LAST_ERROR(L"Failed to set adapter %u pool", Adapter->DevInfoData.DevInst); - return FALSE; - } - - WCHAR PoolDeviceTypeName[MAX_POOL_DEVICE_TYPE]; - if (!GetPoolDeviceTypeName(Adapter->Pool, PoolDeviceTypeName)) - return FALSE; - if (!SetupDiSetDeviceRegistryPropertyW( - Adapter->DevInfo, - &Adapter->DevInfoData, - SPDRP_FRIENDLYNAME, - (const BYTE *)PoolDeviceTypeName, - (DWORD)((wcslen(PoolDeviceTypeName) + 1) * sizeof(*PoolDeviceTypeName)))) - { - LOG_LAST_ERROR(L"Failed to set adapter %u friendly name", Adapter->DevInfoData.DevInst); - return FALSE; - } - - return TRUE; -} - -_Use_decl_annotations_ -WINTUN_ADAPTER_HANDLE WINAPI -WintunOpenAdapter(LPCWSTR Pool, LPCWSTR Name) -{ - WINTUN_ADAPTER *Adapter = Zalloc(sizeof(*Adapter)); - if (!Adapter) - return FALSE; + if (IsWindows7) + return TRUE; DWORD LastError = ERROR_SUCCESS; - HANDLE Mutex = NamespaceTakePoolMutex(Pool); - if (!Mutex) + static const DEVPROP_BOOLEAN DevPropTrue = DEVPROP_TRUE; + const DEVPROP_FILTER_EXPRESSION Filters[] = { { .Operator = DEVPROP_OPERATOR_EQUALS_IGNORE_CASE, + .Property.CompKey.Key = DEVPKEY_Device_InstanceId, + .Property.CompKey.Store = DEVPROP_STORE_SYSTEM, + .Property.Type = DEVPROP_TYPE_STRING, + .Property.Buffer = InstanceId, + .Property.BufferSize = + (ULONG)((wcslen(InstanceId) + 1) * sizeof(InstanceId[0])) }, + { .Operator = DEVPROP_OPERATOR_EQUALS, + .Property.CompKey.Key = DEVPKEY_DeviceInterface_Enabled, + .Property.CompKey.Store = DEVPROP_STORE_SYSTEM, + .Property.Type = DEVPROP_TYPE_BOOLEAN, + .Property.Buffer = (PVOID)&DevPropTrue, + .Property.BufferSize = sizeof(DevPropTrue) }, + { .Operator = DEVPROP_OPERATOR_EQUALS, + .Property.CompKey.Key = DEVPKEY_DeviceInterface_ClassGuid, + .Property.CompKey.Store = DEVPROP_STORE_SYSTEM, + .Property.Type = DEVPROP_TYPE_GUID, + .Property.Buffer = (PVOID)&GUID_DEVINTERFACE_NET, + .Property.BufferSize = sizeof(GUID_DEVINTERFACE_NET) } }; + WAIT_FOR_INTERFACE_CTX Ctx = { .Event = CreateEventW(NULL, FALSE, FALSE, NULL) }; + if (!Ctx.Event) { - LastError = LOG(WINTUN_LOG_ERR, L"Failed to take %s pool mutex", Pool); + LastError = LOG_LAST_ERROR(L"Failed to create event"); goto cleanup; } - - Adapter->DevInfo = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, NULL, NULL, DIGCF_PRESENT, NULL, NULL, NULL); - if (Adapter->DevInfo == INVALID_HANDLE_VALUE) + HDEVQUERY Query; + HRESULT HRet = DevCreateObjectQuery( + DevObjectTypeDeviceInterface, + DevQueryFlagUpdateResults, + 0, + NULL, + _countof(Filters), + Filters, + WaitForInterfaceCallback, + &Ctx, + &Query); + if (FAILED(HRet)) { - LastError = LOG_LAST_ERROR(L"Failed to get present adapters"); - goto cleanupMutex; + LastError = LOG_ERROR(HRet, L"Failed to create device query"); + goto cleanupEvent; } - - Adapter->DevInfoData.cbSize = sizeof(Adapter->DevInfoData); - for (DWORD EnumIndex = 0;; ++EnumIndex) + LastError = WaitForSingleObject(Ctx.Event, 15000); + if (LastError != WAIT_OBJECT_0) { - if (!SetupDiEnumDeviceInfo(Adapter->DevInfo, EnumIndex, &Adapter->DevInfoData)) - { - if (GetLastError() == ERROR_NO_MORE_ITEMS) - break; - continue; - } - - WCHAR Name2[MAX_ADAPTER_NAME]; - if (!WintunGetAdapterName(Adapter, Name2)) - continue; - if (_wcsicmp(Name, Name2)) - continue; - - /* Check the Hardware ID to make sure it's a real Wintun device. */ - if (!IsOurAdapter(Adapter->DevInfo, &Adapter->DevInfoData)) - { - LOG(WINTUN_LOG_ERR, L"Foreign adapter %u named %s exists", Adapter->DevInfoData.DevInst, Name); - LastError = ERROR_ALREADY_EXISTS; - goto cleanupMutex; - } - - if (!IsPoolMember(Pool, Adapter->DevInfo, &Adapter->DevInfoData)) - { - if ((LastError = GetLastError()) == ERROR_SUCCESS) - { - LOG(WINTUN_LOG_ERR, - L"Adapter %u named %s is not a member of %s pool", - Adapter->DevInfoData.DevInst, - Name, - Pool); - LastError = ERROR_ALREADY_EXISTS; - goto cleanupMutex; - } - else - { - LOG(WINTUN_LOG_ERR, L"Failed to get adapter %u pool membership", Adapter->DevInfoData.DevInst); - goto cleanupMutex; - } - } - - if (!PopulateAdapterData(Adapter, Pool)) - { - LastError = LOG(WINTUN_LOG_ERR, L"Failed to populate adapter %u data", Adapter->DevInfoData.DevInst); - goto cleanupMutex; - } - - if (!EnsureDeviceObject(Adapter->DevInstanceID)) - { - LastError = GetLastError(); - goto cleanupMutex; - } - - /* Our comparison was case-insensitive, and we also might want to reenforce the NCI connection. */ - WintunSetAdapterName(Adapter, Name); - - LastError = ERROR_SUCCESS; - goto cleanupMutex; + if (LastError == WAIT_FAILED) + LastError = LOG_LAST_ERROR(L"Failed to wait for device query"); + else + LastError = LOG_ERROR(LastError, L"Timed out waiting for device query"); + goto cleanupQuery; } - LastError = ERROR_FILE_NOT_FOUND; -cleanupMutex: - NamespaceReleaseMutex(Mutex); + LastError = Ctx.LastError; + if (LastError != ERROR_SUCCESS) + LastError = LOG_ERROR(LastError, L"Failed to get enabled device"); +cleanupQuery: + DevCloseObjectQuery(Query); +cleanupEvent: + CloseHandle(Ctx.Event); cleanup: - if (LastError != ERROR_SUCCESS) - WintunFreeAdapter(Adapter); - return RET_ERROR(Adapter, LastError); -} - -_Use_decl_annotations_ -VOID WINAPI -WintunGetAdapterLUID(WINTUN_ADAPTER *Adapter, NET_LUID *Luid) -{ - Luid->Info.Reserved = 0; - Luid->Info.NetLuidIndex = Adapter->LuidIndex; - Luid->Info.IfType = Adapter->IfType; -} - -_Use_decl_annotations_ -HANDLE WINAPI -AdapterOpenDeviceObject(const WINTUN_ADAPTER *Adapter) -{ - return OpenDeviceObject(Adapter->DevInstanceID); -} - -static BOOL -IsOurDrvInfoDetail(_In_ const SP_DRVINFO_DETAIL_DATA_W *DrvInfoDetailData) -{ - if (DrvInfoDetailData->CompatIDsOffset > 1 && !_wcsicmp(DrvInfoDetailData->HardwareID, WINTUN_HWID)) - return TRUE; - if (DrvInfoDetailData->CompatIDsLength && - IsOurHardwareID(DrvInfoDetailData->HardwareID + DrvInfoDetailData->CompatIDsOffset)) - return TRUE; - return FALSE; -} - -static BOOL -IsNewer( - _In_ const FILETIME *DriverDate1, - _In_ DWORDLONG DriverVersion1, - _In_ const FILETIME *DriverDate2, - _In_ DWORDLONG DriverVersion2) -{ - if (DriverDate1->dwHighDateTime > DriverDate2->dwHighDateTime) - return TRUE; - if (DriverDate1->dwHighDateTime < DriverDate2->dwHighDateTime) - return FALSE; - - if (DriverDate1->dwLowDateTime > DriverDate2->dwLowDateTime) - return TRUE; - if (DriverDate1->dwLowDateTime < DriverDate2->dwLowDateTime) - return FALSE; - - if (DriverVersion1 > DriverVersion2) - return TRUE; - if (DriverVersion1 < DriverVersion2) - return FALSE; - - return FALSE; -} - -_Must_inspect_result_ -static _Return_type_success_(return != FALSE) -BOOL -GetTcpipAdapterRegPath(_In_ const WINTUN_ADAPTER *Adapter, _Out_writes_z_(MAX_REG_PATH) LPWSTR Path) -{ - WCHAR Guid[MAX_GUID_STRING_LEN]; - if (_snwprintf_s( - Path, - MAX_REG_PATH, - _TRUNCATE, - L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Adapters\\%.*s", - StringFromGUID2(&Adapter->CfgInstanceID, Guid, _countof(Guid)), - Guid) == -1) - { - LOG(WINTUN_LOG_ERR, L"Registry path too long"); - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - return TRUE; -} - -_Must_inspect_result_ -static _Return_type_success_(return != FALSE) -BOOL -GetTcpipInterfaceRegPath(_In_ const WINTUN_ADAPTER *Adapter, _Out_writes_z_(MAX_REG_PATH) LPWSTR Path) -{ - HKEY TcpipAdapterRegKey; - WCHAR TcpipAdapterRegPath[MAX_REG_PATH]; - if (!GetTcpipAdapterRegPath(Adapter, TcpipAdapterRegPath)) - return FALSE; - DWORD LastError = RegOpenKeyExW(HKEY_LOCAL_MACHINE, TcpipAdapterRegPath, 0, KEY_QUERY_VALUE, &TcpipAdapterRegKey); - if (LastError != ERROR_SUCCESS) - { - SetLastError(LOG_ERROR(LastError, L"Failed to open registry key %s", TcpipAdapterRegPath)); - return FALSE; - } - LPWSTR Paths = RegistryQueryString(TcpipAdapterRegKey, L"IpConfig", TRUE); - if (!Paths) - { - LastError = LOG(WINTUN_LOG_ERR, L"Failed to get %s\\IpConfig", TcpipAdapterRegPath); - goto cleanupTcpipAdapterRegKey; - } - if (!Paths[0]) - { - LOG(WINTUN_LOG_ERR, L"%s\\IpConfig is empty", TcpipAdapterRegPath); - LastError = ERROR_INVALID_DATA; - goto cleanupPaths; - } - if (_snwprintf_s(Path, MAX_REG_PATH, _TRUNCATE, L"SYSTEM\\CurrentControlSet\\Services\\%s", Paths) == -1) - { - LOG(WINTUN_LOG_ERR, L"Registry path too long: %s", Paths); - LastError = ERROR_INVALID_PARAMETER; - goto cleanupPaths; - } -cleanupPaths: - Free(Paths); -cleanupTcpipAdapterRegKey: - RegCloseKey(TcpipAdapterRegKey); return RET_ERROR(TRUE, LastError); } -static _Return_type_success_(return != 0) -DWORD -VersionOfFile(_In_z_ LPCWSTR Filename) +typedef struct _SW_DEVICE_CREATE_CTX { - DWORD Zero; - DWORD Len = GetFileVersionInfoSizeW(Filename, &Zero); - if (!Len) - { - LOG_LAST_ERROR(L"Failed to query %s version info size", Filename); - return 0; - } - VOID *VersionInfo = Alloc(Len); - if (!VersionInfo) - return 0; - DWORD LastError = ERROR_SUCCESS, Version = 0; - VS_FIXEDFILEINFO *FixedInfo; - UINT FixedInfoLen = sizeof(*FixedInfo); - if (!GetFileVersionInfoW(Filename, 0, Len, VersionInfo)) - { - LastError = LOG_LAST_ERROR(L"Failed to get %s version info", Filename); - goto out; - } - if (!VerQueryValueW(VersionInfo, L"\\", &FixedInfo, &FixedInfoLen)) - { - LastError = LOG_LAST_ERROR(L"Failed to get %s version info root", Filename); - goto out; - } - Version = FixedInfo->dwFileVersionMS; - if (!Version) - { - LOG(WINTUN_LOG_WARN, L"Determined version of %s, but was v0.0, so returning failure", Filename); - LastError = ERROR_VERSION_PARSE_ERROR; - } -out: - Free(VersionInfo); - return RET_ERROR(Version, LastError); -} - -static DWORD WINAPI -MaybeGetRunningDriverVersion(BOOL ReturnOneIfRunningInsteadOfVersion) -{ - PRTL_PROCESS_MODULES Modules; - ULONG BufferSize = 128 * 1024; - for (;;) - { - Modules = Alloc(BufferSize); - if (!Modules) - return 0; - NTSTATUS Status = NtQuerySystemInformation(SystemModuleInformation, Modules, BufferSize, &BufferSize); - if (NT_SUCCESS(Status)) - break; - Free(Modules); - if (Status == STATUS_INFO_LENGTH_MISMATCH) - continue; - LOG(WINTUN_LOG_ERR, L"Failed to enumerate drivers (status: 0x%x)", Status); - SetLastError(RtlNtStatusToDosError(Status)); - return 0; - } - DWORD LastError = ERROR_SUCCESS, Version = 0; - for (ULONG i = Modules->NumberOfModules; i-- > 0;) - { - LPCSTR NtPath = (LPCSTR)Modules->Modules[i].FullPathName; - if (!_stricmp(&NtPath[Modules->Modules[i].OffsetToFileName], "wintun.sys")) - { - if (ReturnOneIfRunningInsteadOfVersion) - { - Version = 1; - goto cleanupModules; - } - WCHAR FilePath[MAX_PATH * 3 + 15]; - if (_snwprintf_s(FilePath, _countof(FilePath), _TRUNCATE, L"\\\\?\\GLOBALROOT%S", NtPath) == -1) - continue; - Version = VersionOfFile(FilePath); - if (!Version) - LastError = GetLastError(); - goto cleanupModules; - } - } - LastError = ERROR_FILE_NOT_FOUND; -cleanupModules: - Free(Modules); - return RET_ERROR(Version, LastError); -} - -_Use_decl_annotations_ -DWORD WINAPI WintunGetRunningDriverVersion(VOID) -{ - return MaybeGetRunningDriverVersion(FALSE); -} - -static BOOL EnsureWintunUnloaded(VOID) -{ - BOOL Loaded; - for (int i = 0; (Loaded = MaybeGetRunningDriverVersion(TRUE) != 0) != FALSE && i < 300; ++i) - Sleep(50); - return !Loaded; -} + HRESULT CreateResult; + WCHAR *DeviceInstanceId; + HANDLE Triggered; +} SW_DEVICE_CREATE_CTX; static VOID -SelectDriverDeferredCleanup(_In_ HDEVINFO DevInfoExistingAdapters, _In_opt_ SP_DEVINFO_DATA_LIST *ExistingAdapters) +DeviceCreateCallback( + _In_ HSWDEVICE SwDevice, + _In_ HRESULT CreateResult, + _In_ VOID *Context, + _In_opt_ PCWSTR DeviceInstanceId) { - if (ExistingAdapters) - { - EnableAllOurAdapters(DevInfoExistingAdapters, ExistingAdapters); - while (ExistingAdapters) - { - SP_DEVINFO_DATA_LIST *Next = ExistingAdapters->Next; - Free(ExistingAdapters); - ExistingAdapters = Next; - } - } - if (DevInfoExistingAdapters != INVALID_HANDLE_VALUE) - SetupDiDestroyDeviceInfoList(DevInfoExistingAdapters); -} - -_Must_inspect_result_ -static _Return_type_success_(return != FALSE) -BOOL -SelectDriver( - _In_ HDEVINFO DevInfo, - _In_ SP_DEVINFO_DATA *DevInfoData, - _Inout_ SP_DEVINSTALL_PARAMS_W *DevInstallParams, - _Out_ HDEVINFO *DevInfoExistingAdaptersForCleanup, - _Out_ SP_DEVINFO_DATA_LIST **ExistingAdaptersForCleanup) -{ - static const FILETIME OurDriverDate = WINTUN_INF_FILETIME; - static const DWORDLONG OurDriverVersion = WINTUN_INF_VERSION; - HANDLE DriverInstallationLock = NamespaceTakeDriverInstallationMutex(); - if (!DriverInstallationLock) - { - LOG(WINTUN_LOG_ERR, L"Failed to take driver installation mutex"); - return FALSE; - } - DWORD LastError; - if (!SetupDiBuildDriverInfoList(DevInfo, DevInfoData, SPDIT_COMPATDRIVER)) - { - LastError = LOG_LAST_ERROR(L"Failed building adapter %u driver info list", DevInfoData->DevInst); - goto cleanupDriverInstallationLock; - } - BOOL DestroyDriverInfoListOnCleanup = TRUE; - FILETIME DriverDate = { 0 }; - DWORDLONG DriverVersion = 0; - HDEVINFO DevInfoExistingAdapters = INVALID_HANDLE_VALUE; - SP_DEVINFO_DATA_LIST *ExistingAdapters = NULL; - for (DWORD EnumIndex = 0;; ++EnumIndex) - { - SP_DRVINFO_DATA_W DrvInfoData = { .cbSize = sizeof(SP_DRVINFO_DATA_W) }; - if (!SetupDiEnumDriverInfoW(DevInfo, DevInfoData, SPDIT_COMPATDRIVER, EnumIndex, &DrvInfoData)) - { - if (GetLastError() == ERROR_NO_MORE_ITEMS) - break; - continue; - } - SP_DRVINFO_DETAIL_DATA_W *DrvInfoDetailData = GetAdapterDrvInfoDetail(DevInfo, DevInfoData, &DrvInfoData); - if (!DrvInfoDetailData) - { - LOG(WINTUN_LOG_WARN, L"Failed getting adapter %u driver info detail", DevInfoData->DevInst); - continue; - } - if (!IsOurDrvInfoDetail(DrvInfoDetailData)) - goto next; - if (IsNewer(&OurDriverDate, OurDriverVersion, &DrvInfoData.DriverDate, DrvInfoData.DriverVersion)) - { - if (DevInfoExistingAdapters == INVALID_HANDLE_VALUE) - { - DevInfoExistingAdapters = - SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, NULL, NULL, DIGCF_PRESENT, NULL, NULL, NULL); - if (DevInfoExistingAdapters == INVALID_HANDLE_VALUE) - { - LastError = LOG_LAST_ERROR(L"Failed to get present adapters"); - Free(DrvInfoDetailData); - goto cleanupExistingAdapters; - } - _Analysis_assume_(DevInfoExistingAdapters != NULL); - DisableAllOurAdapters(DevInfoExistingAdapters, &ExistingAdapters); - LOG(WINTUN_LOG_INFO, L"Waiting for existing driver to unload from kernel"); - if (!EnsureWintunUnloaded()) - LOG(WINTUN_LOG_WARN, - L"Failed to unload existing driver, which means a reboot will likely be required"); - } - LOG(WINTUN_LOG_INFO, - L"Removing existing driver %u.%u", - (DWORD)((DrvInfoData.DriverVersion & 0xffff000000000000) >> 48), - (DWORD)((DrvInfoData.DriverVersion & 0x0000ffff00000000) >> 32)); - LPWSTR InfFileName = PathFindFileNameW(DrvInfoDetailData->InfFileName); - if (!SetupUninstallOEMInfW(InfFileName, SUOI_FORCEDELETE, NULL)) - LOG_LAST_ERROR(L"Unable to remove existing driver %s", InfFileName); - goto next; - } - if (!IsNewer(&DrvInfoData.DriverDate, DrvInfoData.DriverVersion, &DriverDate, DriverVersion)) - goto next; - if (!SetupDiSetSelectedDriverW(DevInfo, DevInfoData, &DrvInfoData)) - { - LOG_LAST_ERROR( - L"Failed to select driver %s for adapter %u", DrvInfoDetailData->InfFileName, DevInfoData->DevInst); - goto next; - } - DriverDate = DrvInfoData.DriverDate; - DriverVersion = DrvInfoData.DriverVersion; - next: - Free(DrvInfoDetailData); - } - - if (DriverVersion) - { - LOG(WINTUN_LOG_INFO, - L"Using existing driver %u.%u", - (DWORD)((DriverVersion & 0xffff000000000000) >> 48), - (DWORD)((DriverVersion & 0x0000ffff00000000) >> 32)); - LastError = ERROR_SUCCESS; - DestroyDriverInfoListOnCleanup = FALSE; - goto cleanupExistingAdapters; - } - - LOG(WINTUN_LOG_INFO, - L"Installing driver %u.%u", - (DWORD)((OurDriverVersion & 0xffff000000000000) >> 48), - (DWORD)((OurDriverVersion & 0x0000ffff00000000) >> 32)); - WCHAR RandomTempSubDirectory[MAX_PATH]; - if (!ResourceCreateTemporaryDirectory(RandomTempSubDirectory)) - { - LastError = LOG_LAST_ERROR(L"Failed to create temporary folder %s", RandomTempSubDirectory); - goto cleanupExistingAdapters; - } - - WCHAR CatPath[MAX_PATH] = { 0 }; - WCHAR SysPath[MAX_PATH] = { 0 }; - WCHAR InfPath[MAX_PATH] = { 0 }; - WCHAR DownlevelShimPath[MAX_PATH] = { 0 }; - - if (!PathCombineW(CatPath, RandomTempSubDirectory, L"wintun.cat") || - !PathCombineW(SysPath, RandomTempSubDirectory, L"wintun.sys") || - !PathCombineW(InfPath, RandomTempSubDirectory, L"wintun.inf")) - { - LastError = ERROR_BUFFER_OVERFLOW; - goto cleanupDirectory; - } - - LOG(WINTUN_LOG_INFO, L"Extracting driver"); - if (!ResourceCopyToFile(CatPath, L"wintun.cat") || !ResourceCopyToFile(SysPath, L"wintun.sys") || - !ResourceCopyToFile(InfPath, L"wintun.inf")) - { - LastError = LOG_LAST_ERROR(L"Failed to extract driver"); - goto cleanupDelete; - } - - WCHAR *WintrustKeyOriginalValue = NULL; - HKEY WintrustKey = NULL; - if (!IsWindows10) - { - LOG(WINTUN_LOG_INFO, L"Shimming downlevel driver loader"); - if (!PathCombineW(DownlevelShimPath, RandomTempSubDirectory, L"downlevelshim.dll")) - { - DownlevelShimPath[0] = L'\0'; - LastError = ERROR_BUFFER_OVERFLOW; - goto cleanupDelete; - } - if (!ResourceCopyToFile(DownlevelShimPath, L"downlevelshim.dll")) - { - LastError = LOG_LAST_ERROR(L"Failed to extract downlevel shim"); - goto cleanupDelete; - } - LastError = RegOpenKeyExW( - HKEY_LOCAL_MACHINE, - L"SOFTWARE\\Microsoft\\Cryptography\\Providers\\Trust\\FinalPolicy\\{F750E6C3-38EE-11D1-85E5-00C04FC295EE}", - 0, - KEY_QUERY_VALUE | KEY_SET_VALUE, - &WintrustKey); - if (LastError != ERROR_SUCCESS) - { - LOG_ERROR(LastError, L"Failed to open Wintrust FinalPolicy key"); - goto cleanupDelete; - } - WintrustKeyOriginalValue = RegistryQueryString(WintrustKey, L"$DLL", TRUE); - if (!WintrustKeyOriginalValue) - { - LastError = LOG_LAST_ERROR(L"Failed to read current Wintrust FinalPolicy key"); - goto cleanupWintrustKey; - } - LastError = RegSetValueExW( - WintrustKey, - L"$DLL", - 0, - REG_SZ, - (BYTE *)DownlevelShimPath, - (DWORD)((wcslen(DownlevelShimPath) + 1) * sizeof(DownlevelShimPath[0]))); - if (LastError != ERROR_SUCCESS) - { - LOG_ERROR(LastError, L"Failed to set Wintrust FinalPolicy key"); - goto cleanupWintrustChangedKey; - } - } - LOG(WINTUN_LOG_INFO, L"Installing driver"); - WCHAR InfStorePath[MAX_PATH]; - if (!SetupCopyOEMInfW(InfPath, NULL, SPOST_NONE, 0, InfStorePath, MAX_PATH, NULL, NULL)) - { - LastError = LOG_LAST_ERROR(L"Could not install driver %s to store", InfPath); - goto cleanupWintrustChangedKey; - } - _Analysis_assume_nullterminated_(InfStorePath); - - SetupDiDestroyDriverInfoList(DevInfo, DevInfoData, SPDIT_COMPATDRIVER); - DestroyDriverInfoListOnCleanup = FALSE; - DevInstallParams->Flags |= DI_ENUMSINGLEINF; - if (wcsncpy_s(DevInstallParams->DriverPath, _countof(DevInstallParams->DriverPath), InfStorePath, _TRUNCATE) == - STRUNCATE) - { - LOG(WINTUN_LOG_ERR, L"Inf path too long: %s", InfStorePath); - LastError = ERROR_INVALID_PARAMETER; - goto cleanupWintrustChangedKey; - } - if (!SetupDiSetDeviceInstallParamsW(DevInfo, DevInfoData, DevInstallParams)) - { - LastError = LOG_LAST_ERROR(L"Failed to set adapter %u device installation parameters", DevInfoData->DevInst); - goto cleanupWintrustChangedKey; - } - if (!SetupDiBuildDriverInfoList(DevInfo, DevInfoData, SPDIT_COMPATDRIVER)) - { - LastError = LOG_LAST_ERROR(L"Failed rebuilding adapter %u driver info list", DevInfoData->DevInst); - goto cleanupWintrustChangedKey; - } - DestroyDriverInfoListOnCleanup = TRUE; - SP_DRVINFO_DATA_W DrvInfoData = { .cbSize = sizeof(SP_DRVINFO_DATA_W) }; - if (!SetupDiEnumDriverInfoW(DevInfo, DevInfoData, SPDIT_COMPATDRIVER, 0, &DrvInfoData)) - { - LastError = LOG_LAST_ERROR(L"Failed to get adapter %u driver", DevInfoData->DevInst); - goto cleanupWintrustChangedKey; - } - if (!SetupDiSetSelectedDriverW(DevInfo, DevInfoData, &DrvInfoData)) - { - LastError = LOG_LAST_ERROR(L"Failed to set adapter %u driver", DevInfoData->DevInst); - goto cleanupWintrustChangedKey; - } - LastError = ERROR_SUCCESS; - DestroyDriverInfoListOnCleanup = FALSE; - -cleanupWintrustChangedKey: - if (WintrustKeyOriginalValue) - RegSetValueExW( - WintrustKey, - L"$DLL", - 0, - REG_SZ, - (BYTE *)WintrustKeyOriginalValue, - (DWORD)((wcslen(WintrustKeyOriginalValue) + 1) * sizeof(WintrustKeyOriginalValue[0]))); -cleanupWintrustKey: - if (WintrustKey) - RegCloseKey(WintrustKey); - if (WintrustKeyOriginalValue) - Free(WintrustKeyOriginalValue); -cleanupDelete: - DeleteFileW(CatPath); - DeleteFileW(SysPath); - DeleteFileW(InfPath); - if (DownlevelShimPath[0]) - DeleteFileW(DownlevelShimPath); -cleanupDirectory: - RemoveDirectoryW(RandomTempSubDirectory); -cleanupExistingAdapters: - if (LastError == ERROR_SUCCESS) - { - *DevInfoExistingAdaptersForCleanup = DevInfoExistingAdapters; - *ExistingAdaptersForCleanup = ExistingAdapters; - } - else - SelectDriverDeferredCleanup(DevInfoExistingAdapters, ExistingAdapters); - if (DestroyDriverInfoListOnCleanup) - SetupDiDestroyDriverInfoList(DevInfo, DevInfoData, SPDIT_COMPATDRIVER); -cleanupDriverInstallationLock: - NamespaceReleaseMutex(DriverInstallationLock); - return RET_ERROR(TRUE, LastError); -} - -_Use_decl_annotations_ -WINTUN_ADAPTER * -AdapterOpenFromDevInstanceId(LPCWSTR Pool, LPCWSTR DevInstanceID) -{ - WINTUN_ADAPTER *Adapter = Zalloc(sizeof(*Adapter)); - if (!Adapter) - return FALSE; - - DWORD LastError = ERROR_SUCCESS; - HANDLE Mutex = NamespaceTakePoolMutex(Pool); - if (!Mutex) - { - LastError = LOG(WINTUN_LOG_ERR, L"Failed to take %s pool mutex", Pool); - goto cleanup; - } - Adapter->DevInfo = SetupDiCreateDeviceInfoListExW(&GUID_DEVCLASS_NET, NULL, NULL, NULL); - if (Adapter->DevInfo == INVALID_HANDLE_VALUE) - { - LastError = LOG_LAST_ERROR(L"Failed to get present adapters"); - goto cleanupMutex; - } - Adapter->DevInfoData.cbSize = sizeof(Adapter->DevInfoData); - if (!SetupDiOpenDeviceInfoW(Adapter->DevInfo, DevInstanceID, NULL, 0, &Adapter->DevInfoData)) - { - LastError = LOG_LAST_ERROR(L"Failed to open device instance ID %s", DevInstanceID); - goto cleanupMutex; - } - if (!PopulateAdapterData(Adapter, Pool)) - { - LastError = LOG(WINTUN_LOG_ERR, L"Failed to populate adapter %u data", Adapter->DevInfoData.DevInst); - goto cleanupMutex; - } -cleanupMutex: - NamespaceReleaseMutex(Mutex); -cleanup: - if (LastError != ERROR_SUCCESS) - WintunFreeAdapter(Adapter); - return RET_ERROR(Adapter, LastError); + SW_DEVICE_CREATE_CTX *Ctx = Context; + Ctx->CreateResult = CreateResult; + if (DeviceInstanceId) + wcsncpy_s(Ctx->DeviceInstanceId, MAX_INSTANCE_ID, DeviceInstanceId, _TRUNCATE); + SetEvent(Ctx->Triggered); } _Use_decl_annotations_ WINTUN_ADAPTER_HANDLE WINAPI -WintunCreateAdapter(LPCWSTR Pool, LPCWSTR Name, const GUID *RequestedGUID, BOOL *RebootRequired) +WintunCreateAdapter(LPCWSTR Name, LPCWSTR TunnelType, const GUID *RequestedGUID) { - BOOL DummyRebootRequired; - if (!RebootRequired) - RebootRequired = &DummyRebootRequired; - *RebootRequired = FALSE; - -#ifdef MAYBE_WOW64 - if (NativeMachine != IMAGE_FILE_PROCESS) - return CreateAdapterViaRundll32(Pool, Name, RequestedGUID, RebootRequired); -#endif - DWORD LastError = ERROR_SUCCESS; - LOG(WINTUN_LOG_INFO, L"Creating adapter"); + WINTUN_ADAPTER *Adapter = NULL; - if (!IsWindows10) - RequestedGUID = NULL; - - WINTUN_ADAPTER *Adapter = Zalloc(sizeof(*Adapter)); - if (!Adapter) - return NULL; - - Adapter->DevInfo = SetupDiCreateDeviceInfoListExW(&GUID_DEVCLASS_NET, NULL, NULL, NULL); - if (Adapter->DevInfo == INVALID_HANDLE_VALUE) + HANDLE DeviceInstallationMutex = NamespaceTakeDeviceInstallationMutex(); + if (!DeviceInstallationMutex) { - LastError = LOG_LAST_ERROR(L"Failed to create empty device information set"); - goto cleanupAdapter; - } - WCHAR ClassName[MAX_CLASS_NAME_LEN]; - if (!SetupDiClassNameFromGuidExW(&GUID_DEVCLASS_NET, ClassName, _countof(ClassName), NULL, NULL, NULL)) - { - LastError = LOG_LAST_ERROR(L"Failed to retrieve class name associated with class GUID"); - goto cleanupAdapter; - } - - WCHAR PoolDeviceTypeName[MAX_POOL_DEVICE_TYPE]; - if (!GetPoolDeviceTypeName(Pool, PoolDeviceTypeName)) - { - LastError = GetLastError(); - goto cleanupAdapter; - } - Adapter->DevInfoData.cbSize = sizeof(Adapter->DevInfoData); - if (!SetupDiCreateDeviceInfoW( - Adapter->DevInfo, - ClassName, - &GUID_DEVCLASS_NET, - PoolDeviceTypeName, - NULL, - DICD_GENERATE_ID, - &Adapter->DevInfoData)) - { - LastError = LOG_LAST_ERROR(L"Failed to create new device information element"); - goto cleanupAdapter; - } - SP_DEVINSTALL_PARAMS_W DevInstallParams = { .cbSize = sizeof(DevInstallParams) }; - if (!SetupDiGetDeviceInstallParamsW(Adapter->DevInfo, &Adapter->DevInfoData, &DevInstallParams)) - { - LastError = LOG_LAST_ERROR( - L"Failed to retrieve adapter %u device installation parameters", Adapter->DevInfoData.DevInst); - goto cleanupAdapter; - } - DevInstallParams.Flags |= DI_QUIETINSTALL; - if (!SetupDiSetDeviceInstallParamsW(Adapter->DevInfo, &Adapter->DevInfoData, &DevInstallParams)) - { - LastError = - LOG_LAST_ERROR(L"Failed to set adapter %u device installation parameters", Adapter->DevInfoData.DevInst); - goto cleanupAdapter; - } - if (!SetupDiSetSelectedDevice(Adapter->DevInfo, &Adapter->DevInfoData)) - { - LastError = LOG_LAST_ERROR(L"Failed to select adapter %u device", Adapter->DevInfoData.DevInst); - goto cleanupAdapter; - } - static const WCHAR Hwids[_countof(WINTUN_HWID) + 1 /*Multi-string terminator*/] = WINTUN_HWID; - if (!SetupDiSetDeviceRegistryPropertyW( - Adapter->DevInfo, &Adapter->DevInfoData, SPDRP_HARDWAREID, (const BYTE *)Hwids, sizeof(Hwids))) - { - LastError = LOG_LAST_ERROR(L"Failed to set adapter %u hardware ID", Adapter->DevInfoData.DevInst); - goto cleanupAdapter; + LastError = LOG_LAST_ERROR(L"Failed to take device installation mutex"); + goto cleanup; } HDEVINFO DevInfoExistingAdapters; SP_DEVINFO_DATA_LIST *ExistingAdapters; - if (!SelectDriver( - Adapter->DevInfo, &Adapter->DevInfoData, &DevInstallParams, &DevInfoExistingAdapters, &ExistingAdapters)) - { - LastError = LOG(WINTUN_LOG_ERR, L"Failed to select adapter %u driver", Adapter->DevInfoData.DevInst); - goto cleanupAdapter; - } - - HANDLE Mutex = NamespaceTakePoolMutex(Pool); - if (!Mutex) - { - LastError = LOG(WINTUN_LOG_ERR, L"Failed to take %s pool mutex", Pool); - goto cleanupDriverInfoList; - } - - if (!SetupDiCallClassInstaller(DIF_REGISTERDEVICE, Adapter->DevInfo, &Adapter->DevInfoData)) - { - LastError = LOG_LAST_ERROR(L"Failed to register adapter %u device", Adapter->DevInfoData.DevInst); - goto cleanupDevice; - } - if (!SetupDiCallClassInstaller(DIF_REGISTER_COINSTALLERS, Adapter->DevInfo, &Adapter->DevInfoData)) - LOG_LAST_ERROR(L"Failed to register adapter %u coinstallers", Adapter->DevInfoData.DevInst); - - HKEY NetDevRegKey = INVALID_HANDLE_VALUE; - const int PollTimeout = 50 /* ms */; - for (int i = 0; NetDevRegKey == INVALID_HANDLE_VALUE && i < WAIT_FOR_REGISTRY_TIMEOUT / PollTimeout; ++i) - { - if (i) - Sleep(PollTimeout); - NetDevRegKey = SetupDiOpenDevRegKey( - Adapter->DevInfo, - &Adapter->DevInfoData, - DICS_FLAG_GLOBAL, - 0, - DIREG_DRV, - KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_NOTIFY); - } - if (NetDevRegKey == INVALID_HANDLE_VALUE) - { - LastError = - LOG_LAST_ERROR(L"Failed to open adapter %u device-specific registry key", Adapter->DevInfoData.DevInst); - goto cleanupDevice; - } - if (RequestedGUID) - { - LastError = RegSetValueExW( - NetDevRegKey, L"SuggestedInstanceId", 0, REG_BINARY, (const BYTE *)RequestedGUID, sizeof(*RequestedGUID)); - if (LastError != ERROR_SUCCESS) - { - WCHAR RegPath[MAX_REG_PATH]; - LoggerGetRegistryKeyPath(NetDevRegKey, RegPath); - LOG_ERROR(LastError, L"Failed to set %.*s\\SuggestedInstanceId", MAX_REG_PATH, RegPath); - goto cleanupNetDevRegKey; - } - } - - if (!SetupDiCallClassInstaller(DIF_INSTALLINTERFACES, Adapter->DevInfo, &Adapter->DevInfoData)) - LOG_LAST_ERROR(L"Failed to install adapter %u interfaces", Adapter->DevInfoData.DevInst); - - if (!SetupDiCallClassInstaller(DIF_INSTALLDEVICE, Adapter->DevInfo, &Adapter->DevInfoData)) - { - LastError = LOG_LAST_ERROR(L"Failed to install adapter %u device", Adapter->DevInfoData.DevInst); - goto cleanupNetDevRegKey; - } - *RebootRequired = *RebootRequired || CheckReboot(Adapter->DevInfo, &Adapter->DevInfoData); - - if (!SetupDiSetDevicePropertyW( - Adapter->DevInfo, - &Adapter->DevInfoData, - &DEVPKEY_Wintun_Pool, - DEVPROP_TYPE_STRING, -#pragma warning(suppress : 4090) - (const BYTE *)Pool, - (DWORD)((wcslen(Pool) + 1) * sizeof(*Pool)), - 0)) - { - LastError = LOG_LAST_ERROR(L"Failed to set adapter %u pool", Adapter->DevInfoData.DevInst); - goto cleanupNetDevRegKey; - } - if (!SetupDiSetDeviceRegistryPropertyW( - Adapter->DevInfo, - &Adapter->DevInfoData, - SPDRP_DEVICEDESC, - (const BYTE *)PoolDeviceTypeName, - (DWORD)((wcslen(PoolDeviceTypeName) + 1) * sizeof(*PoolDeviceTypeName)))) - { - LastError = LOG_LAST_ERROR(L"Failed to set adapter %u description", Adapter->DevInfoData.DevInst); - goto cleanupNetDevRegKey; - } - - /* DIF_INSTALLDEVICE returns almost immediately, while the device installation continues in the background. It might - * take a while, before all registry keys and values are populated. */ - LPWSTR DummyStr = RegistryQueryStringWait(NetDevRegKey, L"NetCfgInstanceId", WAIT_FOR_REGISTRY_TIMEOUT); - if (!DummyStr) - { - WCHAR RegPath[MAX_REG_PATH]; - LoggerGetRegistryKeyPath(NetDevRegKey, RegPath); - LastError = LOG(WINTUN_LOG_ERR, L"Failed to get %.*s\\NetCfgInstanceId", MAX_REG_PATH, RegPath); - goto cleanupNetDevRegKey; - } - Free(DummyStr); - DWORD DummyDWORD; - if (!RegistryQueryDWORDWait(NetDevRegKey, L"NetLuidIndex", WAIT_FOR_REGISTRY_TIMEOUT, &DummyDWORD)) - { - WCHAR RegPath[MAX_REG_PATH]; - LoggerGetRegistryKeyPath(NetDevRegKey, RegPath); - LastError = LOG(WINTUN_LOG_ERR, L"Failed to get %.*s\\NetLuidIndex", MAX_REG_PATH, RegPath); - goto cleanupNetDevRegKey; - } - if (!RegistryQueryDWORDWait(NetDevRegKey, L"*IfType", WAIT_FOR_REGISTRY_TIMEOUT, &DummyDWORD)) - { - WCHAR RegPath[MAX_REG_PATH]; - LoggerGetRegistryKeyPath(NetDevRegKey, RegPath); - LastError = LOG(WINTUN_LOG_ERR, L"Failed to get %.*s\\*IfType", MAX_REG_PATH, RegPath); - goto cleanupNetDevRegKey; - } - - if (!PopulateAdapterData(Adapter, Pool)) - { - LastError = LOG(WINTUN_LOG_ERR, L"Failed to populate adapter %u data", Adapter->DevInfoData.DevInst); - goto cleanupNetDevRegKey; - } - - HKEY TcpipAdapterRegKey; - WCHAR TcpipAdapterRegPath[MAX_REG_PATH]; - if (!GetTcpipAdapterRegPath(Adapter, TcpipAdapterRegPath)) + if (!DriverInstall(&DevInfoExistingAdapters, &ExistingAdapters)) { LastError = GetLastError(); + goto cleanupDeviceInstallationMutex; + } + + LOG(WINTUN_LOG_INFO, L"Creating adapter"); + + Adapter = Zalloc(sizeof(*Adapter)); + if (!Adapter) + goto cleanupDriverInstall; + + WCHAR TunnelTypeName[MAX_ADAPTER_NAME + 8]; + if (_snwprintf_s(TunnelTypeName, _countof(TunnelTypeName), _TRUNCATE, L"%s Tunnel", TunnelType) == -1) + { + LastError = ERROR_BUFFER_OVERFLOW; goto cleanupAdapter; } - TcpipAdapterRegKey = RegistryOpenKeyWait( - HKEY_LOCAL_MACHINE, TcpipAdapterRegPath, KEY_QUERY_VALUE | KEY_NOTIFY, WAIT_FOR_REGISTRY_TIMEOUT); - if (!TcpipAdapterRegKey) + + DEVINST RootNode; + WCHAR RootNodeName[200 /* rasmans.dll uses 200 hard coded instead of calling CM_Get_Device_ID_Size. */]; + CONFIGRET ConfigRet; + if ((ConfigRet = CM_Locate_DevNodeW(&RootNode, NULL, CM_LOCATE_DEVNODE_NORMAL)) != CR_SUCCESS || + (ConfigRet = CM_Get_Device_IDW(RootNode, RootNodeName, _countof(RootNodeName), 0)) != CR_SUCCESS) { - LastError = LOG( - WINTUN_LOG_ERR, L"Failed to open adapter-specific TCP/IP interface registry key %s", TcpipAdapterRegPath); + LastError = LOG_ERROR(CM_MapCrToWin32Err(ConfigRet, ERROR_GEN_FAILURE), L"Failed to get root node name"); goto cleanupAdapter; } - DummyStr = RegistryQueryStringWait(TcpipAdapterRegKey, L"IpConfig", WAIT_FOR_REGISTRY_TIMEOUT); - if (!DummyStr) - { - LastError = LOG(WINTUN_LOG_ERR, L"Failed to get %s\\IpConfig", TcpipAdapterRegPath); - goto cleanupTcpipAdapterRegKey; - } - Free(DummyStr); - WCHAR TcpipInterfaceRegPath[MAX_REG_PATH]; - if (!GetTcpipInterfaceRegPath(Adapter, TcpipInterfaceRegPath)) + GUID InstanceId; + HRESULT HRet = S_OK; + if (RequestedGUID) + memcpy(&InstanceId, RequestedGUID, sizeof(InstanceId)); + else + HRet = CoCreateGuid(&InstanceId); + WCHAR InstanceIdStr[MAX_GUID_STRING_LEN]; + if (FAILED(HRet) || !StringFromGUID2(&InstanceId, InstanceIdStr, _countof(InstanceIdStr))) { - LastError = LOG(WINTUN_LOG_ERR, L"Failed to determine interface-specific TCP/IP network registry key path"); - goto cleanupTcpipAdapterRegKey; + LastError = LOG_ERROR(HRet, L"Failed to convert GUID"); + goto cleanupAdapter; } - for (int Tries = 0; Tries < 300; ++Tries) + SW_DEVICE_CREATE_CTX CreateContext = { .DeviceInstanceId = Adapter->DevInstanceID, + .Triggered = CreateEventW(NULL, FALSE, FALSE, NULL) }; + if (!CreateContext.Triggered) { - HKEY TcpipInterfaceRegKey = RegistryOpenKeyWait( - HKEY_LOCAL_MACHINE, TcpipInterfaceRegPath, KEY_QUERY_VALUE | KEY_SET_VALUE, WAIT_FOR_REGISTRY_TIMEOUT); - if (!TcpipInterfaceRegKey) + LastError = LOG_LAST_ERROR(L"Failed to create event trigger"); + goto cleanupAdapter; + } + + if (IsWindows7) + { + if (!CreateAdapterWin7(Adapter, Name, TunnelTypeName)) { - LastError = - LOG(WINTUN_LOG_ERR, - L"Failed to open interface-specific TCP/IP network registry key %s", - TcpipInterfaceRegPath); - goto cleanupTcpipAdapterRegKey; + LastError = GetLastError(); + goto cleanupCreateContext; } + goto skipSwDevice; + } + if (!IsWindows10) + goto skipStub; - static const DWORD EnableDeadGWDetect = 0; - LastError = RegSetKeyValueW( - TcpipInterfaceRegKey, - NULL, - L"EnableDeadGWDetect", - REG_DWORD, - &EnableDeadGWDetect, - sizeof(EnableDeadGWDetect)); - RegCloseKey(TcpipInterfaceRegKey); - if (LastError == ERROR_SUCCESS) - break; - if (LastError != ERROR_TRANSACTION_NOT_ACTIVE) + SW_DEVICE_CREATE_INFO StubCreateInfo = { .cbSize = sizeof(StubCreateInfo), + .pszInstanceId = InstanceIdStr, + .pszzHardwareIds = L"", + .CapabilityFlags = + SWDeviceCapabilitiesSilentInstall | SWDeviceCapabilitiesDriverRequired, + .pszDeviceDescription = TunnelTypeName }; + DEVPROPERTY StubDeviceProperties[] = { { .CompKey = { .Key = DEVPKEY_Device_ClassGuid, + .Store = DEVPROP_STORE_SYSTEM }, + .Type = DEVPROP_TYPE_GUID, + .Buffer = (PVOID)&GUID_DEVCLASS_NET, + .BufferSize = sizeof(GUID_DEVCLASS_NET) } }; + HRet = SwDeviceCreate( + WINTUN_HWID, + RootNodeName, + &StubCreateInfo, + _countof(StubDeviceProperties), + StubDeviceProperties, + DeviceCreateCallback, + &CreateContext, + &Adapter->SwDevice); + if (FAILED(HRet)) + { + LastError = LOG_ERROR(HRet, L"Failed to initiate stub device creation"); + goto cleanupCreateContext; + } + if (WaitForSingleObject(CreateContext.Triggered, INFINITE) != WAIT_OBJECT_0) + { + LastError = LOG_LAST_ERROR(L"Failed to wait for stub device creation trigger"); + goto cleanupCreateContext; + } + if (FAILED(CreateContext.CreateResult)) + { + LastError = LOG_ERROR(CreateContext.CreateResult, L"Failed to create stub device"); + goto cleanupCreateContext; + } + DEVINST DevInst; + CONFIGRET CRet = CM_Locate_DevNodeW(&DevInst, Adapter->DevInstanceID, CM_LOCATE_DEVNODE_PHANTOM); + if (CRet != CR_SUCCESS) + { + LastError = + LOG_ERROR(CM_MapCrToWin32Err(CRet, ERROR_DEVICE_ENUMERATION_ERROR), L"Failed to make stub device list"); + goto cleanupCreateContext; + } + HKEY DriverKey; + CRet = CM_Open_DevNode_Key(DevInst, KEY_SET_VALUE, 0, RegDisposition_OpenAlways, &DriverKey, CM_REGISTRY_SOFTWARE); + if (CRet != CR_SUCCESS) + { + LastError = + LOG_ERROR(CM_MapCrToWin32Err(CRet, ERROR_PNP_REGISTRY_ERROR), L"Failed to create software registry key"); + goto cleanupCreateContext; + } + LastError = + RegSetValueExW(DriverKey, L"SuggestedInstanceId", 0, REG_BINARY, (const BYTE *)&InstanceId, sizeof(InstanceId)); + RegCloseKey(DriverKey); + if (LastError != ERROR_SUCCESS) + { + LastError = LOG_ERROR(LastError, L"Failed to set SuggestedInstanceId to %s", InstanceIdStr); + goto cleanupCreateContext; + } + SwDeviceClose(Adapter->SwDevice); + Adapter->SwDevice = NULL; + +skipStub:; + static const WCHAR Hwids[_countof(WINTUN_HWID) + 1 /*Multi-string terminator*/] = WINTUN_HWID; + SW_DEVICE_CREATE_INFO CreateInfo = { .cbSize = sizeof(CreateInfo), + .pszInstanceId = InstanceIdStr, + .pszzHardwareIds = Hwids, + .CapabilityFlags = + SWDeviceCapabilitiesSilentInstall | SWDeviceCapabilitiesDriverRequired, + .pszDeviceDescription = TunnelTypeName }; + DEVPROPERTY DeviceProperties[] = { + { .CompKey = { .Key = DEVPKEY_Wintun_Name, .Store = DEVPROP_STORE_SYSTEM }, + .Type = DEVPROP_TYPE_STRING, + .Buffer = (WCHAR *)Name, + .BufferSize = (ULONG)((wcslen(Name) + 1) * sizeof(*Name)) }, + { .CompKey = { .Key = DEVPKEY_Device_FriendlyName, .Store = DEVPROP_STORE_SYSTEM }, + .Type = DEVPROP_TYPE_STRING, + .Buffer = TunnelTypeName, + .BufferSize = (ULONG)((wcslen(TunnelTypeName) + 1) * sizeof(*TunnelTypeName)) }, + { .CompKey = { .Key = DEVPKEY_Device_DeviceDesc, .Store = DEVPROP_STORE_SYSTEM }, + .Type = DEVPROP_TYPE_STRING, + .Buffer = TunnelTypeName, + .BufferSize = (ULONG)((wcslen(TunnelTypeName) + 1) * sizeof(*TunnelTypeName)) } + }; + + HRet = SwDeviceCreate( + WINTUN_HWID, + RootNodeName, + &CreateInfo, + _countof(DeviceProperties), + DeviceProperties, + DeviceCreateCallback, + &CreateContext, + &Adapter->SwDevice); + if (FAILED(HRet)) + { + LastError = LOG_ERROR(HRet, L"Failed to initiate device creation"); + goto cleanupCreateContext; + } + if (WaitForSingleObject(CreateContext.Triggered, INFINITE) != WAIT_OBJECT_0) + { + LastError = LOG_LAST_ERROR(L"Failed to wait for device creation trigger"); + goto cleanupCreateContext; + } + if (FAILED(CreateContext.CreateResult)) + { + LastError = LOG_ERROR(CreateContext.CreateResult, L"Failed to create device"); + goto cleanupCreateContext; + } + + if (!WaitForInterface(Adapter->DevInstanceID)) + { + LastError = GetLastError(); + DEVPROPTYPE PropertyType = 0; + NTSTATUS NtStatus = 0; + INT32 ProblemCode = 0; + Adapter->DevInfo = SetupDiCreateDeviceInfoListExW(NULL, NULL, NULL, NULL); + if (Adapter->DevInfo == INVALID_HANDLE_VALUE) { - LOG_ERROR(LastError, L"Failed to set %s\\EnableDeadGWDetect", TcpipInterfaceRegPath); - goto cleanupTcpipAdapterRegKey; + Adapter->DevInfo = NULL; + goto cleanupCreateContext; } - Sleep(10); - } - - if (!WintunSetAdapterName(Adapter, Name)) - { - LastError = LOG(WINTUN_LOG_ERR, L"Failed to set adapter name %s", Name); - goto cleanupTcpipAdapterRegKey; - } - - for (int Tries = 0; Tries < 1000; ++Tries) - { - DEVPROPTYPE PropertyType; - NTSTATUS ProblemStatus; - if (SetupDiGetDevicePropertyW( + Adapter->DevInfoData.cbSize = sizeof(Adapter->DevInfoData); + if (!SetupDiOpenDeviceInfoW( + Adapter->DevInfo, Adapter->DevInstanceID, NULL, DIOD_INHERIT_CLASSDRVS, &Adapter->DevInfoData)) + { + SetupDiDestroyDeviceInfoList(Adapter->DevInfo); + Adapter->DevInfo = NULL; + goto cleanupCreateContext; + } + if (!SetupDiGetDevicePropertyW( Adapter->DevInfo, &Adapter->DevInfoData, &DEVPKEY_Device_ProblemStatus, &PropertyType, - (PBYTE)&ProblemStatus, - sizeof(ProblemStatus), + (PBYTE)&NtStatus, + sizeof(NtStatus), NULL, - 0) && - PropertyType == DEVPROP_TYPE_NTSTATUS) - { - if (ProblemStatus != STATUS_PNP_DEVICE_CONFIGURATION_PENDING || Tries == 999) - { - INT32 ProblemCode; - if (!SetupDiGetDevicePropertyW( - Adapter->DevInfo, - &Adapter->DevInfoData, - &DEVPKEY_Device_ProblemCode, - &PropertyType, - (PBYTE)&ProblemCode, - sizeof(ProblemCode), - NULL, - 0) || - PropertyType != DEVPROP_TYPE_INT32) - ProblemCode = 0; - LastError = RtlNtStatusToDosError(ProblemStatus); - if (LastError == ERROR_SUCCESS) - LastError = ERROR_NOT_READY; - LOG_ERROR(LastError, L"Failed to setup adapter (code: 0x%x, status: 0x%x)", ProblemCode, ProblemStatus); - goto cleanupTcpipAdapterRegKey; - } - Sleep(10); - } - else - break; - } - if (!EnsureDeviceObject(Adapter->DevInstanceID)) - { - LastError = LOG_LAST_ERROR(L"Device object file did not appear"); - goto cleanupTcpipAdapterRegKey; - } - LastError = ERROR_SUCCESS; - -cleanupTcpipAdapterRegKey: - RegCloseKey(TcpipAdapterRegKey); -cleanupNetDevRegKey: - RegCloseKey(NetDevRegKey); -cleanupDevice: - if (LastError != ERROR_SUCCESS) - { - SP_REMOVEDEVICE_PARAMS RemoveDeviceParams = { .ClassInstallHeader = { .cbSize = sizeof(SP_CLASSINSTALL_HEADER), - .InstallFunction = DIF_REMOVE }, - .Scope = DI_REMOVEDEVICE_GLOBAL }; - if (SetupDiSetClassInstallParamsW( + 0) || + PropertyType != DEVPROP_TYPE_NTSTATUS) + NtStatus = 0; + if (!SetupDiGetDevicePropertyW( Adapter->DevInfo, &Adapter->DevInfoData, - &RemoveDeviceParams.ClassInstallHeader, - sizeof(RemoveDeviceParams)) && - SetupDiCallClassInstaller(DIF_REMOVE, Adapter->DevInfo, &Adapter->DevInfoData)) - *RebootRequired = *RebootRequired || CheckReboot(Adapter->DevInfo, &Adapter->DevInfoData); + &DEVPKEY_Device_ProblemCode, + &PropertyType, + (PBYTE)&ProblemCode, + sizeof(ProblemCode), + NULL, + 0) || + (PropertyType != DEVPROP_TYPE_INT32 && PropertyType != DEVPROP_TYPE_UINT32)) + ProblemCode = 0; + LastError = RtlNtStatusToDosError(NtStatus); + if (LastError == ERROR_SUCCESS) + LastError = ERROR_DEVICE_NOT_AVAILABLE; + LOG_ERROR(LastError, L"Failed to setup adapter (problem code: 0x%X, ntstatus: 0x%X)", ProblemCode, NtStatus); + goto cleanupCreateContext; } - NamespaceReleaseMutex(Mutex); -cleanupDriverInfoList: - SelectDriverDeferredCleanup(DevInfoExistingAdapters, ExistingAdapters); - SetupDiDestroyDriverInfoList(Adapter->DevInfo, &Adapter->DevInfoData, SPDIT_COMPATDRIVER); + +skipSwDevice: + Adapter->DevInfo = SetupDiCreateDeviceInfoListExW(&GUID_DEVCLASS_NET, NULL, NULL, NULL); + if (Adapter->DevInfo == INVALID_HANDLE_VALUE) + { + Adapter->DevInfo = NULL; + LastError = LOG_LAST_ERROR(L"Failed to make device list"); + goto cleanupCreateContext; + } + Adapter->DevInfoData.cbSize = sizeof(Adapter->DevInfoData); + if (!SetupDiOpenDeviceInfoW( + Adapter->DevInfo, Adapter->DevInstanceID, NULL, DIOD_INHERIT_CLASSDRVS, &Adapter->DevInfoData)) + { + LastError = LOG_LAST_ERROR(L"Failed to open device instance ID %s", Adapter->DevInstanceID); + SetupDiDestroyDeviceInfoList(Adapter->DevInfo); + Adapter->DevInfo = NULL; + goto cleanupCreateContext; + } + + if (!PopulateAdapterData(Adapter)) + { + LastError = LOG(WINTUN_LOG_ERR, L"Failed to populate adapter data"); + goto cleanupCreateContext; + } + + if (!NciSetAdapterName(&Adapter->CfgInstanceID, Name)) + { + LastError = LOG(WINTUN_LOG_ERR, L"Failed to set adapter name \"%s\"", Name); + goto cleanupCreateContext; + } + + if (IsWindows7) + CreateAdapterPostWin7(Adapter, TunnelTypeName); + +cleanupCreateContext: + CloseHandle(CreateContext.Triggered); cleanupAdapter: if (LastError != ERROR_SUCCESS) - WintunFreeAdapter(Adapter); + { + WintunCloseAdapter(Adapter); + Adapter = NULL; + } +cleanupDriverInstall: + DriverInstallDeferredCleanup(DevInfoExistingAdapters, ExistingAdapters); +cleanupDeviceInstallationMutex: + NamespaceReleaseMutex(DeviceInstallationMutex); +cleanup: + QueueUpOrphanedDeviceCleanupRoutine(); return RET_ERROR(Adapter, LastError); } _Use_decl_annotations_ -BOOL WINAPI -WintunDeleteAdapter(WINTUN_ADAPTER *Adapter, BOOL ForceCloseSessions, BOOL *RebootRequired) +WINTUN_ADAPTER_HANDLE WINAPI +WintunOpenAdapter(LPCWSTR Name) { - BOOL DummyRebootRequired; - if (!RebootRequired) - RebootRequired = &DummyRebootRequired; - *RebootRequired = FALSE; -#ifdef MAYBE_WOW64 - if (NativeMachine != IMAGE_FILE_PROCESS) - return DeleteAdapterViaRundll32(Adapter, ForceCloseSessions, RebootRequired); -#endif - DWORD LastError = ERROR_SUCCESS; - HANDLE Mutex = NamespaceTakePoolMutex(Adapter->Pool); - if (!Mutex) + WINTUN_ADAPTER *Adapter = NULL; + + HANDLE DeviceInstallationMutex = NamespaceTakeDeviceInstallationMutex(); + if (!DeviceInstallationMutex) { - LastError = LOG(WINTUN_LOG_ERR, L"Failed to take %s pool mutex", Adapter->Pool); + LastError = LOG_LAST_ERROR(L"Failed to take device installation mutex"); goto cleanup; } - SP_DEVINSTALL_PARAMS_W DevInstallParams = { .cbSize = sizeof(DevInstallParams) }; - if (!SetupDiGetDeviceInstallParamsW(Adapter->DevInfo, &Adapter->DevInfoData, &DevInstallParams)) - { - LastError = LOG_LAST_ERROR( - L"Failed to retrieve adapter %u device installation parameters", Adapter->DevInfoData.DevInst); - goto cleanupMutex; - } - DevInstallParams.Flags |= DI_QUIETINSTALL; - if (!SetupDiSetDeviceInstallParamsW(Adapter->DevInfo, &Adapter->DevInfoData, &DevInstallParams)) - { - LastError = - LOG_LAST_ERROR(L"Failed to set adapter %u device installation parameters", Adapter->DevInfoData.DevInst); - goto cleanupMutex; - } + Adapter = Zalloc(sizeof(*Adapter)); + if (!Adapter) + goto cleanupDeviceInstallationMutex; - if (ForceCloseSessions && !ForceCloseWintunAdapterHandle(Adapter->DevInfo, &Adapter->DevInfoData)) - LOG(WINTUN_LOG_WARN, L"Failed to force close adapter %u handles", Adapter->DevInfoData.DevInst); - - SP_REMOVEDEVICE_PARAMS Params = { .ClassInstallHeader = { .cbSize = sizeof(SP_CLASSINSTALL_HEADER), - .InstallFunction = DIF_REMOVE }, - .Scope = DI_REMOVEDEVICE_GLOBAL }; - if ((!SetupDiSetClassInstallParamsW( - Adapter->DevInfo, &Adapter->DevInfoData, &Params.ClassInstallHeader, sizeof(Params)) || - !SetupDiCallClassInstaller(DIF_REMOVE, Adapter->DevInfo, &Adapter->DevInfoData)) && - GetLastError() != ERROR_NO_SUCH_DEVINST) - LastError = LOG_LAST_ERROR(L"Failed to remove adapter %u", Adapter->DevInfoData.DevInst); - - *RebootRequired = *RebootRequired || CheckReboot(Adapter->DevInfo, &Adapter->DevInfoData); - -cleanupMutex: - NamespaceReleaseMutex(Mutex); -cleanup: - return RET_ERROR(TRUE, LastError); -} - -static _Return_type_success_(return != FALSE) -BOOL -DeleteAllOurAdapters(_In_z_ LPCWSTR Pool, _Inout_ BOOL *RebootRequired) -{ - HANDLE Mutex = NamespaceTakePoolMutex(Pool); - if (!Mutex) - { - LOG(WINTUN_LOG_ERR, L"Failed to take %s pool mutex", Pool); - return FALSE; - } - DWORD LastError = ERROR_SUCCESS; - HDEVINFO DevInfo = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, NULL, NULL, DIGCF_PRESENT, NULL, NULL, NULL); + HDEVINFO DevInfo = + SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, WINTUN_ENUMERATOR, NULL, DIGCF_PRESENT, NULL, NULL, NULL); if (DevInfo == INVALID_HANDLE_VALUE) { LastError = LOG_LAST_ERROR(L"Failed to get present adapters"); - goto cleanupMutex; + goto cleanupAdapter; } - SP_REMOVEDEVICE_PARAMS Params = { .ClassInstallHeader = { .cbSize = sizeof(SP_CLASSINSTALL_HEADER), - .InstallFunction = DIF_REMOVE }, - .Scope = DI_REMOVEDEVICE_GLOBAL }; - for (DWORD EnumIndex = 0;; ++EnumIndex) + + SP_DEVINFO_DATA DevInfoData = { .cbSize = sizeof(DevInfoData) }; + BOOL Found = FALSE; + for (DWORD EnumIndex = 0; !Found; ++EnumIndex) { - SP_DEVINFO_DATA DevInfoData = { .cbSize = sizeof(SP_DEVINFO_DATA) }; if (!SetupDiEnumDeviceInfo(DevInfo, EnumIndex, &DevInfoData)) { if (GetLastError() == ERROR_NO_MORE_ITEMS) @@ -1746,144 +847,142 @@ DeleteAllOurAdapters(_In_z_ LPCWSTR Pool, _Inout_ BOOL *RebootRequired) continue; } - if (!IsOurAdapter(DevInfo, &DevInfoData) || !IsPoolMember(Pool, DevInfo, &DevInfoData)) - continue; - - LOG(WINTUN_LOG_INFO, L"Force closing all adapter %u open handles", DevInfoData.DevInst); - if (!ForceCloseWintunAdapterHandle(DevInfo, &DevInfoData)) - LOG(WINTUN_LOG_WARN, L"Failed to force close adapter %u handles", DevInfoData.DevInst); - - LOG(WINTUN_LOG_INFO, L"Removing adapter %u", DevInfoData.DevInst); - if ((!SetupDiSetClassInstallParamsW(DevInfo, &DevInfoData, &Params.ClassInstallHeader, sizeof(Params)) || - !SetupDiCallClassInstaller(DIF_REMOVE, DevInfo, &DevInfoData)) && - GetLastError() != ERROR_NO_SUCH_DEVINST) - { - LOG_LAST_ERROR(L"Failed to remove adapter %u", DevInfoData.DevInst); - LastError = LastError != ERROR_SUCCESS ? LastError : GetLastError(); - } - *RebootRequired = *RebootRequired || CheckReboot(DevInfo, &DevInfoData); + DEVPROPTYPE PropType; + WCHAR OtherName[MAX_ADAPTER_NAME]; + Found = SetupDiGetDevicePropertyW( + DevInfo, + &DevInfoData, + &DEVPKEY_Wintun_Name, + &PropType, + (PBYTE)OtherName, + MAX_ADAPTER_NAME * sizeof(OtherName[0]), + NULL, + 0) && + PropType == DEVPROP_TYPE_STRING && !_wcsicmp(Name, OtherName); } + if (!Found) + { + LastError = LOG_ERROR(ERROR_NOT_FOUND, L"Failed to find matching adapter name"); + goto cleanupDevInfo; + } + DWORD RequiredChars = _countof(Adapter->DevInstanceID); + if (!SetupDiGetDeviceInstanceIdW(DevInfo, &DevInfoData, Adapter->DevInstanceID, RequiredChars, &RequiredChars)) + { + LastError = LOG_LAST_ERROR(L"Failed to get adapter instance ID"); + goto cleanupDevInfo; + } + Adapter->DevInfo = DevInfo; + Adapter->DevInfoData = DevInfoData; + BOOL Ret = WaitForInterface(Adapter->DevInstanceID) && PopulateAdapterData(Adapter); + Adapter->DevInfo = NULL; + if (!Ret) + { + LastError = LOG_LAST_ERROR(L"Failed to populate adapter"); + goto cleanupDevInfo; + } + +cleanupDevInfo: SetupDiDestroyDeviceInfoList(DevInfo); -cleanupMutex: - NamespaceReleaseMutex(Mutex); +cleanupAdapter: + if (LastError != ERROR_SUCCESS) + { + WintunCloseAdapter(Adapter); + Adapter = NULL; + } +cleanupDeviceInstallationMutex: + NamespaceReleaseMutex(DeviceInstallationMutex); +cleanup: + QueueUpOrphanedDeviceCleanupRoutine(); + return RET_ERROR(Adapter, LastError); +} + +#define TUN_IOCTL_FORCE_CLOSE_HANDLES CTL_CODE(51820U, 0x971U, METHOD_NEITHER, FILE_READ_DATA | FILE_WRITE_DATA) + +_Use_decl_annotations_ +BOOL +AdapterForceCloseHandles(HDEVINFO DevInfo, SP_DEVINFO_DATA *DevInfoData) +{ + DWORD LastError = ERROR_SUCCESS; + WCHAR InstanceId[MAX_INSTANCE_ID]; + DWORD RequiredChars = _countof(InstanceId); + if (!SetupDiGetDeviceInstanceIdW(DevInfo, DevInfoData, InstanceId, RequiredChars, &RequiredChars)) + { + LOG_LAST_ERROR(L"Failed to get adapter instance ID"); + return FALSE; + } + WINTUN_ADAPTER Adapter = { .InterfaceFilename = AdapterGetDeviceObjectFileName(InstanceId) }; + if (!Adapter.InterfaceFilename) + { + LOG_LAST_ERROR(L"Failed to get adapter file name"); + return FALSE; + } + HANDLE Handle = AdapterOpenDeviceObject(&Adapter); + Free(Adapter.InterfaceFilename); + if (Handle == INVALID_HANDLE_VALUE) + { + LastError = LOG(WINTUN_LOG_ERR, L"Failed to get adapter file object"); + return FALSE; + } + DWORD RequiredBytes; + if (DeviceIoControl(Handle, TUN_IOCTL_FORCE_CLOSE_HANDLES, NULL, 0, NULL, 0, &RequiredBytes, NULL)) + { + LastError = ERROR_SUCCESS; + Sleep(200); + } + else if (GetLastError() == ERROR_NOTHING_TO_TERMINATE) + LastError = ERROR_SUCCESS; + else + LastError = LOG_LAST_ERROR(L"Failed to perform force close ioctl"); + CloseHandle(Handle); return RET_ERROR(TRUE, LastError); } _Use_decl_annotations_ -BOOL WINAPI -WintunDeletePoolDriver(LPCWSTR Pool, BOOL *RebootRequired) +BOOL +AdapterRemoveInstance(HDEVINFO DevInfo, SP_DEVINFO_DATA *DevInfoData) { - BOOL DummyRebootRequired; - if (!RebootRequired) - RebootRequired = &DummyRebootRequired; - *RebootRequired = FALSE; - - DWORD LastError = ERROR_SUCCESS; #ifdef MAYBE_WOW64 if (NativeMachine != IMAGE_FILE_PROCESS) - { - LastError = DeletePoolDriverViaRundll32(Pool, RebootRequired) ? ERROR_SUCCESS : GetLastError(); - goto cleanup; - } + return RemoveInstanceViaRundll32(DevInfo, DevInfoData); #endif - if (!DeleteAllOurAdapters(Pool, RebootRequired)) - { - LastError = GetLastError(); - goto cleanup; - } - - HANDLE DriverInstallationLock = NamespaceTakeDriverInstallationMutex(); - if (!DriverInstallationLock) - { - LastError = LOG(WINTUN_LOG_ERR, L"Failed to take driver installation mutex"); - goto cleanup; - } - HDEVINFO DeviceInfoSet = SetupDiGetClassDevsW(&GUID_DEVCLASS_NET, NULL, NULL, 0); - if (!DeviceInfoSet) - { - LastError = LOG_LAST_ERROR(L"Failed to get adapter information"); - goto cleanupDriverInstallationLock; - } - if (!SetupDiBuildDriverInfoList(DeviceInfoSet, NULL, SPDIT_CLASSDRIVER)) - { - LastError = LOG_LAST_ERROR(L"Failed building driver info list"); - goto cleanupDeviceInfoSet; - } - for (DWORD EnumIndex = 0;; ++EnumIndex) - { - SP_DRVINFO_DATA_W DriverInfo = { .cbSize = sizeof(DriverInfo) }; - if (!SetupDiEnumDriverInfoW(DeviceInfoSet, NULL, SPDIT_CLASSDRIVER, EnumIndex, &DriverInfo)) - { - if (GetLastError() == ERROR_NO_MORE_ITEMS) - break; - continue; - } - SP_DRVINFO_DETAIL_DATA_W *DriverDetail = GetAdapterDrvInfoDetail(DeviceInfoSet, NULL, &DriverInfo); - if (!DriverDetail) - continue; - if (!_wcsicmp(DriverDetail->HardwareID, WINTUN_HWID)) - { - LPCWSTR Path = PathFindFileNameW(DriverDetail->InfFileName); - LOG(WINTUN_LOG_INFO, L"Removing driver %s", Path); - if (!SetupUninstallOEMInfW(Path, 0, NULL)) - { - LOG_LAST_ERROR(L"Unable to remove driver %s", Path); - LastError = LastError != ERROR_SUCCESS ? LastError : GetLastError(); - } - } - Free(DriverDetail); - } - SetupDiDestroyDriverInfoList(DeviceInfoSet, NULL, SPDIT_CLASSDRIVER); -cleanupDeviceInfoSet: - SetupDiDestroyDeviceInfoList(DeviceInfoSet); -cleanupDriverInstallationLock: - NamespaceReleaseMutex(DriverInstallationLock); -cleanup: - return RET_ERROR(TRUE, LastError); + SP_REMOVEDEVICE_PARAMS RemoveDeviceParams = { .ClassInstallHeader = { .cbSize = sizeof(SP_CLASSINSTALL_HEADER), + .InstallFunction = DIF_REMOVE }, + .Scope = DI_REMOVEDEVICE_GLOBAL }; + return SetupDiSetClassInstallParamsW( + DevInfo, DevInfoData, &RemoveDeviceParams.ClassInstallHeader, sizeof(RemoveDeviceParams)) && + SetupDiCallClassInstaller(DIF_REMOVE, DevInfo, DevInfoData); } _Use_decl_annotations_ -BOOL WINAPI -WintunEnumAdapters(LPCWSTR Pool, WINTUN_ENUM_CALLBACK Func, LPARAM Param) +BOOL +AdapterEnableInstance(HDEVINFO DevInfo, SP_DEVINFO_DATA *DevInfoData) { - DWORD LastError = ERROR_SUCCESS; - HANDLE Mutex = NamespaceTakePoolMutex(Pool); - if (!Mutex) - { - LastError = LOG(WINTUN_LOG_ERR, L"Failed to take %s pool mutex", Pool); - goto cleanup; - } - HDEVINFO DevInfo = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, NULL, NULL, DIGCF_PRESENT, NULL, NULL, NULL); - if (DevInfo == INVALID_HANDLE_VALUE) - { - LastError = LOG_LAST_ERROR(L"Failed to get present adapters"); - goto cleanupMutex; - } - BOOL Continue = TRUE; - for (DWORD EnumIndex = 0; Continue; ++EnumIndex) - { - WINTUN_ADAPTER Adapter = { .DevInfo = DevInfo, .DevInfoData.cbSize = sizeof(Adapter.DevInfoData) }; - if (!SetupDiEnumDeviceInfo(DevInfo, EnumIndex, &Adapter.DevInfoData)) - { - if (GetLastError() == ERROR_NO_MORE_ITEMS) - break; - continue; - } +#ifdef MAYBE_WOW64 + if (NativeMachine != IMAGE_FILE_PROCESS) + return EnableInstanceViaRundll32(DevInfo, DevInfoData); +#endif - if (!IsOurAdapter(DevInfo, &Adapter.DevInfoData) || !IsPoolMember(Pool, DevInfo, &Adapter.DevInfoData)) - continue; - - if (!PopulateAdapterData(&Adapter, Pool)) - { - LastError = LOG(WINTUN_LOG_ERR, L"Failed to populate adapter %u data", Adapter.DevInfoData.DevInst); - break; - } - Continue = Func(&Adapter, Param); - } - SetupDiDestroyDeviceInfoList(DevInfo); -cleanupMutex: - NamespaceReleaseMutex(Mutex); -cleanup: - return RET_ERROR(TRUE, LastError); + SP_PROPCHANGE_PARAMS Params = { .ClassInstallHeader = { .cbSize = sizeof(SP_CLASSINSTALL_HEADER), + .InstallFunction = DIF_PROPERTYCHANGE }, + .StateChange = DICS_ENABLE, + .Scope = DICS_FLAG_GLOBAL }; + return SetupDiSetClassInstallParamsW(DevInfo, DevInfoData, &Params.ClassInstallHeader, sizeof(Params)) && + SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, DevInfo, DevInfoData); +} + +_Use_decl_annotations_ +BOOL +AdapterDisableInstance(HDEVINFO DevInfo, SP_DEVINFO_DATA *DevInfoData) +{ +#ifdef MAYBE_WOW64 + if (NativeMachine != IMAGE_FILE_PROCESS) + return DisableInstanceViaRundll32(DevInfo, DevInfoData); +#endif + SP_PROPCHANGE_PARAMS Params = { .ClassInstallHeader = { .cbSize = sizeof(SP_CLASSINSTALL_HEADER), + .InstallFunction = DIF_PROPERTYCHANGE }, + .StateChange = DICS_DISABLE, + .Scope = DICS_FLAG_GLOBAL }; + return SetupDiSetClassInstallParamsW(DevInfo, DevInfoData, &Params.ClassInstallHeader, sizeof(Params)) && + SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, DevInfo, DevInfoData); } diff --git a/api/adapter.h b/api/adapter.h index 5f468b0..ec84d70 100644 --- a/api/adapter.h +++ b/api/adapter.h @@ -12,71 +12,46 @@ #define MAX_INSTANCE_ID MAX_PATH /* TODO: Is MAX_PATH always enough? */ #define WINTUN_HWID L"Wintun" +#define WINTUN_ENUMERATOR (IsWindows7 ? L"ROOT\\" WINTUN_HWID : L"SWD\\" WINTUN_HWID) + +extern const DEVPROPKEY DEVPKEY_Wintun_Name; + +typedef struct HSWDEVICE__ *HSWDEVICE; /** * Wintun adapter descriptor. */ typedef struct _WINTUN_ADAPTER { + HSWDEVICE SwDevice; HDEVINFO DevInfo; SP_DEVINFO_DATA DevInfoData; + WCHAR *InterfaceFilename; GUID CfgInstanceID; WCHAR DevInstanceID[MAX_INSTANCE_ID]; DWORD LuidIndex; DWORD IfType; DWORD IfIndex; - WCHAR Pool[WINTUN_MAX_POOL]; } WINTUN_ADAPTER; - -/** - * @copydoc WINTUN_FREE_ADAPTER_FUNC - */ -WINTUN_FREE_ADAPTER_FUNC_IMPL WintunFreeAdapter; - /** * @copydoc WINTUN_CREATE_ADAPTER_FUNC */ -WINTUN_CREATE_ADAPTER_FUNC_IMPL WintunCreateAdapter; +WINTUN_CREATE_ADAPTER_FUNC WintunCreateAdapter; /** * @copydoc WINTUN_OPEN_ADAPTER_FUNC */ -WINTUN_OPEN_ADAPTER_FUNC_IMPL WintunOpenAdapter; +WINTUN_OPEN_ADAPTER_FUNC WintunOpenAdapter; /** - * @copydoc WINTUN_DELETE_ADAPTER_FUNC + * @copydoc WINTUN_CLOSE_ADAPTER_FUNC */ -WINTUN_DELETE_ADAPTER_FUNC_IMPL WintunDeleteAdapter; - -/** - * @copydoc WINTUN_ENUM_ADAPTERS_FUNC - */ -WINTUN_ENUM_ADAPTERS_FUNC_IMPL WintunEnumAdapters; - -/** - * @copydoc WINTUN_DELETE_POOL_DRIVER_FUNC - */ -WINTUN_DELETE_POOL_DRIVER_FUNC_IMPL WintunDeletePoolDriver; +WINTUN_CLOSE_ADAPTER_FUNC WintunCloseAdapter; /** * @copydoc WINTUN_GET_ADAPTER_LUID_FUNC */ -WINTUN_GET_ADAPTER_LUID_FUNC_IMPL WintunGetAdapterLUID; - -/** - * @copydoc WINTUN_GET_ADAPTER_NAME_FUNC - */ -WINTUN_GET_ADAPTER_NAME_FUNC_IMPL WintunGetAdapterName; - -/** - * @copydoc WINTUN_SET_ADAPTER_NAME_FUNC - */ -WINTUN_SET_ADAPTER_NAME_FUNC_IMPL WintunSetAdapterName; - -/** - * @copydoc WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC - */ -WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC_IMPL WintunGetRunningDriverVersion; +WINTUN_GET_ADAPTER_LUID_FUNC WintunGetAdapterLUID; /** * Returns a handle to the adapter device object. @@ -90,19 +65,88 @@ WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC_IMPL WintunGetRunningDriverVersion; _Return_type_success_(return != INVALID_HANDLE_VALUE) HANDLE WINAPI AdapterOpenDeviceObject(_In_ const WINTUN_ADAPTER *Adapter); + /** - * Returns an adapter object based on a devnode instance ID. + * Returns the device object file name for an adapter instance ID. * - * @param Pool Pool name of adapter object to be opened. + * @param InstanceID The device instance ID of the adapter. * - * @param DevInstanceID Instance ID of devnode for opening adapter. - * - * @return If the function succeeds, the return value is adapter object.. - * If the function fails, the return value is NULL. To get extended error - * information, call GetLastError. + * @return If the function succeeds, the return value is the filename of the device object, which + * must be freed with Free(). If the function fails, the return value is INVALID_HANDLE_VALUE. + * To get extended error information, call GetLastError. */ _Must_inspect_result_ _Return_type_success_(return != NULL) _Post_maybenull_ -WINTUN_ADAPTER * -AdapterOpenFromDevInstanceId(_In_z_ LPCWSTR Pool, _In_z_ LPCWSTR DevInstanceID); +LPWSTR +AdapterGetDeviceObjectFileName(_In_z_ LPCWSTR InstanceId); + +/** + * Cleans up adapters with no attached process. + */ +VOID AdapterCleanupOrphanedDevices(VOID); + +/** + * Cleans up adapters that use the old enumerator. + */ +VOID AdapterCleanupLegacyDevices(VOID); + +/** + * Removes the specified device instance. + * + * @param DevInfo Device info handle from SetupAPI. + * @param DevInfoData Device info data specifying which device. + * + * @return If the function succeeds, the return value is TRUE. If the + * function fails, the return value is FALSE. To get extended + * error information, call GetLastError. + */ + +_Return_type_success_(return != FALSE) +BOOL +AdapterRemoveInstance(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData); + +/** + * Enables the specified device instance. + * + * @param DevInfo Device info handle from SetupAPI. + * @param DevInfoData Device info data specifying which device. + * + * @return If the function succeeds, the return value is TRUE. If the + * function fails, the return value is FALSE. To get extended + * error information, call GetLastError. + */ + +_Return_type_success_(return != FALSE) +BOOL +AdapterEnableInstance(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData); + +/** + * Disables the specified device instance. + * + * @param DevInfo Device info handle from SetupAPI. + * @param DevInfoData Device info data specifying which device. + * + * @return If the function succeeds, the return value is TRUE. If the + * function fails, the return value is FALSE. To get extended + * error information, call GetLastError. + */ + +_Return_type_success_(return != FALSE) +BOOL +AdapterDisableInstance(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData); + +/** + * Force closes all device handles of the specified device instance. + * + * @param DevInfo Device info handle from SetupAPI. + * @param DevInfoData Device info data specifying which device. + * + * @return If the function succeeds, the return value is TRUE. If the + * function fails, the return value is FALSE. To get extended + * error information, call GetLastError. + */ + +_Return_type_success_(return != FALSE) +BOOL +AdapterForceCloseHandles(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData); diff --git a/api/adapter_win7.h b/api/adapter_win7.h new file mode 100644 index 0000000..affbd09 --- /dev/null +++ b/api/adapter_win7.h @@ -0,0 +1,351 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved. + */ + +static const DEVPROPKEY DEVPKEY_Wintun_OwningProcess = { + { 0x3361c968, 0x2f2e, 0x4660, { 0xb4, 0x7e, 0x69, 0x9c, 0xdc, 0x4c, 0x32, 0xb9 } }, + DEVPROPID_FIRST_USABLE + 3 +}; + +typedef struct _OWNING_PROCESS +{ + DWORD ProcessId; + FILETIME CreationTime; +} OWNING_PROCESS; + +_Must_inspect_result_ +static _Return_type_success_(return != FALSE) +BOOL +WaitForInterfaceWin7(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData, _In_ LPCWSTR DevInstanceId) +{ + ULONG Status, Number; + DWORD ValType, Zero; + WCHAR *FileName = NULL; + HKEY Key = INVALID_HANDLE_VALUE; + HANDLE FileHandle = INVALID_HANDLE_VALUE; + BOOLEAN Ret = FALSE; + for (DWORD Tries = 0; Tries < 1500; ++Tries) + { + if (Tries) + Sleep(10); + if (Key == INVALID_HANDLE_VALUE) + Key = SetupDiOpenDevRegKey(DevInfo, DevInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_QUERY_VALUE); + if (!FileName) + FileName = AdapterGetDeviceObjectFileName(DevInstanceId); + if (FileName && FileHandle == INVALID_HANDLE_VALUE) + FileHandle = CreateFileW( + FileName, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + 0, + NULL); + Zero = 0; + if (FileName && FileHandle != INVALID_HANDLE_VALUE && Key != INVALID_HANDLE_VALUE && Key && + RegQueryValueExW(Key, L"NetCfgInstanceId", NULL, &ValType, NULL, &Zero) != ERROR_MORE_DATA && + CM_Get_DevNode_Status(&Status, &Number, DevInfoData->DevInst, 0) == CR_SUCCESS && + !(Status & DN_HAS_PROBLEM) && !Number) + { + Ret = TRUE; + break; + } + } + if (Key != INVALID_HANDLE_VALUE && Key) + RegCloseKey(Key); + if (FileHandle != INVALID_HANDLE_VALUE) + CloseHandle(FileHandle); + Free(FileName); + return Ret; +} + +_Must_inspect_result_ +static _Return_type_success_(return != FALSE) +BOOL +CreateAdapterWin7(_Inout_ WINTUN_ADAPTER *Adapter, _In_z_ LPCWSTR Name, _In_z_ LPCWSTR TunnelTypeName) +{ + DWORD LastError = ERROR_SUCCESS; + + HDEVINFO DevInfo = SetupDiCreateDeviceInfoListExW(&GUID_DEVCLASS_NET, NULL, NULL, NULL); + if (DevInfo == INVALID_HANDLE_VALUE) + { + LastError = LOG_LAST_ERROR(L"Failed to create empty device information set"); + goto cleanup; + } + SP_DEVINFO_DATA DevInfoData = { .cbSize = sizeof(DevInfoData) }; + +#ifdef MAYBE_WOW64 + if (NativeMachine != IMAGE_FILE_PROCESS) + { + if (!CreateInstanceWin7ViaRundll32(Adapter->DevInstanceID)) + { + LastError = LOG_LAST_ERROR(L"Failed to create device instance"); + goto cleanup; + } + if (!SetupDiOpenDeviceInfoW(DevInfo, Adapter->DevInstanceID, NULL, DIOD_INHERIT_CLASSDRVS, &DevInfoData)) + { + LastError = GetLastError(); + goto cleanupDevInfo; + } + goto resumeAfterInstance; + } +#endif + + if (!SetupDiCreateDeviceInfoW( + DevInfo, WINTUN_HWID, &GUID_DEVCLASS_NET, TunnelTypeName, NULL, DICD_GENERATE_ID, &DevInfoData)) + { + LastError = LOG_LAST_ERROR(L"Failed to create new device information element"); + goto cleanupDevInfo; + } + SP_DEVINSTALL_PARAMS_W DevInstallParams = { .cbSize = sizeof(DevInstallParams) }; + if (!SetupDiGetDeviceInstallParamsW(DevInfo, &DevInfoData, &DevInstallParams)) + { + LastError = LOG_LAST_ERROR(L"Failed to retrieve adapter device installation parameters"); + goto cleanupDevInfo; + } + DevInstallParams.Flags |= DI_QUIETINSTALL; + if (!SetupDiSetDeviceInstallParamsW(DevInfo, &DevInfoData, &DevInstallParams)) + { + LastError = LOG_LAST_ERROR(L"Failed to set adapter device installation parameters"); + goto cleanupDevInfo; + } + if (!SetupDiSetSelectedDevice(DevInfo, &DevInfoData)) + { + LastError = LOG_LAST_ERROR(L"Failed to select adapter device"); + goto cleanupDevInfo; + } + static const WCHAR Hwids[_countof(WINTUN_HWID) + 1 /*Multi-string terminator*/] = WINTUN_HWID; + if (!SetupDiSetDeviceRegistryPropertyW(DevInfo, &DevInfoData, SPDRP_HARDWAREID, (const BYTE *)Hwids, sizeof(Hwids))) + { + LastError = LOG_LAST_ERROR(L"Failed to set adapter hardware ID"); + goto cleanupDevInfo; + } + if (!SetupDiBuildDriverInfoList(DevInfo, &DevInfoData, SPDIT_COMPATDRIVER)) + { + LastError = LOG_LAST_ERROR(L"Failed building adapter driver info list"); + goto cleanupDevInfo; + } + SP_DRVINFO_DATA_W DrvInfoData = { .cbSize = sizeof(SP_DRVINFO_DATA_W) }; + if (!SetupDiEnumDriverInfoW(DevInfo, &DevInfoData, SPDIT_COMPATDRIVER, 0, &DrvInfoData) || + !SetupDiSetSelectedDriverW(DevInfo, &DevInfoData, &DrvInfoData)) + { + LastError = LOG_ERROR(ERROR_DRIVER_INSTALL_BLOCKED, L"Failed to select a driver"); + goto cleanupDriverInfo; + } + + if (!SetupDiCallClassInstaller(DIF_REGISTERDEVICE, DevInfo, &DevInfoData)) + { + LastError = LOG_LAST_ERROR(L"Failed to register adapter device"); + goto cleanupDevInfo; + } + if (!SetupDiCallClassInstaller(DIF_REGISTER_COINSTALLERS, DevInfo, &DevInfoData)) + LOG_LAST_ERROR(L"Failed to register adapter coinstallers"); + if (!SetupDiCallClassInstaller(DIF_INSTALLINTERFACES, DevInfo, &DevInfoData)) + LOG_LAST_ERROR(L"Failed to install adapter interfaces"); + if (!SetupDiCallClassInstaller(DIF_INSTALLDEVICE, DevInfo, &DevInfoData)) + { + LastError = LOG_LAST_ERROR(L"Failed to install adapter device"); + goto cleanupDevice; + } + +#ifdef MAYBE_WOW64 +resumeAfterInstance:; +#endif + + OWNING_PROCESS OwningProcess = { .ProcessId = GetCurrentProcessId() }; + FILETIME Unused; + if (!GetProcessTimes(GetCurrentProcess(), &OwningProcess.CreationTime, &Unused, &Unused, &Unused)) + { + LastError = LOG_LAST_ERROR(L"Failed to get process creation time"); + goto cleanupDevice; + } + + if (!SetupDiSetDeviceRegistryPropertyW( + DevInfo, + &DevInfoData, + SPDRP_FRIENDLYNAME, + (PBYTE)TunnelTypeName, + (DWORD)((wcslen(TunnelTypeName) + 1) * sizeof(TunnelTypeName[0]))) || + !SetupDiSetDeviceRegistryPropertyW( + DevInfo, + &DevInfoData, + SPDRP_DEVICEDESC, + (PBYTE)TunnelTypeName, + (DWORD)((wcslen(TunnelTypeName) + 1) * sizeof(TunnelTypeName[0]))) || + !SetupDiSetDevicePropertyW( + DevInfo, + &DevInfoData, + &DEVPKEY_Wintun_Name, + DEVPROP_TYPE_STRING, + (PBYTE)Name, + (DWORD)((wcslen(Name) + 1) * sizeof(Name[0])), + 0) || + !SetupDiSetDevicePropertyW( + DevInfo, + &DevInfoData, + &DEVPKEY_Wintun_OwningProcess, + DEVPROP_TYPE_BINARY, + (PBYTE)&OwningProcess, + sizeof(OwningProcess), + 0)) + { + LastError = LOG_LAST_ERROR(L"Failed to set device properties"); + goto cleanupDevice; + } + + DWORD RequiredChars = _countof(Adapter->DevInstanceID); + if (!SetupDiGetDeviceInstanceIdW(DevInfo, &DevInfoData, Adapter->DevInstanceID, RequiredChars, &RequiredChars)) + { + LastError = LOG_LAST_ERROR(L"Failed to get adapter instance ID"); + goto cleanupDevice; + } + + if (!WaitForInterfaceWin7(DevInfo, &DevInfoData, Adapter->DevInstanceID)) + { + DEVPROPTYPE PropertyType = 0; + INT32 ProblemCode = 0; + if (!SetupDiGetDevicePropertyW( + DevInfo, + &DevInfoData, + &DEVPKEY_Device_ProblemCode, + &PropertyType, + (PBYTE)&ProblemCode, + sizeof(ProblemCode), + NULL, + 0) || + (PropertyType != DEVPROP_TYPE_INT32 && PropertyType != DEVPROP_TYPE_UINT32)) + ProblemCode = 0; + LastError = LOG_ERROR( + ERROR_DEVICE_REINITIALIZATION_NEEDED, L"Failed to setup adapter (problem code: 0x%x)", ProblemCode); + goto cleanupDevice; + } + +cleanupDevice: + if (LastError != ERROR_SUCCESS) + AdapterRemoveInstance(DevInfo, &DevInfoData); +cleanupDriverInfo: + SetupDiDestroyDriverInfoList(DevInfo, &DevInfoData, SPDIT_COMPATDRIVER); +cleanupDevInfo: + SetupDiDestroyDeviceInfoList(DevInfo); +cleanup: + return RET_ERROR(TRUE, LastError); +} + +static VOID +CreateAdapterPostWin7(_Inout_ WINTUN_ADAPTER *Adapter, _In_z_ LPCWSTR TunnelTypeName) +{ + SetupDiSetDeviceRegistryPropertyW( + Adapter->DevInfo, + &Adapter->DevInfoData, + SPDRP_FRIENDLYNAME, + (PBYTE)TunnelTypeName, + (DWORD)((wcslen(TunnelTypeName) + 1) * sizeof(TunnelTypeName[0]))); + SetupDiSetDeviceRegistryPropertyW( + Adapter->DevInfo, + &Adapter->DevInfoData, + SPDRP_DEVICEDESC, + (PBYTE)TunnelTypeName, + (DWORD)((wcslen(TunnelTypeName) + 1) * sizeof(TunnelTypeName[0]))); +} + +static BOOL +ProcessIsStale(_In_ OWNING_PROCESS *OwningProcess) +{ + HANDLE Process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, OwningProcess->ProcessId); + if (!Process) + return TRUE; + FILETIME CreationTime, Unused; + BOOL Ret = GetProcessTimes(Process, &CreationTime, &Unused, &Unused, &Unused); + CloseHandle(Process); + if (!Ret) + return FALSE; + return !!memcmp(&CreationTime, &OwningProcess->CreationTime, sizeof(CreationTime)); +} + +VOID AdapterCleanupOrphanedDevicesWin7(VOID) +{ + HDEVINFO DevInfo = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, WINTUN_ENUMERATOR, NULL, 0, NULL, NULL, NULL); + if (DevInfo == INVALID_HANDLE_VALUE) + { + if (GetLastError() != ERROR_INVALID_DATA) + LOG_LAST_ERROR(L"Failed to get adapters"); + return; + } + + SP_DEVINFO_DATA DevInfoData = { .cbSize = sizeof(DevInfoData) }; + for (DWORD EnumIndex = 0;; ++EnumIndex) + { + if (!SetupDiEnumDeviceInfo(DevInfo, EnumIndex, &DevInfoData)) + { + if (GetLastError() == ERROR_NO_MORE_ITEMS) + break; + continue; + } + + OWNING_PROCESS OwningProcess; + DEVPROPTYPE PropType; + if (SetupDiGetDevicePropertyW( + DevInfo, + &DevInfoData, + &DEVPKEY_Wintun_OwningProcess, + &PropType, + (PBYTE)&OwningProcess, + sizeof(OwningProcess), + NULL, + 0) && + PropType == DEVPROP_TYPE_BINARY && !ProcessIsStale(&OwningProcess)) + continue; + + WCHAR Name[MAX_ADAPTER_NAME] = L""; + SetupDiGetDevicePropertyW( + DevInfo, + &DevInfoData, + &DEVPKEY_Wintun_Name, + &PropType, + (PBYTE)Name, + MAX_ADAPTER_NAME * sizeof(Name[0]), + NULL, + 0); + if (!AdapterRemoveInstance(DevInfo, &DevInfoData)) + { + LOG_LAST_ERROR(L"Failed to remove orphaned adapter \"%s\"", Name); + continue; + } + LOG(WINTUN_LOG_INFO, L"Removed orphaned adapter \"%s\"", Name); + } + SetupDiDestroyDeviceInfoList(DevInfo); +} + +VOID AdapterCleanupLegacyDevices(VOID) +{ + HDEVINFO DevInfo = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, L"ROOT\\NET", NULL, 0, NULL, NULL, NULL); + if (DevInfo == INVALID_HANDLE_VALUE) + return; + SP_DEVINFO_DATA DevInfoData = { .cbSize = sizeof(DevInfoData) }; + for (DWORD EnumIndex = 0;; ++EnumIndex) + { + if (!SetupDiEnumDeviceInfo(DevInfo, EnumIndex, &DevInfoData)) + { + if (GetLastError() == ERROR_NO_MORE_ITEMS) + break; + continue; + } + WCHAR HardwareIDs[0x400] = { 0 }; + DWORD ValueType, Size = sizeof(HardwareIDs) - sizeof(HardwareIDs[0]); + if (!SetupDiGetDeviceRegistryPropertyW( + DevInfo, &DevInfoData, SPDRP_HARDWAREID, &ValueType, (PBYTE)HardwareIDs, Size, &Size) || + Size > sizeof(HardwareIDs) - sizeof(HardwareIDs[0])) + continue; + Size /= sizeof(HardwareIDs[0]); + for (WCHAR *P = HardwareIDs; P < HardwareIDs + Size; P += wcslen(P) + 1) + { + if (!_wcsicmp(P, WINTUN_HWID)) + { + AdapterRemoveInstance(DevInfo, &DevInfoData); + break; + } + } + } + SetupDiDestroyDeviceInfoList(DevInfo); +} \ No newline at end of file diff --git a/api/api.vcxproj b/api/api.vcxproj index 2155ef5..d0d0dcb 100644 --- a/api/api.vcxproj +++ b/api/api.vcxproj @@ -18,24 +18,25 @@ _WINDOWS;_USRDLL;%(PreprocessorDefinitions) MAYBE_WOW64;%(PreprocessorDefinitions) - ACCEPT_WOW64;MAYBE_WOW64;%(PreprocessorDefinitions) + MAYBE_WOW64;%(PreprocessorDefinitions) MAYBE_WOW64;%(PreprocessorDefinitions) - ACCEPT_WOW64;%(PreprocessorDefinitions) + %(PreprocessorDefinitions) /volatile:iso %(AdditionalOptions) 4100;4201;$(DisableSpecificWarnings) $(IntDir);%(AdditionalIncludeDirectories) ..\$(Configuration)\$(WintunPlatform);..\$(Configuration);%(AdditionalIncludeDirectories) - BUILT_ARM64_WOW64;%(PreprocessorDefinitions) - BUILT_AMD64_WOW64;%(PreprocessorDefinitions) + BUILT_ARM64_WOW64;%(PreprocessorDefinitions) + BUILT_AMD64_WOW64;%(PreprocessorDefinitions) WANT_ARM64_WOW64;WANT_AMD64_WOW64;%(PreprocessorDefinitions) WANT_ARM64_WOW64;%(PreprocessorDefinitions) WANT_ARM64_WOW64;%(PreprocessorDefinitions) - advapi32.dll;bcrypt.dll;cfgmgr32.dll;iphlpapi.dll;ole32.dll;nci.dll;setupapi.dll;shell32.dll;shlwapi.dll;version.dll - Bcrypt.lib;Cfgmgr32.lib;Iphlpapi.lib;$(IntDir)nci.lib;ntdll.lib;Setupapi.lib;shlwapi.lib;version.lib;%(AdditionalDependencies) + advapi32.dll;api-ms-win-devices-query-l1-1-0.dll;api-ms-win-devices-swdevice-l1-1-0.dll;cfgmgr32.dll;iphlpapi.dll;ole32.dll;nci.dll;setupapi.dll;shlwapi.dll;version.dll + shell32.dll;%(DelayLoadDLLs) + Cfgmgr32.lib;Iphlpapi.lib;onecore.lib;$(IntDir)nci.lib;ntdll.lib;Setupapi.lib;shlwapi.lib;swdevice.lib;version.lib;%(AdditionalDependencies) exports.def Windows @@ -48,8 +49,10 @@ + + @@ -62,6 +65,7 @@ + @@ -88,4 +92,4 @@ - \ No newline at end of file + diff --git a/api/api.vcxproj.filters b/api/api.vcxproj.filters index 1a31cf2..5fb8b10 100644 --- a/api/api.vcxproj.filters +++ b/api/api.vcxproj.filters @@ -58,6 +58,12 @@ Header Files + + Header Files + + + Header Files + @@ -66,9 +72,6 @@ Source Files - - Source Files - Source Files @@ -84,5 +87,11 @@ Source Files + + Source Files + + + Source Files + - \ No newline at end of file + diff --git a/api/driver.c b/api/driver.c new file mode 100644 index 0000000..3fb4909 --- /dev/null +++ b/api/driver.c @@ -0,0 +1,587 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "driver.h" +#include "adapter.h" +#include "logger.h" +#include "namespace.h" +#include "resource.h" +#include "registry.h" +#include "ntdll.h" +#include "rundll32.h" +#include "wintun-inf.h" + +#pragma warning(disable : 4221) /* nonstandard: address of automatic in initializer */ + +struct _SP_DEVINFO_DATA_LIST +{ + SP_DEVINFO_DATA Data; + struct _SP_DEVINFO_DATA_LIST *Next; +}; + +static _Return_type_success_(return != FALSE) +BOOL +DisableAllOurAdapters(_In_ HDEVINFO DevInfo, _Inout_ SP_DEVINFO_DATA_LIST **DisabledAdapters) +{ + DWORD LastError = ERROR_SUCCESS; + for (DWORD EnumIndex = 0;; ++EnumIndex) + { + SP_DEVINFO_DATA_LIST *DeviceNode = Zalloc(sizeof(*DeviceNode)); + if (!DeviceNode) + return FALSE; + DeviceNode->Data.cbSize = sizeof(SP_DEVINFO_DATA); + if (!SetupDiEnumDeviceInfo(DevInfo, EnumIndex, &DeviceNode->Data)) + { + if (GetLastError() == ERROR_NO_MORE_ITEMS) + { + Free(DeviceNode); + break; + } + goto cleanupDeviceNode; + } + + DEVPROPTYPE PropType; + WCHAR Name[MAX_ADAPTER_NAME] = L""; + SetupDiGetDevicePropertyW( + DevInfo, + &DeviceNode->Data, + &DEVPKEY_Wintun_Name, + &PropType, + (PBYTE)Name, + MAX_ADAPTER_NAME * sizeof(Name[0]), + NULL, + 0); + + ULONG Status, ProblemCode; + if (CM_Get_DevNode_Status(&Status, &ProblemCode, DeviceNode->Data.DevInst, 0) != CR_SUCCESS || + ((Status & DN_HAS_PROBLEM) && ProblemCode == CM_PROB_DISABLED)) + goto cleanupDeviceNode; + + LOG(WINTUN_LOG_INFO, L"Force closing adapter \"%s\" open handles", Name); + if (!AdapterForceCloseHandles(DevInfo, &DeviceNode->Data)) + LOG(WINTUN_LOG_WARN, L"Failed to force close adapter \"%s\" open handles", Name); + + LOG(WINTUN_LOG_INFO, L"Disabling adapter \"%s\"", Name); + if (!AdapterDisableInstance(DevInfo, &DeviceNode->Data)) + { + LOG_LAST_ERROR(L"Failed to disable adapter \"%s\"", Name); + LastError = LastError != ERROR_SUCCESS ? LastError : GetLastError(); + goto cleanupDeviceNode; + } + + DeviceNode->Next = *DisabledAdapters; + *DisabledAdapters = DeviceNode; + continue; + + cleanupDeviceNode: + Free(DeviceNode); + } + return RET_ERROR(TRUE, LastError); +} + +static _Return_type_success_(return != FALSE) +BOOL +EnableAllOurAdapters(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA_LIST *AdaptersToEnable) +{ + DWORD LastError = ERROR_SUCCESS; + for (SP_DEVINFO_DATA_LIST *DeviceNode = AdaptersToEnable; DeviceNode; DeviceNode = DeviceNode->Next) + { + DEVPROPTYPE PropType; + WCHAR Name[MAX_ADAPTER_NAME] = L""; + SetupDiGetDevicePropertyW( + DevInfo, + &DeviceNode->Data, + &DEVPKEY_Wintun_Name, + &PropType, + (PBYTE)Name, + MAX_ADAPTER_NAME * sizeof(Name[0]), + NULL, + 0); + + LOG(WINTUN_LOG_INFO, L"Enabling adapter \"%s\"", Name); + if (!AdapterEnableInstance(DevInfo, &DeviceNode->Data)) + { + LOG_LAST_ERROR(L"Failed to enable adapter \"%s\"", Name); + LastError = LastError != ERROR_SUCCESS ? LastError : GetLastError(); + } + } + return RET_ERROR(TRUE, LastError); +} + +static BOOL +IsNewer( + _In_ const FILETIME *DriverDate1, + _In_ DWORDLONG DriverVersion1, + _In_ const FILETIME *DriverDate2, + _In_ DWORDLONG DriverVersion2) +{ + if (DriverDate1->dwHighDateTime > DriverDate2->dwHighDateTime) + return TRUE; + if (DriverDate1->dwHighDateTime < DriverDate2->dwHighDateTime) + return FALSE; + + if (DriverDate1->dwLowDateTime > DriverDate2->dwLowDateTime) + return TRUE; + if (DriverDate1->dwLowDateTime < DriverDate2->dwLowDateTime) + return FALSE; + + if (DriverVersion1 > DriverVersion2) + return TRUE; + if (DriverVersion1 < DriverVersion2) + return FALSE; + + return FALSE; +} + +static _Return_type_success_(return != 0) +DWORD +VersionOfFile(_In_z_ LPCWSTR Filename) +{ + DWORD Zero; + DWORD Len = GetFileVersionInfoSizeW(Filename, &Zero); + if (!Len) + { + LOG_LAST_ERROR(L"Failed to query %s version info size", Filename); + return 0; + } + VOID *VersionInfo = Alloc(Len); + if (!VersionInfo) + return 0; + DWORD LastError = ERROR_SUCCESS, Version = 0; + VS_FIXEDFILEINFO *FixedInfo; + UINT FixedInfoLen = sizeof(*FixedInfo); + if (!GetFileVersionInfoW(Filename, 0, Len, VersionInfo)) + { + LastError = LOG_LAST_ERROR(L"Failed to get %s version info", Filename); + goto out; + } + if (!VerQueryValueW(VersionInfo, L"\\", &FixedInfo, &FixedInfoLen)) + { + LastError = LOG_LAST_ERROR(L"Failed to get %s version info root", Filename); + goto out; + } + Version = FixedInfo->dwFileVersionMS; + if (!Version) + { + LOG(WINTUN_LOG_WARN, L"Determined version of %s, but was v0.0, so returning failure", Filename); + LastError = ERROR_VERSION_PARSE_ERROR; + } +out: + Free(VersionInfo); + return RET_ERROR(Version, LastError); +} + +static DWORD WINAPI +MaybeGetRunningDriverVersion(BOOL ReturnOneIfRunningInsteadOfVersion) +{ + PRTL_PROCESS_MODULES Modules; + ULONG BufferSize = 128 * 1024; + for (;;) + { + Modules = Alloc(BufferSize); + if (!Modules) + return 0; + NTSTATUS Status = NtQuerySystemInformation(SystemModuleInformation, Modules, BufferSize, &BufferSize); + if (NT_SUCCESS(Status)) + break; + Free(Modules); + if (Status == STATUS_INFO_LENGTH_MISMATCH) + continue; + LOG(WINTUN_LOG_ERR, L"Failed to enumerate drivers (status: 0x%x)", Status); + SetLastError(RtlNtStatusToDosError(Status)); + return 0; + } + DWORD LastError = ERROR_SUCCESS, Version = 0; + for (ULONG i = Modules->NumberOfModules; i-- > 0;) + { + LPCSTR NtPath = (LPCSTR)Modules->Modules[i].FullPathName; + if (!_stricmp(&NtPath[Modules->Modules[i].OffsetToFileName], "wintun.sys")) + { + if (ReturnOneIfRunningInsteadOfVersion) + { + Version = 1; + goto cleanupModules; + } + WCHAR FilePath[MAX_PATH * 3 + 15]; + if (_snwprintf_s(FilePath, _countof(FilePath), _TRUNCATE, L"\\\\?\\GLOBALROOT%S", NtPath) == -1) + continue; + Version = VersionOfFile(FilePath); + if (!Version) + LastError = GetLastError(); + goto cleanupModules; + } + } + LastError = ERROR_FILE_NOT_FOUND; +cleanupModules: + Free(Modules); + return RET_ERROR(Version, LastError); +} + +_Use_decl_annotations_ +DWORD WINAPI WintunGetRunningDriverVersion(VOID) +{ + return MaybeGetRunningDriverVersion(FALSE); +} + +static BOOL EnsureWintunUnloaded(VOID) +{ + BOOL Loaded; + for (DWORD Tries = 0; Tries < 1500; ++Tries) + { + if (Tries) + Sleep(50); + Loaded = MaybeGetRunningDriverVersion(TRUE) != 0; + if (!Loaded) + break; + } + return !Loaded; +} + +_Use_decl_annotations_ +VOID +DriverInstallDeferredCleanup(HDEVINFO DevInfoExistingAdapters, SP_DEVINFO_DATA_LIST *ExistingAdapters) +{ + if (ExistingAdapters) + { + EnableAllOurAdapters(DevInfoExistingAdapters, ExistingAdapters); + while (ExistingAdapters) + { + SP_DEVINFO_DATA_LIST *Next = ExistingAdapters->Next; + Free(ExistingAdapters); + ExistingAdapters = Next; + } + } + if (DevInfoExistingAdapters != INVALID_HANDLE_VALUE) + SetupDiDestroyDeviceInfoList(DevInfoExistingAdapters); +} + +_Use_decl_annotations_ +BOOL +DriverInstall(HDEVINFO *DevInfoExistingAdaptersForCleanup, SP_DEVINFO_DATA_LIST **ExistingAdaptersForCleanup) +{ + static const FILETIME OurDriverDate = WINTUN_INF_FILETIME; + static const DWORDLONG OurDriverVersion = WINTUN_INF_VERSION; + HANDLE DriverInstallationLock = NamespaceTakeDriverInstallationMutex(); + if (!DriverInstallationLock) + { + LOG(WINTUN_LOG_ERR, L"Failed to take driver installation mutex"); + return FALSE; + } + DWORD LastError = ERROR_SUCCESS; + HDEVINFO DevInfo = SetupDiCreateDeviceInfoListExW(&GUID_DEVCLASS_NET, NULL, NULL, NULL); + if (DevInfo == INVALID_HANDLE_VALUE) + { + LastError = LOG_LAST_ERROR(L"Failed to create empty device information set"); + goto cleanupDriverInstallationLock; + } + SP_DEVINFO_DATA DevInfoData = { .cbSize = sizeof(DevInfoData) }; + if (!SetupDiCreateDeviceInfoW(DevInfo, WINTUN_HWID, &GUID_DEVCLASS_NET, NULL, NULL, DICD_GENERATE_ID, &DevInfoData)) + { + LastError = LOG_LAST_ERROR(L"Failed to create new device information element"); + goto cleanupDevInfo; + } + static const WCHAR Hwids[_countof(WINTUN_HWID) + 1 /*Multi-string terminator*/] = WINTUN_HWID; + if (!SetupDiSetDeviceRegistryPropertyW(DevInfo, &DevInfoData, SPDRP_HARDWAREID, (const BYTE *)Hwids, sizeof(Hwids))) + { + LastError = LOG_LAST_ERROR(L"Failed to set adapter hardware ID"); + goto cleanupDevInfo; + } + if (!SetupDiBuildDriverInfoList(DevInfo, &DevInfoData, SPDIT_COMPATDRIVER)) + { + LastError = LOG_LAST_ERROR(L"Failed building adapter driver info list"); + goto cleanupDevInfo; + } + FILETIME DriverDate = { 0 }; + DWORDLONG DriverVersion = 0; + HDEVINFO DevInfoExistingAdapters = INVALID_HANDLE_VALUE; + SP_DEVINFO_DATA_LIST *ExistingAdapters = NULL; + for (DWORD EnumIndex = 0;; ++EnumIndex) + { + SP_DRVINFO_DATA_W DrvInfoData = { .cbSize = sizeof(SP_DRVINFO_DATA_W) }; + if (!SetupDiEnumDriverInfoW(DevInfo, &DevInfoData, SPDIT_COMPATDRIVER, EnumIndex, &DrvInfoData)) + { + if (GetLastError() == ERROR_NO_MORE_ITEMS) + break; + continue; + } + if (IsNewer(&OurDriverDate, OurDriverVersion, &DrvInfoData.DriverDate, DrvInfoData.DriverVersion)) + { + if (DevInfoExistingAdapters == INVALID_HANDLE_VALUE) + { + DevInfoExistingAdapters = SetupDiGetClassDevsExW( + &GUID_DEVCLASS_NET, WINTUN_ENUMERATOR, NULL, DIGCF_PRESENT, NULL, NULL, NULL); + if (DevInfoExistingAdapters == INVALID_HANDLE_VALUE) + { + LastError = LOG_LAST_ERROR(L"Failed to get present adapters"); + SetupDiDestroyDriverInfoList(DevInfo, &DevInfoData, SPDIT_COMPATDRIVER); + goto cleanupExistingAdapters; + } + _Analysis_assume_(DevInfoExistingAdapters != NULL); + DisableAllOurAdapters(DevInfoExistingAdapters, &ExistingAdapters); + LOG(WINTUN_LOG_INFO, L"Waiting for existing driver to unload from kernel"); + if (!EnsureWintunUnloaded()) + LOG(WINTUN_LOG_WARN, + L"Failed to unload existing driver, which means a reboot will likely be required"); + } + LOG(WINTUN_LOG_INFO, + L"Removing existing driver %u.%u", + (DWORD)((DrvInfoData.DriverVersion & 0xffff000000000000) >> 48), + (DWORD)((DrvInfoData.DriverVersion & 0x0000ffff00000000) >> 32)); + BYTE LargeBuffer[0x2000]; + DWORD Size = sizeof(LargeBuffer); + SP_DRVINFO_DETAIL_DATA_W *DrvInfoDetailData = (SP_DRVINFO_DETAIL_DATA_W *)LargeBuffer; + DrvInfoDetailData->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA_W); + if (!SetupDiGetDriverInfoDetailW(DevInfo, &DevInfoData, &DrvInfoData, DrvInfoDetailData, Size, &Size)) + { + LOG(WINTUN_LOG_WARN, L"Failed getting adapter driver info detail"); + continue; + } + LPWSTR InfFileName = PathFindFileNameW(DrvInfoDetailData->InfFileName); + if (!SetupUninstallOEMInfW(InfFileName, SUOI_FORCEDELETE, NULL)) + LOG_LAST_ERROR(L"Unable to remove existing driver %s", InfFileName); + continue; + } + if (!IsNewer(&DrvInfoData.DriverDate, DrvInfoData.DriverVersion, &DriverDate, DriverVersion)) + continue; + DriverDate = DrvInfoData.DriverDate; + DriverVersion = DrvInfoData.DriverVersion; + } + SetupDiDestroyDriverInfoList(DevInfo, &DevInfoData, SPDIT_COMPATDRIVER); + + if (DriverVersion) + { + LOG(WINTUN_LOG_INFO, + L"Using existing driver %u.%u", + (DWORD)((DriverVersion & 0xffff000000000000) >> 48), + (DWORD)((DriverVersion & 0x0000ffff00000000) >> 32)); + LastError = ERROR_SUCCESS; + goto cleanupExistingAdapters; + } + + LOG(WINTUN_LOG_INFO, + L"Installing driver %u.%u", + (DWORD)((OurDriverVersion & 0xffff000000000000) >> 48), + (DWORD)((OurDriverVersion & 0x0000ffff00000000) >> 32)); + WCHAR RandomTempSubDirectory[MAX_PATH]; + if (!ResourceCreateTemporaryDirectory(RandomTempSubDirectory)) + { + LastError = LOG_LAST_ERROR(L"Failed to create temporary folder %s", RandomTempSubDirectory); + goto cleanupExistingAdapters; + } + + WCHAR CatPath[MAX_PATH] = { 0 }; + WCHAR SysPath[MAX_PATH] = { 0 }; + WCHAR InfPath[MAX_PATH] = { 0 }; + WCHAR DownlevelShimPath[MAX_PATH] = { 0 }; + if (!PathCombineW(CatPath, RandomTempSubDirectory, L"wintun.cat") || + !PathCombineW(SysPath, RandomTempSubDirectory, L"wintun.sys") || + !PathCombineW(InfPath, RandomTempSubDirectory, L"wintun.inf")) + { + LastError = ERROR_BUFFER_OVERFLOW; + goto cleanupDirectory; + } + + WCHAR *CatSource, *SysSource, *InfSource; + if (NativeMachine == IMAGE_FILE_PROCESS) + { + CatSource = L"wintun.cat"; + SysSource = L"wintun.sys"; + InfSource = L"wintun.inf"; + } + else if (NativeMachine == IMAGE_FILE_MACHINE_AMD64) + { + CatSource = L"wintun-amd64.cat"; + SysSource = L"wintun-amd64.sys"; + InfSource = L"wintun-amd64.inf"; + } + else if (NativeMachine == IMAGE_FILE_MACHINE_ARM64) + { + CatSource = L"wintun-arm64.cat"; + SysSource = L"wintun-arm64.sys"; + InfSource = L"wintun-arm64.inf"; + } + else + { + LastError = LOG_ERROR(ERROR_NOT_SUPPORTED, L"Unsupported platform 0x%x", NativeMachine); + goto cleanupDirectory; + } + + LOG(WINTUN_LOG_INFO, L"Extracting driver"); + if (!ResourceCopyToFile(CatPath, CatSource) || !ResourceCopyToFile(SysPath, SysSource) || + !ResourceCopyToFile(InfPath, InfSource)) + { + LastError = LOG_LAST_ERROR(L"Failed to extract driver"); + goto cleanupDelete; + } + + WCHAR *WintrustKeyOriginalValue = NULL; + HKEY WintrustKey = NULL; + if (!IsWindows10) + { + LOG(WINTUN_LOG_INFO, L"Shimming downlevel driver loader"); + if (!PathCombineW(DownlevelShimPath, RandomTempSubDirectory, L"downlevelshim.dll")) + { + DownlevelShimPath[0] = L'\0'; + LastError = ERROR_BUFFER_OVERFLOW; + goto cleanupDelete; + } + if (!ResourceCopyToFile(DownlevelShimPath, L"downlevelshim.dll")) + { + LastError = LOG_LAST_ERROR(L"Failed to extract downlevel shim"); + goto cleanupDelete; + } + LastError = RegOpenKeyExW( + HKEY_LOCAL_MACHINE, + L"SOFTWARE\\Microsoft\\Cryptography\\Providers\\Trust\\FinalPolicy\\{F750E6C3-38EE-11D1-85E5-00C04FC295EE}", + 0, + KEY_QUERY_VALUE | KEY_SET_VALUE, + &WintrustKey); + if (LastError != ERROR_SUCCESS) + { + LOG_ERROR(LastError, L"Failed to open Wintrust FinalPolicy key"); + goto cleanupDelete; + } + WintrustKeyOriginalValue = RegistryQueryString(WintrustKey, L"$DLL", TRUE); + if (!WintrustKeyOriginalValue) + { + LastError = LOG_LAST_ERROR(L"Failed to read current Wintrust FinalPolicy key"); + goto cleanupWintrustKey; + } + LastError = RegSetValueExW( + WintrustKey, + L"$DLL", + 0, + REG_SZ, + (BYTE *)DownlevelShimPath, + (DWORD)((wcslen(DownlevelShimPath) + 1) * sizeof(DownlevelShimPath[0]))); + if (LastError != ERROR_SUCCESS) + { + LOG_ERROR(LastError, L"Failed to set Wintrust FinalPolicy key"); + goto cleanupWintrustChangedKey; + } + } + + LOG(WINTUN_LOG_INFO, L"Installing driver"); + if (!SetupCopyOEMInfW(InfPath, NULL, SPOST_NONE, 0, NULL, 0, NULL, NULL)) + LastError = LOG_LAST_ERROR(L"Could not install driver %s to store", InfPath); + +cleanupWintrustChangedKey: + if (WintrustKeyOriginalValue) + RegSetValueExW( + WintrustKey, + L"$DLL", + 0, + REG_SZ, + (BYTE *)WintrustKeyOriginalValue, + (DWORD)((wcslen(WintrustKeyOriginalValue) + 1) * sizeof(WintrustKeyOriginalValue[0]))); +cleanupWintrustKey: + if (WintrustKey) + RegCloseKey(WintrustKey); + if (WintrustKeyOriginalValue) + Free(WintrustKeyOriginalValue); +cleanupDelete: + DeleteFileW(CatPath); + DeleteFileW(SysPath); + DeleteFileW(InfPath); + if (DownlevelShimPath[0]) + DeleteFileW(DownlevelShimPath); +cleanupDirectory: + RemoveDirectoryW(RandomTempSubDirectory); +cleanupExistingAdapters: + if (LastError == ERROR_SUCCESS) + { + *DevInfoExistingAdaptersForCleanup = DevInfoExistingAdapters; + *ExistingAdaptersForCleanup = ExistingAdapters; + } + else + DriverInstallDeferredCleanup(DevInfoExistingAdapters, ExistingAdapters); +cleanupDevInfo: + SetupDiDestroyDeviceInfoList(DevInfo); +cleanupDriverInstallationLock: + NamespaceReleaseMutex(DriverInstallationLock); + return RET_ERROR(TRUE, LastError); +} + +_Use_decl_annotations_ +BOOL WINAPI WintunDeleteDriver(VOID) +{ + DWORD LastError = ERROR_SUCCESS; + + AdapterCleanupOrphanedDevices(); + + HANDLE DriverInstallationLock = NamespaceTakeDriverInstallationMutex(); + if (!DriverInstallationLock) + { + LastError = LOG(WINTUN_LOG_ERR, L"Failed to take driver installation mutex"); + goto cleanup; + } + + HDEVINFO DevInfo = SetupDiCreateDeviceInfoListExW(&GUID_DEVCLASS_NET, NULL, NULL, NULL); + if (DevInfo == INVALID_HANDLE_VALUE) + { + LastError = LOG_LAST_ERROR(L"Failed to create empty device information set"); + goto cleanupDriverInstallationLock; + } + SP_DEVINFO_DATA DevInfoData = { .cbSize = sizeof(DevInfoData) }; + if (!SetupDiCreateDeviceInfoW(DevInfo, WINTUN_HWID, &GUID_DEVCLASS_NET, NULL, NULL, DICD_GENERATE_ID, &DevInfoData)) + { + LastError = LOG_LAST_ERROR(L"Failed to create new device information element"); + goto cleanupDevInfo; + } + static const WCHAR Hwids[_countof(WINTUN_HWID) + 1 /*Multi-string terminator*/] = WINTUN_HWID; + if (!SetupDiSetDeviceRegistryPropertyW(DevInfo, &DevInfoData, SPDRP_HARDWAREID, (const BYTE *)Hwids, sizeof(Hwids))) + { + LastError = LOG_LAST_ERROR(L"Failed to set adapter hardware ID"); + goto cleanupDevInfo; + } + if (!SetupDiBuildDriverInfoList(DevInfo, &DevInfoData, SPDIT_COMPATDRIVER)) + { + LastError = LOG_LAST_ERROR(L"Failed building adapter driver info list"); + goto cleanupDevInfo; + } + for (DWORD EnumIndex = 0;; ++EnumIndex) + { + SP_DRVINFO_DATA_W DrvInfoData = { .cbSize = sizeof(SP_DRVINFO_DATA_W) }; + if (!SetupDiEnumDriverInfoW(DevInfo, &DevInfoData, SPDIT_COMPATDRIVER, EnumIndex, &DrvInfoData)) + { + if (GetLastError() == ERROR_NO_MORE_ITEMS) + break; + continue; + } + BYTE LargeBuffer[0x2000]; + DWORD Size = sizeof(LargeBuffer); + SP_DRVINFO_DETAIL_DATA_W *DrvInfoDetailData = (SP_DRVINFO_DETAIL_DATA_W *)LargeBuffer; + DrvInfoDetailData->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA_W); + if (!SetupDiGetDriverInfoDetailW(DevInfo, &DevInfoData, &DrvInfoData, DrvInfoDetailData, Size, &Size)) + { + LOG(WINTUN_LOG_WARN, L"Failed getting adapter driver info detail"); + continue; + } + LPCWSTR Path = PathFindFileNameW(DrvInfoDetailData->InfFileName); + LOG(WINTUN_LOG_INFO, L"Removing driver %s", Path); + if (!SetupUninstallOEMInfW(Path, 0, NULL)) + { + LOG_LAST_ERROR(L"Unable to remove driver %s", Path); + LastError = LastError != ERROR_SUCCESS ? LastError : GetLastError(); + } + } + SetupDiDestroyDriverInfoList(DevInfo, &DevInfoData, SPDIT_COMPATDRIVER); +cleanupDevInfo: + SetupDiDestroyDeviceInfoList(DevInfo); +cleanupDriverInstallationLock: + NamespaceReleaseMutex(DriverInstallationLock); +cleanup: + return RET_ERROR(TRUE, LastError); +} diff --git a/api/driver.h b/api/driver.h new file mode 100644 index 0000000..dae4e9b --- /dev/null +++ b/api/driver.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved. + */ + +#pragma once + +#include "wintun.h" +#include +#include + +#define WINTUN_HWID L"Wintun" + +typedef struct _SP_DEVINFO_DATA_LIST SP_DEVINFO_DATA_LIST; + +VOID +DriverInstallDeferredCleanup(_In_ HDEVINFO DevInfoExistingAdapters, _In_opt_ SP_DEVINFO_DATA_LIST *ExistingAdapters); + +_Must_inspect_result_ +_Return_type_success_(return != FALSE) +BOOL +DriverInstall( + _Out_ HDEVINFO *DevInfoExistingAdaptersForCleanup, + _Out_ SP_DEVINFO_DATA_LIST **ExistingAdaptersForCleanup); + +/** + * @copydoc WINTUN_DELETE_DRIVER_FUNC + */ +WINTUN_DELETE_DRIVER_FUNC WintunDeleteDriver; + +/** + * @copydoc WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC + */ +WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC WintunGetRunningDriverVersion; diff --git a/api/exports.def b/api/exports.def index 3539ad8..c12db57 100644 --- a/api/exports.def +++ b/api/exports.def @@ -2,19 +2,15 @@ LIBRARY wintun.dll EXPORTS WintunAllocateSendPacket WintunCreateAdapter - WintunDeleteAdapter - WintunDeletePoolDriver WintunEndSession - WintunEnumAdapters - WintunFreeAdapter WintunOpenAdapter + WintunCloseAdapter WintunGetAdapterLUID - WintunGetAdapterName WintunGetReadWaitEvent WintunGetRunningDriverVersion WintunReceivePacket WintunReleaseReceivePacket WintunSendPacket - WintunSetAdapterName + WintunDeleteDriver WintunSetLogger WintunStartSession diff --git a/api/logger.c b/api/logger.c index 71dff5b..94ce525 100644 --- a/api/logger.c +++ b/api/logger.c @@ -7,18 +7,26 @@ #include "adapter.h" #include "ntdll.h" #include +#include #include #include #include static BOOL CALLBACK -NopLogger(_In_ WINTUN_LOGGER_LEVEL Level, _In_z_ LPCWSTR LogLine) +NopLogger(_In_ WINTUN_LOGGER_LEVEL Level, _In_ DWORD64 Timestamp, _In_z_ LPCWSTR LogLine) { return TRUE; } WINTUN_LOGGER_CALLBACK Logger = NopLogger; +static DWORD64 Now(VOID) +{ + LARGE_INTEGER Timestamp; + NtQuerySystemTime(&Timestamp); + return Timestamp.QuadPart; +} + _Use_decl_annotations_ VOID WINAPI WintunSetLogger(WINTUN_LOGGER_CALLBACK NewLogger) @@ -37,41 +45,30 @@ StrTruncate(_Inout_count_(StrChars) LPWSTR Str, _In_ SIZE_T StrChars) _Use_decl_annotations_ DWORD -LoggerLog(WINTUN_LOGGER_LEVEL Level, LPCWSTR Function, LPCWSTR LogLine) +LoggerLog(WINTUN_LOGGER_LEVEL Level, LPCWSTR LogLine) { DWORD LastError = GetLastError(); - if (Function) - { - WCHAR Combined[0x400]; - if (_snwprintf_s(Combined, _countof(Combined), _TRUNCATE, L"%s: %s", Function, LogLine) == -1) - StrTruncate(Combined, _countof(Combined)); - Logger(Level, Combined); - } - else - Logger(Level, LogLine); + Logger(Level, Now(), LogLine); SetLastError(LastError); return LastError; } _Use_decl_annotations_ DWORD -LoggerLogV(WINTUN_LOGGER_LEVEL Level, LPCWSTR Function, LPCWSTR Format, va_list Args) +LoggerLogV(WINTUN_LOGGER_LEVEL Level, LPCWSTR Format, va_list Args) { DWORD LastError = GetLastError(); WCHAR LogLine[0x400]; if (_vsnwprintf_s(LogLine, _countof(LogLine), _TRUNCATE, Format, Args) == -1) StrTruncate(LogLine, _countof(LogLine)); - if (Function) - LoggerLog(Level, Function, LogLine); - else - Logger(Level, LogLine); + Logger(Level, Now(), LogLine); SetLastError(LastError); return LastError; } _Use_decl_annotations_ DWORD -LoggerError(DWORD Error, LPCWSTR Function, LPCWSTR Prefix) +LoggerError(DWORD Error, LPCWSTR Prefix) { LPWSTR SystemMessage = NULL, FormattedMessage = NULL; FormatMessageW( @@ -85,14 +82,14 @@ LoggerError(DWORD Error, LPCWSTR Function, LPCWSTR Prefix) FormatMessageW( FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_MAX_WIDTH_MASK, - SystemMessage ? L"%4: %1: %3(Code 0x%2!08X!)" : L"%4: %1: Code 0x%2!08X!", + SystemMessage ? L"%1: %3(Code 0x%2!08X!)" : L"%1: Code 0x%2!08X!", 0, 0, (VOID *)&FormattedMessage, 0, - (va_list *)(DWORD_PTR[]){ (DWORD_PTR)Prefix, (DWORD_PTR)Error, (DWORD_PTR)SystemMessage, (DWORD_PTR)Function }); + (va_list *)(DWORD_PTR[]){ (DWORD_PTR)Prefix, (DWORD_PTR)Error, (DWORD_PTR)SystemMessage }); if (FormattedMessage) - Logger(WINTUN_LOG_ERR, FormattedMessage); + Logger(WINTUN_LOG_ERR, Now(), FormattedMessage); LocalFree(FormattedMessage); LocalFree(SystemMessage); return Error; @@ -100,12 +97,12 @@ LoggerError(DWORD Error, LPCWSTR Function, LPCWSTR Prefix) _Use_decl_annotations_ DWORD -LoggerErrorV(DWORD Error, LPCWSTR Function, LPCWSTR Format, va_list Args) +LoggerErrorV(DWORD Error, LPCWSTR Format, va_list Args) { - WCHAR Prefix[0x400]; - if (_vsnwprintf_s(Prefix, _countof(Prefix), _TRUNCATE, Format, Args) == -1) - StrTruncate(Prefix, _countof(Prefix)); - return LoggerError(Error, Function, Prefix); + WCHAR LogLine[0x400]; + if (_vsnwprintf_s(LogLine, _countof(LogLine), _TRUNCATE, Format, Args) == -1) + StrTruncate(LogLine, _countof(LogLine)); + return LoggerError(Error, LogLine); } _Use_decl_annotations_ diff --git a/api/logger.h b/api/logger.h index 72853cc..d83839e 100644 --- a/api/logger.h +++ b/api/logger.h @@ -18,71 +18,63 @@ extern WINTUN_LOGGER_CALLBACK Logger; /** * @copydoc WINTUN_SET_LOGGER_FUNC */ -WINTUN_SET_LOGGER_FUNC_IMPL WintunSetLogger; +WINTUN_SET_LOGGER_FUNC WintunSetLogger; _Post_equals_last_error_ DWORD -LoggerLog(_In_ WINTUN_LOGGER_LEVEL Level, _In_z_ LPCWSTR Function, _In_z_ LPCWSTR LogLine); +LoggerLog(_In_ WINTUN_LOGGER_LEVEL Level, _In_z_ LPCWSTR LogLine); _Post_equals_last_error_ DWORD -LoggerLogV( - _In_ WINTUN_LOGGER_LEVEL Level, - _In_z_ LPCWSTR Function, - _In_z_ _Printf_format_string_ LPCWSTR Format, - _In_ va_list Args); +LoggerLogV(_In_ WINTUN_LOGGER_LEVEL Level, _In_z_ _Printf_format_string_ LPCWSTR Format, _In_ va_list Args); _Post_equals_last_error_ static inline DWORD -LoggerLogFmt(_In_ WINTUN_LOGGER_LEVEL Level, _In_z_ LPCWSTR Function, _In_z_ _Printf_format_string_ LPCWSTR Format, ...) +LoggerLogFmt(_In_ WINTUN_LOGGER_LEVEL Level, _In_z_ _Printf_format_string_ LPCWSTR Format, ...) { va_list Args; va_start(Args, Format); - DWORD LastError = LoggerLogV(Level, Function, Format, Args); + DWORD LastError = LoggerLogV(Level, Format, Args); va_end(Args); return LastError; } _Post_equals_last_error_ DWORD -LoggerError(_In_ DWORD Error, _In_z_ LPCWSTR Function, _In_z_ LPCWSTR Prefix); +LoggerError(_In_ DWORD Error, _In_z_ LPCWSTR Prefix); _Post_equals_last_error_ DWORD -LoggerErrorV( - _In_ DWORD Error, - _In_z_ LPCWSTR Function, - _In_z_ _Printf_format_string_ LPCWSTR Format, - _In_ va_list Args); +LoggerErrorV(_In_ DWORD Error, _In_z_ _Printf_format_string_ LPCWSTR Format, _In_ va_list Args); _Post_equals_last_error_ static inline DWORD -LoggerErrorFmt(_In_ DWORD Error, _In_z_ LPCWSTR Function, _In_z_ _Printf_format_string_ LPCWSTR Format, ...) +LoggerErrorFmt(_In_ DWORD Error, _In_z_ _Printf_format_string_ LPCWSTR Format, ...) { va_list Args; va_start(Args, Format); - DWORD LastError = LoggerErrorV(Error, Function, Format, Args); + DWORD LastError = LoggerErrorV(Error, Format, Args); va_end(Args); return LastError; } _Post_equals_last_error_ static inline DWORD -LoggerLastErrorV(_In_z_ LPCWSTR Function, _In_z_ _Printf_format_string_ LPCWSTR Format, _In_ va_list Args) +LoggerLastErrorV(_In_z_ _Printf_format_string_ LPCWSTR Format, _In_ va_list Args) { DWORD LastError = GetLastError(); - LoggerErrorV(LastError, Function, Format, Args); + LoggerErrorV(LastError, Format, Args); SetLastError(LastError); return LastError; } _Post_equals_last_error_ static inline DWORD -LoggerLastErrorFmt(_In_z_ LPCWSTR Function, _In_z_ _Printf_format_string_ LPCWSTR Format, ...) +LoggerLastErrorFmt(_In_z_ _Printf_format_string_ LPCWSTR Format, ...) { va_list Args; va_start(Args, Format); - DWORD LastError = LoggerLastErrorV(Function, Format, Args); + DWORD LastError = LoggerLastErrorV(Format, Args); va_end(Args); return LastError; } @@ -90,11 +82,9 @@ LoggerLastErrorFmt(_In_z_ LPCWSTR Function, _In_z_ _Printf_format_string_ LPCWST VOID LoggerGetRegistryKeyPath(_In_ HKEY Key, _Out_writes_z_(MAX_REG_PATH) LPWSTR Path); -#define __L(x) L##x -#define _L(x) __L(x) -#define LOG(lvl, msg, ...) (LoggerLogFmt((lvl), _L(__FUNCTION__), msg, __VA_ARGS__)) -#define LOG_ERROR(err, msg, ...) (LoggerErrorFmt((err), _L(__FUNCTION__), msg, __VA_ARGS__)) -#define LOG_LAST_ERROR(msg, ...) (LoggerLastErrorFmt(_L(__FUNCTION__), msg, __VA_ARGS__)) +#define LOG(lvl, msg, ...) (LoggerLogFmt((lvl), msg, __VA_ARGS__)) +#define LOG_ERROR(err, msg, ...) (LoggerErrorFmt((err), msg, __VA_ARGS__)) +#define LOG_LAST_ERROR(msg, ...) (LoggerLastErrorFmt(msg, __VA_ARGS__)) #define RET_ERROR(Ret, Error) ((Error) == ERROR_SUCCESS ? (Ret) : (SetLastError(Error), 0)) @@ -130,6 +120,9 @@ LoggerReAlloc(_In_z_ LPCWSTR Function, _In_ DWORD Flags, _Frees_ptr_opt_ LPVOID } return Data; } + +#define __L(x) L##x +#define _L(x) __L(x) #define Alloc(Size) LoggerAlloc(_L(__FUNCTION__), 0, Size) #define ReAlloc(Mem, Size) LoggerReAlloc(_L(__FUNCTION__), 0, Mem, Size) #define Zalloc(Size) LoggerAlloc(_L(__FUNCTION__), HEAP_ZERO_MEMORY, Size) @@ -179,4 +172,4 @@ Free(_Frees_ptr_opt_ VOID *Ptr) DWORD LastError = GetLastError(); HeapFree(ModuleHeap, 0, Ptr); SetLastError(LastError); -} \ No newline at end of file +} diff --git a/api/main.c b/api/main.c index 294f35d..28937fa 100644 --- a/api/main.c +++ b/api/main.c @@ -21,7 +21,13 @@ HANDLE ModuleHeap; SECURITY_ATTRIBUTES SecurityAttributes = { .nLength = sizeof(SECURITY_ATTRIBUTES) }; BOOL IsLocalSystem; USHORT NativeMachine = IMAGE_FILE_PROCESS; + +#if NTDDI_VERSION == NTDDI_WIN7 +BOOL IsWindows7; +#endif +#if NTDDI_VERSION < NTDDI_WIN10 BOOL IsWindows10; +#endif static FARPROC WINAPI DelayedLoadLibraryHook(unsigned dliNotify, PDelayLoadInfo pdli) @@ -70,11 +76,17 @@ cleanupProcessToken: return Ret; } -static VOID EnvInit(VOID) +static void EnvInit(VOID) { - DWORD MajorVersion; - RtlGetNtVersionNumbers(&MajorVersion, NULL, NULL); + DWORD MajorVersion, MinorVersion; + RtlGetNtVersionNumbers(&MajorVersion, &MinorVersion, NULL); + +#if NTDDI_VERSION == NTDDI_WIN7 + IsWindows7 = MajorVersion == 6 && MinorVersion == 1; +#endif +#if NTDDI_VERSION < NTDDI_WIN10 IsWindows10 = MajorVersion >= 10; +#endif #ifdef MAYBE_WOW64 typedef BOOL(WINAPI * IsWow64Process2_t)( @@ -110,6 +122,7 @@ DllMain(_In_ HINSTANCE hinstDLL, _In_ DWORD fdwReason, _In_ LPVOID lpvReserved) } EnvInit(); NamespaceInit(); + AdapterCleanupLegacyDevices(); break; case DLL_PROCESS_DETACH: diff --git a/api/main.h b/api/main.h index 5d3ebb1..04ba7dd 100644 --- a/api/main.h +++ b/api/main.h @@ -24,4 +24,15 @@ extern HANDLE ModuleHeap; extern SECURITY_ATTRIBUTES SecurityAttributes; extern BOOL IsLocalSystem; extern USHORT NativeMachine; -extern BOOL IsWindows10; \ No newline at end of file + +#if NTDDI_VERSION > NTDDI_WIN7 +# define IsWindows7 FALSE +#else +extern BOOL IsWindows7; +#endif + +#if NTDDI_VERSION >= NTDDI_WIN10 +# define IsWindows10 TRUE +#else +extern BOOL IsWindows10; +#endif \ No newline at end of file diff --git a/api/namespace.c b/api/namespace.c index 760dc6f..3248edb 100644 --- a/api/namespace.c +++ b/api/namespace.c @@ -9,7 +9,6 @@ #include #include -#include #include #include #include @@ -17,32 +16,6 @@ static HANDLE PrivateNamespace = NULL; static HANDLE BoundaryDescriptor = NULL; static CRITICAL_SECTION Initializing; -static BCRYPT_ALG_HANDLE AlgProvider; - -_Must_inspect_result_ -static _Return_type_success_(return != NULL) -_Post_maybenull_ -LPWSTR -NormalizeStringAlloc(_In_ NORM_FORM NormForm, _In_z_ LPCWSTR Source) -{ - int Len = NormalizeString(NormForm, Source, -1, NULL, 0); - for (;;) - { - LPWSTR Str = AllocArray(Len, sizeof(*Str)); - if (!Str) - return NULL; - Len = NormalizeString(NormForm, Source, -1, Str, Len); - if (Len > 0) - return Str; - Free(Str); - if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) - { - LOG_LAST_ERROR(L"Failed: %s", Source); - return NULL; - } - Len = -Len; - } -} static _Return_type_success_(return != FALSE) BOOL NamespaceRuntimeInit(VOID) @@ -56,27 +29,19 @@ BOOL NamespaceRuntimeInit(VOID) return TRUE; } - NTSTATUS Status; - if (!BCRYPT_SUCCESS(Status = BCryptOpenAlgorithmProvider(&AlgProvider, BCRYPT_SHA256_ALGORITHM, NULL, 0))) - { - LOG(WINTUN_LOG_ERR, L"Failed to open algorithm provider (status: 0x%x)", Status); - LastError = RtlNtStatusToDosError(Status); - goto cleanupLeaveCriticalSection; - } - BYTE Sid[MAX_SID_SIZE]; DWORD SidSize = sizeof(Sid); if (!CreateWellKnownSid(IsLocalSystem ? WinLocalSystemSid : WinBuiltinAdministratorsSid, NULL, Sid, &SidSize)) { LastError = LOG_LAST_ERROR(L"Failed to create SID"); - goto cleanupBCryptCloseAlgorithmProvider; + goto cleanupLeaveCriticalSection; } BoundaryDescriptor = CreateBoundaryDescriptorW(L"Wintun", 0); if (!BoundaryDescriptor) { LastError = LOG_LAST_ERROR(L"Failed to create boundary descriptor"); - goto cleanupBCryptCloseAlgorithmProvider; + goto cleanupLeaveCriticalSection; } if (!AddSIDToBoundaryDescriptor(&BoundaryDescriptor, Sid)) { @@ -106,89 +71,12 @@ BOOL NamespaceRuntimeInit(VOID) cleanupBoundaryDescriptor: DeleteBoundaryDescriptor(BoundaryDescriptor); -cleanupBCryptCloseAlgorithmProvider: - BCryptCloseAlgorithmProvider(AlgProvider, 0); cleanupLeaveCriticalSection: LeaveCriticalSection(&Initializing); SetLastError(LastError); return FALSE; } -_Use_decl_annotations_ -HANDLE -NamespaceTakePoolMutex(LPCWSTR Pool) -{ - if (!NamespaceRuntimeInit()) - return NULL; - - BCRYPT_HASH_HANDLE Sha256 = NULL; - NTSTATUS Status; - if (!BCRYPT_SUCCESS(Status = BCryptCreateHash(AlgProvider, &Sha256, NULL, 0, NULL, 0, 0))) - { - LOG(WINTUN_LOG_ERR, L"Failed to create hash (status: 0x%x)", Status); - SetLastError(RtlNtStatusToDosError(Status)); - return NULL; - } - DWORD LastError; - static const WCHAR mutex_label[] = L"Wintun Adapter Name Mutex Stable Suffix v1 jason@zx2c4.com"; - if (!BCRYPT_SUCCESS( - Status = BCryptHashData(Sha256, (PUCHAR)mutex_label, sizeof(mutex_label) /* Including NULL 2 bytes */, 0))) - { - LOG(WINTUN_LOG_ERR, L"Failed to hash data (status: 0x%x)", Status); - LastError = RtlNtStatusToDosError(Status); - goto cleanupSha256; - } - LPWSTR PoolNorm = NormalizeStringAlloc(NormalizationC, Pool); - if (!PoolNorm) - { - LastError = GetLastError(); - goto cleanupSha256; - } - if (!BCRYPT_SUCCESS( - Status = BCryptHashData(Sha256, (PUCHAR)PoolNorm, (int)wcslen(PoolNorm) + 2 /* Add in NULL 2 bytes */, 0))) - { - LOG(WINTUN_LOG_ERR, L"Failed to hash data (status: 0x%x)", Status); - LastError = RtlNtStatusToDosError(Status); - goto cleanupPoolNorm; - } - BYTE Hash[32]; - if (!BCRYPT_SUCCESS(Status = BCryptFinishHash(Sha256, Hash, sizeof(Hash), 0))) - { - LOG(WINTUN_LOG_ERR, L"Failed to calculate hash (status: 0x%x)", Status); - LastError = RtlNtStatusToDosError(Status); - goto cleanupPoolNorm; - } - static const WCHAR MutexNamePrefix[] = L"Wintun\\Wintun-Name-Mutex-"; - WCHAR MutexName[_countof(MutexNamePrefix) + sizeof(Hash) * 2]; - memcpy(MutexName, MutexNamePrefix, sizeof(MutexNamePrefix)); - for (size_t i = 0; i < sizeof(Hash); ++i) - swprintf_s(&MutexName[_countof(MutexNamePrefix) - 1 + i * 2], 3, L"%02x", Hash[i]); - HANDLE Mutex = CreateMutexW(&SecurityAttributes, FALSE, MutexName); - if (!Mutex) - { - LastError = LOG_LAST_ERROR(L"Failed to create mutex %s", MutexName); - goto cleanupPoolNorm; - } - DWORD Result = WaitForSingleObject(Mutex, INFINITE); - switch (Result) - { - case WAIT_OBJECT_0: - case WAIT_ABANDONED: - Free(PoolNorm); - BCryptDestroyHash(Sha256); - return Mutex; - } - LOG(WINTUN_LOG_ERR, L"Failed to get mutex %s (status: 0x%x)", MutexName, Result); - LastError = ERROR_GEN_FAILURE; - CloseHandle(Mutex); -cleanupPoolNorm: - Free(PoolNorm); -cleanupSha256: - BCryptDestroyHash(Sha256); - SetLastError(LastError); - return NULL; -} - _Use_decl_annotations_ HANDLE NamespaceTakeDriverInstallationMutex(VOID) @@ -214,6 +102,31 @@ NamespaceTakeDriverInstallationMutex(VOID) return NULL; } +_Use_decl_annotations_ +HANDLE +NamespaceTakeDeviceInstallationMutex(VOID) +{ + if (!NamespaceRuntimeInit()) + return NULL; + HANDLE Mutex = CreateMutexW(&SecurityAttributes, FALSE, L"Wintun\\Wintun-Device-Installation-Mutex"); + if (!Mutex) + { + LOG_LAST_ERROR(L"Failed to create mutex"); + return NULL; + } + DWORD Result = WaitForSingleObject(Mutex, INFINITE); + switch (Result) + { + case WAIT_OBJECT_0: + case WAIT_ABANDONED: + return Mutex; + } + LOG(WINTUN_LOG_ERR, L"Failed to get mutex (status: 0x%x)", Result); + CloseHandle(Mutex); + SetLastError(ERROR_GEN_FAILURE); + return NULL; +} + _Use_decl_annotations_ VOID NamespaceReleaseMutex(HANDLE Mutex) @@ -232,7 +145,6 @@ VOID NamespaceDone(VOID) EnterCriticalSection(&Initializing); if (PrivateNamespace) { - BCryptCloseAlgorithmProvider(AlgProvider, 0); ClosePrivateNamespace(PrivateNamespace, 0); DeleteBoundaryDescriptor(BoundaryDescriptor); PrivateNamespace = NULL; diff --git a/api/namespace.h b/api/namespace.h index cbd9100..a0397be 100644 --- a/api/namespace.h +++ b/api/namespace.h @@ -12,14 +12,14 @@ _Return_type_success_(return != NULL) _Post_maybenull_ _Acquires_lock_(_Curr_) HANDLE -NamespaceTakePoolMutex(_In_z_ LPCWSTR Pool); +NamespaceTakeDriverInstallationMutex(VOID); _Must_inspect_result_ _Return_type_success_(return != NULL) _Post_maybenull_ _Acquires_lock_(_Curr_) HANDLE -NamespaceTakeDriverInstallationMutex(VOID); +NamespaceTakeDeviceInstallationMutex(VOID); _Releases_lock_(Mutex) VOID diff --git a/api/ntdll.h b/api/ntdll.h index 3782a30..2eb2786 100644 --- a/api/ntdll.h +++ b/api/ntdll.h @@ -39,7 +39,6 @@ typedef struct _KEY_NAME_INFORMATION } KEY_NAME_INFORMATION, *PKEY_NAME_INFORMATION; #define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) // TODO: #include instead of this -#define STATUS_PNP_DEVICE_CONFIGURATION_PENDING ((NTSTATUS)0xC0000495L) /* We can't use RtlGetVersion, because appcompat's aclayers.dll shims it to report Vista * when run from legacy contexts. So, we instead use the undocumented RtlGetNtVersionNumbers. diff --git a/api/registry.c b/api/registry.c index d385d86..4f1001c 100644 --- a/api/registry.c +++ b/api/registry.c @@ -10,100 +10,6 @@ #include #include -_Must_inspect_result_ -static _Return_type_success_(return != NULL) -_Post_maybenull_ -HKEY -OpenKeyWait(_In_ HKEY Key, _Inout_z_ LPWSTR Path, _In_ DWORD Access, _In_ ULONGLONG Deadline) -{ - DWORD LastError; - LPWSTR PathNext = wcschr(Path, L'\\'); - if (PathNext) - *PathNext = 0; - - HANDLE Event = CreateEventW(NULL, FALSE, FALSE, NULL); - if (!Event) - { - LOG_LAST_ERROR(L"Failed to create event"); - return NULL; - } - for (;;) - { - LastError = RegNotifyChangeKeyValue(Key, FALSE, REG_NOTIFY_CHANGE_NAME, Event, TRUE); - if (LastError != ERROR_SUCCESS) - { - WCHAR RegPath[MAX_REG_PATH]; - LoggerGetRegistryKeyPath(Key, RegPath); - LOG_ERROR(LastError, L"Failed to setup registry key %.*s notification", MAX_REG_PATH, RegPath); - break; - } - - HKEY Subkey; - LastError = RegOpenKeyExW(Key, Path, 0, PathNext ? KEY_NOTIFY : Access, &Subkey); - if (LastError == ERROR_SUCCESS) - { - if (PathNext) - { - HKEY KeyOut = OpenKeyWait(Subkey, PathNext + 1, Access, Deadline); - if (KeyOut) - { - RegCloseKey(Subkey); - CloseHandle(Event); - return KeyOut; - } - LastError = GetLastError(); - break; - } - else - { - CloseHandle(Event); - return Subkey; - } - } - if (LastError != ERROR_FILE_NOT_FOUND && LastError != ERROR_PATH_NOT_FOUND) - { - WCHAR RegPath[MAX_REG_PATH]; - LoggerGetRegistryKeyPath(Key, RegPath); - LOG_ERROR(LastError, L"Failed to open registry key %.*s\\%s", MAX_REG_PATH, RegPath, Path); - break; - } - - LONGLONG TimeLeft = Deadline - GetTickCount64(); - if (TimeLeft < 0) - TimeLeft = 0; - DWORD Result = WaitForSingleObject(Event, (DWORD)TimeLeft); - if (Result != WAIT_OBJECT_0) - { - WCHAR RegPath[MAX_REG_PATH]; - LoggerGetRegistryKeyPath(Key, RegPath); - LOG(WINTUN_LOG_ERR, - L"Timeout waiting for registry key %.*s\\%s (status: 0x%x)", - MAX_REG_PATH, - RegPath, - Path, - Result); - break; - } - } - CloseHandle(Event); - SetLastError(LastError); - return NULL; -} - -_Use_decl_annotations_ -HKEY -RegistryOpenKeyWait(HKEY Key, LPCWSTR Path, DWORD Access, DWORD Timeout) -{ - WCHAR Buf[MAX_REG_PATH]; - if (wcsncpy_s(Buf, _countof(Buf), Path, _TRUNCATE) == STRUNCATE) - { - LOG(WINTUN_LOG_ERR, L"Registry path too long: %s", Path); - SetLastError(ERROR_INVALID_PARAMETER); - return NULL; - } - return OpenKeyWait(Key, Buf, Access, GetTickCount64() + Timeout); -} - _Use_decl_annotations_ BOOL RegistryGetString(LPWSTR *Buf, DWORD Len, DWORD ValueType) @@ -150,48 +56,6 @@ RegistryGetString(LPWSTR *Buf, DWORD Len, DWORD ValueType) } } -_Use_decl_annotations_ -BOOL -RegistryGetMultiString(PZZWSTR *Buf, DWORD Len, DWORD ValueType) -{ - if (ValueType == REG_MULTI_SZ) - { - for (size_t i = 0;; i += wcsnlen(*Buf + i, Len - i) + 1) - { - if (i > Len) - { - /* Missing string and list terminators. */ - PZZWSTR BufZ = ReZallocArray(*Buf, (SIZE_T)Len + 2, sizeof(*BufZ)); - if (!BufZ) - return FALSE; - *Buf = BufZ; - return TRUE; - } - if (i == Len) - { - /* Missing list terminator. */ - PZZWSTR BufZ = ReZallocArray(*Buf, (SIZE_T)Len + 1, sizeof(*BufZ)); - if (!BufZ) - return FALSE; - *Buf = BufZ; - return TRUE; - } - if (!(*Buf)[i]) - return TRUE; - } - } - - /* Sanitize REG_SZ/REG_EXPAND_SZ and append a list terminator to make a multi-string. */ - if (!RegistryGetString(Buf, Len, ValueType)) - return FALSE; - Len = (DWORD)wcslen(*Buf) + 1; - PZZWSTR BufZ = ReZallocArray(*Buf, (SIZE_T)Len + 1, sizeof(WCHAR)); - if (!BufZ) - return FALSE; - *Buf = BufZ; - return TRUE; -} - _Must_inspect_result_ static _Return_type_success_(return != NULL) _Post_maybenull_ @@ -256,59 +120,6 @@ RegistryQueryString(HKEY Key, LPCWSTR Name, BOOL Log) return NULL; } -_Use_decl_annotations_ -LPWSTR -RegistryQueryStringWait(HKEY Key, LPCWSTR Name, DWORD Timeout) -{ - DWORD LastError; - ULONGLONG Deadline = GetTickCount64() + Timeout; - HANDLE Event = CreateEventW(NULL, FALSE, FALSE, NULL); - if (!Event) - { - LOG_LAST_ERROR(L"Failed to create event"); - return NULL; - } - for (;;) - { - LastError = RegNotifyChangeKeyValue(Key, FALSE, REG_NOTIFY_CHANGE_LAST_SET, Event, TRUE); - if (LastError != ERROR_SUCCESS) - { - WCHAR RegPath[MAX_REG_PATH]; - LoggerGetRegistryKeyPath(Key, RegPath); - LOG_ERROR(LastError, L"Failed to setup registry key %.*s notification", MAX_REG_PATH, RegPath); - break; - } - LPWSTR Value = RegistryQueryString(Key, Name, FALSE); - if (Value) - { - CloseHandle(Event); - return Value; - } - LastError = GetLastError(); - if (LastError != ERROR_FILE_NOT_FOUND && LastError != ERROR_PATH_NOT_FOUND) - break; - LONGLONG TimeLeft = Deadline - GetTickCount64(); - if (TimeLeft < 0) - TimeLeft = 0; - DWORD Result = WaitForSingleObject(Event, (DWORD)TimeLeft); - if (Result != WAIT_OBJECT_0) - { - WCHAR RegPath[MAX_REG_PATH]; - LoggerGetRegistryKeyPath(Key, RegPath); - LOG(WINTUN_LOG_ERR, - L"Timeout waiting for registry value %.*s\\%s (status: 0x%x)", - MAX_REG_PATH, - RegPath, - Name, - Result); - break; - } - } - CloseHandle(Event); - SetLastError(LastError); - return NULL; -} - _Use_decl_annotations_ BOOL RegistryQueryDWORD(HKEY Key, LPCWSTR Name, DWORD *Value, BOOL Log) @@ -344,55 +155,3 @@ RegistryQueryDWORD(HKEY Key, LPCWSTR Name, DWORD *Value, BOOL Log) } return TRUE; } - -_Use_decl_annotations_ -BOOL -RegistryQueryDWORDWait(HKEY Key, LPCWSTR Name, DWORD Timeout, DWORD *Value) -{ - DWORD LastError; - ULONGLONG Deadline = GetTickCount64() + Timeout; - HANDLE Event = CreateEventW(NULL, FALSE, FALSE, NULL); - if (!Event) - { - LOG_LAST_ERROR(L"Failed to create event"); - return FALSE; - } - for (;;) - { - LastError = RegNotifyChangeKeyValue(Key, FALSE, REG_NOTIFY_CHANGE_LAST_SET, Event, TRUE); - if (LastError != ERROR_SUCCESS) - { - WCHAR RegPath[MAX_REG_PATH]; - LoggerGetRegistryKeyPath(Key, RegPath); - LOG_ERROR(LastError, L"Failed to setup registry key %.*s notification", MAX_REG_PATH, RegPath); - break; - } - if (RegistryQueryDWORD(Key, Name, Value, FALSE)) - { - CloseHandle(Event); - return TRUE; - } - LastError = GetLastError(); - if (LastError != ERROR_FILE_NOT_FOUND && LastError != ERROR_PATH_NOT_FOUND) - break; - LONGLONG TimeLeft = Deadline - GetTickCount64(); - if (TimeLeft < 0) - TimeLeft = 0; - DWORD Result = WaitForSingleObject(Event, (DWORD)TimeLeft); - if (Result != WAIT_OBJECT_0) - { - WCHAR RegPath[MAX_REG_PATH]; - LoggerGetRegistryKeyPath(Key, RegPath); - LOG(WINTUN_LOG_ERR, - L"Timeout waiting registry value %.*s\\%s (status: 0x%x)", - MAX_REG_PATH, - RegPath, - Name, - Result); - break; - } - } - CloseHandle(Event); - SetLastError(LastError); - return FALSE; -} diff --git a/api/registry.h b/api/registry.h index 7a366b0..6beed2f 100644 --- a/api/registry.h +++ b/api/registry.h @@ -12,26 +12,6 @@ 256 /* Maximum registry path length \ https://support.microsoft.com/en-us/help/256986/windows-registry-information-for-advanced-users */ -/** - * Opens the specified registry key. It waits for the registry key to become available. - * - * @param Key Handle of the parent registry key. Must be opened with notify access. - * - * @param Path Subpath of the registry key to open. Zero-terminated string of up to MAX_REG_PATH-1 characters. - * - * @param Access A mask that specifies the desired access rights to the key to be opened. - * - * @param Timeout Timeout to wait for the value in milliseconds. - * - * @return Key handle on success. If the function fails, the return value is zero. To get extended error information, - * call GetLastError. - */ -_Must_inspect_result_ -_Return_type_success_(return != NULL) -_Post_maybenull_ -HKEY -RegistryOpenKeyWait(_In_ HKEY Key, _In_z_ LPCWSTR Path, _In_ DWORD Access, _In_ DWORD Timeout); - /** * Validates and/or sanitizes string value read from registry. * @@ -52,25 +32,6 @@ _Return_type_success_(return != FALSE) BOOL RegistryGetString(_Inout_ LPWSTR *Buf, _In_ DWORD Len, _In_ DWORD ValueType); -/** - * Validates and/or sanitizes multi-string value read from registry. - * - * @param Buf On input, it contains a pointer to pointer where the data is stored. The data must be allocated - * using HeapAlloc(ModuleHeap, 0). On output, it contains a pointer to pointer where the sanitized - * data is stored. It must be released with HeapFree(ModuleHeap, 0, *Buf) after use. - * - * @param Len Length of data string in wide characters. - * - * @param ValueType Type of data. Must be one of REG_MULTI_SZ, REG_SZ or REG_EXPAND_SZ. - * - * @return If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To - * get extended error information, call GetLastError. - */ -_Must_inspect_result_ -_Return_type_success_(return != FALSE) -BOOL -RegistryGetMultiString(_Inout_ PZZWSTR *Buf, _In_ DWORD Len, _In_ DWORD ValueType); - /** * Reads string value from registry key. * @@ -96,27 +57,6 @@ _Post_maybenull_ LPWSTR RegistryQueryString(_In_ HKEY Key, _In_opt_z_ LPCWSTR Name, _In_ BOOL Log); -/** - * Reads string value from registry key. It waits for the registry value to become available. - * - * @param Key Handle of the registry key to read from. Must be opened with read and notify access. - * - * @param Name Name of the value to read. - * - * @param Timeout Timeout to wait for the value in milliseconds. - * - * @return Registry value. If the value type is REG_EXPAND_SZ the value is expanded using ExpandEnvironmentStrings(). If - * the value type is REG_MULTI_SZ, only the first string from the multi-string is returned. The string must be - * released with HeapFree(ModuleHeap, 0, Value) after use. If the function fails, the return value is zero. To - * get extended error information, call GetLastError. Possible errors include the following: - * ERROR_INVALID_DATATYPE when the registry value is not a string - */ -_Must_inspect_result_ -_Return_type_success_(return != NULL) -_Post_maybenull_ -LPWSTR -RegistryQueryStringWait(_In_ HKEY Key, _In_opt_z_ LPCWSTR Name, _In_ DWORD Timeout); - /** * Reads a 32-bit DWORD value from registry key. * @@ -137,24 +77,3 @@ _Must_inspect_result_ _Return_type_success_(return != FALSE) BOOL RegistryQueryDWORD(_In_ HKEY Key, _In_opt_z_ LPCWSTR Name, _Out_ DWORD *Value, _In_ BOOL Log); - -/** - * Reads a 32-bit DWORD value from registry key. It waits for the registry value to become available. - * - * @param Key Handle of the registry key to read from. Must be opened with read access. - * - * @param Name Name of the value to read. - * - * @param Timeout Timeout to wait for the value in milliseconds. - * - * @param Value Pointer to DWORD to retrieve registry value. - * - * @return If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To - * get extended error information, call GetLastError. Possible errors include the following: - * ERROR_INVALID_DATATYPE when registry value exist but not REG_DWORD type; - * ERROR_INVALID_DATA when registry value size is not 4 bytes - */ -_Must_inspect_result_ -_Return_type_success_(return != FALSE) -BOOL -RegistryQueryDWORDWait(_In_ HKEY Key, _In_opt_z_ LPCWSTR Name, _In_ DWORD Timeout, _Out_ DWORD *Value); diff --git a/api/resources.rc b/api/resources.rc index 822ed3f..ca0aacd 100644 --- a/api/resources.rc +++ b/api/resources.rc @@ -16,16 +16,22 @@ downlevelshim.dll RCDATA "downlevelshim.dll" #if defined(WANT_AMD64_WOW64) # if defined(BUILT_AMD64_WOW64) -wintun-amd64.dll RCDATA "amd64\\wintun.dll" +wintun-amd64.cat RCDATA "amd64\\driver\\wintun.cat" +wintun-amd64.inf RCDATA "amd64\\driver\\wintun.inf" +wintun-amd64.sys RCDATA "amd64\\driver\\wintun.sys" +setupapihost-amd64.dll RCDATA "amd64\\setupapihost.dll" # else -# pragma message("AMD64 wintun.dll was not built, so this will not work from WOW64") +# pragma message("AMD64 wintun.sys was not built, so this will not work from WOW64") # endif #endif #if defined(WANT_ARM64_WOW64) # if defined(BUILT_ARM64_WOW64) -wintun-arm64.dll RCDATA "arm64\\wintun.dll" +wintun-arm64.cat RCDATA "arm64\\driver\\wintun.cat" +wintun-arm64.inf RCDATA "arm64\\driver\\wintun.inf" +wintun-arm64.sys RCDATA "arm64\\driver\\wintun.sys" +setupapihost-arm64.dll RCDATA "arm64\\setupapihost.dll" # else -# pragma message("ARM64 wintun.dll was not built, so this will not work from WOW64") +# pragma message("ARM64 wintun.sys was not built, so this will not work from WOW64") # endif #endif diff --git a/api/rundll32.c b/api/rundll32.c index 37d4387..6bdc7c9 100644 --- a/api/rundll32.c +++ b/api/rundll32.c @@ -15,134 +15,6 @@ #include #include -#ifdef ACCEPT_WOW64 - -# define EXPORT comment(linker, "/EXPORT:" __FUNCTION__ "=" __FUNCDNAME__) - -static DWORD -WriteFormatted(_In_ DWORD StdHandle, _In_z_ LPCWSTR Template, ...) -{ - LPWSTR FormattedMessage = NULL; - DWORD Size; - va_list Arguments; - va_start(Arguments, Template); - DWORD Len = FormatMessageW( - FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER, - Template, - 0, - 0, - (VOID *)&FormattedMessage, - 0, - &Arguments); - if (SUCCEEDED(DWordMult(Len, sizeof(*FormattedMessage), &Size))) - WriteFile(GetStdHandle(StdHandle), FormattedMessage, Size, &Size, NULL); - else - Size = 0; - LocalFree(FormattedMessage); - va_end(Arguments); - return Size / sizeof(*FormattedMessage); -} - -static VOID CALLBACK -ConsoleLogger(_In_ WINTUN_LOGGER_LEVEL Level, _In_z_ LPCWSTR LogLine) -{ - LPCWSTR Template; - switch (Level) - { - case WINTUN_LOG_INFO: - Template = L"[+] %1\n"; - break; - case WINTUN_LOG_WARN: - Template = L"[-] %1\n"; - break; - case WINTUN_LOG_ERR: - Template = L"[!] %1\n"; - break; - default: - return; - } - WriteFormatted(STD_ERROR_HANDLE, Template, LogLine); -} - -VOID __stdcall CreateAdapter(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow) -{ -# pragma EXPORT - - int Argc; - LPWSTR *Argv = CommandLineToArgvW(GetCommandLineW(), &Argc); - WintunSetLogger(ConsoleLogger); - - if (Argc < 4) - goto cleanup; - if (wcslen(Argv[2]) >= WINTUN_MAX_POOL) - goto cleanup; - if (wcslen(Argv[3]) >= MAX_ADAPTER_NAME) - goto cleanup; - GUID RequestedGUID; - if (Argc > 4 && FAILED(CLSIDFromString(Argv[4], &RequestedGUID))) - goto cleanup; - - BOOL RebootRequired; - WINTUN_ADAPTER *Adapter = WintunCreateAdapter(Argv[2], Argv[3], Argc > 4 ? &RequestedGUID : NULL, &RebootRequired); - DWORD LastError = Adapter ? ERROR_SUCCESS : GetLastError(); - WriteFormatted( - STD_OUTPUT_HANDLE, L"%1!X! %2!s! %3!X!", LastError, Adapter ? Adapter->DevInstanceID : L"\"\"", RebootRequired); - if (Adapter) - WintunFreeAdapter(Adapter); - -cleanup: - LocalFree(Argv); -} - -VOID __stdcall DeleteAdapter(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow) -{ -# pragma EXPORT - - int Argc; - LPWSTR *Argv = CommandLineToArgvW(GetCommandLineW(), &Argc); - WintunSetLogger(ConsoleLogger); - - if (Argc < 4) - goto cleanup; - - DWORD LastError; - BOOL RebootRequired = FALSE; - WINTUN_ADAPTER *Adapter = AdapterOpenFromDevInstanceId(Argv[2], Argv[3]); - if (!Adapter) - { - LastError = GetLastError(); - goto write; - } - BOOL ForceCloseSessions = wcstoul(Argv[4], NULL, 10); - LastError = WintunDeleteAdapter(Adapter, ForceCloseSessions, &RebootRequired) ? ERROR_SUCCESS : GetLastError(); - WintunFreeAdapter(Adapter); -write: - WriteFormatted(STD_OUTPUT_HANDLE, L"%1!X! %2!X!", LastError, RebootRequired); - -cleanup: - LocalFree(Argv); -} - -VOID __stdcall DeletePoolDriver(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow) -{ -# pragma EXPORT - - int Argc; - LPWSTR *Argv = CommandLineToArgvW(GetCommandLineW(), &Argc); - WintunSetLogger(ConsoleLogger); - - if (Argc < 2) - goto cleanup; - - BOOL RebootRequired; - DWORD LastError = WintunDeletePoolDriver(Argv[2], &RebootRequired) ? ERROR_SUCCESS : GetLastError(); - WriteFormatted(STD_OUTPUT_HANDLE, L"%1!X! %2!X!", LastError, RebootRequired); - -cleanup: - LocalFree(Argv); -} -#endif - #ifdef MAYBE_WOW64 _Return_type_success_(return != FALSE) @@ -265,52 +137,23 @@ ProcessStdout(_Inout_ PROCESS_STDOUT_STATE *State) static DWORD WINAPI ProcessStderr(_In_ HANDLE Stderr) { - enum - { - OnNone, - OnLevelStart, - OnLevel, - OnLevelEnd, - OnSpace, - OnMsg - } State = OnNone; - WCHAR Msg[0x200]; - DWORD Count = 0; - WINTUN_LOGGER_LEVEL Level = WINTUN_LOG_INFO; + WCHAR Msg[0x200], Buf[0x220], LevelRune; + DWORD64 Timestamp; + DWORD SizeRead; + WINTUN_LOGGER_LEVEL Level; for (;;) { - WCHAR Buf[0x200]; - DWORD SizeRead; - if (!ReadFile(Stderr, Buf, sizeof(Buf), &SizeRead, NULL)) + if (!ReadFile(Stderr, Buf, sizeof(Buf), &SizeRead, NULL) || !SizeRead) return ERROR_SUCCESS; if (SizeRead % sizeof(*Buf)) return ERROR_INVALID_DATA; - SizeRead /= sizeof(*Buf); - for (DWORD i = 0; i < SizeRead; ++i) - { - WCHAR c = Buf[i]; - if (State == OnNone && c == L'[') - State = OnLevelStart; - else if ( - State == OnLevelStart && ((Level = WINTUN_LOG_INFO, c == L'+') || - (Level = WINTUN_LOG_WARN, c == L'-') || (Level = WINTUN_LOG_ERR, c == L'!'))) - State = OnLevelEnd; - else if (State == OnLevelEnd && c == L']') - State = OnSpace; - else if (State == OnSpace && !iswspace(c) || State == OnMsg && c != L'\r' && c != L'\n') - { - if (Count < _countof(Msg) - 1) - Msg[Count++] = c; - State = OnMsg; - } - else if (State == OnMsg && c == L'\n') - { - Msg[Count] = 0; - LoggerLog(Level, NULL, Msg); - State = OnNone; - Count = 0; - } - } + Msg[0] = Buf[SizeRead / sizeof(*Buf) - 1] = L'\0'; + if (swscanf_s(Buf, L"[%c %I64u] %[^\n]", &LevelRune, 1, &Timestamp, Msg, (DWORD)_countof(Msg)) != 3 || !Msg[0]) + return ERROR_INVALID_DATA; + if (!((Level = WINTUN_LOG_INFO, LevelRune == L'+') || (Level = WINTUN_LOG_WARN, LevelRune == L'-') || + (Level = WINTUN_LOG_ERR, LevelRune == L'!'))) + return ERROR_INVALID_DATA; + Logger(Level, Timestamp, Msg); } } @@ -343,7 +186,7 @@ ExecuteRunDll32( return FALSE; } WCHAR DllPath[MAX_PATH] = { 0 }; - if (!PathCombineW(DllPath, RandomTempSubDirectory, L"wintun.dll")) + if (!PathCombineW(DllPath, RandomTempSubDirectory, L"setupapihost.dll")) { LastError = ERROR_BUFFER_OVERFLOW; goto cleanupDirectory; @@ -352,10 +195,10 @@ ExecuteRunDll32( switch (NativeMachine) { case IMAGE_FILE_MACHINE_AMD64: - WintunDllResourceName = L"wintun-amd64.dll"; + WintunDllResourceName = L"setupapihost-amd64.dll"; break; case IMAGE_FILE_MACHINE_ARM64: - WintunDllResourceName = L"wintun-arm64.dll"; + WintunDllResourceName = L"setupapihost-arm64.dll"; break; default: LOG(WINTUN_LOG_ERR, L"Unsupported platform 0x%x", NativeMachine); @@ -444,9 +287,9 @@ cleanupThreads: WaitForSingleObject(ThreadStdout, INFINITE); DWORD ThreadResult; if (!GetExitCodeThread(ThreadStdout, &ThreadResult)) - LOG_LAST_ERROR(L"Failed to retrieve stdout reader result"); + LastError = LOG_LAST_ERROR(L"Failed to retrieve stdout reader result"); else if (ThreadResult != ERROR_SUCCESS) - LOG_ERROR(LastError, L"Failed to read process output"); + LastError = LOG_ERROR(ThreadResult, L"Failed to read process output"); CloseHandle(ThreadStdout); } cleanupPipes: @@ -462,90 +305,40 @@ cleanupDirectory: return RET_ERROR(TRUE, LastError); } -_Use_decl_annotations_ -WINTUN_ADAPTER * -CreateAdapterViaRundll32(LPCWSTR Pool, LPCWSTR Name, const GUID *RequestedGUID, BOOL *RebootRequired) -{ - LOG(WINTUN_LOG_INFO, L"Spawning native process"); - LPWSTR Arguments = NULL; - if (RequestedGUID) - { - WCHAR RequestedGUIDStr[MAX_GUID_STRING_LEN]; - if (StringFromGUID2(RequestedGUID, RequestedGUIDStr, _countof(RequestedGUIDStr))) - Arguments = ArgvToCommandLineW(3, Pool, Name, RequestedGUIDStr); - } - else - Arguments = ArgvToCommandLineW(2, Pool, Name); - if (!Arguments) - { - LOG(WINTUN_LOG_ERR, L"Command line too long"); - SetLastError(ERROR_INVALID_PARAMETER); - return NULL; - } - WINTUN_ADAPTER *Adapter = NULL; - DWORD LastError; - WCHAR Response[8 + 1 + MAX_GUID_STRING_LEN + 1 + 8 + 1]; - if (!ExecuteRunDll32(L"CreateAdapter", Arguments, Response, _countof(Response))) - { - LastError = GetLastError(); - LOG(WINTUN_LOG_ERR, L"Error executing worker process: %s", Arguments); - goto cleanupArguments; - } - int Argc; - LPWSTR *Argv = CommandLineToArgvW(Response, &Argc); - if (Argc < 3) - { - LOG(WINTUN_LOG_ERR, L"Incomplete response: %s", Response); - LastError = ERROR_INVALID_PARAMETER; - goto cleanupArgv; - } - LastError = wcstoul(Argv[0], NULL, 16); - if (LastError == ERROR_SUCCESS && (Adapter = AdapterOpenFromDevInstanceId(Pool, Argv[1])) == NULL) - { - LOG(WINTUN_LOG_ERR, L"Failed to get adapter %s", Argv[1]); - LastError = ERROR_FILE_NOT_FOUND; - } - if (wcstoul(Argv[2], NULL, 16)) - *RebootRequired = TRUE; -cleanupArgv: - LocalFree(Argv); -cleanupArguments: - Free(Arguments); - SetLastError(LastError); - return Adapter; -} - -_Use_decl_annotations_ +static _Return_type_success_(return != FALSE) BOOL -DeleteAdapterViaRundll32(const WINTUN_ADAPTER *Adapter, BOOL ForceCloseSessions, BOOL *RebootRequired) +InvokeClassInstaller(_In_ LPCWSTR Action, _In_ LPCWSTR Function, _In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData) { - LOG(WINTUN_LOG_INFO, L"Spawning native process"); - LPWSTR Arguments = ArgvToCommandLineW(3, Adapter->Pool, Adapter->DevInstanceID, ForceCloseSessions ? L"1" : L"0"); - if (!Arguments) + LOG(WINTUN_LOG_INFO, L"Spawning native process to %s instance", Action); + + WCHAR InstanceId[MAX_INSTANCE_ID]; + DWORD RequiredChars = _countof(InstanceId); + if (!SetupDiGetDeviceInstanceIdW(DevInfo, DevInfoData, InstanceId, RequiredChars, &RequiredChars)) { - LOG(WINTUN_LOG_ERR, L"Command line too long"); - SetLastError(ERROR_INVALID_PARAMETER); + LOG_LAST_ERROR(L"Failed to get adapter instance ID"); return FALSE; } - WCHAR Response[8 + 1 + 8 + 1]; - DWORD LastError; - if (!ExecuteRunDll32(L"DeleteAdapter", Arguments, Response, _countof(Response))) + LPWSTR Arguments = ArgvToCommandLineW(1, InstanceId); + if (!Arguments) { - LastError = GetLastError(); - LOG(WINTUN_LOG_ERR, L"Error executing worker process: %s", Arguments); + SetLastError(LOG_ERROR(ERROR_INVALID_PARAMETER, L"Command line too long")); + return FALSE; + } + DWORD LastError; + WCHAR Response[8 + 1]; + if (!ExecuteRunDll32(Function, Arguments, Response, _countof(Response))) + { + LastError = LOG_LAST_ERROR(L"Error executing worker process: %s", Arguments); goto cleanupArguments; } int Argc; LPWSTR *Argv = CommandLineToArgvW(Response, &Argc); - if (Argc < 2) + if (Argc < 1) { - LOG(WINTUN_LOG_ERR, L"Incomplete response: %s", Response); - LastError = ERROR_INVALID_PARAMETER; + LastError = LOG_ERROR(ERROR_INVALID_PARAMETER, L"Incomplete response: %s", Response); goto cleanupArgv; } LastError = wcstoul(Argv[0], NULL, 16); - if (wcstoul(Argv[1], NULL, 16)) - *RebootRequired = TRUE; cleanupArgv: LocalFree(Argv); cleanupArguments: @@ -555,39 +348,51 @@ cleanupArguments: _Use_decl_annotations_ BOOL -DeletePoolDriverViaRundll32(LPCWSTR Pool, BOOL *RebootRequired) +RemoveInstanceViaRundll32(HDEVINFO DevInfo, SP_DEVINFO_DATA *DevInfoData) { - LOG(WINTUN_LOG_INFO, L"Spawning native process"); - LPWSTR Arguments = ArgvToCommandLineW(1, Pool); - if (!Arguments) - { - LOG(WINTUN_LOG_ERR, L"Command line too long"); - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - WCHAR Response[8 + 1 + 8 + 1]; + return InvokeClassInstaller(L"remove", L"RemoveInstance", DevInfo, DevInfoData); +} + +_Use_decl_annotations_ +BOOL +EnableInstanceViaRundll32(HDEVINFO DevInfo, SP_DEVINFO_DATA *DevInfoData) +{ + return InvokeClassInstaller(L"enable", L"EnableInstance", DevInfo, DevInfoData); +} + +_Use_decl_annotations_ +BOOL +DisableInstanceViaRundll32(HDEVINFO DevInfo, SP_DEVINFO_DATA *DevInfoData) +{ + return InvokeClassInstaller(L"disable", L"DisableInstance", DevInfo, DevInfoData); +} + +_Use_decl_annotations_ +BOOL +CreateInstanceWin7ViaRundll32(LPWSTR InstanceId) +{ + LOG(WINTUN_LOG_INFO, L"Spawning native process to create instance"); + DWORD LastError; - if (!ExecuteRunDll32(L"DeletePoolDriver", Arguments, Response, _countof(Response))) + WCHAR Response[MAX_INSTANCE_ID + 1]; + if (!ExecuteRunDll32(L"CreateInstanceWin7", L"", Response, _countof(Response))) { - LastError = GetLastError(); - LOG(WINTUN_LOG_ERR, L"Error executing worker process: %s", Arguments); - goto cleanupArguments; + LastError = LOG_LAST_ERROR(L"Error executing worker process"); + goto cleanup; } int Argc; LPWSTR *Argv = CommandLineToArgvW(Response, &Argc); if (Argc < 2) { - LOG(WINTUN_LOG_ERR, L"Incomplete response: %s", Response); - LastError = ERROR_INVALID_PARAMETER; + LastError = LOG_ERROR(ERROR_INVALID_PARAMETER, L"Incomplete response: %s", Response); goto cleanupArgv; } LastError = wcstoul(Argv[0], NULL, 16); - if (wcstoul(Argv[1], NULL, 16)) - *RebootRequired = TRUE; + if (LastError == ERROR_SUCCESS) + wcsncpy_s(InstanceId, MAX_INSTANCE_ID, Argv[1], _TRUNCATE); cleanupArgv: LocalFree(Argv); -cleanupArguments: - Free(Arguments); +cleanup: return RET_ERROR(TRUE, LastError); } #endif diff --git a/api/rundll32.h b/api/rundll32.h index 1cd3cae..030419c 100644 --- a/api/rundll32.h +++ b/api/rundll32.h @@ -5,25 +5,22 @@ #pragma once +#include +#include #include "adapter.h" -_Must_inspect_result_ -_Return_type_success_(return != NULL) -_Post_maybenull_ -WINTUN_ADAPTER * -CreateAdapterViaRundll32( - _In_z_ LPCWSTR Pool, - _In_z_ LPCWSTR Name, - _In_opt_ const GUID *RequestedGUID, - _Inout_ BOOL *RebootRequired); +_Return_type_success_(return != FALSE) +BOOL +RemoveInstanceViaRundll32(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData); _Return_type_success_(return != FALSE) BOOL -DeleteAdapterViaRundll32( - _In_ const WINTUN_ADAPTER *Adapter, - _In_ BOOL ForceCloseSessions, - _Inout_ BOOL *RebootRequired); +EnableInstanceViaRundll32(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData); _Return_type_success_(return != FALSE) BOOL -DeletePoolDriverViaRundll32(_In_z_ LPCWSTR Pool, _Inout_ BOOL *RebootRequired); +DisableInstanceViaRundll32(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData); + +_Return_type_success_(return != FALSE) +BOOL +CreateInstanceWin7ViaRundll32(_Out_writes_z_(MAX_INSTANCE_ID) LPWSTR InstanceId); \ No newline at end of file diff --git a/api/session.c b/api/session.c index d620411..ab96c64 100644 --- a/api/session.c +++ b/api/session.c @@ -70,7 +70,7 @@ typedef struct _TUN_SESSION HANDLE Handle; } TUN_SESSION; -WINTUN_START_SESSION_FUNC_IMPL WintunStartSession; +WINTUN_START_SESSION_FUNC WintunStartSession; _Use_decl_annotations_ TUN_SESSION *WINAPI WintunStartSession(WINTUN_ADAPTER *Adapter, DWORD Capacity) @@ -146,7 +146,7 @@ cleanup: return NULL; } -WINTUN_END_SESSION_FUNC_IMPL WintunEndSession; +WINTUN_END_SESSION_FUNC WintunEndSession; _Use_decl_annotations_ VOID WINAPI WintunEndSession(TUN_SESSION *Session) @@ -160,7 +160,7 @@ WintunEndSession(TUN_SESSION *Session) Free(Session); } -WINTUN_GET_READ_WAIT_EVENT_FUNC_IMPL WintunGetReadWaitEvent; +WINTUN_GET_READ_WAIT_EVENT_FUNC WintunGetReadWaitEvent; _Use_decl_annotations_ HANDLE WINAPI WintunGetReadWaitEvent(TUN_SESSION *Session) @@ -168,7 +168,7 @@ WintunGetReadWaitEvent(TUN_SESSION *Session) return Session->Descriptor.Send.TailMoved; } -WINTUN_RECEIVE_PACKET_FUNC_IMPL WintunReceivePacket; +WINTUN_RECEIVE_PACKET_FUNC WintunReceivePacket; _Use_decl_annotations_ BYTE *WINAPI WintunReceivePacket(TUN_SESSION *Session, DWORD *PacketSize) @@ -221,7 +221,7 @@ cleanup: return NULL; } -WINTUN_RELEASE_RECEIVE_PACKET_FUNC_IMPL WintunReleaseReceivePacket; +WINTUN_RELEASE_RECEIVE_PACKET_FUNC WintunReleaseReceivePacket; _Use_decl_annotations_ VOID WINAPI WintunReleaseReceivePacket(TUN_SESSION *Session, const BYTE *Packet) @@ -242,7 +242,7 @@ WintunReleaseReceivePacket(TUN_SESSION *Session, const BYTE *Packet) LeaveCriticalSection(&Session->Send.Lock); } -WINTUN_ALLOCATE_SEND_PACKET_FUNC_IMPL WintunAllocateSendPacket; +WINTUN_ALLOCATE_SEND_PACKET_FUNC WintunAllocateSendPacket; _Use_decl_annotations_ BYTE *WINAPI WintunAllocateSendPacket(TUN_SESSION *Session, DWORD PacketSize) @@ -280,7 +280,7 @@ cleanup: return NULL; } -WINTUN_SEND_PACKET_FUNC_IMPL WintunSendPacket; +WINTUN_SEND_PACKET_FUNC WintunSendPacket; _Use_decl_annotations_ VOID WINAPI WintunSendPacket(TUN_SESSION *Session, const BYTE *Packet) diff --git a/api/wintun.h b/api/wintun.h index 9464a96..ff1223a 100644 --- a/api/wintun.h +++ b/api/wintun.h @@ -5,176 +5,102 @@ #pragma once +#include #include #include #include +#include #ifdef __cplusplus extern "C" { #endif +#ifndef ALIGNED +# if defined(_MSC_VER) +# define ALIGNED(n) __declspec(align(n)) +# elif defined(__GNUC__) +# define ALIGNED(n) __attribute__((aligned(n))) +# else +# error "Unable to define ALIGNED" +# endif +#endif + +/* MinGW is missing this one, unfortunately. */ +#ifndef _Post_maybenull_ +# define _Post_maybenull_ +#endif + +#pragma warning(push) +#pragma warning(disable : 4324) /* structure was padded due to alignment specifier */ + /** * A handle representing Wintun adapter */ typedef struct _WINTUN_ADAPTER *WINTUN_ADAPTER_HANDLE; -/** - * Maximum pool name length including zero terminator - */ -#define WINTUN_MAX_POOL 256 - /** * Creates a new Wintun adapter. * - * @param Pool Name of the adapter pool. Zero-terminated string of up to WINTUN_MAX_POOL-1 characters. - * * @param Name The requested name of the adapter. Zero-terminated string of up to MAX_ADAPTER_NAME-1 * characters. * + * @param TunelType Name of the adapter tunnel type. Zero-terminated string of up to MAX_ADAPTER_NAME-1 + * characters. + * * @param RequestedGUID The GUID of the created network adapter, which then influences NLA generation deterministically. * If it is set to NULL, the GUID is chosen by the system at random, and hence a new NLA entry is * created for each new adapter. It is called "requested" GUID because the API it uses is * completely undocumented, and so there could be minor interesting complications with its usage. * - * @param RebootRequired Optional pointer to a boolean flag to be set to TRUE in case SetupAPI suggests a reboot. - * - * @return If the function succeeds, the return value is the adapter handle. Must be released with WintunFreeAdapter. If - * the function fails, the return value is NULL. To get extended error information, call GetLastError. + * @return If the function succeeds, the return value is the adapter handle. Must be released with + * WintunCloseAdapter. If the function fails, the return value is NULL. To get extended error information, call + * GetLastError. */ typedef _Must_inspect_result_ _Return_type_success_(return != NULL) _Post_maybenull_ -WINTUN_ADAPTER_HANDLE(WINAPI WINTUN_CREATE_ADAPTER_FUNC_IMPL) -(_In_z_ LPCWSTR Pool, _In_z_ LPCWSTR Name, _In_opt_ const GUID *RequestedGUID, _Out_opt_ BOOL *RebootRequired); -typedef WINTUN_CREATE_ADAPTER_FUNC_IMPL *WINTUN_CREATE_ADAPTER_FUNC; +WINTUN_ADAPTER_HANDLE(WINAPI WINTUN_CREATE_ADAPTER_FUNC) +(_In_z_ LPCWSTR Name, _In_z_ LPCWSTR TunnelType, _In_opt_ const GUID *RequestedGUID); /** * Opens an existing Wintun adapter. * - * @param Pool Name of the adapter pool. Zero-terminated string of up to WINTUN_MAX_POOL-1 characters. + * @param Name The requested name of the adapter. Zero-terminated string of up to MAX_ADAPTER_NAME-1 + * characters. * - * @param Name Adapter name. Zero-terminated string of up to MAX_ADAPTER_NAME-1 characters. - * - * @return If the function succeeds, the return value is adapter handle. Must be released with WintunFreeAdapter. If the - * function fails, the return value is NULL. To get extended error information, call GetLastError. Possible errors - * include the following: ERROR_FILE_NOT_FOUND if adapter with given name is not found; ERROR_ALREADY_EXISTS if adapter - * is found but not a Wintun-class or not a member of the pool + * @return If the function succeeds, the return value is the adapter handle. Must be released with + * WintunCloseAdapter. If the function fails, the return value is NULL. To get extended error information, call + * GetLastError. */ typedef _Must_inspect_result_ _Return_type_success_(return != NULL) _Post_maybenull_ -WINTUN_ADAPTER_HANDLE(WINAPI WINTUN_OPEN_ADAPTER_FUNC_IMPL)(_In_z_ LPCWSTR Pool, _In_z_ LPCWSTR Name); -typedef WINTUN_OPEN_ADAPTER_FUNC_IMPL *WINTUN_OPEN_ADAPTER_FUNC; +WINTUN_ADAPTER_HANDLE(WINAPI WINTUN_OPEN_ADAPTER_FUNC)(_In_z_ LPCWSTR Name); /** - * Deletes a Wintun adapter. + * Releases Wintun adapter resources and, if adapter was created with WintunCreateAdapter, removes adapter. * - * @param Adapter Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter. - * - * @param ForceCloseSessions Force close adapter handles that may be in use by other processes. Only set this to TRUE - * with extreme care, as this is resource intensive and may put processes into an undefined - * or unpredictable state. Most users should set this to FALSE. - * - * @param RebootRequired Optional pointer to a boolean flag to be set to TRUE in case SetupAPI suggests a reboot. + * @param Adapter Adapter handle obtained with WintunCreateAdapter or WintunOpenAdapter. + */ +typedef VOID(WINAPI WINTUN_CLOSE_ADAPTER_FUNC)(_In_opt_ WINTUN_ADAPTER_HANDLE Adapter); + +/** + * Deletes the Wintun driver if there are no more adapters in use. * * @return If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To * get extended error information, call GetLastError. */ typedef _Return_type_success_(return != FALSE) -BOOL(WINAPI WINTUN_DELETE_ADAPTER_FUNC_IMPL) -(_In_ WINTUN_ADAPTER_HANDLE Adapter, _In_ BOOL ForceCloseSessions, _Out_opt_ BOOL *RebootRequired); -typedef WINTUN_DELETE_ADAPTER_FUNC_IMPL *WINTUN_DELETE_ADAPTER_FUNC; - -/** - * Called by WintunEnumAdapters for each adapter in the pool. - * - * @param Adapter Adapter handle, which will be freed when this function returns. - * - * @param Param An application-defined value passed to the WintunEnumAdapters. - * - * @return Non-zero to continue iterating adapters; zero to stop. - */ -typedef BOOL(CALLBACK *WINTUN_ENUM_CALLBACK)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _In_ LPARAM Param); - -/** - * Enumerates all Wintun adapters. - * - * @param Pool Name of the adapter pool. Zero-terminated string of up to WINTUN_MAX_POOL-1 characters. - * - * @param Callback Callback function. To continue enumeration, the callback function must return TRUE; to stop - * enumeration, it must return FALSE. - * - * @param Param An application-defined value to be passed to the callback function. - * - * @return If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To - * get extended error information, call GetLastError. - */ -typedef _Return_type_success_(return != FALSE) -BOOL(WINAPI WINTUN_ENUM_ADAPTERS_FUNC_IMPL)(_In_z_ LPCWSTR Pool, _In_ WINTUN_ENUM_CALLBACK Callback, _In_ LPARAM Param); -typedef WINTUN_ENUM_ADAPTERS_FUNC_IMPL *WINTUN_ENUM_ADAPTERS_FUNC; - -/** - * Releases Wintun adapter resources. - * - * @param Adapter Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter. - */ -typedef VOID(WINAPI WINTUN_FREE_ADAPTER_FUNC_IMPL)(_In_ WINTUN_ADAPTER_HANDLE Adapter); -typedef WINTUN_FREE_ADAPTER_FUNC_IMPL *WINTUN_FREE_ADAPTER_FUNC; - -/** - * Deletes all Wintun adapters in a pool and if there are no more adapters in any other pools, also removes Wintun - * from the driver store, usually called by uninstallers. - * - * @param Pool Name of the adapter pool. Zero-terminated string of up to WINTUN_MAX_POOL-1 characters. - * - * @param RebootRequired Optional pointer to a boolean flag to be set to TRUE in case SetupAPI suggests a reboot. - * - * @return If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To - * get extended error information, call GetLastError. - */ -typedef _Return_type_success_(return != FALSE) -BOOL(WINAPI WINTUN_DELETE_POOL_DRIVER_FUNC_IMPL)(_In_z_ LPCWSTR Pool, _Out_opt_ BOOL *RebootRequired); -typedef WINTUN_DELETE_POOL_DRIVER_FUNC_IMPL *WINTUN_DELETE_POOL_DRIVER_FUNC; +BOOL(WINAPI WINTUN_DELETE_DRIVER_FUNC)(VOID); /** * Returns the LUID of the adapter. * - * @param Adapter Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter + * @param Adapter Adapter handle obtained with WintunCreateAdapter or WintunOpenAdapter * * @param Luid Pointer to LUID to receive adapter LUID. */ -typedef VOID(WINAPI WINTUN_GET_ADAPTER_LUID_FUNC_IMPL)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _Out_ NET_LUID *Luid); -typedef WINTUN_GET_ADAPTER_LUID_FUNC_IMPL *WINTUN_GET_ADAPTER_LUID_FUNC; - -/** - * Returns the name of the Wintun adapter. - * - * @param Adapter Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter - * - * @param Name Pointer to a string to receive adapter name - * - * @return If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To - * get extended error information, call GetLastError. - */ -typedef _Must_inspect_result_ -_Return_type_success_(return != FALSE) -BOOL(WINAPI WINTUN_GET_ADAPTER_NAME_FUNC_IMPL) -(_In_ WINTUN_ADAPTER_HANDLE Adapter, _Out_writes_z_(MAX_ADAPTER_NAME) LPWSTR Name); -typedef WINTUN_GET_ADAPTER_NAME_FUNC_IMPL *WINTUN_GET_ADAPTER_NAME_FUNC; - -/** - * Sets name of the Wintun adapter. - * - * @param Adapter Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter - * - * @param Name Adapter name. Zero-terminated string of up to MAX_ADAPTER_NAME-1 characters. - * - * @return If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To - * get extended error information, call GetLastError. - */ -typedef _Return_type_success_(return != FALSE) -BOOL(WINAPI WINTUN_SET_ADAPTER_NAME_FUNC_IMPL)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _In_z_ LPCWSTR Name); -typedef WINTUN_SET_ADAPTER_NAME_FUNC_IMPL *WINTUN_SET_ADAPTER_NAME_FUNC; +typedef VOID(WINAPI WINTUN_GET_ADAPTER_LUID_FUNC)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _Out_ NET_LUID *Luid); /** * Determines the version of the Wintun driver currently loaded. @@ -184,8 +110,7 @@ typedef WINTUN_SET_ADAPTER_NAME_FUNC_IMPL *WINTUN_SET_ADAPTER_NAME_FUNC; * ERROR_FILE_NOT_FOUND Wintun not loaded */ typedef _Return_type_success_(return != 0) -DWORD(WINAPI WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC_IMPL)(VOID); -typedef WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC_IMPL *WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC; +DWORD(WINAPI WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC)(VOID); /** * Determines the level of logging, passed to WINTUN_LOGGER_CALLBACK. @@ -202,9 +127,14 @@ typedef enum * * @param Level Message level. * + * @param Timestamp Message timestamp in in 100ns intervals since 1601-01-01 UTC. + * * @param Message Message text. */ -typedef VOID(CALLBACK *WINTUN_LOGGER_CALLBACK)(_In_ WINTUN_LOGGER_LEVEL Level, _In_z_ LPCWSTR Message); +typedef VOID(CALLBACK *WINTUN_LOGGER_CALLBACK)( + _In_ WINTUN_LOGGER_LEVEL Level, + _In_ DWORD64 Timestamp, + _In_z_ LPCWSTR Message); /** * Sets logger callback function. @@ -213,8 +143,7 @@ typedef VOID(CALLBACK *WINTUN_LOGGER_CALLBACK)(_In_ WINTUN_LOGGER_LEVEL Level, _ * threads concurrently. Should the logging require serialization, you must handle serialization in * NewLogger. Set to NULL to disable. */ -typedef VOID(WINAPI WINTUN_SET_LOGGER_FUNC_IMPL)(_In_ WINTUN_LOGGER_CALLBACK NewLogger); -typedef WINTUN_SET_LOGGER_FUNC_IMPL *WINTUN_SET_LOGGER_FUNC; +typedef VOID(WINAPI WINTUN_SET_LOGGER_FUNC)(_In_ WINTUN_LOGGER_CALLBACK NewLogger); /** * Minimum ring capacity. @@ -245,16 +174,14 @@ typedef struct _TUN_SESSION *WINTUN_SESSION_HANDLE; typedef _Must_inspect_result_ _Return_type_success_(return != NULL) _Post_maybenull_ -WINTUN_SESSION_HANDLE(WINAPI WINTUN_START_SESSION_FUNC_IMPL)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _In_ DWORD Capacity); -typedef WINTUN_START_SESSION_FUNC_IMPL *WINTUN_START_SESSION_FUNC; +WINTUN_SESSION_HANDLE(WINAPI WINTUN_START_SESSION_FUNC)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _In_ DWORD Capacity); /** * Ends Wintun session. * * @param Session Wintun session handle obtained with WintunStartSession */ -typedef VOID(WINAPI WINTUN_END_SESSION_FUNC_IMPL)(_In_ WINTUN_SESSION_HANDLE Session); -typedef WINTUN_END_SESSION_FUNC_IMPL *WINTUN_END_SESSION_FUNC; +typedef VOID(WINAPI WINTUN_END_SESSION_FUNC)(_In_ WINTUN_SESSION_HANDLE Session); /** * Gets Wintun session's read-wait event handle. @@ -266,8 +193,7 @@ typedef WINTUN_END_SESSION_FUNC_IMPL *WINTUN_END_SESSION_FUNC; * load), wait for this event to become signaled before retrying WintunReceivePackets. Do not call * CloseHandle on this event - it is managed by the session. */ -typedef HANDLE(WINAPI WINTUN_GET_READ_WAIT_EVENT_FUNC_IMPL)(_In_ WINTUN_SESSION_HANDLE Session); -typedef WINTUN_GET_READ_WAIT_EVENT_FUNC_IMPL *WINTUN_GET_READ_WAIT_EVENT_FUNC; +typedef HANDLE(WINAPI WINTUN_GET_READ_WAIT_EVENT_FUNC)(_In_ WINTUN_SESSION_HANDLE Session); /** * Maximum IP packet size @@ -293,8 +219,7 @@ typedef _Must_inspect_result_ _Return_type_success_(return != NULL) _Post_maybenull_ _Post_writable_byte_size_(*PacketSize) -BYTE *(WINAPI WINTUN_RECEIVE_PACKET_FUNC_IMPL)(_In_ WINTUN_SESSION_HANDLE Session, _Out_ DWORD *PacketSize); -typedef WINTUN_RECEIVE_PACKET_FUNC_IMPL *WINTUN_RECEIVE_PACKET_FUNC; +BYTE *(WINAPI WINTUN_RECEIVE_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _Out_ DWORD *PacketSize); /** * Releases internal buffer after the received packet has been processed by the client. This function is thread-safe. @@ -304,8 +229,7 @@ typedef WINTUN_RECEIVE_PACKET_FUNC_IMPL *WINTUN_RECEIVE_PACKET_FUNC; * @param Packet Packet obtained with WintunReceivePacket */ typedef VOID( - WINAPI WINTUN_RELEASE_RECEIVE_PACKET_FUNC_IMPL)(_In_ WINTUN_SESSION_HANDLE Session, _In_ const BYTE *Packet); -typedef WINTUN_RELEASE_RECEIVE_PACKET_FUNC_IMPL *WINTUN_RELEASE_RECEIVE_PACKET_FUNC; + WINAPI WINTUN_RELEASE_RECEIVE_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ const BYTE *Packet); /** * Allocates memory for a packet to send. After the memory is filled with packet data, call WintunSendPacket to send @@ -326,8 +250,7 @@ typedef _Must_inspect_result_ _Return_type_success_(return != NULL) _Post_maybenull_ _Post_writable_byte_size_(PacketSize) -BYTE *(WINAPI WINTUN_ALLOCATE_SEND_PACKET_FUNC_IMPL)(_In_ WINTUN_SESSION_HANDLE Session, _In_ DWORD PacketSize); -typedef WINTUN_ALLOCATE_SEND_PACKET_FUNC_IMPL *WINTUN_ALLOCATE_SEND_PACKET_FUNC; +BYTE *(WINAPI WINTUN_ALLOCATE_SEND_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ DWORD PacketSize); /** * Sends the packet and releases internal buffer. WintunSendPacket is thread-safe, but the WintunAllocateSendPacket @@ -338,8 +261,9 @@ typedef WINTUN_ALLOCATE_SEND_PACKET_FUNC_IMPL *WINTUN_ALLOCATE_SEND_PACKET_FUNC; * * @param Packet Packet obtained with WintunAllocateSendPacket */ -typedef VOID(WINAPI WINTUN_SEND_PACKET_FUNC_IMPL)(_In_ WINTUN_SESSION_HANDLE Session, _In_ const BYTE *Packet); -typedef WINTUN_SEND_PACKET_FUNC_IMPL *WINTUN_SEND_PACKET_FUNC; +typedef VOID(WINAPI WINTUN_SEND_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ const BYTE *Packet); + +#pragma warning(pop) #ifdef __cplusplus } diff --git a/driver/wintun.inf b/driver/wintun.inf index e9eb3cf..4b69802 100644 --- a/driver/wintun.inf +++ b/driver/wintun.inf @@ -32,17 +32,20 @@ wintun.sys, , , 0x00004002 ; COPYFLG_IN_USE_RENAME | COPYFLG_NOSKIP [Wintun.Install] Characteristics = 0x1 ; NCF_VIRTUAL AddReg = Wintun.Ndi +AddProperty = Wintun.Properties CopyFiles = Wintun.CopyFiles.Sys *IfType = 53 ; IF_TYPE_PROP_VIRTUAL *MediaType = 19 ; NdisMediumIP *PhysicalMediaType = 0 ; NdisPhysicalMediumUnspecified EnableDhcp = 0 ; Disable DHCP +[Wintun.Properties] +DeviceVendorWebsite,,,,"https://www.wintun.net/" + [Wintun.Install.Services] AddService = wintun, 2, Wintun.Service, Wintun.EventLog ; 2=SPSVCINST_ASSOCSERVICE [Wintun.Ndi] -HKR, , DeviceVxDs, , wintun.sys HKR, Ndi, Service, 0, wintun HKR, Ndi\Interfaces, UpperRange, , "ndis5" HKR, Ndi\Interfaces, LowerRange, , "nolower" diff --git a/example/example.c b/example/example.c index aef4687..44ca946 100644 --- a/example/example.c +++ b/example/example.c @@ -9,29 +9,26 @@ #include #include #include +#include #include #include #include #include "wintun.h" -static WINTUN_CREATE_ADAPTER_FUNC WintunCreateAdapter; -static WINTUN_DELETE_ADAPTER_FUNC WintunDeleteAdapter; -static WINTUN_DELETE_POOL_DRIVER_FUNC WintunDeletePoolDriver; -static WINTUN_ENUM_ADAPTERS_FUNC WintunEnumAdapters; -static WINTUN_FREE_ADAPTER_FUNC WintunFreeAdapter; -static WINTUN_OPEN_ADAPTER_FUNC WintunOpenAdapter; -static WINTUN_GET_ADAPTER_LUID_FUNC WintunGetAdapterLUID; -static WINTUN_GET_ADAPTER_NAME_FUNC WintunGetAdapterName; -static WINTUN_SET_ADAPTER_NAME_FUNC WintunSetAdapterName; -static WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC WintunGetRunningDriverVersion; -static WINTUN_SET_LOGGER_FUNC WintunSetLogger; -static WINTUN_START_SESSION_FUNC WintunStartSession; -static WINTUN_END_SESSION_FUNC WintunEndSession; -static WINTUN_GET_READ_WAIT_EVENT_FUNC WintunGetReadWaitEvent; -static WINTUN_RECEIVE_PACKET_FUNC WintunReceivePacket; -static WINTUN_RELEASE_RECEIVE_PACKET_FUNC WintunReleaseReceivePacket; -static WINTUN_ALLOCATE_SEND_PACKET_FUNC WintunAllocateSendPacket; -static WINTUN_SEND_PACKET_FUNC WintunSendPacket; +static WINTUN_CREATE_ADAPTER_FUNC *WintunCreateAdapter; +static WINTUN_CLOSE_ADAPTER_FUNC *WintunCloseAdapter; +static WINTUN_OPEN_ADAPTER_FUNC *WintunOpenAdapter; +static WINTUN_GET_ADAPTER_LUID_FUNC *WintunGetAdapterLUID; +static WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC *WintunGetRunningDriverVersion; +static WINTUN_DELETE_DRIVER_FUNC *WintunDeleteDriver; +static WINTUN_SET_LOGGER_FUNC *WintunSetLogger; +static WINTUN_START_SESSION_FUNC *WintunStartSession; +static WINTUN_END_SESSION_FUNC *WintunEndSession; +static WINTUN_GET_READ_WAIT_EVENT_FUNC *WintunGetReadWaitEvent; +static WINTUN_RECEIVE_PACKET_FUNC *WintunReceivePacket; +static WINTUN_RELEASE_RECEIVE_PACKET_FUNC *WintunReleaseReceivePacket; +static WINTUN_ALLOCATE_SEND_PACKET_FUNC *WintunAllocateSendPacket; +static WINTUN_SEND_PACKET_FUNC *WintunSendPacket; static HMODULE InitializeWintun(void) @@ -40,16 +37,13 @@ InitializeWintun(void) LoadLibraryExW(L"wintun.dll", NULL, LOAD_LIBRARY_SEARCH_APPLICATION_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32); if (!Wintun) return NULL; -#define X(Name, Type) ((Name = (Type)GetProcAddress(Wintun, #Name)) == NULL) - if (X(WintunCreateAdapter, WINTUN_CREATE_ADAPTER_FUNC) || X(WintunDeleteAdapter, WINTUN_DELETE_ADAPTER_FUNC) || - X(WintunDeletePoolDriver, WINTUN_DELETE_POOL_DRIVER_FUNC) || X(WintunEnumAdapters, WINTUN_ENUM_ADAPTERS_FUNC) || - X(WintunFreeAdapter, WINTUN_FREE_ADAPTER_FUNC) || X(WintunOpenAdapter, WINTUN_OPEN_ADAPTER_FUNC) || - X(WintunGetAdapterLUID, WINTUN_GET_ADAPTER_LUID_FUNC) || - X(WintunGetAdapterName, WINTUN_GET_ADAPTER_NAME_FUNC) || - X(WintunSetAdapterName, WINTUN_SET_ADAPTER_NAME_FUNC) || +#define X(Name, Type) ((Name = (Type *)GetProcAddress(Wintun, #Name)) == NULL) + if (X(WintunCreateAdapter, WINTUN_CREATE_ADAPTER_FUNC) || X(WintunCloseAdapter, WINTUN_CLOSE_ADAPTER_FUNC) || + X(WintunOpenAdapter, WINTUN_OPEN_ADAPTER_FUNC) || X(WintunGetAdapterLUID, WINTUN_GET_ADAPTER_LUID_FUNC) || X(WintunGetRunningDriverVersion, WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC) || - X(WintunSetLogger, WINTUN_SET_LOGGER_FUNC) || X(WintunStartSession, WINTUN_START_SESSION_FUNC) || - X(WintunEndSession, WINTUN_END_SESSION_FUNC) || X(WintunGetReadWaitEvent, WINTUN_GET_READ_WAIT_EVENT_FUNC) || + X(WintunDeleteDriver, WINTUN_DELETE_DRIVER_FUNC) || X(WintunSetLogger, WINTUN_SET_LOGGER_FUNC) || + X(WintunStartSession, WINTUN_START_SESSION_FUNC) || X(WintunEndSession, WINTUN_END_SESSION_FUNC) || + X(WintunGetReadWaitEvent, WINTUN_GET_READ_WAIT_EVENT_FUNC) || X(WintunReceivePacket, WINTUN_RECEIVE_PACKET_FUNC) || X(WintunReleaseReceivePacket, WINTUN_RELEASE_RECEIVE_PACKET_FUNC) || X(WintunAllocateSendPacket, WINTUN_ALLOCATE_SEND_PACKET_FUNC) || X(WintunSendPacket, WINTUN_SEND_PACKET_FUNC)) @@ -64,12 +58,10 @@ InitializeWintun(void) } static void CALLBACK -ConsoleLogger(_In_ WINTUN_LOGGER_LEVEL Level, _In_z_ const WCHAR *LogLine) +ConsoleLogger(_In_ WINTUN_LOGGER_LEVEL Level, _In_ DWORD64 Timestamp, _In_z_ const WCHAR *LogLine) { - FILETIME Timestamp; - GetSystemTimePreciseAsFileTime(&Timestamp); SYSTEMTIME SystemTime; - FileTimeToSystemTime(&Timestamp, &SystemTime); + FileTimeToSystemTime((FILETIME *)&Timestamp, &SystemTime); WCHAR LevelMarker; switch (Level) { @@ -99,6 +91,13 @@ ConsoleLogger(_In_ WINTUN_LOGGER_LEVEL Level, _In_z_ const WCHAR *LogLine) LogLine); } +static DWORD64 Now(VOID) +{ + LARGE_INTEGER Timestamp; + NtQuerySystemTime(&Timestamp); + return Timestamp.QuadPart; +} + static DWORD LogError(_In_z_ const WCHAR *Prefix, _In_ DWORD Error) { @@ -121,7 +120,7 @@ LogError(_In_z_ const WCHAR *Prefix, _In_ DWORD Error) 0, (va_list *)(DWORD_PTR[]){ (DWORD_PTR)Prefix, (DWORD_PTR)Error, (DWORD_PTR)SystemMessage }); if (FormattedMessage) - ConsoleLogger(WINTUN_LOG_ERR, FormattedMessage); + ConsoleLogger(WINTUN_LOG_ERR, Now(), FormattedMessage); LocalFree(FormattedMessage); LocalFree(SystemMessage); return Error; @@ -144,7 +143,7 @@ Log(_In_ WINTUN_LOGGER_LEVEL Level, _In_z_ const WCHAR *Format, ...) va_start(args, Format); _vsnwprintf_s(LogLine, _countof(LogLine), _TRUNCATE, Format, args); va_end(args); - ConsoleLogger(Level, LogLine); + ConsoleLogger(Level, Now(), LogLine); } static HANDLE QuitEvent; @@ -295,25 +294,13 @@ SendPackets(_Inout_ DWORD_PTR SessionPtr) return ERROR_SUCCESS; } -static BOOL CALLBACK -PrintAdapter(_In_ WINTUN_ADAPTER_HANDLE Adapter, _In_ LPARAM Param) -{ - UNREFERENCED_PARAMETER(Param); - WCHAR szAdapterName[MAX_ADAPTER_NAME]; - if (WintunGetAdapterName(Adapter, szAdapterName)) - Log(WINTUN_LOG_INFO, L"Existing Wintun adapter: %s", szAdapterName); - return TRUE; -} - -int -main(void) +int __cdecl main(void) { HMODULE Wintun = InitializeWintun(); if (!Wintun) return LogError(L"Failed to initialize Wintun", GetLastError()); WintunSetLogger(ConsoleLogger); Log(WINTUN_LOG_INFO, L"Wintun library loaded"); - WintunEnumAdapters(L"Example", PrintAdapter, 0); DWORD LastError; HaveQuit = FALSE; @@ -330,16 +317,12 @@ main(void) } GUID ExampleGuid = { 0xdeadbabe, 0xcafe, 0xbeef, { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef } }; - WINTUN_ADAPTER_HANDLE Adapter = WintunOpenAdapter(L"Example", L"Demo"); + WINTUN_ADAPTER_HANDLE Adapter = WintunCreateAdapter(L"Demo", L"Example", &ExampleGuid); if (!Adapter) { - Adapter = WintunCreateAdapter(L"Example", L"Demo", &ExampleGuid, NULL); - if (!Adapter) - { - LastError = GetLastError(); - LogError(L"Failed to create adapter", LastError); - goto cleanupQuit; - } + LastError = GetLastError(); + LogError(L"Failed to create adapter", LastError); + goto cleanupQuit; } DWORD Version = WintunGetRunningDriverVersion(); @@ -391,8 +374,7 @@ cleanupWorkers: } WintunEndSession(Session); cleanupAdapter: - WintunDeleteAdapter(Adapter, FALSE, NULL); - WintunFreeAdapter(Adapter); + WintunCloseAdapter(Adapter); cleanupQuit: SetConsoleCtrlHandler(CtrlHandler, FALSE); CloseHandle(QuitEvent); diff --git a/setupapihost/host.c b/setupapihost/host.c new file mode 100644 index 0000000..e930358 --- /dev/null +++ b/setupapihost/host.c @@ -0,0 +1,184 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define EXPORT comment(linker, "/EXPORT:" __FUNCTION__ "=" __FUNCDNAME__) + +static FARPROC WINAPI +DelayedLoadLibraryHook(unsigned dliNotify, PDelayLoadInfo pdli) +{ + if (dliNotify != dliNotePreLoadLibrary) + return NULL; + HMODULE Library = LoadLibraryExA(pdli->szDll, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); + if (!Library) + abort(); + return (FARPROC)Library; +} + +const PfnDliHook __pfnDliNotifyHook2 = DelayedLoadLibraryHook; + +static DWORD +WriteFormatted(_In_ DWORD StdHandle, _In_z_ LPCWSTR Template, ...) +{ + LPWSTR FormattedMessage = NULL; + DWORD Size; + va_list Arguments; + va_start(Arguments, Template); + DWORD Len = FormatMessageW( + FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER, + Template, + 0, + 0, + (VOID *)&FormattedMessage, + 0, + &Arguments); + if (SUCCEEDED(DWordMult(Len, sizeof(*FormattedMessage), &Size))) + WriteFile(GetStdHandle(StdHandle), FormattedMessage, Size, &Size, NULL); + else + Size = 0; + LocalFree(FormattedMessage); + va_end(Arguments); + return Size / sizeof(*FormattedMessage); +} + +VOID __stdcall RemoveInstance(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow) +{ +#pragma EXPORT + + DWORD LastError = ERROR_SUCCESS; + int Argc; + LPWSTR *Argv = CommandLineToArgvW(GetCommandLineW(), &Argc); + + if (Argc < 3) + goto cleanup; + WCHAR *InstanceId = Argv[2]; + + HDEVINFO DevInfo = SetupDiCreateDeviceInfoListExW(&GUID_DEVCLASS_NET, NULL, NULL, NULL); + if (DevInfo == INVALID_HANDLE_VALUE) + { + LastError = GetLastError(); + goto cleanup; + } + SP_DEVINFO_DATA DevInfoData = { .cbSize = sizeof(DevInfoData) }; + if (!SetupDiOpenDeviceInfoW(DevInfo, InstanceId, NULL, DIOD_INHERIT_CLASSDRVS, &DevInfoData)) + { + LastError = GetLastError(); + goto cleanupDevInfo; + } + SP_REMOVEDEVICE_PARAMS RemoveDeviceParams = { .ClassInstallHeader = { .cbSize = sizeof(SP_CLASSINSTALL_HEADER), + .InstallFunction = DIF_REMOVE }, + .Scope = DI_REMOVEDEVICE_GLOBAL }; + if (!SetupDiSetClassInstallParamsW( + DevInfo, &DevInfoData, &RemoveDeviceParams.ClassInstallHeader, sizeof(RemoveDeviceParams)) || + !SetupDiCallClassInstaller(DIF_REMOVE, DevInfo, &DevInfoData)) + { + LastError = GetLastError(); + goto cleanupDevInfo; + } + +cleanupDevInfo: + SetupDiDestroyDeviceInfoList(DevInfo); +cleanup: + LocalFree(Argv); + + WriteFormatted(STD_OUTPUT_HANDLE, L"%1!X!", LastError); +} + +VOID __stdcall EnableInstance(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow) +{ +#pragma EXPORT + + DWORD LastError = ERROR_SUCCESS; + int Argc; + LPWSTR *Argv = CommandLineToArgvW(GetCommandLineW(), &Argc); + + if (Argc < 3) + goto cleanup; + WCHAR *InstanceId = Argv[2]; + + HDEVINFO DevInfo = SetupDiCreateDeviceInfoListExW(&GUID_DEVCLASS_NET, NULL, NULL, NULL); + if (DevInfo == INVALID_HANDLE_VALUE) + { + LastError = GetLastError(); + goto cleanup; + } + SP_DEVINFO_DATA DevInfoData = { .cbSize = sizeof(DevInfoData) }; + if (!SetupDiOpenDeviceInfoW(DevInfo, InstanceId, NULL, DIOD_INHERIT_CLASSDRVS, &DevInfoData)) + { + LastError = GetLastError(); + goto cleanupDevInfo; + } + SP_PROPCHANGE_PARAMS Params = { .ClassInstallHeader = { .cbSize = sizeof(SP_CLASSINSTALL_HEADER), + .InstallFunction = DIF_PROPERTYCHANGE }, + .StateChange = DICS_ENABLE, + .Scope = DICS_FLAG_GLOBAL }; + if (!SetupDiSetClassInstallParamsW(DevInfo, &DevInfoData, &Params.ClassInstallHeader, sizeof(Params)) || + !SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, DevInfo, &DevInfoData)) + { + LastError = GetLastError(); + goto cleanupDevInfo; + } + +cleanupDevInfo: + SetupDiDestroyDeviceInfoList(DevInfo); +cleanup: + LocalFree(Argv); + + WriteFormatted(STD_OUTPUT_HANDLE, L"%1!X!", LastError); +} + +VOID __stdcall DisableInstance(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow) +{ +#pragma EXPORT + + DWORD LastError = ERROR_SUCCESS; + int Argc; + LPWSTR *Argv = CommandLineToArgvW(GetCommandLineW(), &Argc); + + if (Argc < 3) + goto cleanup; + WCHAR *InstanceId = Argv[2]; + + HDEVINFO DevInfo = SetupDiCreateDeviceInfoListExW(&GUID_DEVCLASS_NET, NULL, NULL, NULL); + if (DevInfo == INVALID_HANDLE_VALUE) + { + LastError = GetLastError(); + goto cleanup; + } + SP_DEVINFO_DATA DevInfoData = { .cbSize = sizeof(DevInfoData) }; + if (!SetupDiOpenDeviceInfoW(DevInfo, InstanceId, NULL, DIOD_INHERIT_CLASSDRVS, &DevInfoData)) + { + LastError = GetLastError(); + goto cleanupDevInfo; + } + SP_PROPCHANGE_PARAMS Params = { .ClassInstallHeader = { .cbSize = sizeof(SP_CLASSINSTALL_HEADER), + .InstallFunction = DIF_PROPERTYCHANGE }, + .StateChange = DICS_DISABLE, + .Scope = DICS_FLAG_GLOBAL }; + if (!SetupDiSetClassInstallParamsW(DevInfo, &DevInfoData, &Params.ClassInstallHeader, sizeof(Params)) || + !SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, DevInfo, &DevInfoData)) + { + LastError = GetLastError(); + goto cleanupDevInfo; + } + +cleanupDevInfo: + SetupDiDestroyDeviceInfoList(DevInfo); +cleanup: + LocalFree(Argv); + + WriteFormatted(STD_OUTPUT_HANDLE, L"%1!X!", LastError); +} + +#if NTDDI_VERSION == NTDDI_WIN7 +#include "host_win7.h" +#endif diff --git a/setupapihost/host_win7.h b/setupapihost/host_win7.h new file mode 100644 index 0000000..4e65bff --- /dev/null +++ b/setupapihost/host_win7.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved. + */ + +#include + +#define MAX_INSTANCE_ID MAX_PATH /* TODO: Is MAX_PATH always enough? */ +#define WINTUN_HWID L"Wintun" + +VOID __stdcall CreateInstanceWin7(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow) +{ +#pragma EXPORT + + DWORD LastError = ERROR_SUCCESS; + WCHAR InstanceId[MAX_INSTANCE_ID] = { 0 }; + + HDEVINFO DevInfo = SetupDiCreateDeviceInfoListExW(&GUID_DEVCLASS_NET, NULL, NULL, NULL); + if (DevInfo == INVALID_HANDLE_VALUE) + { + LastError = GetLastError(); + goto cleanup; + } + SP_DEVINFO_DATA DevInfoData = { .cbSize = sizeof(DevInfoData) }; + if (!SetupDiCreateDeviceInfoW( + DevInfo, WINTUN_HWID, &GUID_DEVCLASS_NET, NULL, NULL, DICD_GENERATE_ID, &DevInfoData)) + { + LastError = GetLastError(); + goto cleanupDevInfo; + } + SP_DEVINSTALL_PARAMS_W DevInstallParams = { .cbSize = sizeof(DevInstallParams) }; + if (!SetupDiGetDeviceInstallParamsW(DevInfo, &DevInfoData, &DevInstallParams)) + { + LastError = GetLastError(); + goto cleanupDevInfo; + } + DevInstallParams.Flags |= DI_QUIETINSTALL; + if (!SetupDiSetDeviceInstallParamsW(DevInfo, &DevInfoData, &DevInstallParams)) + { + LastError = GetLastError(); + goto cleanupDevInfo; + } + if (!SetupDiSetSelectedDevice(DevInfo, &DevInfoData)) + { + LastError = GetLastError(); + goto cleanupDevInfo; + } + static const WCHAR Hwids[_countof(WINTUN_HWID) + 1 /*Multi-string terminator*/] = WINTUN_HWID; + if (!SetupDiSetDeviceRegistryPropertyW(DevInfo, &DevInfoData, SPDRP_HARDWAREID, (const BYTE *)Hwids, sizeof(Hwids))) + { + LastError = GetLastError(); + goto cleanupDevInfo; + } + if (!SetupDiBuildDriverInfoList(DevInfo, &DevInfoData, SPDIT_COMPATDRIVER)) + { + LastError = GetLastError(); + goto cleanupDevInfo; + } + SP_DRVINFO_DATA_W DrvInfoData = { .cbSize = sizeof(SP_DRVINFO_DATA_W) }; + if (!SetupDiEnumDriverInfoW(DevInfo, &DevInfoData, SPDIT_COMPATDRIVER, 0, &DrvInfoData) || + !SetupDiSetSelectedDriverW(DevInfo, &DevInfoData, &DrvInfoData)) + { + LastError = GetLastError(); + goto cleanupDriverInfo; + } + + if (!SetupDiCallClassInstaller(DIF_REGISTERDEVICE, DevInfo, &DevInfoData)) + { + LastError = GetLastError(); + goto cleanupDevInfo; + } + SetupDiCallClassInstaller(DIF_REGISTER_COINSTALLERS, DevInfo, &DevInfoData); + SetupDiCallClassInstaller(DIF_INSTALLINTERFACES, DevInfo, &DevInfoData); + if (!SetupDiCallClassInstaller(DIF_INSTALLDEVICE, DevInfo, &DevInfoData)) + { + LastError = GetLastError(); + goto cleanupDevice; + } + DWORD RequiredChars = _countof(InstanceId); + if (!SetupDiGetDeviceInstanceIdW(DevInfo, &DevInfoData, InstanceId, RequiredChars, &RequiredChars)) + { + LastError = GetLastError(); + goto cleanupDevice; + } + +cleanupDevice: + if (LastError != ERROR_SUCCESS) + { + SP_REMOVEDEVICE_PARAMS RemoveDeviceParams = { .ClassInstallHeader = { .cbSize = sizeof(SP_CLASSINSTALL_HEADER), + .InstallFunction = DIF_REMOVE }, + .Scope = DI_REMOVEDEVICE_GLOBAL }; + if (SetupDiSetClassInstallParamsW( + DevInfo, &DevInfoData, &RemoveDeviceParams.ClassInstallHeader, sizeof(RemoveDeviceParams))) + SetupDiCallClassInstaller(DIF_REMOVE, DevInfo, &DevInfoData); + } +cleanupDriverInfo: + SetupDiDestroyDriverInfoList(DevInfo, &DevInfoData, SPDIT_COMPATDRIVER); +cleanupDevInfo: + SetupDiDestroyDeviceInfoList(DevInfo); +cleanup: + WriteFormatted(STD_OUTPUT_HANDLE, L"%1!X! %2!s!", LastError, LastError == ERROR_SUCCESS ? InstanceId : L"\"\""); +} diff --git a/setupapihost/setupapihost.vcxproj b/setupapihost/setupapihost.vcxproj new file mode 100644 index 0000000..147c429 --- /dev/null +++ b/setupapihost/setupapihost.vcxproj @@ -0,0 +1,37 @@ + + + + {9911D673-CF5F-4B41-B190-807AA1BE445B} + setupapihost + setupapihost + + + DynamicLibrary + WindowsApplicationForDrivers10.0 + + + + setupapihost + + + + _WINDOWS;_USRDLL;%(PreprocessorDefinitions) + /volatile:iso %(AdditionalOptions) + 4100;4201;$(DisableSpecificWarnings) + + + setupapi.dll;shell32.dll + Setupapi.lib;%(AdditionalDependencies) + Windows + + + + + + + + + + + + diff --git a/wintun.proj b/wintun.proj index 07310cd..73ae397 100644 --- a/wintun.proj +++ b/wintun.proj @@ -59,21 +59,21 @@ - + - + - + - +