d8fe1419fb
There's only one handle that's likely to be open in a long lived way: the tun registration handle. So we can force that closed automatically when the device is about to close, if it's been improperly left open. Other handles will indeed hold up closing, but if those exist, they're a sign of a larger bug elsewhere that should be addressed. On the other hand, tun registration handles might legitimately be open during driver upgrades. This also saves us the trouble of dereferencing a freed FileObject as in the general case. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
946 lines
33 KiB
C
946 lines
33 KiB
C
/* SPDX-License-Identifier: GPL-2.0
|
|
*
|
|
* Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
|
|
*/
|
|
|
|
#include <Windows.h>
|
|
#include <winternl.h>
|
|
#include <cfgmgr32.h>
|
|
#include <devguid.h>
|
|
#include <iphlpapi.h>
|
|
#include <objbase.h>
|
|
#include <ndisguid.h>
|
|
#include <SetupAPI.h>
|
|
#include <Shlwapi.h>
|
|
#include <devioctl.h>
|
|
#include <wchar.h>
|
|
#include <initguid.h> /* 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 <devpkey.h>
|
|
|
|
/* 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 <devquery.h>
|
|
# include <swdevice.h>
|
|
# undef NTDDI_VERSION
|
|
# define NTDDI_VERSION NTDDI_WIN7
|
|
#else
|
|
# include <devquery.h>
|
|
# include <swdevice.h>
|
|
#endif
|
|
|
|
#include "adapter.h"
|
|
#include "driver.h"
|
|
#include "logger.h"
|
|
#include "main.h"
|
|
#include "namespace.h"
|
|
#include "nci.h"
|
|
#include "ntdll.h"
|
|
#include "rundll32.h"
|
|
#include "registry.h"
|
|
#include "adapter_win7.h"
|
|
|
|
#pragma warning(disable : 4221) /* nonstandard: address of automatic in initializer */
|
|
|
|
const DEVPROPKEY DEVPKEY_Wintun_Name = {
|
|
{ 0x3361c968, 0x2f2e, 0x4660, { 0xb4, 0x7e, 0x69, 0x9c, 0xdc, 0x4c, 0x32, 0xb9 } },
|
|
DEVPROPID_FIRST_USABLE + 1
|
|
};
|
|
|
|
_Must_inspect_result_
|
|
static _Return_type_success_(return != FALSE)
|
|
BOOL
|
|
PopulateAdapterData(_Inout_ WINTUN_ADAPTER *Adapter)
|
|
{
|
|
DWORD LastError = ERROR_SUCCESS;
|
|
|
|
/* Open HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\<class>\<id> 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 device registry key");
|
|
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;
|
|
}
|
|
|
|
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"<unknown>";
|
|
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->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 != 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
|
|
AdapterGetDeviceObjectFileName(LPCWSTR InstanceId)
|
|
{
|
|
ULONG InterfacesLen;
|
|
DWORD LastError = CM_MapCrToWin32Err(
|
|
CM_Get_Device_Interface_List_SizeW(
|
|
&InterfacesLen,
|
|
(GUID *)&GUID_DEVINTERFACE_NET,
|
|
(DEVINSTID_W)InstanceId,
|
|
CM_GET_DEVICE_INTERFACE_LIST_PRESENT),
|
|
ERROR_GEN_FAILURE);
|
|
if (LastError != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(LOG_ERROR(LastError, L"Failed to query adapter %s associated instances size", InstanceId));
|
|
return NULL;
|
|
}
|
|
LPWSTR Interfaces = AllocArray(InterfacesLen, sizeof(*Interfaces));
|
|
if (!Interfaces)
|
|
return NULL;
|
|
LastError = CM_MapCrToWin32Err(
|
|
CM_Get_Device_Interface_ListW(
|
|
(GUID *)&GUID_DEVINTERFACE_NET,
|
|
(DEVINSTID_W)InstanceId,
|
|
Interfaces,
|
|
InterfacesLen,
|
|
CM_GET_DEVICE_INTERFACE_LIST_PRESENT),
|
|
ERROR_GEN_FAILURE);
|
|
if (LastError != ERROR_SUCCESS)
|
|
{
|
|
LOG_ERROR(LastError, L"Failed to get adapter %s associated instances", InstanceId);
|
|
Free(Interfaces);
|
|
SetLastError(LastError);
|
|
return NULL;
|
|
}
|
|
if (!Interfaces[0])
|
|
{
|
|
Free(Interfaces);
|
|
SetLastError(ERROR_DEVICE_NOT_AVAILABLE);
|
|
return NULL;
|
|
}
|
|
return Interfaces;
|
|
}
|
|
|
|
typedef struct _WAIT_FOR_INTERFACE_CTX
|
|
{
|
|
HANDLE Event;
|
|
DWORD LastError;
|
|
} WAIT_FOR_INTERFACE_CTX;
|
|
|
|
static VOID WINAPI
|
|
WaitForInterfaceCallback(
|
|
_In_ HDEVQUERY DevQuery,
|
|
_Inout_ PVOID Context,
|
|
_In_ const DEV_QUERY_RESULT_ACTION_DATA *ActionData)
|
|
{
|
|
WAIT_FOR_INTERFACE_CTX *Ctx = Context;
|
|
Ctx->LastError = ERROR_SUCCESS;
|
|
if (ActionData->Action == DevQueryResultStateChange)
|
|
{
|
|
if (ActionData->Data.State != DevQueryStateAborted)
|
|
return;
|
|
Ctx->LastError = ERROR_DEVICE_NOT_AVAILABLE;
|
|
}
|
|
else if (ActionData->Action == DevQueryResultRemove)
|
|
return;
|
|
SetEvent(Ctx->Event);
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
static _Return_type_success_(return != FALSE)
|
|
BOOL
|
|
WaitForInterface(_In_ WCHAR *InstanceId)
|
|
{
|
|
if (IsWindows7)
|
|
return TRUE;
|
|
|
|
DWORD LastError = ERROR_SUCCESS;
|
|
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_LAST_ERROR(L"Failed to create event");
|
|
goto cleanup;
|
|
}
|
|
HDEVQUERY Query;
|
|
HRESULT HRet = DevCreateObjectQuery(
|
|
DevObjectTypeDeviceInterface,
|
|
DevQueryFlagUpdateResults,
|
|
0,
|
|
NULL,
|
|
_countof(Filters),
|
|
Filters,
|
|
WaitForInterfaceCallback,
|
|
&Ctx,
|
|
&Query);
|
|
if (FAILED(HRet))
|
|
{
|
|
LastError = LOG_ERROR(HRet, L"Failed to create device query");
|
|
goto cleanupEvent;
|
|
}
|
|
LastError = WaitForSingleObject(Ctx.Event, 15000);
|
|
if (LastError != WAIT_OBJECT_0)
|
|
{
|
|
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 = Ctx.LastError;
|
|
if (LastError != ERROR_SUCCESS)
|
|
LastError = LOG_ERROR(LastError, L"Failed to get enabled device");
|
|
cleanupQuery:
|
|
DevCloseObjectQuery(Query);
|
|
cleanupEvent:
|
|
CloseHandle(Ctx.Event);
|
|
cleanup:
|
|
return RET_ERROR(TRUE, LastError);
|
|
}
|
|
|
|
typedef struct _SW_DEVICE_CREATE_CTX
|
|
{
|
|
HRESULT CreateResult;
|
|
WCHAR *DeviceInstanceId;
|
|
HANDLE Triggered;
|
|
} SW_DEVICE_CREATE_CTX;
|
|
|
|
static VOID
|
|
DeviceCreateCallback(
|
|
_In_ HSWDEVICE SwDevice,
|
|
_In_ HRESULT CreateResult,
|
|
_In_ VOID *Context,
|
|
_In_opt_ PCWSTR DeviceInstanceId)
|
|
{
|
|
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 Name, LPCWSTR TunnelType, const GUID *RequestedGUID)
|
|
{
|
|
DWORD LastError = ERROR_SUCCESS;
|
|
WINTUN_ADAPTER *Adapter = NULL;
|
|
|
|
HANDLE DeviceInstallationMutex = NamespaceTakeDeviceInstallationMutex();
|
|
if (!DeviceInstallationMutex)
|
|
{
|
|
LastError = LOG_LAST_ERROR(L"Failed to take device installation mutex");
|
|
goto cleanup;
|
|
}
|
|
|
|
HDEVINFO DevInfoExistingAdapters;
|
|
SP_DEVINFO_DATA_LIST *ExistingAdapters;
|
|
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;
|
|
}
|
|
|
|
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_ERROR(CM_MapCrToWin32Err(ConfigRet, ERROR_GEN_FAILURE), L"Failed to get root node name");
|
|
goto cleanupAdapter;
|
|
}
|
|
|
|
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_ERROR(HRet, L"Failed to convert GUID");
|
|
goto cleanupAdapter;
|
|
}
|
|
SW_DEVICE_CREATE_CTX CreateContext = { .DeviceInstanceId = Adapter->DevInstanceID,
|
|
.Triggered = CreateEventW(NULL, FALSE, FALSE, NULL) };
|
|
if (!CreateContext.Triggered)
|
|
{
|
|
LastError = LOG_LAST_ERROR(L"Failed to create event trigger");
|
|
goto cleanupAdapter;
|
|
}
|
|
|
|
if (IsWindows7)
|
|
{
|
|
if (!CreateAdapterWin7(Adapter, Name, TunnelTypeName))
|
|
{
|
|
LastError = GetLastError();
|
|
goto cleanupCreateContext;
|
|
}
|
|
goto skipSwDevice;
|
|
}
|
|
if (!IsWindows10)
|
|
goto skipStub;
|
|
|
|
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)
|
|
{
|
|
Adapter->DevInfo = NULL;
|
|
goto cleanupCreateContext;
|
|
}
|
|
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)&NtStatus,
|
|
sizeof(NtStatus),
|
|
NULL,
|
|
0) ||
|
|
PropertyType != DEVPROP_TYPE_NTSTATUS)
|
|
NtStatus = 0;
|
|
if (!SetupDiGetDevicePropertyW(
|
|
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;
|
|
}
|
|
|
|
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)
|
|
{
|
|
WintunCloseAdapter(Adapter);
|
|
Adapter = NULL;
|
|
}
|
|
cleanupDriverInstall:
|
|
DriverInstallDeferredCleanup(DevInfoExistingAdapters, ExistingAdapters);
|
|
cleanupDeviceInstallationMutex:
|
|
NamespaceReleaseMutex(DeviceInstallationMutex);
|
|
cleanup:
|
|
QueueUpOrphanedDeviceCleanupRoutine();
|
|
return RET_ERROR(Adapter, LastError);
|
|
}
|
|
|
|
_Use_decl_annotations_
|
|
WINTUN_ADAPTER_HANDLE WINAPI
|
|
WintunOpenAdapter(LPCWSTR Name)
|
|
{
|
|
DWORD LastError = ERROR_SUCCESS;
|
|
WINTUN_ADAPTER *Adapter = NULL;
|
|
|
|
HANDLE DeviceInstallationMutex = NamespaceTakeDeviceInstallationMutex();
|
|
if (!DeviceInstallationMutex)
|
|
{
|
|
LastError = LOG_LAST_ERROR(L"Failed to take device installation mutex");
|
|
goto cleanup;
|
|
}
|
|
|
|
Adapter = Zalloc(sizeof(*Adapter));
|
|
if (!Adapter)
|
|
goto cleanupDeviceInstallationMutex;
|
|
|
|
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 cleanupAdapter;
|
|
}
|
|
|
|
SP_DEVINFO_DATA DevInfoData = { .cbSize = sizeof(DevInfoData) };
|
|
BOOL Found = FALSE;
|
|
for (DWORD EnumIndex = 0; !Found; ++EnumIndex)
|
|
{
|
|
if (!SetupDiEnumDeviceInfo(DevInfo, EnumIndex, &DevInfoData))
|
|
{
|
|
if (GetLastError() == ERROR_NO_MORE_ITEMS)
|
|
break;
|
|
continue;
|
|
}
|
|
|
|
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);
|
|
cleanupAdapter:
|
|
if (LastError != ERROR_SUCCESS)
|
|
{
|
|
WintunCloseAdapter(Adapter);
|
|
Adapter = NULL;
|
|
}
|
|
cleanupDeviceInstallationMutex:
|
|
NamespaceReleaseMutex(DeviceInstallationMutex);
|
|
cleanup:
|
|
QueueUpOrphanedDeviceCleanupRoutine();
|
|
return RET_ERROR(Adapter, LastError);
|
|
}
|
|
|
|
_Use_decl_annotations_
|
|
BOOL
|
|
AdapterRemoveInstance(HDEVINFO DevInfo, SP_DEVINFO_DATA *DevInfoData)
|
|
{
|
|
#ifdef MAYBE_WOW64
|
|
if (NativeMachine != IMAGE_FILE_PROCESS)
|
|
return RemoveInstanceViaRundll32(DevInfo, DevInfoData);
|
|
#endif
|
|
|
|
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
|
|
AdapterEnableInstance(HDEVINFO DevInfo, SP_DEVINFO_DATA *DevInfoData)
|
|
{
|
|
#ifdef MAYBE_WOW64
|
|
if (NativeMachine != IMAGE_FILE_PROCESS)
|
|
return EnableInstanceViaRundll32(DevInfo, DevInfoData);
|
|
#endif
|
|
|
|
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);
|
|
}
|