From aeb5ce0ad7fdfa08794076e1a749a6cdee60e9b2 Mon Sep 17 00:00:00 2001 From: Lev Stipakov Date: Mon, 29 Jul 2019 16:41:18 +0300 Subject: [PATCH] wintun: open device with SetupAPI With introduction of ring buffers Wintun device must be opened via SetupAPI. Signed-off-by: Lev Stipakov --- openvpn/tun/win/tunutil.hpp | 187 +++++++++++++++++++++++++++++++----- win/build.py | 2 +- 2 files changed, 163 insertions(+), 26 deletions(-) diff --git a/openvpn/tun/win/tunutil.hpp b/openvpn/tun/win/tunutil.hpp index e064313d..ef26ba74 100644 --- a/openvpn/tun/win/tunutil.hpp +++ b/openvpn/tun/win/tunutil.hpp @@ -35,6 +35,12 @@ #include // for IPv6 #include // for impersonating as LocalSystem + +#include +#include +#include +#include + #include #include #include @@ -343,12 +349,123 @@ namespace openvpn { } }; - // given a TAP GUID, form the pathname of the TAP device node - inline std::string tap_path(const TapNameGuidPair& tap, bool wintun) + struct DeviceInstanceIdInterfacePair + { + std::string net_cfg_instance_id; + std::string device_interface_list; + }; + + class DevInfoSetHelper + { + public: + DevInfoSetHelper() + { + handle = SetupDiGetClassDevsEx(&GUID_DEVCLASS_NET, NULL, NULL, DIGCF_PRESENT, NULL, NULL, NULL); + } + + bool is_valid() + { + return handle != INVALID_HANDLE_VALUE; + } + + operator HDEVINFO() + { + return handle; + } + + ~DevInfoSetHelper() + { + if (is_valid()) + { + SetupDiDestroyDeviceInfoList(handle); + } + } + + private: + HDEVINFO handle; + }; + + struct DeviceInstanceIdInterfaceList : public std::vector + { + DeviceInstanceIdInterfaceList() + { + DevInfoSetHelper device_info_set; + if (!device_info_set.is_valid()) + return; + + for (DWORD i = 0;; ++i) + { + SP_DEVINFO_DATA dev_info_data; + ZeroMemory(&dev_info_data, sizeof(SP_DEVINFO_DATA)); + dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA); + BOOL res = SetupDiEnumDeviceInfo(device_info_set, i, &dev_info_data); + if (!res) + { + if (GetLastError() == ERROR_NO_MORE_ITEMS) + break; + else + continue; + } + + Win::RegKey regkey; + *regkey.ref() = SetupDiOpenDevRegKey(device_info_set, &dev_info_data, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_QUERY_VALUE); + if (!regkey.defined()) + continue; + + std::string str_net_cfg_instance_id; + + DWORD size; + LONG status = RegQueryValueExA(regkey(), "NetCfgInstanceId", NULL, NULL, NULL, &size); + if (status != ERROR_SUCCESS) + continue; + BufferAllocatedType buf_net_cfg_inst_id(size, BufferAllocated::CONSTRUCT_ZERO); + + status = RegQueryValueExA(regkey(), "NetCfgInstanceId", NULL, NULL, (LPBYTE)buf_net_cfg_inst_id.data(), &size); + if (status == ERROR_SUCCESS) + { + buf_net_cfg_inst_id.data()[size - 1] = '\0'; + str_net_cfg_instance_id = std::string(buf_net_cfg_inst_id.data()); + } + else + continue; + + res = SetupDiGetDeviceInstanceId(device_info_set, &dev_info_data, NULL, 0, &size); + if (res != FALSE && GetLastError() != ERROR_INSUFFICIENT_BUFFER) + continue; + + BufferAllocatedType buf_dev_inst_id(size, BufferAllocated::CONSTRUCT_ZERO); + if (!SetupDiGetDeviceInstanceId(device_info_set, &dev_info_data, buf_dev_inst_id.data(), size, &size)) + continue; + buf_dev_inst_id.set_size(size); + + ULONG dev_interface_list_size = 0; + CONFIGRET cr = CM_Get_Device_Interface_List_Size(&dev_interface_list_size, + (LPGUID)& GUID_DEVINTERFACE_NET, + buf_dev_inst_id.data(), + CM_GET_DEVICE_INTERFACE_LIST_PRESENT); + + if (cr != CR_SUCCESS) + continue; + + BufferAllocatedType buf_dev_iface_list(dev_interface_list_size, BufferAllocated::CONSTRUCT_ZERO); + cr = CM_Get_Device_Interface_List((LPGUID)& GUID_DEVINTERFACE_NET, buf_dev_inst_id.data(), + buf_dev_iface_list.data(), + dev_interface_list_size, + CM_GET_DEVICE_INTERFACE_LIST_PRESENT); + if (cr != CR_SUCCESS) + continue; + + DeviceInstanceIdInterfacePair pair; + pair.net_cfg_instance_id = str_net_cfg_instance_id; + pair.device_interface_list = std::string(buf_dev_iface_list.data()); + push_back(pair); + } + } + }; + + // given a TAP GUID, form the pathname of the TAP device node + inline std::string tap_path(const TapNameGuidPair& tap) { - if (wintun) - return std::string(USERMODEDEVICEDIR) + "WINTUN" + std::to_string(tap.net_luid_index); - else return std::string(USERMODEDEVICEDIR) + tap.guid + std::string(TAP_WIN_SUFFIX); } @@ -364,30 +481,50 @@ namespace openvpn { for (TapNameGuidPairList::const_iterator i = guids.begin(); i != guids.end(); i++) { const TapNameGuidPair& tap = *i; - const std::string path = tap_path(tap, wintun); - { - // wintun device can be only opened under LocalSystem account - std::unique_ptr imp; + std::string path; - if (wintun) - imp.reset(new Win::Impersonate(true)); - - hand.reset(::CreateFileA(path.c_str(), - GENERIC_READ | GENERIC_WRITE, - 0, /* was: FILE_SHARE_READ */ - 0, - OPEN_EXISTING, - FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, - 0)); - } - - if (hand.defined()) + if (wintun) { - used = tap; - path_opened = path; - break; + DeviceInstanceIdInterfaceList inst_id_interface_list; + + for (const auto& inst_id_interface : inst_id_interface_list) + { + if (inst_id_interface.net_cfg_instance_id != tap.guid) + continue; + + path = inst_id_interface.device_interface_list; + break; + } } + else + { + path = tap_path(tap); + } + + if (path.length() > 0) + { + // wintun device can be only opened under LocalSystem account + std::unique_ptr imp; + + if (wintun) + imp.reset(new Win::Impersonate(true)); + + hand.reset(::CreateFileA(path.c_str(), + GENERIC_READ | GENERIC_WRITE, + 0, /* was: FILE_SHARE_READ */ + 0, + OPEN_EXISTING, + FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, + 0)); + + if (hand.defined()) + { + used = tap; + path_opened = path; + break; + } + } } return hand.release(); } diff --git a/win/build.py b/win/build.py index 6873738c..b349336d 100644 --- a/win/build.py +++ b/win/build.py @@ -88,7 +88,7 @@ def build(parms, srcfile, unit_test=False): options['extra_lib'] += ' mbedtls.lib' # build it - vc_cmd(parms, r"cl %(extra_defs)s /DNOMINMAX /bigobj /D_CRT_SECURE_NO_WARNINGS /DUSE_ASIO /DASIO_STANDALONE /DASIO_NO_DEPRECATED /I %(asio)s\asio\include /DHAVE_LZ4 /I %(lz4)s %(extra_inc)s -DTAP_WIN_COMPONENT_ID=%(tap_component_id)s /I %(tap)s /I %(ovpn3)s\core /EHsc %(link_static_dynamic_flags)s /W0 %(dbg_rel_flags)s /nologo %(srcfile)s /link /LIBPATH:%(lz4)s%(extra_lib_path)s lz4.lib%(extra_lib)s ws2_32.lib crypt32.lib iphlpapi.lib winmm.lib user32.lib gdi32.lib advapi32.lib wininet.lib shell32.lib ole32.lib rpcrt4.lib Wtsapi32.lib" % options, arch=os.environ.get("ARCH")) + vc_cmd(parms, r"cl %(extra_defs)s /DNOMINMAX /bigobj /D_CRT_SECURE_NO_WARNINGS /DUSE_ASIO /DASIO_STANDALONE /DASIO_NO_DEPRECATED /I %(asio)s\asio\include /DHAVE_LZ4 /I %(lz4)s %(extra_inc)s -DTAP_WIN_COMPONENT_ID=%(tap_component_id)s /I %(tap)s /I %(ovpn3)s\core /EHsc %(link_static_dynamic_flags)s /W0 %(dbg_rel_flags)s /nologo %(srcfile)s /link /LIBPATH:%(lz4)s%(extra_lib_path)s lz4.lib%(extra_lib)s ws2_32.lib crypt32.lib iphlpapi.lib winmm.lib user32.lib gdi32.lib advapi32.lib wininet.lib shell32.lib ole32.lib rpcrt4.lib Wtsapi32.lib Setupapi.lib Cfgmgr32.lib" % options, arch=os.environ.get("ARCH")) if __name__ == "__main__": import sys