aboutsummaryrefslogtreecommitdiffstats
path: root/api
diff options
context:
space:
mode:
authorSimon Rozman <simon@rozman.si>2020-10-13 19:42:30 +0200
committerJason A. Donenfeld <Jason@zx2c4.com>2020-10-30 16:50:59 +0100
commitc324d07ffb6c7dc2a59ad968a6d9322af1a446e2 (patch)
tree36fb32ed73b6ceb1154f99af888cbbb7288ede2f /api
parentapi: introduce logging (diff)
downloadwintun-c324d07ffb6c7dc2a59ad968a6d9322af1a446e2.tar.xz
wintun-c324d07ffb6c7dc2a59ad968a6d9322af1a446e2.zip
api: add driver management
Signed-off-by: Simon Rozman <simon@rozman.si>
Diffstat (limited to 'api')
-rw-r--r--api/api.vcxproj8
-rw-r--r--api/api.vcxproj.filters6
-rw-r--r--api/driver.c559
-rw-r--r--api/driver.h4
-rw-r--r--api/pch.h6
-rw-r--r--api/resource.c50
-rw-r--r--api/resource.h15
-rw-r--r--api/resources.rc12
8 files changed, 659 insertions, 1 deletions
diff --git a/api/api.vcxproj b/api/api.vcxproj
index 9dca84f..e405f0e 100644
--- a/api/api.vcxproj
+++ b/api/api.vcxproj
@@ -143,14 +143,18 @@
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions>_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Platform)'=='Win32' Or '$(Platform)'=='x64' Or '$(Configuration)|$(Platform)'=='Debug|ARM64'">HAVE_EV;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="Exists('..\$(WintunPlatform)\$(Configuration)\whql\')">HAVE_WHQL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<ResourceCompile>
<AdditionalIncludeDirectories>..\$(WintunPlatform)\$(Configuration);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="'$(Platform)'=='Win32' Or '$(Platform)'=='x64' Or '$(Configuration)|$(Platform)'=='Debug|ARM64'">HAVE_EV;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="Exists('..\$(WintunPlatform)\$(Configuration)\whql\')">HAVE_WHQL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
<Link>
- <AdditionalDependencies>Bcrypt.lib;Cfgmgr32.lib;Iphlpapi.lib;Setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalDependencies>Bcrypt.lib;Cfgmgr32.lib;Crypt32.lib;Iphlpapi.lib;newdev.lib;ntdll.lib;Setupapi.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>exports.def</ModuleDefinitionFile>
<SubSystem>Windows</SubSystem>
</Link>
@@ -193,6 +197,7 @@
<ClInclude Include="nci.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="registry.h" />
+ <ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="api.c" />
@@ -205,6 +210,7 @@
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="registry.c" />
+ <ClCompile Include="resource.c" />
<ClCompile Include="rundll32.c" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
diff --git a/api/api.vcxproj.filters b/api/api.vcxproj.filters
index 20b91f1..188ad86 100644
--- a/api/api.vcxproj.filters
+++ b/api/api.vcxproj.filters
@@ -46,6 +46,9 @@
<ClInclude Include="logger.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="driver.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -75,6 +78,9 @@
<ClCompile Include="logger.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="resource.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="driver.c">
<Filter>Source Files</Filter>
</ClCompile>
diff --git a/api/driver.c b/api/driver.c
index b6bf11c..df10ace 100644
--- a/api/driver.c
+++ b/api/driver.c
@@ -5,6 +5,14 @@
#include "pch.h"
+#pragma warning(disable : 4221) /* nonstandard: address of automatic in initializer */
+
+typedef struct _SP_DEVINFO_DATA_LIST
+{
+ SP_DEVINFO_DATA Data;
+ struct _SP_DEVINFO_DATA_LIST *Next;
+} SP_DEVINFO_DATA_LIST;
+
/**
* Retrieves driver information detail for a device information set or a particular device information element in the
* device information set.
@@ -55,6 +63,308 @@ out:
}
/**
+ * Checks if the Wintun driver is loaded.
+ *
+ * Note: This function does not log any errors, not to flood the log when called from the EnsureDriverUnloaded() loop.
+ *
+ * @return non-zero when loaded; zero when not loaded or error - use GetLastError().
+ */
+static BOOL IsDriverLoaded(VOID)
+{
+ VOID *StackBuffer[0x80];
+ VOID **Drivers = StackBuffer;
+ DWORD Size = 0;
+ if (!EnumDeviceDrivers(Drivers, sizeof(StackBuffer), &Size))
+ return FALSE;
+ if (Size > sizeof(StackBuffer))
+ {
+ HANDLE Heap = GetProcessHeap();
+ Drivers = HeapAlloc(Heap, 0, Size);
+ if (!Drivers)
+ {
+ SetLastError(ERROR_OUTOFMEMORY);
+ return FALSE;
+ }
+ if (!EnumDeviceDrivers(Drivers, Size, &Size))
+ {
+ DWORD Result = GetLastError();
+ HeapFree(Heap, 0, Drivers);
+ SetLastError(Result);
+ return FALSE;
+ }
+ }
+ BOOL Found = FALSE;
+ for (DWORD i = Size / sizeof(Drivers[0]); i-- > 0;)
+ {
+ WCHAR MaybeWintun[11];
+ if (GetDeviceDriverBaseNameW(Drivers[i], MaybeWintun, _countof(MaybeWintun)) == 10 &&
+ !_wcsicmp(MaybeWintun, L"wintun.sys"))
+ {
+ Found = TRUE;
+ break;
+ }
+ }
+ if (Drivers != StackBuffer)
+ HeapFree(GetProcessHeap(), 0, Drivers);
+ SetLastError(ERROR_SUCCESS);
+ return Found;
+}
+
+/**
+ * Polls for 15 sec until the Wintun driver is unloaded.
+ *
+ * @return non-zero if the driver unloaded; zero on error or timeout - use GetLastError().
+ */
+static BOOL EnsureDriverUnloaded(VOID)
+{
+ BOOL Loaded;
+ for (int i = 0; (Loaded = IsDriverLoaded()) != 0 && i < 300; ++i)
+ Sleep(50);
+ return !Loaded;
+}
+
+/**
+ * Installs code-signing certificate to the computer's Trusted Publishers certificate store.
+ *
+ * @param SignedResource ID of the RT_RCDATA resource containing the signed binary to extract the code-signing
+ * certificate from.
+ *
+ * @return ERROR_SUCCESS on success; Win32 error code otherwise.
+ */
+static WINTUN_STATUS
+InstallCertificate(_In_z_ const WCHAR *SignedResource)
+{
+ WINTUN_LOGGER(WINTUN_LOG_INFO, L"Trusting code signing certificate");
+ HRSRC FoundResource = FindResourceW(ResourceModule, SignedResource, RT_RCDATA);
+ if (!FoundResource)
+ return WINTUN_LOGGER_LAST_ERROR(L"Failed to find resource");
+ DWORD SizeResource = SizeofResource(ResourceModule, FoundResource);
+ if (!SizeResource)
+ return WINTUN_LOGGER_LAST_ERROR(L"Failed to size resource");
+ HGLOBAL LoadedResource = LoadResource(ResourceModule, FoundResource);
+ if (!LoadedResource)
+ return WINTUN_LOGGER_LAST_ERROR(L"Failed to load resource");
+ LPVOID LockedResource = LockResource(LoadedResource);
+ if (!LockedResource)
+ {
+ WINTUN_LOGGER(WINTUN_LOG_ERR, L"Failed to lock resource");
+ return ERROR_LOCK_FAILED;
+ }
+ const CERT_BLOB CertBlob = { .cbData = SizeResource, .pbData = LockedResource };
+ HCERTSTORE QueriedStore;
+ if (!CryptQueryObject(
+ CERT_QUERY_OBJECT_BLOB,
+ &CertBlob,
+ CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
+ CERT_QUERY_FORMAT_FLAG_ALL,
+ 0,
+ 0,
+ 0,
+ 0,
+ &QueriedStore,
+ 0,
+ NULL))
+ return WINTUN_LOGGER_LAST_ERROR("Failed to find certificate");
+ DWORD Result = ERROR_SUCCESS;
+ HCERTSTORE TrustedStore =
+ CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_LOCAL_MACHINE, L"TrustedPublisher");
+ if (!TrustedStore)
+ {
+ Result = WINTUN_LOGGER_LAST_ERROR(L"Failed to open store");
+ goto cleanupQueriedStore;
+ }
+ LPSTR CodeSigningOid[] = { szOID_PKIX_KP_CODE_SIGNING };
+ CERT_ENHKEY_USAGE EnhancedUsage = { .cUsageIdentifier = 1, .rgpszUsageIdentifier = CodeSigningOid };
+ for (const CERT_CONTEXT *CertContext = NULL; (CertContext = CertFindCertificateInStore(
+ QueriedStore,
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG,
+ CERT_FIND_ENHKEY_USAGE,
+ &EnhancedUsage,
+ CertContext)) != NULL;)
+ {
+ CERT_EXTENSION *Ext = CertFindExtension(
+ szOID_BASIC_CONSTRAINTS2, CertContext->pCertInfo->cExtension, CertContext->pCertInfo->rgExtension);
+ CERT_BASIC_CONSTRAINTS2_INFO Constraints;
+ DWORD Size = sizeof(Constraints);
+ if (Ext &&
+ CryptDecodeObjectEx(
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ szOID_BASIC_CONSTRAINTS2,
+ Ext->Value.pbData,
+ Ext->Value.cbData,
+ 0,
+ NULL,
+ &Constraints,
+ &Size) &&
+ !Constraints.fCA)
+ if (!CertAddCertificateContextToStore(TrustedStore, CertContext, CERT_STORE_ADD_REPLACE_EXISTING, NULL))
+ {
+ WINTUN_LOGGER_LAST_ERROR(L"Failed to add certificate to store");
+ Result = Result != ERROR_SUCCESS ? Result : GetLastError();
+ }
+ }
+ CertCloseStore(TrustedStore, 0);
+cleanupQueriedStore:
+ CertCloseStore(QueriedStore, 0);
+ return Result;
+}
+
+/* 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.
+ *
+ * Another way would be reading from the PEB directly:
+ * ((DWORD *)NtCurrentTeb()->ProcessEnvironmentBlock)[sizeof(void *) == 8 ? 70 : 41]
+ * Or just read from KUSER_SHARED_DATA the same way on 32-bit and 64-bit:
+ * *(DWORD *)0x7FFE026C
+ */
+extern VOID NTAPI
+RtlGetNtVersionNumbers(_Out_opt_ DWORD *MajorVersion, _Out_opt_ DWORD *MinorVersion, _Out_opt_ DWORD *BuildNumber);
+
+/**
+ * Installs Wintun driver to the Windows driver store and updates existing adapters to use it.
+ *
+ * @param UpdateExisting Set to non-zero when existing adapters should be upgraded to the newest driver.
+ *
+ * @return ERROR_SUCCESS on success; Win32 error code otherwise.
+ */
+static WINTUN_STATUS
+InstallDriver(_In_ BOOL UpdateExisting)
+{
+ WCHAR WindowsDirectory[MAX_PATH];
+ if (!GetWindowsDirectoryW(WindowsDirectory, _countof(WindowsDirectory)))
+ return WINTUN_LOGGER_LAST_ERROR(L"Failed to get Windows folder");
+ WCHAR WindowsTempDirectory[MAX_PATH];
+ if (!PathCombineW(WindowsTempDirectory, WindowsDirectory, L"Temp"))
+ return ERROR_BUFFER_OVERFLOW;
+ UCHAR RandomBytes[32] = { 0 };
+#pragma warning(suppress : 6387)
+ if (!RtlGenRandom(RandomBytes, sizeof(RandomBytes)))
+ return WINTUN_LOGGER_LAST_ERROR(L"Failed to generate random");
+ WCHAR RandomSubDirectory[sizeof(RandomBytes) * 2 + 1];
+ for (int i = 0; i < sizeof(RandomBytes); ++i)
+ swprintf_s(&RandomSubDirectory[i * 2], 3, L"%02x", RandomBytes[i]);
+ WCHAR RandomTempSubDirectory[MAX_PATH];
+ if (!PathCombineW(RandomTempSubDirectory, WindowsTempDirectory, RandomSubDirectory))
+ return ERROR_BUFFER_OVERFLOW;
+ SECURITY_ATTRIBUTES SecurityAttributes = { .nLength = sizeof(SecurityAttributes) };
+ if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(
+ L"O:SYD:P(A;;GA;;;SY)", SDDL_REVISION_1, &SecurityAttributes.lpSecurityDescriptor, NULL))
+ return WINTUN_LOGGER_LAST_ERROR(L"Failed to convert security descriptor");
+ DWORD Result = ERROR_SUCCESS;
+ if (!CreateDirectoryW(RandomTempSubDirectory, &SecurityAttributes))
+ {
+ Result = WINTUN_LOGGER_LAST_ERROR(L"Failed to create temporary folder");
+ goto cleanupFree;
+ }
+
+ WCHAR CatPath[MAX_PATH] = { 0 };
+ WCHAR SysPath[MAX_PATH] = { 0 };
+ WCHAR InfPath[MAX_PATH] = { 0 };
+ if (!PathCombineW(CatPath, RandomTempSubDirectory, L"wintun.cat") ||
+ !PathCombineW(SysPath, RandomTempSubDirectory, L"wintun.sys") ||
+ !PathCombineW(InfPath, RandomTempSubDirectory, L"wintun.inf"))
+ {
+ Result = ERROR_BUFFER_OVERFLOW;
+ goto cleanupFree;
+ }
+
+ BOOL UseWHQL = FALSE;
+#if defined(HAVE_EV) && defined(HAVE_WHQL)
+ DWORD MajorVersion;
+ RtlGetNtVersionNumbers(&MajorVersion, NULL, NULL);
+ UseWHQL = MajorVersion >= 10;
+#elif defined(HAVE_EV)
+ UseWHQL = FALSE;
+#elif defined(HAVE_WHQL)
+ UseWHQL = TRUE;
+#else
+# error No driver available
+#endif
+ if (!UseWHQL && (Result = InstallCertificate(L"wintun.sys")) != ERROR_SUCCESS)
+ WINTUN_LOGGER_ERROR(L"Unable to install code signing certificate", Result);
+
+ WINTUN_LOGGER(WINTUN_LOG_INFO, L"Copying resources to temporary path");
+ if ((Result = CopyResource(CatPath, &SecurityAttributes, UseWHQL ? L"wintun-whql.cat" : L"wintun.cat")) !=
+ ERROR_SUCCESS ||
+ (Result = CopyResource(SysPath, &SecurityAttributes, UseWHQL ? L"wintun-whql.sys" : L"wintun.sys")) !=
+ ERROR_SUCCESS ||
+ (Result = CopyResource(InfPath, &SecurityAttributes, UseWHQL ? L"wintun-whql.inf" : L"wintun.inf")) !=
+ ERROR_SUCCESS)
+ {
+ Result = WINTUN_LOGGER_LAST_ERROR(L"Failed to copy resources");
+ goto cleanupDelete;
+ }
+
+ WINTUN_LOGGER(WINTUN_LOG_INFO, L"Installing driver");
+ if (!SetupCopyOEMInfW(InfPath, NULL, SPOST_PATH, 0, NULL, 0, NULL, NULL))
+ Result = WINTUN_LOGGER_LAST_ERROR(L"Could not install driver to store");
+ BOOL RebootRequired = FALSE;
+ if (UpdateExisting &&
+ !UpdateDriverForPlugAndPlayDevicesW(
+ NULL, L"Wintun", InfPath, INSTALLFLAG_FORCE | INSTALLFLAG_NONINTERACTIVE, &RebootRequired))
+ WINTUN_LOGGER_LAST_ERROR(L"Could not update existing adapters");
+ if (RebootRequired)
+ WINTUN_LOGGER(WINTUN_LOG_WARN, L"A reboot might be required, which really should not be the case");
+
+cleanupDelete:
+ DeleteFileW(CatPath);
+ DeleteFileW(SysPath);
+ DeleteFileW(InfPath);
+ RemoveDirectoryW(RandomTempSubDirectory);
+cleanupFree:
+ LocalFree(SecurityAttributes.lpSecurityDescriptor);
+ return Result;
+}
+
+/**
+ * Removes Wintun driver from the Windows driver store.
+ *
+ * @return ERROR_SUCCESS on success; Win32 error code otherwise.
+ */
+static WINTUN_STATUS RemoveDriver(VOID)
+{
+ HDEVINFO DevInfo = SetupDiGetClassDevsW(&GUID_DEVCLASS_NET, NULL, NULL, 0);
+ if (!DevInfo)
+ return WINTUN_LOGGER_LAST_ERROR(L"Failed to request device information");
+ DWORD Result = ERROR_SUCCESS;
+ if (!SetupDiBuildDriverInfoList(DevInfo, NULL, SPDIT_CLASSDRIVER))
+ {
+ Result = WINTUN_LOGGER_LAST_ERROR(L"Failed to build list of drivers");
+ goto cleanupDeviceInfoSet;
+ }
+ HANDLE Heap = GetProcessHeap();
+ for (DWORD EnumIndex = 0;; ++EnumIndex)
+ {
+ SP_DRVINFO_DATA_W DrvInfoData = { .cbSize = sizeof(DrvInfoData) };
+ if (!SetupDiEnumDriverInfoW(DevInfo, NULL, SPDIT_CLASSDRIVER, EnumIndex, &DrvInfoData))
+ {
+ if (GetLastError() == ERROR_NO_MORE_ITEMS)
+ break;
+ continue;
+ }
+ SP_DRVINFO_DETAIL_DATA_W *DrvInfoDetailData = DriverGetDrvInfoDetail(DevInfo, NULL, &DrvInfoData);
+ if (!DrvInfoDetailData)
+ continue;
+ if (!_wcsicmp(DrvInfoDetailData->HardwareID, L"wintun"))
+ {
+ PathStripPathW(DrvInfoDetailData->InfFileName);
+ WINTUN_LOGGER(WINTUN_LOG_INFO, L"Removing existing driver");
+ if (!SetupUninstallOEMInfW(DrvInfoDetailData->InfFileName, SUOI_FORCEDELETE, NULL))
+ {
+ WINTUN_LOGGER_LAST_ERROR(L"Unable to remove existing driver");
+ Result = Result != ERROR_SUCCESS ? Result : GetLastError();
+ }
+ }
+ HeapFree(Heap, 0, DrvInfoDetailData);
+ }
+ SetupDiDestroyDriverInfoList(DevInfo, NULL, SPDIT_CLASSDRIVER);
+cleanupDeviceInfoSet:
+ SetupDiDestroyDeviceInfoList(DevInfo);
+ return Result;
+}
+
+/**
* Checks if the device (i.e. network adapter) is using Wintun driver.
*
* @param DevInfo A handle to the device information set that contains a device information element that
@@ -147,3 +457,252 @@ cleanupBuf:
SetLastError(Result);
return Handle;
}
+
+#define TUN_IOCTL_FORCE_CLOSE_HANDLES CTL_CODE(51820U, 0x971U, METHOD_NEITHER, FILE_READ_DATA | FILE_WRITE_DATA)
+
+/**
+ * Closes all client handles to the Wintun adapter.
+ *
+ * @param DevInfo A handle to the device information set that contains a device information element that
+ * represents the device.
+ *
+ * @param DevInfoData A pointer to a structure that specifies the device information element in DevInfo.
+ *
+ * @return ERROR_SUCCESS on success; Win32 error code otherwise.
+ */
+static WINTUN_STATUS
+ForceCloseWintunAdapterHandle(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData)
+{
+ DWORD Result = ERROR_SUCCESS;
+ DWORD RequiredBytes;
+ if (SetupDiGetDeviceInstanceIdW(DevInfo, DevInfoData, NULL, 0, &RequiredBytes) ||
+ (Result = GetLastError()) != ERROR_INSUFFICIENT_BUFFER)
+ return WINTUN_LOGGER_ERROR(L"Failed to query device instance ID size", Result);
+ HANDLE Heap = GetProcessHeap();
+ WCHAR *InstanceId = HeapAlloc(Heap, HEAP_ZERO_MEMORY, sizeof(*InstanceId) * RequiredBytes);
+ if (!InstanceId)
+ return ERROR_OUTOFMEMORY;
+ if (!SetupDiGetDeviceInstanceIdW(DevInfo, DevInfoData, InstanceId, RequiredBytes, &RequiredBytes))
+ {
+ Result = WINTUN_LOGGER_LAST_ERROR(L"Failed to get device instance ID");
+ goto out;
+ }
+ HANDLE NdisHandle = DriverGetAdapterDeviceObject(InstanceId);
+ if (NdisHandle == INVALID_HANDLE_VALUE)
+ {
+ Result = GetLastError();
+ goto out;
+ }
+ Result = DeviceIoControl(NdisHandle, TUN_IOCTL_FORCE_CLOSE_HANDLES, NULL, 0, NULL, 0, &RequiredBytes, NULL)
+ ? ERROR_SUCCESS
+ : WINTUN_LOGGER_LAST_ERROR(L"Failed to perform ioctl");
+ CloseHandle(NdisHandle);
+out:
+ HeapFree(Heap, 0, InstanceId);
+ return Result;
+}
+
+/**
+ * Disables Wintun adapters.
+ *
+ * @param DevInfo A handle to the device information set.
+ *
+ * @param DisabledAdapters Output list of disabled adapters. The adapters disabled are inserted in the list head.
+ *
+ * @return ERROR_SUCCESS on success; Win32 error code otherwise.
+ */
+static WINTUN_STATUS
+DisableWintunAdapters(_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 Result = ERROR_SUCCESS;
+ HANDLE Heap = GetProcessHeap();
+ for (DWORD EnumIndex = 0;; ++EnumIndex)
+ {
+ SP_DEVINFO_DATA_LIST *DeviceNode = HeapAlloc(Heap, 0, sizeof(SP_DEVINFO_DATA_LIST));
+ if (!DeviceNode)
+ return ERROR_OUTOFMEMORY;
+ DeviceNode->Data.cbSize = sizeof(SP_DEVINFO_DATA);
+ if (!SetupDiEnumDeviceInfo(DevInfo, EnumIndex, &DeviceNode->Data))
+ {
+ if (GetLastError() == ERROR_NO_MORE_ITEMS)
+ {
+ HeapFree(Heap, 0, DeviceNode);
+ break;
+ }
+ goto cleanupDeviceInfoData;
+ }
+ if (!DriverIsWintunAdapter(DevInfo, &DeviceNode->Data))
+ goto cleanupDeviceInfoData;
+
+ 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 cleanupDeviceInfoData;
+
+ WINTUN_LOGGER(WINTUN_LOG_INFO, L"Force closing all open handles for existing adapter");
+ if (ForceCloseWintunAdapterHandle(DevInfo, &DeviceNode->Data) != ERROR_SUCCESS)
+ WINTUN_LOGGER(WINTUN_LOG_WARN, L"Failed to force close adapter handles");
+ Sleep(200);
+
+ WINTUN_LOGGER(WINTUN_LOG_INFO, L"Disabling existing adapter");
+ if (!SetupDiSetClassInstallParamsW(DevInfo, &DeviceNode->Data, &Params.ClassInstallHeader, sizeof(Params)) ||
+ !SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, DevInfo, &DeviceNode->Data))
+ {
+ WINTUN_LOGGER_LAST_ERROR(L"Unable to disable existing adapter");
+ Result = Result != ERROR_SUCCESS ? Result : GetLastError();
+ goto cleanupDeviceInfoData;
+ }
+
+ DeviceNode->Next = *DisabledAdapters;
+ *DisabledAdapters = DeviceNode;
+ continue;
+
+ cleanupDeviceInfoData:
+ HeapFree(Heap, 0, &DeviceNode->Data);
+ }
+ return Result;
+}
+
+/**
+ * Removes all Wintun adapters.
+ *
+ * @param DevInfo A handle to the device information set.
+ *
+ * @param DisabledAdapters Output list of disabled adapters.
+ *
+ * @return ERROR_SUCCESS on success; Win32 error code otherwise.
+ */
+static WINTUN_STATUS
+RemoveWintunAdapters(_In_ HDEVINFO DevInfo)
+{
+ SP_REMOVEDEVICE_PARAMS Params = { .ClassInstallHeader = { .cbSize = sizeof(SP_CLASSINSTALL_HEADER),
+ .InstallFunction = DIF_REMOVE },
+ .Scope = DI_REMOVEDEVICE_GLOBAL };
+ DWORD Result = ERROR_SUCCESS;
+ for (DWORD EnumIndex = 0;; ++EnumIndex)
+ {
+ SP_DEVINFO_DATA DevInfoData = { .cbSize = sizeof(SP_DEVINFO_DATA) };
+ if (!SetupDiEnumDeviceInfo(DevInfo, EnumIndex, &DevInfoData))
+ {
+ if (GetLastError() == ERROR_NO_MORE_ITEMS)
+ break;
+ continue;
+ }
+ if (!DriverIsWintunAdapter(DevInfo, &DevInfoData))
+ continue;
+
+ WINTUN_LOGGER(WINTUN_LOG_INFO, L"Force closing all open handles for existing adapter");
+ if (ForceCloseWintunAdapterHandle(DevInfo, &DevInfoData) != ERROR_SUCCESS)
+ WINTUN_LOGGER(WINTUN_LOG_WARN, L"Failed to force close adapter handles");
+ Sleep(200);
+
+ WINTUN_LOGGER(WINTUN_LOG_INFO, L"Removing existing adapter");
+ if (!SetupDiSetClassInstallParamsW(DevInfo, &DevInfoData, &Params.ClassInstallHeader, sizeof(Params)) ||
+ !SetupDiCallClassInstaller(DIF_REMOVE, DevInfo, &DevInfoData))
+ {
+ WINTUN_LOGGER_LAST_ERROR(L"Unable to remove existing adapter");
+ Result = Result != ERROR_SUCCESS ? Result : GetLastError();
+ }
+ }
+ return Result;
+}
+
+/**
+ * Enables Wintun adapters.
+ *
+ * @param DevInfo A handle to the device information set.
+ *
+ * @param AdaptersToEnable Input list of adapters to enable.
+ *
+ * @return ERROR_SUCCESS on success; Win32 error code otherwise.
+ */
+static WINTUN_STATUS
+EnableWintunAdapters(_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 Result = ERROR_SUCCESS;
+ for (SP_DEVINFO_DATA_LIST *DeviceNode = AdaptersToEnable; DeviceNode; DeviceNode = DeviceNode->Next)
+ {
+ WINTUN_LOGGER(WINTUN_LOG_INFO, L"Enabling existing adapter");
+ if (!SetupDiSetClassInstallParamsW(DevInfo, &DeviceNode->Data, &Params.ClassInstallHeader, sizeof(Params)) ||
+ !SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, DevInfo, &DeviceNode->Data))
+ {
+ WINTUN_LOGGER_LAST_ERROR(L"Unable to enable existing adapter");
+ Result = Result != ERROR_SUCCESS ? Result : GetLastError();
+ }
+ }
+ return Result;
+}
+
+/**
+ * Installs or updates Wintun driver.
+ *
+ * @return ERROR_SUCCESS on success; Win32 error code otherwise.
+ */
+WINTUN_STATUS DriverInstallOrUpdate(VOID)
+{
+ HANDLE Heap = GetProcessHeap();
+ HDEVINFO DevInfo = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, NULL, NULL, DIGCF_PRESENT, NULL, NULL, NULL);
+ if (DevInfo == INVALID_HANDLE_VALUE)
+ return WINTUN_LOGGER_LAST_ERROR(L"Failed to get present class devices");
+ SP_DEVINFO_DATA_LIST *ExistingAdapters = NULL;
+ if (IsDriverLoaded())
+ {
+ DisableWintunAdapters(DevInfo, &ExistingAdapters);
+ WINTUN_LOGGER(WINTUN_LOG_INFO, L"Waiting for driver to unload from kernel");
+ if (!EnsureDriverUnloaded())
+ WINTUN_LOGGER(WINTUN_LOG_WARN, L"Unable to unload driver, which means a reboot will likely be required");
+ }
+ DWORD Result = ERROR_SUCCESS;
+ if ((Result = RemoveDriver()) != ERROR_SUCCESS)
+ {
+ WINTUN_LOGGER_ERROR(L"Failed to uninstall old drivers", Result);
+ goto cleanupAdapters;
+ }
+ if ((Result = InstallDriver(!!ExistingAdapters)) != ERROR_SUCCESS)
+ {
+ WINTUN_LOGGER_ERROR(L"Failed to install driver", Result);
+ goto cleanupAdapters;
+ }
+ WINTUN_LOGGER(WINTUN_LOG_INFO, L"Installation successful");
+
+cleanupAdapters:;
+ if (ExistingAdapters)
+ {
+ EnableWintunAdapters(DevInfo, ExistingAdapters);
+ while (ExistingAdapters)
+ {
+ SP_DEVINFO_DATA_LIST *Next = ExistingAdapters->Next;
+ HeapFree(Heap, 0, ExistingAdapters);
+ ExistingAdapters = Next;
+ }
+ }
+ SetupDiDestroyDeviceInfoList(DevInfo);
+ return Result;
+}
+
+/**
+ * Uninstalls Wintun driver.
+ *
+ * @return ERROR_SUCCESS on success; Win32 error code otherwise.
+ */
+WINTUN_STATUS DriverUninstall(VOID)
+{
+ HDEVINFO DevInfo = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, NULL, NULL, DIGCF_PRESENT, NULL, NULL, NULL);
+ if (DevInfo == INVALID_HANDLE_VALUE)
+ return WINTUN_LOGGER_LAST_ERROR(L"Failed to get present class devices");
+ RemoveWintunAdapters(DevInfo);
+ DWORD Result = RemoveDriver();
+ if (Result != ERROR_SUCCESS)
+ WINTUN_LOGGER_ERROR(L"Failed to uninstall driver", Result);
+ else
+ WINTUN_LOGGER(WINTUN_LOG_INFO, L"Uninstallation successful");
+ return Result;
+}
diff --git a/api/driver.h b/api/driver.h
index ab9a922..5d30878 100644
--- a/api/driver.h
+++ b/api/driver.h
@@ -19,3 +19,7 @@ DriverIsWintunAdapter(_In_ HDEVINFO DevInfo, _In_opt_ SP_DEVINFO_DATA *DevInfoDa
_Return_type_success_(return != INVALID_HANDLE_VALUE) HANDLE
DriverGetAdapterDeviceObject(_In_opt_z_ const WCHAR *InstanceId);
+
+WINTUN_STATUS DriverInstallOrUpdate(VOID);
+
+WINTUN_STATUS DriverUninstall(VOID);
diff --git a/api/pch.h b/api/pch.h
index 7ffad4e..5af7fbd 100644
--- a/api/pch.h
+++ b/api/pch.h
@@ -12,14 +12,20 @@
#include "namespace.h"
#include "nci.h"
#include "registry.h"
+#include "resource.h"
#include <bcrypt.h>
#include <cfgmgr32.h>
+#include <devguid.h>
#include <iphlpapi.h>
#include <locale.h>
#include <ndisguid.h>
+#include <newdev.h>
+#include <NTSecAPI.h>
#include <objbase.h>
+#include <Psapi.h>
#include <sddl.h>
#include <SetupAPI.h>
+#include <Shlwapi.h>
#include <string.h>
#include <wchar.h>
diff --git a/api/resource.c b/api/resource.c
new file mode 100644
index 0000000..b25b96d
--- /dev/null
+++ b/api/resource.c
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018-2020 WireGuard LLC. All Rights Reserved.
+ */
+
+#include "pch.h"
+
+WINTUN_STATUS
+CopyResource(
+ _In_z_ const WCHAR *DestinationPath,
+ _In_opt_ SECURITY_ATTRIBUTES *SecurityAttributes,
+ _In_z_ const WCHAR *ResourceName)
+{
+ HRSRC FoundResource = FindResourceW(ResourceModule, ResourceName, RT_RCDATA);
+ if (!FoundResource)
+ return WINTUN_LOGGER_LAST_ERROR(L"Failed to find resource");
+ DWORD SizeResource = SizeofResource(ResourceModule, FoundResource);
+ if (!SizeResource)
+ return WINTUN_LOGGER_LAST_ERROR(L"Failed to size resource");
+ HGLOBAL LoadedResource = LoadResource(ResourceModule, FoundResource);
+ if (!LoadedResource)
+ return WINTUN_LOGGER_LAST_ERROR(L"Failed to load resource");
+ LPVOID LockedResource = LockResource(LoadedResource);
+ if (!LockedResource)
+ {
+ WINTUN_LOGGER(WINTUN_LOG_ERR, L"Failed to lock resource");
+ return ERROR_LOCK_FAILED;
+ }
+ HANDLE DestinationHandle = CreateFileW(
+ DestinationPath,
+ GENERIC_WRITE,
+ 0,
+ SecurityAttributes,
+ CREATE_NEW,
+ FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_TEMPORARY,
+ NULL);
+ if (DestinationHandle == INVALID_HANDLE_VALUE)
+ return WINTUN_LOGGER_LAST_ERROR(L"Failed to create file");
+ DWORD BytesWritten;
+ DWORD Result = ERROR_SUCCESS;
+ if (!WriteFile(DestinationHandle, LockedResource, SizeResource, &BytesWritten, NULL))
+ Result = WINTUN_LOGGER_LAST_ERROR(L"Failed to write file");
+ if (BytesWritten != SizeResource)
+ {
+ WINTUN_LOGGER(WINTUN_LOG_ERR, L"Incomplete write");
+ Result = Result != ERROR_SUCCESS ? Result : ERROR_WRITE_FAULT;
+ }
+ CloseHandle(DestinationHandle);
+ return Result;
+}
diff --git a/api/resource.h b/api/resource.h
new file mode 100644
index 0000000..3693bbf
--- /dev/null
+++ b/api/resource.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018-2020 WireGuard LLC. All Rights Reserved.
+ */
+
+#pragma once
+
+#include "api.h"
+#include <Windows.h>
+
+WINTUN_STATUS
+CopyResource(
+ _In_z_ const WCHAR *DestinationPath,
+ _In_opt_ SECURITY_ATTRIBUTES *SecurityAttributes,
+ _In_z_ const WCHAR *ResourceName);
diff --git a/api/resources.rc b/api/resources.rc
index 6f73a78..efcd47b 100644
--- a/api/resources.rc
+++ b/api/resources.rc
@@ -6,6 +6,18 @@
#include <windows.h>
#include <ntverp.h>
+#ifdef HAVE_EV
+wintun.cat RCDATA "wintun\\wintun.cat"
+wintun.inf RCDATA "wintun\\wintun.inf"
+wintun.sys RCDATA "wintun\\wintun.sys"
+#endif
+
+#ifdef HAVE_WHQL
+wintun-whql.cat RCDATA "whql\\wintun.cat"
+wintun-whql.inf RCDATA "whql\\wintun.inf"
+wintun-whql.sys RCDATA "whql\\wintun.sys"
+#endif
+
#define STRINGIZE(x) #x
#define EXPAND(x) STRINGIZE(x)