diff options
| author | David Lönnhager <david.l@mullvad.net> | 2019-10-18 13:41:10 +0200 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2019-10-18 13:41:10 +0200 |
| commit | ef07d2adf5738a052837781082ea6f845bd7706c (patch) | |
| tree | 7928bebc2297e595774d05169a2deb011a39c7d8 | |
| parent | d86a380293c643ea22e94b7c26e7debe0c4068b8 (diff) | |
| parent | febc3e914f22e4653e0bd79adeaff0400b767c15 (diff) | |
| download | mullvadvpn-ef07d2adf5738a052837781082ea6f845bd7706c.tar.xz mullvadvpn-ef07d2adf5738a052837781082ea6f845bd7706c.zip | |
Merge branch 'win-tap-uninstall-new'
| -rw-r--r-- | CHANGELOG.md | 1 | ||||
| -rw-r--r-- | dist-assets/windows/installer.nsh | 92 | ||||
| -rw-r--r-- | windows/nsis-plugins/src/driverlogic/context.cpp | 212 | ||||
| -rw-r--r-- | windows/nsis-plugins/src/driverlogic/context.h | 11 | ||||
| -rw-r--r-- | windows/nsis-plugins/src/driverlogic/driverlogic.cpp | 62 | ||||
| -rw-r--r-- | windows/nsis-plugins/src/driverlogic/driverlogic.def | 1 | ||||
| -rw-r--r-- | windows/nsis-plugins/src/driverlogic/driverlogic.vcxproj | 4 | ||||
| -rw-r--r-- | windows/nsis-plugins/src/log/log.cpp | 61 | ||||
| -rw-r--r-- | windows/nsis-plugins/src/log/logger.h | 7 |
9 files changed, 414 insertions, 37 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 368f26467d..e473fd16a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ Line wrap the file at 100 chars. Th #### Windows - Install the OpenVPN certificate to avoid the TAP adapter driver installation warning on Windows 8 and newer. +- Remove Mullvad TAP adapter on uninstall. Also remove the TAP driver if there are no other TAP adapters in the system. ### Changed #### Windows diff --git a/dist-assets/windows/installer.nsh b/dist-assets/windows/installer.nsh index 6d43b4d000..4f92961f7f 100644 --- a/dist-assets/windows/installer.nsh +++ b/dist-assets/windows/installer.nsh @@ -29,6 +29,11 @@ !define INA_GENERAL_ERROR 0 !define INA_SUCCESS 1 +# Return codes from driverlogic::RemoveMullvadTap +!define RMT_GENERAL_ERROR 0 +!define RMT_NO_REMAINING_ADAPTERS 1 +!define RMT_SOME_REMAINING_ADAPTERS 2 + # Return codes from driverlogic::Initialize/Deinitialize !define DRIVERLOGIC_GENERAL_ERROR 0 !define DRIVERLOGIC_SUCCESS 1 @@ -45,6 +50,10 @@ !define PE_GENERAL_ERROR 0 !define PE_SUCCESS 1 +# Log targets +!define LOG_FILE 0 +!define LOG_VOID 1 + # Windows error codes !define ERROR_SERVICE_DEPENDENCY_DELETED 1075 @@ -151,6 +160,58 @@ !define ForceRenameAdapter '!insertmacro "ForceRenameAdapter"' # +# RemoveTap +# +# Try to remove the Mullvad TAP adapter +# and driver if there are no other TAPs available. +# +!macro RemoveTap + Push $0 + Push $1 + + driverlogic::Initialize + + Pop $0 + Pop $1 + + ${If} $0 != ${DRIVERLOGIC_SUCCESS} + Goto RemoveTap_return_only + ${EndIf} + + driverlogic::RemoveMullvadTap + + Pop $0 + Pop $1 + + ${If} $0 == ${RMT_GENERAL_ERROR} + Goto RemoveTap_return + ${EndIf} + + ${If} $0 == ${RMT_NO_REMAINING_ADAPTERS} + # Remove the driver altogether + nsExec::ExecToStack '"$TEMP\driver\tapinstall.exe" remove ${TAP_HARDWARE_ID}' + + Pop $0 + Pop $1 + ${EndIf} + + RemoveTap_return: + + driverlogic::Deinitialize + + Pop $0 + Pop $1 + + RemoveTap_return_only: + + Pop $1 + Pop $0 + +!macroend + +!define RemoveTap '!insertmacro "RemoveTap"' + +# # InstallDriver # # Install tunnel driver or update it if already present on the system @@ -213,6 +274,7 @@ ${If} $InstallDriver_BaselineStatus == ${EB_MULLVAD_ADAPTER_PRESENT} log::Log "Virtual adapter with custom name already present on system" + Goto InstallDriver_return_success ${EndIf} @@ -302,7 +364,7 @@ ${EndIf} InstallDriver_return_only: - + Pop $1 Pop $0 @@ -596,7 +658,7 @@ Push $R0 - log::Initialize + log::Initialize LOG_FILE log::Log "Running installer for ${PRODUCT_NAME} ${VERSION}" log::LogWindowsVersion @@ -669,22 +731,38 @@ Sleep 1000 - # Original removal functionality provided by Electron-builder - RMDir /r $INSTDIR - # Check command line arguments + Var /GLOBAL FullUninstall + ${GetParameters} $0 ${GetOptions} $0 "/S" $1 + ${If} ${Errors} + Push 1 + log::Initialize LOG_VOID + ${Else} + Push 0 + log::Initialize LOG_FILE + ${EndIf} + Pop $FullUninstall + + log::Log "Running uninstaller for ${PRODUCT_NAME} ${VERSION}" + + ${RemoveCLIFromEnvironPath} # If not ran silently - ${If} ${Errors} + ${If} $FullUninstall == 1 + # Remove the TAP adapter + ${ExtractDriver} + ${RemoveTap} + ${RemoveLogsAndCache} MessageBox MB_ICONQUESTION|MB_YESNO "Would you like to remove settings files as well?" IDNO customRemoveFiles_after_remove_settings ${RemoveSettings} customRemoveFiles_after_remove_settings: ${EndIf} - ${RemoveCLIFromEnvironPath} + # Original removal functionality provided by Electron-builder + RMDir /r $INSTDIR Pop $1 Pop $0 diff --git a/windows/nsis-plugins/src/driverlogic/context.cpp b/windows/nsis-plugins/src/driverlogic/context.cpp index 13cb781863..4f0cb2d291 100644 --- a/windows/nsis-plugins/src/driverlogic/context.cpp +++ b/windows/nsis-plugins/src/driverlogic/context.cpp @@ -1,21 +1,33 @@ #include "stdafx.h" #include "context.h" + #include <libcommon/string.h> #include <libcommon/error.h> +#include <libcommon/memory.h> #include <log/log.h> + #include <winsock2.h> #include <ws2ipdef.h> #include <iphlpapi.h> #include <windows.h> + #include <vector> #include <list> #include <stdexcept> #include <sstream> #include <algorithm> +#include <setupapi.h> +#include <devguid.h> +#include <combaseapi.h> +#include <initguid.h> +#include <devpkey.h> + namespace { +const wchar_t TAP_HARDWARE_ID[] = L"tap0901"; + std::set<Context::NetworkAdapter> GetAllAdapters() { ULONG bufferSize = 0; @@ -89,19 +101,57 @@ void LogAdapters(const std::wstring &description, const std::set<Context::Networ PluginLogWithDetails(description, details); } -} // anonymous namespace - -Context::BaselineStatus Context::establishBaseline() +std::wstring GetNetCfgInstanceId(HDEVINFO devInfo, const SP_DEVINFO_DATA &devInfoData) { - m_baseline = GetAllAdapters(); + HKEY hNet = SetupDiOpenDevRegKey( + devInfo, + const_cast<SP_DEVINFO_DATA *>(&devInfoData), + DICS_FLAG_GLOBAL, + 0, + DIREG_DRV, + KEY_READ + ); - auto tapAdapters = GetTapAdapters(m_baseline); + if (hNet == INVALID_HANDLE_VALUE) + { + throw std::runtime_error("SetupDiOpenDevRegKey Failed"); + } - if (tapAdapters.empty()) + std::vector<wchar_t> instanceId(MAX_PATH + sizeof(L'\0')); + DWORD strSize = instanceId.size() * sizeof(wchar_t); + + const auto status = RegGetValueW( + hNet, + nullptr, + L"NetCfgInstanceId", + RRF_RT_REG_SZ, + nullptr, + instanceId.data(), + &strSize + ); + + RegCloseKey(hNet); + + if (ERROR_SUCCESS != status) { - return BaselineStatus::NO_TAP_ADAPTERS_PRESENT; + throw std::runtime_error("RegGetValue for NetCfgInstanceId failed"); } + instanceId[strSize / sizeof(wchar_t)] = L'\0'; + + return instanceId.data(); +} + +} // anonymous namespace + +//static +std::optional<Context::NetworkAdapter> Context::FindMullvadAdapter(const std::set<Context::NetworkAdapter> &tapAdapters) +{ + if (tapAdapters.empty()) + { + return std::nullopt; + } + // // Look for TAP adapter with alias "Mullvad". // @@ -113,14 +163,16 @@ Context::BaselineStatus Context::establishBaseline() return 0 == _wcsicmp(candidate.alias.c_str(), alias.c_str()); }); - return it != adapters.end(); + return it; }; static const wchar_t baseAlias[] = L"Mullvad"; - if (findByAlias(tapAdapters, baseAlias)) + const auto mullvadAdapter = findByAlias(tapAdapters, baseAlias); + + if (tapAdapters.end() != mullvadAdapter) { - return BaselineStatus::MULLVAD_ADAPTER_PRESENT; + return std::optional{ *mullvadAdapter }; } // @@ -135,12 +187,32 @@ Context::BaselineStatus Context::establishBaseline() const auto alias = ss.str(); - if (findByAlias(tapAdapters, alias)) + const auto mullvadAdapter = findByAlias(tapAdapters, alias); + + if (tapAdapters.end() != mullvadAdapter) { - return BaselineStatus::MULLVAD_ADAPTER_PRESENT; + return std::optional{ *mullvadAdapter }; } } + return std::nullopt; +} + +Context::BaselineStatus Context::establishBaseline() +{ + m_baseline = GetAllAdapters(); + const auto tapAdapters = GetTapAdapters(m_baseline); + + if (tapAdapters.empty()) + { + return BaselineStatus::NO_TAP_ADAPTERS_PRESENT; + } + + if (FindMullvadAdapter(tapAdapters).has_value()) + { + return BaselineStatus::MULLVAD_ADAPTER_PRESENT; + } + return BaselineStatus::SOME_TAP_ADAPTERS_PRESENT; } @@ -173,3 +245,119 @@ Context::NetworkAdapter Context::getNewAdapter() return *added.begin(); } + +//static +Context::DeletionResult Context::DeleteMullvadAdapter() +{ + auto tapAdapters = GetTapAdapters(GetAllAdapters()); + std::optional<NetworkAdapter> mullvadAdapter = FindMullvadAdapter(tapAdapters); + + if (!mullvadAdapter.has_value()) + { + throw std::runtime_error("Mullvad TAP adapter not found"); + } + + const auto mullvadGuid = mullvadAdapter.value().guid; + + HDEVINFO devInfo = SetupDiGetClassDevsW( + &GUID_DEVCLASS_NET, + nullptr, + nullptr, + DIGCF_PRESENT + ); + + THROW_GLE_IF(INVALID_HANDLE_VALUE, devInfo, "SetupDiGetClassDevs() failed"); + + common::memory::ScopeDestructor cleanupDevList; + cleanupDevList += [&devInfo]() + { + SetupDiDestroyDeviceInfoList(devInfo); + }; + + SP_DEVINFO_DATA devInfoData; + + std::vector<wchar_t> buffer; + DWORD nameLen; + + int numRemainingAdapters = 0; + + for (int memberIndex = 0; ; memberIndex++) + { + devInfoData = { 0 }; + devInfoData.cbSize = sizeof(devInfoData); + + if (FALSE == SetupDiEnumDeviceInfo(devInfo, memberIndex, &devInfoData)) + { + if (GetLastError() == ERROR_NO_MORE_ITEMS) + { + /* done */ + break; + } + THROW_GLE("Error enumerating network adapters"); + } + + if (FALSE == SetupDiGetDeviceRegistryPropertyW( + devInfo, + &devInfoData, + SPDRP_HARDWAREID, + nullptr, + nullptr, + 0, + &nameLen + )) + { + const auto status = GetLastError(); + if (ERROR_INSUFFICIENT_BUFFER != status) + { + /* ERROR_INSUFFICIENT_BUFFER is expected */ + if (ERROR_INVALID_DATA == status) + { + /* ERROR_INVALID_DATA may mean that the property does not exist */ + continue; + } + THROW_GLE("Error obtaining network adapter hardware ID length"); + } + } + + buffer.resize(nameLen / sizeof(wchar_t) + 1); + buffer[nameLen / sizeof(wchar_t)] = L'\0'; + + if (FALSE == SetupDiGetDeviceRegistryPropertyW( + devInfo, + &devInfoData, + SPDRP_HARDWAREID, + nullptr, + reinterpret_cast<PBYTE>(buffer.data()), + (buffer.size() - 1) * sizeof(wchar_t), + nullptr + )) + { + THROW_GLE("Error obtaining network adapter hardware ID"); + } + + if (wcscmp(TAP_HARDWARE_ID, buffer.data()) == 0) + { + std::wstring netCfgInstanceId = GetNetCfgInstanceId(devInfo, devInfoData); + if (netCfgInstanceId.compare(mullvadGuid) != 0) + { + numRemainingAdapters++; + continue; + } + + if (FALSE == SetupDiRemoveDevice( + devInfo, + &devInfoData + )) + { + THROW_GLE("Error removing Mullvad TAP device"); + } + } + } + + if (numRemainingAdapters > 0) + { + return DeletionResult::SOME_REMAINING_TAP_ADAPTERS; + } + + return DeletionResult::NO_REMAINING_TAP_ADAPTERS; +} diff --git a/windows/nsis-plugins/src/driverlogic/context.h b/windows/nsis-plugins/src/driverlogic/context.h index a93a3bcfcf..eabf884ba8 100644 --- a/windows/nsis-plugins/src/driverlogic/context.h +++ b/windows/nsis-plugins/src/driverlogic/context.h @@ -2,6 +2,7 @@ #include <set> #include <string> +#include <optional> class Context { @@ -46,8 +47,18 @@ public: // NetworkAdapter getNewAdapter(); + enum class DeletionResult + { + NO_REMAINING_TAP_ADAPTERS, + SOME_REMAINING_TAP_ADAPTERS + }; + + static DeletionResult DeleteMullvadAdapter(); + private: + static std::optional<NetworkAdapter> FindMullvadAdapter(const std::set<NetworkAdapter> &tapAdapters); + std::set<NetworkAdapter> m_baseline; std::set<NetworkAdapter> m_currentState; }; diff --git a/windows/nsis-plugins/src/driverlogic/driverlogic.cpp b/windows/nsis-plugins/src/driverlogic/driverlogic.cpp index 3bbe98bf8b..82259c015d 100644 --- a/windows/nsis-plugins/src/driverlogic/driverlogic.cpp +++ b/windows/nsis-plugins/src/driverlogic/driverlogic.cpp @@ -157,6 +157,68 @@ void __declspec(dllexport) NSISCALL EstablishBaseline } // +// RemoveMullvadTap +// +// Deletes the Mullvad TAP adapter. +// +// +enum class RemoveMullvadTapStatus +{ + GENERAL_ERROR = 0, + SUCCESS_NO_REMAINING_TAP_ADAPTERS, + SUCCESS_SOME_REMAINING_TAP_ADAPTERS +}; + +void __declspec(dllexport) NSISCALL RemoveMullvadTap +( + HWND hwndParent, + int string_size, + LPTSTR variables, + stack_t **stacktop, + extra_parameters *extra, + ... +) +{ + EXDLL_INIT(); + + try + { + pushstring(L""); + + switch (Context::DeleteMullvadAdapter()) + { + case Context::DeletionResult::NO_REMAINING_TAP_ADAPTERS: + { + pushint(RemoveMullvadTapStatus::SUCCESS_NO_REMAINING_TAP_ADAPTERS); + break; + } + + case Context::DeletionResult::SOME_REMAINING_TAP_ADAPTERS: + { + pushint(RemoveMullvadTapStatus::SUCCESS_SOME_REMAINING_TAP_ADAPTERS); + break; + } + + default: + { + throw std::runtime_error("Unexpected case"); + } + } + } + catch (std::exception &err) + { + pushstring(common::string::ToWide(err.what()).c_str()); + pushint(RemoveMullvadTapStatus::GENERAL_ERROR); + } + catch (...) + { + pushstring(L"Unspecified error"); + pushint(RemoveMullvadTapStatus::GENERAL_ERROR); + } +} + + +// // IdentifyNewAdapter // // Call this function after installing a TAP adapter. diff --git a/windows/nsis-plugins/src/driverlogic/driverlogic.def b/windows/nsis-plugins/src/driverlogic/driverlogic.def index 1bd484d6be..b8735f1ada 100644 --- a/windows/nsis-plugins/src/driverlogic/driverlogic.def +++ b/windows/nsis-plugins/src/driverlogic/driverlogic.def @@ -5,4 +5,5 @@ EXPORTS Initialize EstablishBaseline IdentifyNewAdapter +RemoveMullvadTap Deinitialize diff --git a/windows/nsis-plugins/src/driverlogic/driverlogic.vcxproj b/windows/nsis-plugins/src/driverlogic/driverlogic.vcxproj index 0e3932e8fa..d7cad2635f 100644 --- a/windows/nsis-plugins/src/driverlogic/driverlogic.vcxproj +++ b/windows/nsis-plugins/src/driverlogic/driverlogic.vcxproj @@ -70,7 +70,7 @@ <GenerateDebugInformation>true</GenerateDebugInformation> <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> <AdditionalLibraryDirectories>$(ProjectDir)../../../../dist-assets/binaries/x86_64-pc-windows-msvc/nsis/;$(SolutionDir)bin\$(Platform)-$(Configuration)\</AdditionalLibraryDirectories> - <AdditionalDependencies>iphlpapi.lib;log.lib;libcommon.lib;pluginapi-x86-unicode.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies>setupapi.lib;iphlpapi.lib;log.lib;libcommon.lib;pluginapi-x86-unicode.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> <IgnoreSpecificDefaultLibraries>libc.lib</IgnoreSpecificDefaultLibraries> <ModuleDefinitionFile>driverlogic.def</ModuleDefinitionFile> </Link> @@ -96,7 +96,7 @@ <GenerateDebugInformation>true</GenerateDebugInformation> <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> <AdditionalLibraryDirectories>$(ProjectDir)../../../../dist-assets/binaries/x86_64-pc-windows-msvc/nsis/;$(SolutionDir)bin\$(Platform)-$(Configuration)\</AdditionalLibraryDirectories> - <AdditionalDependencies>iphlpapi.lib;log.lib;libcommon.lib;pluginapi-x86-unicode.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies>setupapi.lib;iphlpapi.lib;log.lib;libcommon.lib;pluginapi-x86-unicode.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> <IgnoreSpecificDefaultLibraries>libc.lib</IgnoreSpecificDefaultLibraries> <ModuleDefinitionFile>driverlogic.def</ModuleDefinitionFile> </Link> diff --git a/windows/nsis-plugins/src/log/log.cpp b/windows/nsis-plugins/src/log/log.cpp index d9ab10f4ea..f0a050aadb 100644 --- a/windows/nsis-plugins/src/log/log.cpp +++ b/windows/nsis-plugins/src/log/log.cpp @@ -172,6 +172,12 @@ std::wstring GetWindowsVersion() // // Opens and maintains an open handle to the log file. // +enum class LogTarget +{ + LOG_FILE = 0, + LOG_VOID +}; + void __declspec(dllexport) NSISCALL Initialize ( HWND hwndParent, @@ -188,29 +194,52 @@ void __declspec(dllexport) NSISCALL Initialize { PinDll(); - auto logpath = std::experimental::filesystem::path(common::fs::GetKnownFolderPath( - FOLDERID_ProgramData, 0, nullptr)); - - logpath.append(L"Mullvad VPN"); - - if (FALSE == CreateDirectoryW(logpath.c_str(), nullptr)) + int target = popint(); + switch (target) { - if (ERROR_ALREADY_EXISTS != GetLastError()) + case static_cast<int>(LogTarget::LOG_FILE): { - std::wstringstream ss; + auto logpath = std::experimental::filesystem::path(common::fs::GetKnownFolderPath( + FOLDERID_ProgramData, 0, nullptr)); + + logpath.append(L"Mullvad VPN"); + + if (FALSE == CreateDirectoryW(logpath.c_str(), nullptr)) + { + if (ERROR_ALREADY_EXISTS != GetLastError()) + { + std::wstringstream ss; + + ss << L"Cannot create folder: " + << L"\"" + << logpath + << L"\""; + + throw std::runtime_error(common::string::ToAnsi(ss.str())); + } + } + + const auto logfile = decltype(logpath)(logpath).append(L"install.log"); - ss << L"Cannot create folder: " - << L"\"" - << logpath - << L"\""; + g_logger = new Logger(std::make_unique<AnsiFileLogSink>(logfile)); + + break; - throw std::runtime_error(common::string::ToAnsi(ss.str())); } - } - const auto logfile = decltype(logpath)(logpath).append(L"install.log"); + case static_cast<int>(LogTarget::LOG_VOID): + { + g_logger = new Logger(std::make_unique<VoidLogSink>()); - g_logger = new Logger(std::make_unique<AnsiFileLogSink>(logfile)); + break; + + } + + default: + { + throw std::runtime_error("Invalid log target"); + } + } } catch (std::exception &err) { diff --git a/windows/nsis-plugins/src/log/logger.h b/windows/nsis-plugins/src/log/logger.h index 6f28e5c73a..fe06072581 100644 --- a/windows/nsis-plugins/src/log/logger.h +++ b/windows/nsis-plugins/src/log/logger.h @@ -14,6 +14,13 @@ struct ILogSink virtual void log(const std::wstring &message) = 0; }; +class VoidLogSink : public ILogSink +{ +public: + + void log(const std::wstring &message) override {} +}; + class AnsiFileLogSink : public ILogSink { public: |
