470bdf3e26
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
951 lines
33 KiB
C
951 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;
|
|
DWORD Ret = ERROR_SUCCESS;
|
|
switch (ActionData->Action)
|
|
{
|
|
case DevQueryResultStateChange:
|
|
if (ActionData->Data.State != DevQueryStateAborted)
|
|
return;
|
|
Ret = ERROR_DEVICE_NOT_AVAILABLE;
|
|
case DevQueryResultAdd:
|
|
case DevQueryResultUpdate:
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
Ctx->LastError = Ret;
|
|
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_DEVICE_ID_LEN, 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);
|
|
}
|