summaryrefslogtreecommitdiffhomepage
path: root/windows/nsis-plugins/src/driverlogic/driverlogicops.cpp
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2020-01-21 15:24:07 +0100
committerDavid Lönnhager <david.l@mullvad.net>2020-01-31 16:24:48 +0100
commit60d01c3525f4d3c133d517702997d83c463df3ba (patch)
tree56fb5ec432fdca3ffc9194dfe9b6e6020760576c /windows/nsis-plugins/src/driverlogic/driverlogicops.cpp
parentddf28d3a7599ebeed20a4b425630aa4a8bf7e9b4 (diff)
downloadmullvadvpn-60d01c3525f4d3c133d517702997d83c463df3ba.tar.xz
mullvadvpn-60d01c3525f4d3c133d517702997d83c463df3ba.zip
Refactor driverlogic
Diffstat (limited to 'windows/nsis-plugins/src/driverlogic/driverlogicops.cpp')
-rw-r--r--windows/nsis-plugins/src/driverlogic/driverlogicops.cpp481
1 files changed, 481 insertions, 0 deletions
diff --git a/windows/nsis-plugins/src/driverlogic/driverlogicops.cpp b/windows/nsis-plugins/src/driverlogic/driverlogicops.cpp
new file mode 100644
index 0000000000..66fa2d9f24
--- /dev/null
+++ b/windows/nsis-plugins/src/driverlogic/driverlogicops.cpp
@@ -0,0 +1,481 @@
+#include "stdafx.h"
+#include "driverlogicops.h"
+
+#include <libcommon/guid.h>
+#include <libcommon/string.h>
+#include <libcommon/error.h>
+#include <libcommon/memory.h>
+#include <libcommon/network/nci.h>
+#include <log/log.h>
+
+#include <windows.h>
+
+#include <vector>
+#include <list>
+#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"tapmullvad0901";
+const wchar_t DEPRECATED_TAP_HARDWARE_ID[] = L"tap0901";
+
+template<typename T>
+void LogAdapters(const std::wstring &description, const T &adapters)
+{
+ //
+ // Flatten the information so we can log it more easily.
+ //
+
+ std::vector<std::wstring> details;
+
+ for (const auto &adapter : adapters)
+ {
+ details.emplace_back(L"Adapter");
+
+ details.emplace_back(std::wstring(L" Guid: ").append(adapter.guid));
+ details.emplace_back(std::wstring(L" Name: ").append(adapter.name));
+ details.emplace_back(std::wstring(L" Alias: ").append(adapter.alias));
+ }
+
+ PluginLogWithDetails(description, details);
+}
+
+} // anonymous namespace
+
+namespace driverlogic
+{
+
+std::wstring GetNetCfgInstanceId(HDEVINFO devInfo, const SP_DEVINFO_DATA &devInfoData)
+{
+ HKEY hNet = SetupDiOpenDevRegKey(
+ devInfo,
+ const_cast<SP_DEVINFO_DATA *>(&devInfoData),
+ DICS_FLAG_GLOBAL,
+ 0,
+ DIREG_DRV,
+ KEY_READ
+ );
+
+ if (hNet == INVALID_HANDLE_VALUE)
+ {
+ THROW_WINDOWS_ERROR(GetLastError(), "SetupDiOpenDevRegKey");
+ }
+
+ 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)
+ {
+ THROW_WINDOWS_ERROR(status, "RegGetValueW");
+ }
+
+ instanceId[strSize / sizeof(wchar_t)] = L'\0';
+
+ return instanceId.data();
+}
+
+std::wstring GetDeviceInstanceId(
+ HDEVINFO devInfo,
+ SP_DEVINFO_DATA* devInfoData
+)
+{
+ DWORD requiredSize = 0;
+
+ SetupDiGetDeviceInstanceIdW(
+ devInfo,
+ devInfoData,
+ nullptr,
+ 0,
+ &requiredSize
+ );
+
+ std::vector<wchar_t> deviceInstanceId;
+ deviceInstanceId.resize(1 + requiredSize * sizeof(wchar_t));
+
+ const auto status = SetupDiGetDeviceInstanceIdW(
+ devInfo,
+ devInfoData,
+ &deviceInstanceId[0],
+ deviceInstanceId.size(),
+ nullptr
+ );
+
+ if (FALSE == status)
+ {
+ THROW_WINDOWS_ERROR(GetLastError(), "SetupDiGetDeviceInstanceIdW() failed");
+ }
+
+ return deviceInstanceId.data();
+}
+
+std::wstring GetDeviceStringProperty(
+ HDEVINFO devInfo,
+ SP_DEVINFO_DATA *devInfoData,
+ const DEVPROPKEY *property
+)
+{
+ //
+ // Obtain required buffer size
+ //
+
+ DWORD requiredSize = 0;
+ DEVPROPTYPE type;
+
+ const auto sizeStatus = SetupDiGetDevicePropertyW(
+ devInfo,
+ devInfoData,
+ property,
+ &type,
+ nullptr,
+ 0,
+ &requiredSize,
+ 0
+ );
+
+ if (FALSE == sizeStatus)
+ {
+ const auto lastError = GetLastError();
+
+ if (ERROR_INSUFFICIENT_BUFFER != lastError)
+ {
+ THROW_WINDOWS_ERROR(lastError, "SetupDiGetDevicePropertyW");
+ }
+ }
+
+ std::vector<wchar_t> buffer;
+ buffer.resize(1 + requiredSize / sizeof(wchar_t));
+
+ //
+ // Read property
+ //
+
+ const auto status = SetupDiGetDevicePropertyW(
+ devInfo,
+ devInfoData,
+ property,
+ &type,
+ reinterpret_cast<PBYTE>(&buffer[0]),
+ buffer.size() * sizeof(wchar_t),
+ nullptr,
+ 0
+ );
+
+ if (FALSE == status)
+ {
+ THROW_WINDOWS_ERROR(GetLastError(), "Failed to read device property");
+ }
+
+ return buffer.data();
+}
+
+std::optional<std::wstring> GetDeviceRegistryStringProperty(
+ HDEVINFO devInfo,
+ SP_DEVINFO_DATA *devInfoData,
+ DWORD property
+)
+{
+ //
+ // Obtain required buffer size
+ //
+
+ DWORD requiredSize = 0;
+
+ const auto sizeStatus = SetupDiGetDeviceRegistryPropertyW(
+ devInfo,
+ devInfoData,
+ property,
+ nullptr,
+ nullptr,
+ 0,
+ &requiredSize
+ );
+
+ const DWORD lastError = GetLastError();
+ if (FALSE == sizeStatus && ERROR_INSUFFICIENT_BUFFER != lastError)
+ {
+ // ERROR_INVALID_DATA may mean that the property does not exist
+ // TODO: Check if there may be other causes.
+ if (ERROR_INVALID_DATA != lastError)
+ {
+ THROW_WINDOWS_ERROR(lastError, "SetupDiGetDeviceRegistryPropertyW");
+ }
+
+ return std::nullopt;
+ }
+
+ //
+ // Read property
+ //
+
+ std::vector<wchar_t> buffer;
+ buffer.resize(1 + requiredSize / sizeof(wchar_t));
+
+ const auto status = SetupDiGetDeviceRegistryPropertyW(
+ devInfo,
+ devInfoData,
+ property,
+ nullptr,
+ reinterpret_cast<PBYTE>(&buffer[0]),
+ buffer.size() * sizeof(wchar_t),
+ nullptr
+ );
+
+ if (FALSE == status)
+ {
+ THROW_WINDOWS_ERROR(GetLastError(), "Failed to read device property");
+ }
+
+ return { buffer.data() };
+}
+
+std::set<NetworkAdapter> GetTapAdapters(const std::wstring &tapHardwareId)
+{
+ std::set<NetworkAdapter> adapters;
+
+ HDEVINFO devInfo = SetupDiGetClassDevs(
+ &GUID_DEVCLASS_NET,
+ nullptr,
+ nullptr,
+ DIGCF_PRESENT
+ );
+
+ if (INVALID_HANDLE_VALUE == devInfo)
+ {
+ THROW_WINDOWS_ERROR(GetLastError(), "SetupDiGetClassDevs() failed");
+ }
+
+ common::memory::ScopeDestructor scopeDestructor;
+ scopeDestructor += [devInfo]()
+ {
+ SetupDiDestroyDeviceInfoList(devInfo);
+ };
+
+ common::network::Nci nci;
+
+ for (int memberIndex = 0; ; memberIndex++)
+ {
+ SP_DEVINFO_DATA devInfoData = { 0 };
+ devInfoData.cbSize = sizeof(devInfoData);
+
+ if (FALSE == SetupDiEnumDeviceInfo(devInfo, memberIndex, &devInfoData))
+ {
+ const auto lastError = GetLastError();
+
+ if (ERROR_NO_MORE_ITEMS == lastError)
+ {
+ // Done
+ break;
+ }
+
+ THROW_WINDOWS_ERROR(lastError, "SetupDiEnumDeviceInfo() failed while enumerating network adapters");
+ }
+
+ //
+ // Check whether this is a TAP adapter
+ //
+
+ const auto hardwareId = GetDeviceRegistryStringProperty(devInfo, &devInfoData, SPDRP_HARDWAREID);
+ if (!hardwareId.has_value()
+ || wcscmp(hardwareId.value().c_str(), tapHardwareId.c_str()) != 0)
+ {
+ continue;
+ }
+
+ //
+ // Construct NetworkAdapter
+ //
+
+ try
+ {
+ const std::wstring guid = GetNetCfgInstanceId(devInfo, devInfoData);
+ GUID guidObj = common::Guid::FromString(guid);
+
+ adapters.emplace(NetworkAdapter(
+ guid,
+ GetDeviceStringProperty(devInfo, &devInfoData, &DEVPKEY_Device_DriverDesc),
+ nci.getConnectionName(guidObj),
+ GetDeviceInstanceId(devInfo, &devInfoData)
+ ));
+ }
+ catch (const std::exception &e)
+ {
+ //
+ // Log exception and skip this adapter
+ //
+
+ const auto msg =
+ std::string("Skipping TAP adapter due to exception caught while iterating: ").append(e.what());
+ PluginLog(common::string::ToWide(msg));
+ }
+ }
+
+ return adapters;
+}
+
+std::optional<NetworkAdapter> FindMullvadAdapter(const std::set<NetworkAdapter> &tapAdapters)
+{
+ if (tapAdapters.empty())
+ {
+ return std::nullopt;
+ }
+
+ //
+ // Look for TAP adapter with alias "Mullvad".
+ //
+
+ auto findByAlias = [](const std::set<NetworkAdapter> &adapters, const std::wstring &alias)
+ {
+ const auto it = std::find_if(adapters.begin(), adapters.end(), [&alias](const NetworkAdapter &candidate)
+ {
+ return 0 == _wcsicmp(candidate.alias.c_str(), alias.c_str());
+ });
+
+ return it;
+ };
+
+ static const wchar_t baseAlias[] = L"Mullvad";
+
+ const auto mullvadAdapter = findByAlias(tapAdapters, baseAlias);
+
+ if (tapAdapters.end() != mullvadAdapter)
+ {
+ return { *mullvadAdapter };
+ }
+
+ //
+ // Look for TAP adapter with alias "Mullvad-1", "Mullvad-2", etc.
+ //
+
+ for (auto i = 0; i < 10; ++i)
+ {
+ std::wstringstream ss;
+
+ ss << baseAlias << L"-" << i;
+
+ const auto alias = ss.str();
+
+ const auto mullvadAdapter = findByAlias(tapAdapters, alias);
+
+ if (tapAdapters.end() != mullvadAdapter)
+ {
+ return { *mullvadAdapter };
+ }
+ }
+
+ return std::nullopt;
+}
+
+NetworkAdapter GetAdapter()
+{
+ std::set<NetworkAdapter> added = GetTapAdapters(TAP_HARDWARE_ID);
+
+ if (added.empty())
+ {
+ THROW_ERROR("Could not identify TAP");
+ }
+ else if (added.size() > 1)
+ {
+ LogAdapters(L"Enumerable network TAP adapters", added);
+
+ THROW_ERROR("Identified more TAP adapters than expected");
+ }
+
+ return *added.begin();
+}
+
+DeletionResult DeleteOldMullvadAdapter()
+{
+ auto tapAdapters = GetTapAdapters(DEPRECATED_TAP_HARDWARE_ID);
+ std::optional<NetworkAdapter> mullvadAdapter = FindMullvadAdapter(tapAdapters);
+
+ if (!mullvadAdapter.has_value())
+ {
+ THROW_ERROR("Mullvad TAP adapter not found");
+ }
+
+ const auto mullvadGuid = mullvadAdapter.value().guid;
+
+ HDEVINFO devInfo = SetupDiGetClassDevsW(
+ &GUID_DEVCLASS_NET,
+ nullptr,
+ nullptr,
+ DIGCF_PRESENT
+ );
+
+ if (INVALID_HANDLE_VALUE == devInfo)
+ {
+ THROW_WINDOWS_ERROR(GetLastError(), "SetupDiGetClassDevs() failed");
+ }
+
+ common::memory::ScopeDestructor cleanupDevList;
+ cleanupDevList += [&devInfo]()
+ {
+ SetupDiDestroyDeviceInfoList(devInfo);
+ };
+
+ int numRemainingAdapters = 0;
+
+ for (int memberIndex = 0; ; memberIndex++)
+ {
+ SP_DEVINFO_DATA devInfoData = { 0 };
+ devInfoData.cbSize = sizeof(devInfoData);
+
+ if (FALSE == SetupDiEnumDeviceInfo(devInfo, memberIndex, &devInfoData))
+ {
+ const auto lastError = GetLastError();
+
+ if (ERROR_NO_MORE_ITEMS == lastError)
+ {
+ break;
+ }
+
+ THROW_WINDOWS_ERROR(lastError, "Error enumerating network adapters");
+ }
+
+ const auto hardwareId = GetDeviceRegistryStringProperty(devInfo, &devInfoData, SPDRP_HARDWAREID);
+
+ if (hardwareId.has_value()
+ && wcscmp(DEPRECATED_TAP_HARDWARE_ID, hardwareId.value().data()) == 0)
+ {
+ if (0 != GetNetCfgInstanceId(devInfo, devInfoData).compare(mullvadGuid))
+ {
+ numRemainingAdapters++;
+ continue;
+ }
+
+ if (FALSE == SetupDiRemoveDevice(
+ devInfo,
+ &devInfoData
+ ))
+ {
+ THROW_WINDOWS_ERROR(GetLastError(), "Error removing Mullvad TAP device");
+ }
+ }
+ }
+
+ return (numRemainingAdapters > 0)
+ ? DeletionResult::SOME_REMAINING_TAP_ADAPTERS
+ : DeletionResult::NO_REMAINING_TAP_ADAPTERS;
+}
+
+}