From 22e2da002d8ad18b3d3f1286191f03854939f3c4 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Mon, 22 Jul 2019 07:36:21 +0000 Subject: [PATCH] Rewrite installer logic in C Signed-off-by: Jason A. Donenfeld --- .clang-format | 2 +- .editorconfig | 4 +- .gitignore | 3 + installer/exports.def | 5 + installer/installation.c | 654 ++++++++++++++++++ installer/installation.h | 23 + installer/installer.vcxproj | 194 ++++++ installer/installer.vcxproj.filters | 52 ++ wintun.wixproj => installer/installer.wixproj | 47 +- installer/installer.wxs | 51 ++ installer/msi.c | 266 +++++++ installer/resources.rc | 48 ++ installer/rundll32.c | 160 +++++ wintun.c | 6 +- wintun.proj | 30 +- wintun.props | 25 +- wintun.rc | Bin 1892 -> 1073 bytes wintun.sln | 25 +- wintun.vcxproj | 15 +- wintun.vcxproj.filters | 2 +- wintun.wxs | 136 ---- 21 files changed, 1552 insertions(+), 196 deletions(-) create mode 100644 installer/exports.def create mode 100644 installer/installation.c create mode 100644 installer/installation.h create mode 100644 installer/installer.vcxproj create mode 100644 installer/installer.vcxproj.filters rename wintun.wixproj => installer/installer.wixproj (68%) create mode 100644 installer/installer.wxs create mode 100644 installer/msi.c create mode 100644 installer/resources.rc create mode 100644 installer/rundll32.c delete mode 100644 wintun.wxs diff --git a/.clang-format b/.clang-format index c7d38ff..a77c201 100644 --- a/.clang-format +++ b/.clang-format @@ -98,7 +98,7 @@ StatementMacros: [ '__drv_preferredFunction', '__drv_allocatesMem', '__drv_freesMem', - '_Field_size_bytes_', + '_Field_size_bytes_', '_Function_class_', '_Dispatch_type_' ] diff --git a/.editorconfig b/.editorconfig index 6eee1a2..15595b4 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,7 +1,7 @@ -[*.{c,h,inf,rc,wxs}] +[*.{c,h,inf,rc}] indent_style = space indent_style = 4 -[*.{proj,props,vcxproj,wixproj}] +[*.{proj,props,vcxproj,wixproj,wxs}] indent_style = space indent_size = 2 diff --git a/.gitignore b/.gitignore index fc1043b..7db1686 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,6 @@ # Driver Verification Log /wintun.DVL.XML + +# Temporary files +*~ diff --git a/installer/exports.def b/installer/exports.def new file mode 100644 index 0000000..95eafde --- /dev/null +++ b/installer/exports.def @@ -0,0 +1,5 @@ +EXPORTS + InstallWintun + UninstallWintun + MsiEvaluate + MsiProcess diff --git a/installer/installation.c b/installer/installation.c new file mode 100644 index 0000000..b0644a4 --- /dev/null +++ b/installer/installation.c @@ -0,0 +1,654 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018-2019 WireGuard LLC. All Rights Reserved. + */ + +#include "installation.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma warning(disable : 4100) /* unreferenced formal parameter */ +#pragma warning(disable : 4204) /* nonstandard: non-constant aggregate initializer */ +#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; + +static VOID +NopLogger(_In_ LOGGER_LEVEL Level, _In_ const TCHAR *LogLine) +{ +} + +static LoggerFunction Logger = NopLogger; + +VOID +SetLogger(_In_ LoggerFunction NewLogger) +{ + Logger = NewLogger; +} + +static VOID +PrintError(_In_ LOGGER_LEVEL Level, _In_ const TCHAR *Prefix) +{ + DWORD ErrorCode = GetLastError(); + TCHAR *SystemMessage = NULL, *FormattedMessage = NULL; + FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK, + NULL, + HRESULT_FROM_SETUPAPI(ErrorCode), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (VOID *)&SystemMessage, + 0, + NULL); + FormatMessage( + FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY | + FORMAT_MESSAGE_MAX_WIDTH_MASK, + SystemMessage ? TEXT("%1: %3(Code 0x%2!08X!)") : TEXT("%1: Code 0x%2!08X!"), + 0, + 0, + (VOID *)&FormattedMessage, + 0, + (va_list *)(DWORD_PTR[]){ (DWORD_PTR)Prefix, (DWORD_PTR)ErrorCode, (DWORD_PTR)SystemMessage }); + if (FormattedMessage) + Logger(Level, FormattedMessage); + LocalFree(FormattedMessage); + LocalFree(SystemMessage); +} + +HINSTANCE ResourceModule; + +static BOOL IsWintunLoaded(VOID) +{ + DWORD RequiredSize = 0, CurrentSize = 0; + VOID **Drivers = NULL; + BOOL Found = FALSE; + for (;;) + { + if (!EnumDeviceDrivers(Drivers, CurrentSize, &RequiredSize)) + goto out; + if (CurrentSize == RequiredSize) + break; + free(Drivers); + Drivers = malloc(RequiredSize); + if (!Drivers) + goto out; + CurrentSize = RequiredSize; + } + TCHAR MaybeWintun[11]; + for (DWORD i = CurrentSize / sizeof(Drivers[0]); i-- > 0;) + { + if (GetDeviceDriverBaseName(Drivers[i], MaybeWintun, _countof(MaybeWintun)) == 10 && + !_tcsicmp(MaybeWintun, TEXT("wintun.sys"))) + { + Found = TRUE; + goto out; + } + } +out: + free(Drivers); + return Found; +} + +static BOOL EnsureWintunUnloaded(VOID) +{ + BOOL Loaded; + for (int i = 0; (Loaded = IsWintunLoaded()) != 0 && i < 300; ++i) + Sleep(50); + return !Loaded; +} + +static BOOL +CopyResource( + _In_ const TCHAR *DestinationPath, + _In_opt_ SECURITY_ATTRIBUTES *SecurityAttributes, + _In_ const TCHAR *ResourceName) +{ + HRSRC FoundResource = FindResource(ResourceModule, ResourceName, RT_RCDATA); + if (!FoundResource) + return FALSE; + DWORD SizeResource = SizeofResource(ResourceModule, FoundResource); + if (!SizeResource) + return FALSE; + HGLOBAL LoadedResource = LoadResource(ResourceModule, FoundResource); + if (!LoadedResource) + return FALSE; + LPVOID LockedResource = LockResource(LoadedResource); + if (!LockedResource) + return FALSE; + HANDLE DestinationHandle = CreateFile( + DestinationPath, + GENERIC_WRITE, + 0, + SecurityAttributes, + CREATE_NEW, + FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_TEMPORARY, + NULL); + if (DestinationHandle == INVALID_HANDLE_VALUE) + return FALSE; + DWORD BytesWritten; + BOOL Ret = + WriteFile(DestinationHandle, LockedResource, SizeResource, &BytesWritten, NULL) && BytesWritten == SizeResource; + CloseHandle(DestinationHandle); + return Ret; +} + +static BOOL +InstallWintunCertificate(const TCHAR *SignedResource) +{ + DWORD LastError = ERROR_SUCCESS; + Logger(LOG_INFO, TEXT("Trusting code signing certificate")); + BOOL Ret = TRUE; + HRSRC FoundResource = FindResource(ResourceModule, SignedResource, RT_RCDATA); + if (!FoundResource) + return FALSE; + DWORD SizeResource = SizeofResource(ResourceModule, FoundResource); + if (!SizeResource) + return FALSE; + HGLOBAL LoadedResource = LoadResource(ResourceModule, FoundResource); + if (!LoadedResource) + return FALSE; + LPVOID LockedResource = LockResource(LoadedResource); + if (!LockedResource) + return FALSE; + 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 FALSE; + HCERTSTORE TrustedStore = + CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_LOCAL_MACHINE, TEXT("TrustedPublisher")); + if (!TrustedStore) + { + LastError = GetLastError(); + 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) + Ret &= CertAddCertificateContextToStore(TrustedStore, CertContext, CERT_STORE_ADD_REPLACE_EXISTING, NULL); + if (!Ret) + LastError = LastError ? LastError : GetLastError(); + } + CertCloseStore(TrustedStore, 0); +cleanupQueriedStore: + CertCloseStore(QueriedStore, 0); + SetLastError(LastError); + return Ret; +} + +/* We can't use RtlGetVersion, because appcompat's aclayers.dll shims it to report Vista + * when run from MSI context. 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); + +static BOOL +InstallWintun(BOOL UpdateExisting) +{ + DWORD LastError = ERROR_SUCCESS; + TCHAR WindowsDirectory[MAX_PATH]; + if (!GetWindowsDirectory(WindowsDirectory, _countof(WindowsDirectory))) + return FALSE; + TCHAR WindowsTempDirectory[MAX_PATH]; + if (!PathCombine(WindowsTempDirectory, WindowsDirectory, TEXT("Temp"))) + return FALSE; + UCHAR RandomBytes[32] = { 0 }; +#pragma warning(suppress : 6387) + if (!RtlGenRandom(RandomBytes, sizeof(RandomBytes))) + return FALSE; + TCHAR RandomSubDirectory[sizeof(RandomBytes) * 2 + 1]; + for (int i = 0; i < sizeof(RandomBytes); ++i) + _stprintf_s(&RandomSubDirectory[i * 2], 3, TEXT("%02x"), RandomBytes[i]); + TCHAR RandomTempSubDirectory[MAX_PATH]; + if (!PathCombine(RandomTempSubDirectory, WindowsTempDirectory, RandomSubDirectory)) + return FALSE; + SECURITY_ATTRIBUTES SecurityAttributes = { .nLength = sizeof(SecurityAttributes) }; + if (!ConvertStringSecurityDescriptorToSecurityDescriptor( + TEXT("O:SYD:P(A;;GA;;;SY)"), SDDL_REVISION_1, &SecurityAttributes.lpSecurityDescriptor, NULL)) + return FALSE; + BOOL Ret = CreateDirectory(RandomTempSubDirectory, &SecurityAttributes); + if (!Ret) + goto cleanupFree; + + TCHAR CatPath[MAX_PATH] = { 0 }; + if (!PathCombine(CatPath, RandomTempSubDirectory, TEXT("wintun.cat"))) + goto cleanupFree; + TCHAR SysPath[MAX_PATH] = { 0 }; + if (!PathCombine(SysPath, RandomTempSubDirectory, TEXT("wintun.sys"))) + goto cleanupFree; + TCHAR InfPath[MAX_PATH] = { 0 }; + if (!PathCombine(InfPath, RandomTempSubDirectory, TEXT("wintun.inf"))) + goto cleanupFree; + + BOOL UseWHQL = FALSE; +#ifdef HAVE_WHQL + DWORD MajorVersion; + RtlGetNtVersionNumbers(&MajorVersion, NULL, NULL); + UseWHQL = MajorVersion >= 10; +#endif + if (!UseWHQL && !InstallWintunCertificate(TEXT("wintun.sys"))) + PrintError(LOG_WARN, TEXT("Unable to install code signing certificate")); + + Logger(LOG_INFO, TEXT("Copying resources to temporary path")); + Ret = CopyResource(CatPath, &SecurityAttributes, UseWHQL ? TEXT("wintun-whql.cat") : TEXT("wintun.cat")) && + CopyResource(SysPath, &SecurityAttributes, UseWHQL ? TEXT("wintun-whql.sys") : TEXT("wintun.sys")) && + CopyResource(InfPath, &SecurityAttributes, UseWHQL ? TEXT("wintun-whql.inf") : TEXT("wintun.inf")); + if (!Ret) + goto cleanupDelete; + + Logger(LOG_INFO, TEXT("Installing driver")); + Ret = SetupCopyOEMInf(InfPath, NULL, SPOST_PATH, 0, NULL, 0, NULL, NULL); + BOOL RebootRequired = FALSE; + if (UpdateExisting && + !UpdateDriverForPlugAndPlayDevices( + NULL, TEXT("Wintun"), InfPath, INSTALLFLAG_FORCE | INSTALLFLAG_NONINTERACTIVE, &RebootRequired)) + PrintError(LOG_WARN, TEXT("Could not update existing adapters")); + if (RebootRequired) + Logger(LOG_WARN, TEXT("A reboot might be required, which really should not be the case")); + +cleanupDelete: + LastError = LastError ? LastError : GetLastError(); + DeleteFile(CatPath); + DeleteFile(SysPath); + DeleteFile(InfPath); + RemoveDirectory(RandomTempSubDirectory); +cleanupFree: + LastError = LastError ? LastError : GetLastError(); + LocalFree(SecurityAttributes.lpSecurityDescriptor); + SetLastError(LastError); + return Ret; +} + +static BOOL RemoveWintun(VOID) +{ + BOOL Ret = FALSE; + HDEVINFO DeviceInfoSet = SetupDiGetClassDevs(&GUID_DEVCLASS_NET, NULL, NULL, 0); + if (!DeviceInfoSet) + return FALSE; + if (!SetupDiBuildDriverInfoList(DeviceInfoSet, NULL, SPDIT_CLASSDRIVER)) + goto cleanupDeviceInfoSet; + Ret = TRUE; + for (DWORD EnumIndex = 0;; ++EnumIndex) + { + SP_DRVINFO_DATA DriverInfo = { .cbSize = sizeof(DriverInfo) }; + if (!SetupDiEnumDriverInfo(DeviceInfoSet, NULL, SPDIT_CLASSDRIVER, EnumIndex, &DriverInfo)) + { + if (GetLastError() == ERROR_NO_MORE_ITEMS) + break; + goto cleanupDriverInfoList; + } + DWORD RequiredSize; + if (SetupDiGetDriverInfoDetail(DeviceInfoSet, NULL, &DriverInfo, NULL, 0, &RequiredSize) || + GetLastError() != ERROR_INSUFFICIENT_BUFFER) + goto cleanupDriverInfoList; + PSP_DRVINFO_DETAIL_DATA DriverDetail = calloc(1, RequiredSize); + if (!DriverDetail) + goto cleanupDriverInfoList; + DriverDetail->cbSize = sizeof(*DriverDetail); + if (!SetupDiGetDriverInfoDetail(DeviceInfoSet, NULL, &DriverInfo, DriverDetail, RequiredSize, &RequiredSize)) + { + free(DriverDetail); + goto cleanupDriverInfoList; + } + if (!_tcsicmp(DriverDetail->HardwareID, TEXT("wintun"))) + { + PathStripPath(DriverDetail->InfFileName); + Logger(LOG_INFO, TEXT("Removing existing driver")); + if (!SetupUninstallOEMInf(DriverDetail->InfFileName, SUOI_FORCEDELETE, NULL)) + { + PrintError(LOG_ERR, TEXT("Unable to remove existing driver")); + Ret = FALSE; + } + } + free(DriverDetail); + } + +cleanupDriverInfoList: + SetupDiDestroyDriverInfoList(DeviceInfoSet, NULL, SPDIT_CLASSDRIVER); +cleanupDeviceInfoSet: + SetupDiDestroyDeviceInfoList(DeviceInfoSet); + return Ret; +} + +static BOOL +IsWintunAdapter(_In_ HDEVINFO DeviceInfoSet, _Inout_ SP_DEVINFO_DATA *DeviceInfo) +{ + BOOL Found = FALSE; + if (!SetupDiBuildDriverInfoList(DeviceInfoSet, DeviceInfo, SPDIT_COMPATDRIVER)) + return FALSE; + for (DWORD EnumIndex = 0;; ++EnumIndex) + { + SP_DRVINFO_DATA DriverInfo = { .cbSize = sizeof(SP_DRVINFO_DATA) }; + if (!SetupDiEnumDriverInfo(DeviceInfoSet, DeviceInfo, SPDIT_COMPATDRIVER, EnumIndex, &DriverInfo)) + { + if (GetLastError() == ERROR_NO_MORE_ITEMS) + break; + continue; + } + DWORD RequiredSize; + if (SetupDiGetDriverInfoDetail(DeviceInfoSet, DeviceInfo, &DriverInfo, NULL, 0, &RequiredSize) || + GetLastError() != ERROR_INSUFFICIENT_BUFFER) + continue; + PSP_DRVINFO_DETAIL_DATA DriverDetail = calloc(1, RequiredSize); + if (!DriverDetail) + continue; + DriverDetail->cbSize = sizeof(*DriverDetail); + if (SetupDiGetDriverInfoDetail( + DeviceInfoSet, DeviceInfo, &DriverInfo, DriverDetail, RequiredSize, &RequiredSize) && + !_tcsicmp(DriverDetail->HardwareID, TEXT("wintun"))) + { + free(DriverDetail); + Found = TRUE; + break; + } + free(DriverDetail); + } + SetupDiDestroyDriverInfoList(DeviceInfoSet, DeviceInfo, SPDIT_COMPATDRIVER); + return Found; +} + +#define TUN_IOCTL_FORCE_CLOSE_HANDLES CTL_CODE(51820U, 0x971U, METHOD_NEITHER, FILE_READ_DATA | FILE_WRITE_DATA) + +static BOOL +ForceCloseWintunAdapterHandle(_In_ HDEVINFO DeviceInfoSet, _In_ SP_DEVINFO_DATA *DeviceInfo) +{ + DWORD RequiredBytes; + if (SetupDiGetDeviceInstanceId(DeviceInfoSet, DeviceInfo, NULL, 0, &RequiredBytes) || + GetLastError() != ERROR_INSUFFICIENT_BUFFER) + return FALSE; + TCHAR *InstanceId = calloc(sizeof(*InstanceId), RequiredBytes); + if (!InstanceId) + return FALSE; + if (!SetupDiGetDeviceInstanceId(DeviceInfoSet, DeviceInfo, InstanceId, RequiredBytes, &RequiredBytes)) + return FALSE; + TCHAR *InterfaceList = NULL; + for (;;) + { + free(InterfaceList); + if (CM_Get_Device_Interface_List_Size( + &RequiredBytes, (LPGUID)&GUID_DEVINTERFACE_NET, InstanceId, CM_GET_DEVICE_INTERFACE_LIST_PRESENT) != + CR_SUCCESS) + return FALSE; + InterfaceList = calloc(sizeof(*InterfaceList), RequiredBytes); + if (!InterfaceList) + return FALSE; + CONFIGRET Ret = CM_Get_Device_Interface_List( + (LPGUID)&GUID_DEVINTERFACE_NET, + InstanceId, + InterfaceList, + RequiredBytes, + CM_GET_DEVICE_INTERFACE_LIST_PRESENT); + if (Ret == CR_SUCCESS) + break; + if (Ret != CR_BUFFER_SMALL) + return FALSE; + } + + HANDLE NdisHandle = CreateFile( + InterfaceList, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + 0, + NULL); + free(InterfaceList); + if (NdisHandle == INVALID_HANDLE_VALUE) + return FALSE; + BOOL Ret = DeviceIoControl(NdisHandle, TUN_IOCTL_FORCE_CLOSE_HANDLES, NULL, 0, NULL, 0, &RequiredBytes, NULL); + DWORD LastError = GetLastError(); + CloseHandle(NdisHandle); + SetLastError(LastError); + return Ret; +} + +static BOOL +DisableWintunAdapters(_In_ HDEVINFO DeviceInfoSet, _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 }; + BOOL Ret = TRUE; + DWORD LastError = ERROR_SUCCESS; + for (DWORD EnumIndex = 0;; ++EnumIndex) + { + SP_DEVINFO_DATA_LIST *DeviceNode = malloc(sizeof(SP_DEVINFO_DATA_LIST)); + if (!DeviceNode) + return FALSE; + DeviceNode->Data.cbSize = sizeof(SP_DEVINFO_DATA); + if (!SetupDiEnumDeviceInfo(DeviceInfoSet, EnumIndex, &DeviceNode->Data)) + { + if (GetLastError() == ERROR_NO_MORE_ITEMS) + { + free(DeviceNode); + break; + } + goto cleanupDeviceInfoData; + } + if (!IsWintunAdapter(DeviceInfoSet, &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; + + Logger(LOG_INFO, TEXT("Force closing all open handles for existing adapter")); + if (!ForceCloseWintunAdapterHandle(DeviceInfoSet, &DeviceNode->Data)) + PrintError(LOG_WARN, TEXT("Failed to force close adapter handles")); + Sleep(200); + + Logger(LOG_INFO, TEXT("Disabling existing adapter")); + if (!SetupDiSetClassInstallParams( + DeviceInfoSet, &DeviceNode->Data, &Params.ClassInstallHeader, sizeof(Params)) || + !SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, DeviceInfoSet, &DeviceNode->Data)) + { + PrintError(LOG_WARN, TEXT("Unable to disable existing adapter")); + LastError = LastError ? LastError : GetLastError(); + Ret = FALSE; + goto cleanupDeviceInfoData; + } + + DeviceNode->Next = *DisabledAdapters; + *DisabledAdapters = DeviceNode; + continue; + + cleanupDeviceInfoData: + free(&DeviceNode->Data); + } + SetLastError(LastError); + return Ret; +} + +static BOOL +RemoveWintunAdapters(_In_ HDEVINFO DeviceInfoSet) +{ + SP_REMOVEDEVICE_PARAMS Params = { .ClassInstallHeader = { .cbSize = sizeof(SP_CLASSINSTALL_HEADER), + .InstallFunction = DIF_REMOVE }, + .Scope = DI_REMOVEDEVICE_GLOBAL }; + BOOL Ret = TRUE; + DWORD LastError = ERROR_SUCCESS; + for (DWORD EnumIndex = 0;; ++EnumIndex) + { + SP_DEVINFO_DATA DeviceInfo = { .cbSize = sizeof(SP_DEVINFO_DATA) }; + if (!SetupDiEnumDeviceInfo(DeviceInfoSet, EnumIndex, &DeviceInfo)) + { + if (GetLastError() == ERROR_NO_MORE_ITEMS) + break; + continue; + } + if (!IsWintunAdapter(DeviceInfoSet, &DeviceInfo)) + continue; + + Logger(LOG_INFO, TEXT("Force closing all open handles for existing adapter")); + if (!ForceCloseWintunAdapterHandle(DeviceInfoSet, &DeviceInfo)) + PrintError(LOG_WARN, TEXT("Failed to force close adapter handles")); + Sleep(200); + + Logger(LOG_INFO, TEXT("Removing existing adapter")); + if (!SetupDiSetClassInstallParams(DeviceInfoSet, &DeviceInfo, &Params.ClassInstallHeader, sizeof(Params)) || + !SetupDiCallClassInstaller(DIF_REMOVE, DeviceInfoSet, &DeviceInfo)) + { + PrintError(LOG_WARN, TEXT("Unable to remove existing adapter")); + LastError = LastError ? LastError : GetLastError(); + Ret = FALSE; + } + } + SetLastError(LastError); + return Ret; +} + +static BOOL +EnableWintunAdapters(_In_ HDEVINFO DeviceInfoSet, _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 }; + BOOL Ret = TRUE; + DWORD LastError = ERROR_SUCCESS; + + for (SP_DEVINFO_DATA_LIST *DeviceNode = AdaptersToEnable; DeviceNode; DeviceNode = DeviceNode->Next) + { + Logger(LOG_INFO, TEXT("Enabling existing adapter")); + if (!SetupDiSetClassInstallParams( + DeviceInfoSet, &DeviceNode->Data, &Params.ClassInstallHeader, sizeof(Params)) || + !SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, DeviceInfoSet, &DeviceNode->Data)) + { + LastError = LastError ? LastError : GetLastError(); + PrintError(LOG_WARN, TEXT("Unable to enable existing adapter")); + Ret = FALSE; + } + } + SetLastError(LastError); + return Ret; +} + +BOOL InstallOrUpdate(VOID) +{ + BOOL Ret = FALSE; + HDEVINFO DeviceInfoSet = SetupDiGetClassDevsEx(&GUID_DEVCLASS_NET, NULL, NULL, DIGCF_PRESENT, NULL, NULL, NULL); + if (DeviceInfoSet == INVALID_HANDLE_VALUE) + { + PrintError(LOG_ERR, TEXT("Failed to get present class devices")); + return FALSE; + } + SP_DEVINFO_DATA_LIST *ExistingAdapters = NULL; + if (IsWintunLoaded()) + { + DisableWintunAdapters(DeviceInfoSet, &ExistingAdapters); + Logger(LOG_INFO, TEXT("Waiting for driver to unload from kernel")); + if (!EnsureWintunUnloaded()) + Logger(LOG_WARN, TEXT("Unable to unload driver, which means a reboot will likely be required")); + } + if (!RemoveWintun()) + { + PrintError(LOG_ERR, TEXT("Failed to uninstall old drivers")); + goto cleanupAdapters; + } + if (!InstallWintun(!!ExistingAdapters)) + { + PrintError(LOG_ERR, TEXT("Failed to install driver")); + goto cleanupAdapters; + } + Logger(LOG_INFO, TEXT("Installation successful")); + Ret = TRUE; + +cleanupAdapters: + if (ExistingAdapters) + { + EnableWintunAdapters(DeviceInfoSet, ExistingAdapters); + while (ExistingAdapters) + { + SP_DEVINFO_DATA_LIST *Next = ExistingAdapters->Next; + free(ExistingAdapters); + ExistingAdapters = Next; + } + } + SetupDiDestroyDeviceInfoList(DeviceInfoSet); + return Ret; +} + +BOOL Uninstall(VOID) +{ + HDEVINFO DeviceInfoSet = SetupDiGetClassDevsEx(&GUID_DEVCLASS_NET, NULL, NULL, DIGCF_PRESENT, NULL, NULL, NULL); + if (DeviceInfoSet == INVALID_HANDLE_VALUE) + { + PrintError(LOG_ERR, TEXT("Failed to get present class devices")); + return FALSE; + } + RemoveWintunAdapters(DeviceInfoSet); + BOOL Ret = RemoveWintun(); + if (!Ret) + PrintError(LOG_ERR, TEXT("Failed to uninstall driver")); + else + Logger(LOG_INFO, TEXT("Uninstallation successful")); + return Ret; +} + +BOOL APIENTRY +DllMain(_In_ HINSTANCE hinstDLL, _In_ DWORD fdwReason, _In_ LPVOID lpvReserved) +{ + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + ResourceModule = hinstDLL; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} \ No newline at end of file diff --git a/installer/installation.h b/installer/installation.h new file mode 100644 index 0000000..e5ce781 --- /dev/null +++ b/installer/installation.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018-2019 WireGuard LLC. All Rights Reserved. + */ + +#pragma once + +#include + +typedef enum _LOGGER_LEVEL +{ + LOG_INFO = 0, + LOG_WARN, + LOG_ERR +} LOGGER_LEVEL; +typedef VOID (*LoggerFunction)(_In_ LOGGER_LEVEL, _In_ const TCHAR *); +VOID +SetLogger(_In_ LoggerFunction NewLogger); + +BOOL InstallOrUpdate(VOID); +BOOL Uninstall(VOID); + +extern HINSTANCE ResourceModule; diff --git a/installer/installer.vcxproj b/installer/installer.vcxproj new file mode 100644 index 0000000..306f05f --- /dev/null +++ b/installer/installer.vcxproj @@ -0,0 +1,194 @@ + + + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {D19E6354-A643-4ACC-82D5-B2780BB83475} + Win32Proj + installer + 10.0 + installer + + + + DynamicLibrary + Unicode + v142 + true + false + + + DynamicLibrary + true + Unicode + v142 + false + + + DynamicLibrary + Unicode + v142 + true + false + + + DynamicLibrary + Unicode + v142 + true + + + DynamicLibrary + true + Unicode + v142 + false + + + DynamicLibrary + true + Unicode + v142 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ..\$(WintunPlatform)\$(Configuration)\ + ..\$(WintunPlatform)\$(Configuration)\installer-intermediate\ + NativeRecommendedRules.ruleset + true + rundll32.exe + ..\$(WintunPlatform)\$(Configuration)\installer.dll,InstallWintun + WindowsLocalDebugger + + + + _WINDOWS;_USRDLL;%(PreprocessorDefinitions) + HAVE_WHQL;%(PreprocessorDefinitions) + + + ..\$(WintunPlatform)\$(Configuration);%(AdditionalIncludeDirectories) + HAVE_WHQL;%(PreprocessorDefinitions) + + + exports.def + newdev.lib;ntdll.lib;Crypt32.lib;Msi.lib;Setupapi.lib;shlwapi.lib;%(AdditionalDependencies) + Windows + + + + + _DEBUG;%(PreprocessorDefinitions) + MultiThreadedDebug + + + _DEBUG;%(PreprocessorDefinitions) + + + + + NDEBUG;%(PreprocessorDefinitions) + MultiThreaded + + + NDEBUG;%(PreprocessorDefinitions) + + + true + true + UseLinkTimeCodeGeneration + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(TestCertificate) + $(ProductionCertificate) + $(BuildDependsOn);SignTarget + CleanSignTarget;$(CleanDependsOn) + + + + + + + + + \ No newline at end of file diff --git a/installer/installer.vcxproj.filters b/installer/installer.vcxproj.filters new file mode 100644 index 0000000..bfbd0ec --- /dev/null +++ b/installer/installer.vcxproj.filters @@ -0,0 +1,52 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {1ae72c39-669d-49e5-85c7-f6956595e079} + + + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + + + Header Files + + + + + Source Files + + + MSM + + + MSM + + + \ No newline at end of file diff --git a/wintun.wixproj b/installer/installer.wixproj similarity index 68% rename from wintun.wixproj rename to installer/installer.wixproj index 7e8c7f2..69b40a6 100644 --- a/wintun.wixproj +++ b/installer/installer.wixproj @@ -1,4 +1,4 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/msi.c b/installer/msi.c new file mode 100644 index 0000000..348ef42 --- /dev/null +++ b/installer/msi.c @@ -0,0 +1,266 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018-2019 WireGuard LLC. All Rights Reserved. + */ + +#include "installation.h" +#include +#include +#include +#include + +#pragma warning(disable : 4100) /* unreferenced formal parameter */ + +static MSIHANDLE MsiHandle; + +#define ANCHOR_COMPONENT TEXT("{B668D4C7-ABB3-485A-B8DF-D34200489A43}") +#define PROCESS_ACTION TEXT("ProcessWintun") +#define ACTION_INSTALL TEXT("/WintunAction=Install") +#define ACTION_INSTALL_SEPERATOR TEXT('-') +#define ACTION_INSTALL_SEPERATORS TEXT("-%s-%s-%s") +#define ACTION_UNINSTALL TEXT("/WintunAction=Uninstall") +#define PROPERTY_INSTALLER_HASH TEXT("WintunInstallerHash") +#define PROPERTY_INSTALLER_BUILDTIME TEXT("WintunInstallerBuildtime") +#define PROPERTY_VERSION TEXT("WintunVersion") +#define REGKEY_WINTUN TEXT("Software\\Wintun") +#define REGKEY_INSTALLER_HASH TEXT("InstallerHash") +#define REGKEY_INSTALLER_BUILDTIME TEXT("InstallerBuildtime") +#define REGKEY_VERSION TEXT("Version") + +static VOID +MsiLogger(_In_ LOGGER_LEVEL Level, _In_ const TCHAR *LogLine) +{ + MSIHANDLE Record = MsiCreateRecord(2); + if (!Record) + return; + TCHAR *Template; + INSTALLMESSAGE Type; + switch (Level) + { + case LOG_INFO: + Template = TEXT("Wintun: [1]"); + Type = INSTALLMESSAGE_INFO; + break; + case LOG_WARN: + Template = TEXT("Wintun warning: [1]"); + Type = INSTALLMESSAGE_INFO; + break; + case LOG_ERR: + Template = TEXT("Wintun error: [1]"); + Type = INSTALLMESSAGE_ERROR; + break; + default: + goto cleanup; + } + MsiRecordSetString(Record, 0, Template); + MsiRecordSetString(Record, 1, LogLine); + MsiProcessMessage(MsiHandle, Type, Record); +cleanup: + MsiCloseHandle(Record); +} + +static BOOL +IsInstalling(_In_ INSTALLSTATE InstallState, _In_ INSTALLSTATE ActionState) +{ + return INSTALLSTATE_LOCAL == ActionState || INSTALLSTATE_SOURCE == ActionState || + (INSTALLSTATE_DEFAULT == ActionState && + (INSTALLSTATE_LOCAL == InstallState || INSTALLSTATE_SOURCE == InstallState)); +} + +static BOOL +IsReInstalling(_In_ INSTALLSTATE InstallState, _In_ INSTALLSTATE ActionState) +{ + return (INSTALLSTATE_LOCAL == ActionState || INSTALLSTATE_SOURCE == ActionState || + INSTALLSTATE_DEFAULT == ActionState) && + (INSTALLSTATE_LOCAL == InstallState || INSTALLSTATE_SOURCE == InstallState); +} + +static BOOL +IsUninstalling(_In_ INSTALLSTATE InstallState, _In_ INSTALLSTATE ActionState) +{ + return (INSTALLSTATE_ABSENT == ActionState || INSTALLSTATE_REMOVED == ActionState) && + (INSTALLSTATE_LOCAL == InstallState || INSTALLSTATE_SOURCE == InstallState); +} + +static UINT64 +ParseVersion(_In_ const TCHAR *Version) +{ + ULONG Major = 0, Minor = 0, Revision = 0, Build = 0; + _stscanf_s(Version, TEXT("%u.%u.%u.%u"), &Major, &Minor, &Revision, &Build); + return ((UINT64)Major << 48) | ((UINT64)Minor << 32) | ((UINT64)Revision << 16) | ((UINT64)Build << 0); +} + +_Success_(return ) +static BOOL +Newer(_In_ MSIHANDLE Handle, _In_ BOOL SkipHashComparison, _Out_ TCHAR *InstallAction, _In_ SIZE_T InstallActionSize) +{ + INT64 NewTime, OldTime; + UINT64 NewVersion, OldVersion; + TCHAR NewHash[0x100], OldHash[0x100], NewTimeString[0x100], OldTimeString[0x100], NewVersionString[0x100], + OldVersionString[0x100]; + DWORD Size, Type; + HKEY Key; + BOOL Ret = TRUE; + + Size = _countof(NewHash); + if (MsiGetProperty(Handle, PROPERTY_INSTALLER_HASH, NewHash, &Size) != ERROR_SUCCESS) + return FALSE; + Size = _countof(NewTimeString); + if (MsiGetProperty(Handle, PROPERTY_INSTALLER_BUILDTIME, NewTimeString, &Size) != ERROR_SUCCESS) + return FALSE; + NewTime = _tstoll(NewTimeString); + Size = _countof(NewVersionString); + if (MsiGetProperty(Handle, PROPERTY_VERSION, NewVersionString, &Size) != ERROR_SUCCESS) + return FALSE; + NewVersion = ParseVersion(NewVersionString); + + _stprintf_s( + InstallAction, + InstallActionSize, + ACTION_INSTALL ACTION_INSTALL_SEPERATORS, + NewHash, + NewTimeString, + NewVersionString); + + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY_WINTUN, 0, KEY_READ, &Key) != ERROR_SUCCESS) + return TRUE; + Size = sizeof(OldHash); + if (RegQueryValueEx(Key, REGKEY_INSTALLER_HASH, NULL, &Type, (LPBYTE)OldHash, &Size) != ERROR_SUCCESS || + Type != REG_SZ) + goto cleanup; + Size = sizeof(OldTimeString); + if (RegQueryValueEx(Key, REGKEY_INSTALLER_BUILDTIME, NULL, &Type, (LPBYTE)OldTimeString, &Size) != ERROR_SUCCESS || + Type != REG_SZ) + goto cleanup; + OldTime = _tstoll(OldTimeString); + Size = sizeof(OldVersionString); + if (RegQueryValueEx(Key, REGKEY_VERSION, NULL, &Type, (LPBYTE)OldVersionString, &Size) != ERROR_SUCCESS || + Type != REG_SZ) + goto cleanup; + OldVersion = ParseVersion(OldVersionString); + + Ret = NewVersion >= OldVersion && NewTime >= OldTime && (SkipHashComparison || _tcscmp(NewHash, OldHash)); + +cleanup: + RegCloseKey(Key); + return Ret; +} + +UINT __stdcall MsiEvaluate(MSIHANDLE Handle) +{ + MsiHandle = Handle; + SetLogger(MsiLogger); + BOOL IsComInitialized = SUCCEEDED(CoInitialize(NULL)); + UINT Ret = ERROR_INSTALL_FAILURE; + MSIHANDLE View = 0, Record = 0, Database = MsiGetActiveDatabase(Handle); + if (!Database) + goto cleanup; + Ret = MsiDatabaseOpenView( + Database, TEXT("SELECT `Component` FROM `Component` WHERE `ComponentId` = '" ANCHOR_COMPONENT "'"), &View); + if (Ret != ERROR_SUCCESS) + goto cleanup; + Ret = MsiViewExecute(View, 0); + if (Ret != ERROR_SUCCESS) + goto cleanup; + Ret = MsiViewFetch(View, &Record); + if (Ret != ERROR_SUCCESS) + goto cleanup; + TCHAR ComponentName[0x1000]; + DWORD Size = _countof(ComponentName); + Ret = MsiRecordGetString(Record, 1, ComponentName, &Size); + if (Ret != ERROR_SUCCESS) + goto cleanup; + INSTALLSTATE InstallState, ActionState; + Ret = MsiGetComponentState(Handle, ComponentName, &InstallState, &ActionState); + if (Ret != ERROR_SUCCESS) + goto cleanup; + TCHAR InstallAction[0x400]; + if ((IsReInstalling(InstallState, ActionState) || IsInstalling(InstallState, ActionState)) && + Newer(Handle, IsReInstalling(InstallState, ActionState), InstallAction, _countof(InstallAction))) + Ret = MsiSetProperty(Handle, PROCESS_ACTION, InstallAction); + else if (IsUninstalling(InstallState, ActionState)) + Ret = MsiSetProperty(Handle, PROCESS_ACTION, ACTION_UNINSTALL); + if (Ret != ERROR_SUCCESS) + goto cleanup; + + Ret = MsiDoAction(Handle, TEXT("DisableRollback")); + +cleanup: + if (View) + MsiCloseHandle(View); + if (Record) + MsiCloseHandle(Record); + if (Database) + MsiCloseHandle(Database); + if (IsComInitialized) + CoUninitialize(); + return Ret; +} + +static BOOL +WriteRegKeys(_In_ TCHAR *Values) +{ + TCHAR *Hash, *Time, *Version; + Hash = Values; + Time = _tcschr(Hash, ACTION_INSTALL_SEPERATOR); + if (!Time) + return FALSE; + *Time++ = TEXT('\0'); + Version = _tcschr(Time, ACTION_INSTALL_SEPERATOR); + if (!Version) + return FALSE; + *Version++ = TEXT('\0'); + + HKEY Key; + if (RegCreateKeyEx( + HKEY_LOCAL_MACHINE, REGKEY_WINTUN, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &Key, NULL) != + ERROR_SUCCESS) + return FALSE; + BOOL Ret = + RegSetValueEx( + Key, REGKEY_INSTALLER_HASH, 0, REG_SZ, (LPBYTE)Hash, ((DWORD)_tcslen(Hash) + 1) * sizeof(*Hash)) == + ERROR_SUCCESS && + RegSetValueEx( + Key, REGKEY_INSTALLER_BUILDTIME, 0, REG_SZ, (LPBYTE)Time, ((DWORD)_tcslen(Time) + 1) * sizeof(*Time)) == + ERROR_SUCCESS && + RegSetValueEx( + Key, REGKEY_VERSION, 0, REG_SZ, (LPBYTE)Version, ((DWORD)_tcslen(Version) + 1) * sizeof(*Version)) == + ERROR_SUCCESS; + RegCloseKey(Key); + return Ret; +} + +UINT __stdcall MsiProcess(MSIHANDLE Handle) +{ + MsiHandle = Handle; + SetLogger(MsiLogger); + BOOL IsComInitialized = SUCCEEDED(CoInitialize(NULL)); + DWORD LastError = ERROR_SUCCESS; + BOOL Ret = FALSE; + TCHAR Value[0x1000], *RegValues; + DWORD Size = _countof(Value); + LastError = MsiGetProperty(Handle, TEXT("CustomActionData"), Value, &Size); + if (LastError != ERROR_SUCCESS) + goto cleanup; + if ((RegValues = _tcschr(Value, ACTION_INSTALL_SEPERATOR)) != NULL) + *RegValues++ = TEXT('\0'); + if (!_tcscmp(Value, ACTION_INSTALL)) + { + Ret = InstallOrUpdate(); + if (RegValues && Ret) + Ret = WriteRegKeys(RegValues); + } + else if (!_tcscmp(Value, ACTION_UNINSTALL)) + { + Ret = Uninstall(); + if (Ret) + RegDeleteKeyEx(HKEY_LOCAL_MACHINE, REGKEY_WINTUN, 0, 0); + } + else + Ret = TRUE; + LastError = GetLastError(); +cleanup: + if (IsComInitialized) + CoUninitialize(); + return Ret ? ERROR_SUCCESS : LastError ? LastError : ERROR_INSTALL_FAILED; +} diff --git a/installer/resources.rc b/installer/resources.rc new file mode 100644 index 0000000..fd73afd --- /dev/null +++ b/installer/resources.rc @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018-2019 WireGuard LLC. All Rights Reserved. + */ + +#include +#include + +wintun.cat RCDATA "wintun\\wintun.cat" +wintun.inf RCDATA "wintun\\wintun.inf" +wintun.sys RCDATA "wintun\\wintun.sys" + +#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) + +VS_VERSION_INFO VERSIONINFO +FILEVERSION WINTUN_VERSION_MAJ, WINTUN_VERSION_MIN, 0, 0 +PRODUCTVERSION WINTUN_VERSION_MAJ, WINTUN_VERSION_MIN, 0, 0 +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "WireGuard LLC" + VALUE "FileDescription", "Wintun Installer Library" + VALUE "FileVersion", EXPAND(WINTUN_VERSION_STR) + VALUE "InternalName", "installer.dll" + VALUE "LegalCopyright", "Copyright \xa9 2018-2019 WireGuard LLC. All Rights Reserved." + VALUE "OriginalFilename", "installer.dll" + VALUE "ProductName", "Wintun Driver" + VALUE "ProductVersion", EXPAND(WINTUN_VERSION_STR) + VALUE "Comments", "https://www.wintun.net/" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/installer/rundll32.c b/installer/rundll32.c new file mode 100644 index 0000000..3a482f1 --- /dev/null +++ b/installer/rundll32.c @@ -0,0 +1,160 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2018-2019 WireGuard LLC. All Rights Reserved. + */ + +#include "installation.h" +#include +#include +#include +#include +#include + +#pragma warning(disable : 4100) /* unreferenced formal parameter */ + +static VOID +ConsoleLogger(_In_ LOGGER_LEVEL Level, _In_ const TCHAR *LogLine) +{ + TCHAR *Template; + switch (Level) + { + case LOG_INFO: + Template = TEXT("[+] %s\n"); + break; + case LOG_WARN: + Template = TEXT("[-] %s\n"); + break; + case LOG_ERR: + Template = TEXT("[!] %s\n"); + break; + default: + return; + } + _ftprintf(stdout, Template, LogLine); +} + +static BOOL ElevateToSystem(VOID) +{ + HANDLE ThreadToken, ProcessSnapshot, WinlogonProcess, WinlogonToken, DuplicatedToken; + PROCESSENTRY32 ProcessEntry = { .dwSize = sizeof(PROCESSENTRY32) }; + BOOL Ret; + DWORD LastError = ERROR_SUCCESS; + TOKEN_PRIVILEGES Privileges = { .PrivilegeCount = 1, .Privileges = { { .Attributes = SE_PRIVILEGE_ENABLED } } }; + CHAR LocalSystemSid[0x400]; + DWORD RequiredBytes = sizeof(LocalSystemSid); + + if (!CreateWellKnownSid(WinLocalSystemSid, NULL, &LocalSystemSid, &RequiredBytes)) + goto cleanup; + struct + { + TOKEN_USER MaybeLocalSystem; + CHAR LargeEnoughForLocalSystem[0x400]; + } TokenUserBuffer; + if (!GetTokenInformation( + GetCurrentProcessToken(), TokenUser, &TokenUserBuffer, sizeof(TokenUserBuffer), &RequiredBytes)) + goto cleanup; + if (EqualSid(TokenUserBuffer.MaybeLocalSystem.User.Sid, LocalSystemSid)) + return TRUE; + if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &Privileges.Privileges[0].Luid)) + goto cleanup; + ProcessSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (ProcessSnapshot == INVALID_HANDLE_VALUE) + goto cleanup; + for (Ret = Process32First(ProcessSnapshot, &ProcessEntry); Ret; + LastError = GetLastError(), Ret = Process32Next(ProcessSnapshot, &ProcessEntry)) + { + if (_tcsicmp(ProcessEntry.szExeFile, TEXT("winlogon.exe"))) + continue; + RevertToSelf(); + if (!ImpersonateSelf(SecurityImpersonation)) + continue; + if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, FALSE, &ThreadToken)) + continue; + if (!AdjustTokenPrivileges(ThreadToken, FALSE, &Privileges, sizeof(Privileges), NULL, NULL)) + { + LastError = GetLastError(); + CloseHandle(ThreadToken); + continue; + } + CloseHandle(ThreadToken); + + WinlogonProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, ProcessEntry.th32ProcessID); + if (!WinlogonProcess) + continue; + if (!OpenProcessToken(WinlogonProcess, TOKEN_IMPERSONATE | TOKEN_DUPLICATE, &WinlogonToken)) + continue; + CloseHandle(WinlogonProcess); + if (!DuplicateToken(WinlogonToken, SecurityImpersonation, &DuplicatedToken)) + { + LastError = GetLastError(); + continue; + } + CloseHandle(WinlogonToken); + if (!GetTokenInformation(DuplicatedToken, TokenUser, &TokenUserBuffer, sizeof(TokenUserBuffer), &RequiredBytes)) + goto next; + if (SetLastError(ERROR_ACCESS_DENIED), !EqualSid(TokenUserBuffer.MaybeLocalSystem.User.Sid, LocalSystemSid)) + goto next; + if (!SetThreadToken(NULL, DuplicatedToken)) + goto next; + CloseHandle(DuplicatedToken); + CloseHandle(ProcessSnapshot); + SetLastError(ERROR_SUCCESS); + return TRUE; + next: + LastError = GetLastError(); + CloseHandle(DuplicatedToken); + } + RevertToSelf(); + CloseHandle(ProcessSnapshot); +cleanup: + SetLastError(LastError); + return FALSE; +} + +static VOID +RunAsAdministrator(HWND hwnd, TCHAR *Verb, int nCmdShow) +{ + TOKEN_ELEVATION Elevation; + DWORD Required; + if (!GetTokenInformation(GetCurrentProcessToken(), TokenElevation, &Elevation, sizeof(Elevation), &Required)) + return; + if (Elevation.TokenIsElevated) + return; + TCHAR ProcessPath[MAX_PATH], DllPath[MAX_PATH]; + if (!GetModuleFileName(NULL, ProcessPath, _countof(ProcessPath)) || + !GetModuleFileName(ResourceModule, DllPath, _countof(DllPath))) + return; + TCHAR Params[0x1000]; + _stprintf_s(Params, _countof(Params), TEXT("\"%s\",%s"), DllPath, Verb); + ShellExecute(hwnd, TEXT("runas"), ProcessPath, Params, NULL, nCmdShow); + exit(0); +} + +static VOID +Do(BOOL Install, BOOL ShowConsole) +{ + if (ShowConsole) + { + AllocConsole(); + FILE *Stream; + freopen_s(&Stream, "CONOUT$", "w", stdout); + } + SetLogger(ConsoleLogger); + ElevateToSystem(); + Install ? InstallOrUpdate() : Uninstall(); + RevertToSelf(); + _putws(TEXT("\nPress any key to close . . .")); + (VOID) _getch(); +} + +VOID __stdcall InstallWintun(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow) +{ + RunAsAdministrator(hwnd, TEXT(__FUNCTION__), nCmdShow); + Do(TRUE, !!nCmdShow); +} + +VOID __stdcall UninstallWintun(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow) +{ + RunAsAdministrator(hwnd, TEXT(__FUNCTION__) , nCmdShow); + Do(FALSE, !!nCmdShow); +} \ No newline at end of file diff --git a/wintun.c b/wintun.c index a889840..0c83d6c 100644 --- a/wintun.c +++ b/wintun.c @@ -102,9 +102,9 @@ typedef struct _TUN_REGISTER_RINGS /* Register rings hosted by the client. * The lpInBuffer and nInBufferSize parameters of DeviceIoControl() must point to an TUN_REGISTER_RINGS struct. * Client must wait for this IOCTL to finish before adding packets to the ring. */ -#define TUN_IOCTL_REGISTER_RINGS CTL_CODE(51820, 0x970, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA) +#define TUN_IOCTL_REGISTER_RINGS CTL_CODE(51820U, 0x970U, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA) /* Force close all open handles to allow for updating. */ -#define TUN_IOCTL_FORCE_CLOSE_HANDLES CTL_CODE(51820, 0x971, METHOD_NEITHER, FILE_READ_DATA | FILE_WRITE_DATA) +#define TUN_IOCTL_FORCE_CLOSE_HANDLES CTL_CODE(51820U, 0x971U, METHOD_NEITHER, FILE_READ_DATA | FILE_WRITE_DATA) typedef struct _TUN_CTX { @@ -176,7 +176,7 @@ InterlockedSetU(_Inout_ _Interlocked_operand_ ULONG volatile *Target, _In_ ULONG } static __forceinline VOID -InterlockedSetPointer(_Inout_ _Interlocked_operand_ VOID * volatile *Target, _In_opt_ VOID *Value) +InterlockedSetPointer(_Inout_ _Interlocked_operand_ VOID *volatile *Target, _In_opt_ VOID *Value) { *Target = Value; } diff --git a/wintun.proj b/wintun.proj index 7f787a0..2901616 100644 --- a/wintun.proj +++ b/wintun.proj @@ -1,6 +1,6 @@ - + @@ -44,6 +44,22 @@ + + + + + + + + + + + @@ -63,10 +79,12 @@ - - + + - - + + diff --git a/wintun.props b/wintun.props index 61ab893..5ca27c5 100644 --- a/wintun.props +++ b/wintun.props @@ -1,6 +1,6 @@ - + @@ -20,4 +20,25 @@ dist\ sdv\ + + + WINTUN_VERSION_MAJ=$(WintunVersionMaj);WINTUN_VERSION_MIN=$(WintunVersionMin);WINTUN_VERSION_STR="$(WintunVersionStr)";%(PreprocessorDefinitions) + Level4 + + + WINTUN_VERSION_MAJ=$(WintunVersionMaj);WINTUN_VERSION_MIN=$(WintunVersionMin);WINTUN_VERSION_STR="$(WintunVersionStr)";%(PreprocessorDefinitions) + + + + + MaxSpeed + true + Speed + + + + + Disabled + + diff --git a/wintun.rc b/wintun.rc index d0f38529e11d7e1dadf401f471726cca47ad4f0a..7fff28387d221b53927797c90bdda3b26fb908a1 100644 GIT binary patch literal 1073 zcmb7EQIDc95Ps)ZO!Q^lD6-x@oSVxf3TS%A7D5qsFP9Kbsy4x5N?rBeZ;MDg&wH?G z2%Y(6=IhJ{{YM~y^Vaj&g7T8~P)50AOGd>DOaresFf9Wf)dsuR9tB&iB|O+27+6nV zd+ITSITLicOGO5rXPYp}a|ku8gpih0e9+8P;eEqsGrq`o8HI0q#3oX0YY*T*3>4vtt(E4kvr=SL^3puo02)321;3qhjI}VOuYKk}D zF%{(rS`Y84iE5CYidj8s;W&d;q o7dxtPVqZ^+i+7bk6li9i{H=g7p*5sTO*PM;nij z{hF$Js>gc7+QI4`sG+%j=o;%&ebqNzu>XYBeeN|pp6R*swx<`YeZX!3dRv$J%u~u* z$k6~lP+eynfd(tIbN4oNQ7-fo&lJlnj$`*^Z+m#m$mk4Q%Oub%UAeWC2(R$365VTO zUt~KYazkH`&Dzo<8=i48bGWe*;_IkK4jp|`i+q2%eD}B)d9+|WBDWsgXSTPYn12z* zi0=$E(ObS5@|X!Lg?|3$bL_Nu&PGF_3_d2}O$}kS; zOgq + @@ -124,12 +124,11 @@ - WINTUN_VERSION_MAJ=$(WintunVersionMaj);WINTUN_VERSION_MIN=$(WintunVersionMin);WINTUN_VERSION_STR="$(WintunVersionStr)";NDIS_MINIPORT_DRIVER=1;NDIS620_MINIPORT=1;NDIS683_MINIPORT=1;NDIS_WDM=1;%(PreprocessorDefinitions) - Level4 + NDIS_MINIPORT_DRIVER=1;NDIS620_MINIPORT=1;NDIS683_MINIPORT=1;NDIS_WDM=1;%(PreprocessorDefinitions) true - WINTUN_VERSION_MAJ=$(WintunVersionMaj);WINTUN_VERSION_MIN=$(WintunVersionMin);WINTUN_VERSION_STR="$(WintunVersionStr)";NDIS_MINIPORT_DRIVER=1;NDIS620_MINIPORT=1;NDIS683_MINIPORT=1;NDIS_WDM=1;%(PreprocessorDefinitions) + NDIS_MINIPORT_DRIVER=1;NDIS620_MINIPORT=1;NDIS683_MINIPORT=1;NDIS_WDM=1;%(PreprocessorDefinitions) ndis.lib;wdmsec.lib;%(AdditionalDependencies) @@ -142,16 +141,10 @@ $(WintunVersion) - - MaxSpeed - true - Speed - DBG;%(PreprocessorDefinitions) - Disabled DBG;%(PreprocessorDefinitions) @@ -181,4 +174,4 @@ - \ No newline at end of file + diff --git a/wintun.vcxproj.filters b/wintun.vcxproj.filters index 5a7e2d9..3e19120 100644 --- a/wintun.vcxproj.filters +++ b/wintun.vcxproj.filters @@ -1,4 +1,4 @@ - + diff --git a/wintun.wxs b/wintun.wxs deleted file mode 100644 index be8b755..0000000 --- a/wintun.wxs +++ /dev/null @@ -1,136 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 400]]> - - -