From 6dcfbe1b15cc485af081927292771d1c97b82b1e Mon Sep 17 00:00:00 2001 From: Marios Paouris Date: Mon, 17 Jun 2024 09:10:03 +0300 Subject: [PATCH] Verbose reporting on reading adapter name failure. Added test to exercise wintun max adapter name Signed-off-by: Marios Paouris --- .gitlab-ci.yml | 12 ++- Makefile.am | 3 +- tests/Makefile.am | 21 +++- tests/list-taps.c | 4 + tests/wintun-names.c | 251 +++++++++++++++++++++++++++++++++++++++++++ tun-win32.c | 41 +++++-- 6 files changed, 320 insertions(+), 12 deletions(-) create mode 100644 tests/wintun-names.c diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index fdd3f504..d086b1e8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -603,7 +603,8 @@ MinGW32/GnuTLS: - make -j4 # Setup wine path so tests won't fail due to unresolved dll dependencies - export WINEPATH=/usr/i686-w64-mingw32/sys-root/mingw/bin\;. - - make VERBOSE=1 -j4 check +# Wintun tests cannot be run under wine, wintun cannot create the interface on wine. + - make VERBOSE=1 XFAIL_TESTS="wintun-names.exe" -j4 check tags: - saas-linux-small-amd64 except: @@ -626,7 +627,8 @@ MinGW32/OpenSSL: - make -j4 # Setup wine path so tests won't fail due to unresolved dll dependencies - export WINEPATH=/usr/i686-w64-mingw32/sys-root/mingw/bin\;. - - make VERBOSE=1 -j4 check +# Wintun tests cannot be run under wine, wintun cannot create the interface on wine. + - make VERBOSE=1 XFAIL_TESTS="wintun-names.exe" -j4 check tags: - saas-linux-small-amd64 except: @@ -650,7 +652,8 @@ MinGW64/GnuTLS: - make -j4 # Setup wine path so tests won't fail due to unresolved dll dependencies - export WINEPATH=/usr/x86_64-w64-mingw32/sys-root/mingw/bin\;. - - make VERBOSE=1 -j4 check +# Wintun tests cannot be run under wine, wintun cannot create the interface on wine. + - make VERBOSE=1 XFAIL_TESTS="wintun-names.exe" -j4 check tags: - saas-linux-small-amd64 except: @@ -674,7 +677,8 @@ MinGW64/OpenSSL: - make -j4 # Setup wine path so tests won't fail due to unresolved dll dependencies - export WINEPATH=/usr/x86_64-w64-mingw32/sys-root/mingw/bin\;. - - make VERBOSE=1 -j4 check +# Wintun tests cannot be run under wine, wintun cannot create the interface on wine. + - make VERBOSE=1 XFAIL_TESTS="wintun-names.exe" -j4 check tags: - saas-linux-small-amd64 except: diff --git a/Makefile.am b/Makefile.am index b23e0812..e7ead12e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,7 @@ SUBDIRS = tests # We kind of want openconnect to be built before we try to test it -check-recursive: openconnect$(EXEEXT) +# We also want to have wintun library when running wintun tests, if applicable +check-recursive: openconnect$(EXEEXT) $(WINTUN_DLL) # And even *building* some of tests/*.c needs libopenconnect install-recursive: libopenconnect.la all-recursive: libopenconnect.la diff --git a/tests/Makefile.am b/tests/Makefile.am index 7a63da8c..c03671f2 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -151,9 +151,25 @@ TESTS_ENVIRONMENT = srcdir="$(srcdir)" \ C_TESTS = lzstest seqtest buftest +DISTCLEANFILES = + if OPENCONNECT_WIN32 C_TESTS += list-taps -endif + +list_taps_SOURCES = list-taps.c + +if OPENCONNECT_WINTUN +C_TESTS += wintun-names + +wintun_names_SOURCES = wintun-names.c + +#list wintun as a script so it is "rebuilt" (copied from top-level) when the tests run +nodist_check_SCRIPTS = $(WINTUN_DLL) + +WINTUN_DLL = .libs/wintun.dll +DISTCLEANFILES += $(WINTUN_DLL) +endif # OPENCONNECT_WINTUN +endif # OPENCONNECT_WIN32 if CHECK_DTLS C_TESTS += bad_dtls_test @@ -183,6 +199,9 @@ OPENSSL = openssl OSSLARGS = -in $(firstword $|) -out $@ -passout pass:password OSSLARGSP12 = -inkey $(firstword $|) -out $@ -in $${KEYFILE%-key-pkcs8.pem}-cert.pem -passout pass:$${PASSWORD:-password} +.libs/wintun.dll: ../.libs/wintun.dll + cp -f ../$@ $@ + # Strictly speaking this is only PKCS#1 for RSA. For EC it's probably # best described as RFC5915§4, and no idea what defines it for DSA. $(certsdir)/user-key-pkcs1.pem: diff --git a/tests/list-taps.c b/tests/list-taps.c index e01e8313..0ea5e1de 100644 --- a/tests/list-taps.c +++ b/tests/list-taps.c @@ -29,6 +29,10 @@ struct openconnect_info { char *ifname; }; +/* don't link linkopenconnect in this test, just for this function + * it won't get loaded under wine when cross compiling anyway */ +#define openconnect__win32_strerror(err) "(Actual error text not present in tests)" + #define OPEN_TUN_SOFTFAIL 0 #define OPEN_TUN_HARDFAIL -1 diff --git a/tests/wintun-names.c b/tests/wintun-names.c new file mode 100644 index 00000000..496b258f --- /dev/null +++ b/tests/wintun-names.c @@ -0,0 +1,251 @@ +/* + * OpenConnect (SSL + DTLS) VPN client + * + * Copyright © 2019 David Woodhouse + * Copyright © 2024 Marios Paouris + * + * Authors: David Woodhouse + * Marios Paouris + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include + +#include +#include + +#define WIN32_LEAN_AND_MEAN +#include + +#define __OPENCONNECT_INTERNAL_H__ + +#define PRG_ERR 0 +#define PRG_INFO 1 +#define PRG_DEBUG 2 +#define PRG_TRACE 3 + +#define vpn_progress(v, d, ...) do { \ + if ( d < PRG_TRACE) { \ + printf(__VA_ARGS__); \ + } \ +} while (0); + +#define _(x) x + +struct openconnect_info { + char *ifname; +}; + +/* don't link linkopenconnect in this test, just for this function + * it won't get loaded under wine when cross compiling anyway */ +#define openconnect__win32_strerror(err) "(Actual error text not present in tests)" + +#define OPEN_TUN_SOFTFAIL 0 +#define OPEN_TUN_HARDFAIL -1 + +#define WINTUN_TUNNEL_TYPE L"OpenConnect" + +#define VALID_WINTUN_HANDLE 7782 +#define __LIST_TAPS__ + +#include "../tun-win32.c" +#include "../wintun.h" + +static WINTUN_CREATE_ADAPTER_FUNC *WintunCreateAdapter; +static WINTUN_CLOSE_ADAPTER_FUNC *WintunCloseAdapter; +static WINTUN_OPEN_ADAPTER_FUNC *WintunOpenAdapter; +static WINTUN_GET_ADAPTER_LUID_FUNC *WintunGetAdapterLUID; +static WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC *WintunGetRunningDriverVersion; +static WINTUN_DELETE_DRIVER_FUNC *WintunDeleteDriver; +static WINTUN_SET_LOGGER_FUNC *WintunSetLogger; +static WINTUN_START_SESSION_FUNC *WintunStartSession; +static WINTUN_END_SESSION_FUNC *WintunEndSession; +static WINTUN_GET_READ_WAIT_EVENT_FUNC *WintunGetReadWaitEvent; +static WINTUN_RECEIVE_PACKET_FUNC *WintunReceivePacket; +static WINTUN_RELEASE_RECEIVE_PACKET_FUNC *WintunReleaseReceivePacket; +static WINTUN_ALLOCATE_SEND_PACKET_FUNC *WintunAllocateSendPacket; +static WINTUN_SEND_PACKET_FUNC *WintunSendPacket; + +static HMODULE +InitializeWintun(void) +{ + HMODULE Wintun = + LoadLibraryExW(L"wintun.dll", NULL, LOAD_LIBRARY_SEARCH_APPLICATION_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32); + if (!Wintun) + return NULL; +#define X(Name) ((*(FARPROC *)&Name = GetProcAddress(Wintun, #Name)) == NULL) + if (X(WintunCreateAdapter) || X(WintunCloseAdapter) || X(WintunOpenAdapter) || X(WintunGetAdapterLUID) || + X(WintunGetRunningDriverVersion) || X(WintunDeleteDriver) || X(WintunSetLogger) || X(WintunStartSession) || + X(WintunEndSession) || X(WintunGetReadWaitEvent) || X(WintunReceivePacket) || X(WintunReleaseReceivePacket) || + X(WintunAllocateSendPacket) || X(WintunSendPacket)) +#undef X + { + DWORD LastError = GetLastError(); + FreeLibrary(Wintun); + SetLastError(LastError); + return NULL; + } + return Wintun; +} + +static void CALLBACK +ConsoleLogger(_In_ WINTUN_LOGGER_LEVEL Level, _In_ DWORD64 Timestamp, _In_z_ const WCHAR *LogLine) +{ + WCHAR LevelMarker; + switch (Level) + { + case WINTUN_LOG_INFO: + LevelMarker = L'+'; + break; + case WINTUN_LOG_WARN: + LevelMarker = L'-'; + break; + case WINTUN_LOG_ERR: + LevelMarker = L'!'; + break; + default: + return; + } + fwprintf( + stdout, + L"[%c] %ls\n", + LevelMarker, + LogLine); +} + +static void +Log(_In_ WINTUN_LOGGER_LEVEL Level, _In_z_ const WCHAR *Format, ...) +{ + WCHAR LogLine[0x200]; + va_list args; + va_start(args, Format); + _vsnwprintf_s(LogLine, _countof(LogLine), _TRUNCATE, Format, args); + va_end(args); + ConsoleLogger(Level, (DWORD64) 0, LogLine); +} + +WINTUN_ADAPTER_HANDLE create_wintun_adapter(PWSTR adapterName) +{ + DWORD LastError; + + GUID ExampleGuid = { 0xdeadbeef, 0xbaad, 0xcafe, { 0x77, 0x82, 0x07, 0x07, 0x82, 0xab, 0xcd, 0xef } }; + WINTUN_ADAPTER_HANDLE Adapter = WintunCreateAdapter(adapterName, WINTUN_TUNNEL_TYPE, &ExampleGuid); + if (!Adapter) + { + LastError = GetLastError(); + Log(WINTUN_LOG_ERR, L"Failed to create adapter (%lu)", LastError); + goto cleanupWintun; + } + + DWORD Version = WintunGetRunningDriverVersion(); + + NETIO_STATUS ret; + NET_LUID luid; + MIB_IF_ROW2 row; + memset(&row, 0, sizeof(MIB_IF_ROW2)); + + WintunGetAdapterLUID(Adapter, &luid); + + row.InterfaceLuid = luid; + + ret = GetIfEntry2(&row); + + if (ret == 0) { + Log(WINTUN_LOG_INFO, L"Adapter Alias is: %ls", row.Alias); + } + + WINTUN_SESSION_HANDLE Session = WintunStartSession(Adapter, 0x400000); + + if (!Session) + { + LastError = GetLastError(); + Log(WINTUN_LOG_ERR, L"Failed to create session (%lu)", LastError); + goto cleanupAdapter; + } + else { + ; /*Log(WINTUN_LOG_INFO, L"Wintun session started");*/ + } + + LastError = ERROR_SUCCESS; + + WintunEndSession(Session); + /*Log(WINTUN_LOG_INFO, L"Wintun session ended");*/ + return Adapter; + +cleanupAdapter: + WintunCloseAdapter(Adapter); + /*Log(WINTUN_LOG_INFO, L"Wintun adapter closed");*/ +cleanupWintun: + return NULL; +} + +wchar_t * currentAdapterName; + +static intptr_t check_tun(struct openconnect_info *vpninfo, int type, char *guid, wchar_t *wname) +{ + if (currentAdapterName != 0) { + if ( ! wcscmp(currentAdapterName, wname) ) { + printf("Found %s device '%S' guid %s\n", + type ? "Wintun" : "Tap", wname, guid); + return VALID_WINTUN_HANDLE; + } + } + return 0; +} + +int main(void) +{ + HMODULE Wintun = InitializeWintun(); + if (!Wintun) { + DWORD LastError = GetLastError(); + Log(WINTUN_LOG_ERR, L"Failed to initialize Wintun (%lu)", LastError); + return LastError; + } + + WintunSetLogger(ConsoleLogger); + Log(WINTUN_LOG_INFO, L"Wintun library loaded"); + + Log(WINTUN_LOG_INFO, L"MAX_ADAPTER_NAME is: %d", MAX_ADAPTER_NAME); + + DWORD ret = 0; + WINTUN_ADAPTER_HANDLE adapter; + WCHAR adapterName[MAX_ADAPTER_NAME]; + + memset(adapterName, 0, sizeof(adapterName)); + + wsprintfW(adapterName, L"testAdapterNameForRunningLoops_"); + int add = 0; + int len = 0; + + do { + wsprintfW(adapterName, L"%s%d", adapterName, add); + add = (add+1)%10; + len = wcslen(adapterName); + Log(WINTUN_LOG_INFO, L"len=%3d name=%ls", len, adapterName); + adapter = create_wintun_adapter(adapterName); + + if (adapter == NULL) + ret = OPEN_TUN_HARDFAIL; + else { + currentAdapterName = adapterName; + ret = search_taps(NULL, check_tun); + WintunCloseAdapter(adapter); + Log(WINTUN_LOG_INFO, L"Wintun adapter closed"); + } + } while ( (len < (MAX_ADAPTER_NAME - 1)) && (ret == VALID_WINTUN_HANDLE) ); + + FreeLibrary(Wintun); + + if (ret == VALID_WINTUN_HANDLE) /* last test was succesful*/ + return 0; + + return 1; +} diff --git a/tun-win32.c b/tun-win32.c index 749072c2..f69aa112 100644 --- a/tun-win32.c +++ b/tun-win32.c @@ -111,9 +111,21 @@ static intptr_t search_taps(struct openconnect_info *vpninfo, tap_callback *cb) status = RegQueryValueExA(hkey, "ComponentId", NULL, &type, (unsigned char *)buf, &len); if (status || type != REG_SZ) { - vpn_progress(vpninfo, PRG_TRACE, - _("Cannot read %s\\%s or is not string\n"), - keyname, "ComponentId"); + if (status == ERROR_FILE_NOT_FOUND) { + vpn_progress(vpninfo, PRG_TRACE, + _("Cannot read registry key %s\\%s: value not found\n"), + keyname, "ComponentId"); + } + else if (type != REG_SZ) { + vpn_progress(vpninfo, PRG_TRACE, + _("Cannot read registry key %s\\%s: value is not a string (%ld)\n"), + keyname, "ComponentId", type); + } + else { + vpn_progress(vpninfo, PRG_TRACE, + _("Cannot read registry key %s\\%s or is not string: %s (%ld)\n"), + keyname, "ComponentId", openconnect__win32_strerror(status), status); + } RegCloseKey(hkey); continue; } @@ -157,9 +169,26 @@ static intptr_t search_taps(struct openconnect_info *vpninfo, tap_callback *cb) (unsigned char *)name, &len); RegCloseKey(hkey); if (status || type != REG_SZ) { - vpn_progress(vpninfo, PRG_INFO, - _("Cannot read registry key %s\\%s or is not string\n"), - keyname, "Name"); + if (status == ERROR_FILE_NOT_FOUND) { + vpn_progress(vpninfo, PRG_INFO, + _("Cannot read registry key %s\\%s: value not found\n"), + keyname, "Name"); + } + else if (status == ERROR_MORE_DATA) { + vpn_progress(vpninfo, PRG_INFO, + _("Cannot read registry key %s\\%s: character buffer too small: %llu chars required\n"), + keyname, "Name", (long long unsigned int)(len / sizeof(wchar_t))); + } + else if (type != REG_SZ) { + vpn_progress(vpninfo, PRG_INFO, + _("Cannot read registry key %s\\%s: value is not a string (%ld)\n"), + keyname, "Name", type); + } + else { + vpn_progress(vpninfo, PRG_INFO, + _("Cannot read registry key %s\\%s: %s (%ld)\n"), + keyname, "Name", openconnect__win32_strerror(status), status); + } continue; } -- 2.50.1