From f359a42a91fb925f37b2ca4e7157acda9a4794ee Mon Sep 17 00:00:00 2001 From: Marios Paouris Date: Mon, 7 Oct 2024 08:53:35 +0300 Subject: [PATCH] Use hostname as Wintun ifname (if ifname not specified), v2 The intention for the commit 48bd28aa was a bit different from what was actually implemented. Although it states that "Instead, we should use the VPN server's hostname as a sane default interface name with Wintun, and only attempt to use TAP-Windows as a fallback in the case where Wintun can't be initialized.", it first tries with an empty interface name, which uses the first available interface found, whether it is tap or tun, and if that fails then creates the same default with the server name, which will prioritize wintun over tap. Instead, implement the following flow: If the user did specify an interface name: - Try to find an adapter with the specified name (whether it's tun or tap) and use it. - If no adapter found, try to create a wintun adapter. If wintun is not available then bail out. If the user did not specify an interface name: - Generate a default interface name based on the server URL. - If the generated interface already exists don't try to use it and fallback to using the first available adapter. - If the generated interface doesn't exist, try to create a wintun adapter. If wintun in not available then fallback to using the first available adapter. See https://gitlab.com/openconnect/openconnect-gui/-/issues/357#note_1758999655 and https://gitlab.com/openconnect/openconnect/-/issues/699#note_1762029017 Signed-off-by: Marios Paouris --- tests/list-taps.c | 4 +- tests/wintun-names.c | 7 +- tun-win32.c | 192 +++++++++++++++++++++++++++++++++---------- wintun.c | 4 +- 4 files changed, 157 insertions(+), 50 deletions(-) diff --git a/tests/list-taps.c b/tests/list-taps.c index c63ab07d..0d1cd051 100644 --- a/tests/list-taps.c +++ b/tests/list-taps.c @@ -27,6 +27,7 @@ struct openconnect_info { char *ifname; + wchar_t *ifname_w; }; /* don't link linkopenconnect in this test, just for this function @@ -51,13 +52,14 @@ int main(void) { intptr_t ret; struct oc_adapter_info *list = NULL; + struct openconnect_info empty_info = { NULL, NULL}; list = get_adapter_list(NULL); if (!list) return 1; - search_taps(NULL, list, print_tun); + search_taps(&empty_info, list, print_tun); free_adapter_list(list); return 0; diff --git a/tests/wintun-names.c b/tests/wintun-names.c index ab9a073b..8ac9e4b7 100644 --- a/tests/wintun-names.c +++ b/tests/wintun-names.c @@ -43,6 +43,7 @@ struct openconnect_info { char *ifname; + wchar_t *ifname_w; }; /* don't link linkopenconnect in this test, just for this function @@ -210,6 +211,8 @@ int main(void) { _setmode(_fileno(stdout), _O_U16TEXT); + struct openconnect_info empty_info = { NULL, NULL}; + HMODULE Wintun = InitializeWintun(); if (!Wintun) { DWORD LastError = GetLastError(); @@ -244,7 +247,7 @@ int main(void) if (adapter) { list = get_adapter_list(NULL); if (list) { - ret = check_tun(NULL, list, adapterName); + ret = check_tun(&empty_info, list, adapterName); free_adapter_list(list); } @@ -274,7 +277,7 @@ int main(void) if (adapter) { list = get_adapter_list(NULL); if (list) { - ret = check_tun(NULL, list, adapterName); + ret = check_tun(&empty_info, list, adapterName); free_adapter_list(list); } diff --git a/tun-win32.c b/tun-win32.c index c09756b2..39ce0da7 100644 --- a/tun-win32.c +++ b/tun-win32.c @@ -93,6 +93,24 @@ static void free_adapter_list(struct oc_adapter_info *first) } } +static struct oc_adapter_info * adapter_alloc(struct openconnect_info *vpninfo, int type, const wchar_t* name, const char *guid) +{ + struct oc_adapter_info *new = malloc(sizeof(struct oc_adapter_info)); + + if (!new) { + vpn_progress(vpninfo, PRG_ERR, + _("Cannot allocate memory for adapter info\n")); + return NULL; + } + + new->type = type; + new->ifname = wcsdup(name); + new->guid = strdup(guid); + new->next = NULL; + + return new; +} + static struct oc_adapter_info * find_adapter_by_name(struct openconnect_info *vpninfo, struct oc_adapter_info *adapter_list, wchar_t *wname) { struct oc_adapter_info *this = adapter_list; @@ -112,14 +130,44 @@ static intptr_t search_taps(struct openconnect_info *vpninfo, struct oc_adapter_ { struct oc_adapter_info *this = adapter_list; intptr_t ret = OPEN_TUN_SOFTFAIL; + int wintun_failed_loading = 0; while (this && ret == OPEN_TUN_SOFTFAIL) { if (this->type != ADAPTER_NONE) { - ret = cb(vpninfo, this->type, this->guid, this->ifname); + if (vpninfo->ifname_w && wcscmp(this->ifname, vpninfo->ifname_w)) { + vpn_progress(vpninfo, PRG_DEBUG, + _("Ignoring non-matching interface \"%S\"\n"), + this->ifname); + ret = OPEN_TUN_SOFTFAIL; /* keep searching */ + } + else { + ret = cb(vpninfo, this->type, this->guid, this->ifname); + + if (!vpninfo->ifname_w ) { + /* we are not searching for a specific adapter */ + + if (this->type == ADAPTER_WINTUN && ret == OPEN_TUN_SOFTFAIL) { + /* keep track of wintun failing to load */ + wintun_failed_loading = 1; + } + + if (ret == OPEN_TUN_HARDFAIL) { + /* the adapter failed to open */ + ret = OPEN_TUN_SOFTFAIL; /* keep searching; we might be able to open a TAP */ + } + } + } } this = this->next; } + if (ret == OPEN_TUN_SOFTFAIL && (!vpninfo->ifname_w) && wintun_failed_loading) { + /* wintun failed to load at least once when searching for any usable adapter + notify the caller of a hard failure to force them not to try a wintun fallback + */ + ret = OPEN_TUN_HARDFAIL; + } + return ret; } @@ -261,21 +309,14 @@ static struct oc_adapter_info * get_adapter_list(struct openconnect_info *vpninf continue; } - struct oc_adapter_info *new = malloc(sizeof(struct oc_adapter_info)); + struct oc_adapter_info *new = adapter_alloc(vpninfo, adapter_type, name, buf); if (!new) { - vpn_progress(vpninfo, PRG_ERR, - _("Cannot allocate memory for adapter info\n")); free_adapter_list(first); first = NULL; break; } - new->type = adapter_type; - new->ifname = wcsdup(name); - new->guid = strdup(buf); - new->next = NULL; - if (last) { last->next = new; } @@ -292,6 +333,42 @@ static struct oc_adapter_info * get_adapter_list(struct openconnect_info *vpninf #ifndef __LIST_TAPS__ +static struct oc_adapter_info * create_wintun_adapter(struct openconnect_info *vpninfo, tap_callback *cb, intptr_t *fh) +{ + struct oc_adapter_info *ret = NULL; + + int retw = create_wintun(vpninfo); + if (!retw) { + char wintun_adapter_guid[40]; + /* we have a wintun adapter. Get its guid and return adapter info */ + if (! get_wintun_adapter_guid(vpninfo, wintun_adapter_guid, sizeof(wintun_adapter_guid))) { + ret = adapter_alloc(vpninfo, ADAPTER_WINTUN, vpninfo->ifname_w, wintun_adapter_guid); + } + } else if (retw == -EPERM) { + vpn_progress(vpninfo, PRG_ERR, + _("Access denied creating Wintun adapter. Are you running with Administrator privileges?\n")); + } + /* no need to worry about other return codes for retw (-ENOENT or -EIO) - we will return NULL */ + + if ( ret ) { + /* we have a wintun adapter. Open it via the callback */ + intptr_t retcb = cb(vpninfo, ret->type, ret->guid, ret->ifname); + + if (retcb == OPEN_TUN_SOFTFAIL || retcb == OPEN_TUN_HARDFAIL) { + os_shutdown_wintun(vpninfo); + free_adapter_list(ret); + ret = NULL; + } + *fh = retcb; + } + else { + os_shutdown_wintun(vpninfo); + *fh = OPEN_TUN_SOFTFAIL; + } + + return ret; +} + static int get_adapter_index(struct openconnect_info *vpninfo, char *guid) { struct oc_text_buf *buf = buf_alloc(); @@ -489,9 +566,11 @@ static intptr_t open_tuntap(struct openconnect_info *vpninfo, char *guid, wchar_ FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0); if (tun_fh == INVALID_HANDLE_VALUE) { - vpn_progress(vpninfo, PRG_ERR, _("Failed to open %s\n"), - devname); - return OPEN_TUN_SOFTFAIL; + char *errstr = openconnect__win32_strerror(GetLastError()); + vpn_progress(vpninfo, PRG_ERR, _("Failed to open %s: %s\n"), + devname, errstr); + free(errstr); + return OPEN_TUN_HARDFAIL; } vpn_progress(vpninfo, PRG_DEBUG, _("Opened tun device %S\n"), wname); @@ -553,13 +632,6 @@ static intptr_t open_tun(struct openconnect_info *vpninfo, int adapter_type, cha { intptr_t ret = -1; - if (vpninfo->ifname_w && wcscmp(wname, vpninfo->ifname_w)) { - vpn_progress(vpninfo, PRG_DEBUG, - _("Ignoring non-matching interface \"%S\"\n"), - wname); - return OPEN_TUN_SOFTFAIL; - } - if (adapter_type == ADAPTER_TUNTAP) ret = open_tuntap(vpninfo, guid, wname); else @@ -624,6 +696,13 @@ static intptr_t create_ifname_w(struct openconnect_info *vpninfo, return 0; } +static void clear_ifname_w(struct openconnect_info *vpninfo) +{ + free(vpninfo->ifname_w); + vpninfo->ifname_w = NULL; +} + + intptr_t os_setup_tun(struct openconnect_info *vpninfo) { intptr_t ret; @@ -635,42 +714,65 @@ intptr_t os_setup_tun(struct openconnect_info *vpninfo) goto safe_return; if (vpninfo->ifname) { + /* the user specified an interface name; try to find an existing adapter */ ret = create_ifname_w(vpninfo, vpninfo->ifname); if (ret) goto safe_return; - } - ret = search_taps(vpninfo, list, open_tun); + ret = search_taps(vpninfo, list, open_tun); - if (ret == OPEN_TUN_SOFTFAIL) { - if (!vpninfo->ifname_w) { - ret = create_ifname_w(vpninfo, vpninfo->hostname); - if (ret) - goto safe_return; - } - - /* Try creating a Wintun instead of TAP */ - int retw = create_wintun(vpninfo); - if (!retw) { - char wintun_adapter_guid[40]; - /* we have a wintun adapter. Get its guid and open it via the callback */ - if (get_wintun_adapter_guid(vpninfo, wintun_adapter_guid, sizeof(wintun_adapter_guid))) { - ret = OPEN_TUN_HARDFAIL; + if (ret == OPEN_TUN_SOFTFAIL) { + /* no adapter was found; Try creating a Wintun adapter */ + struct oc_adapter_info * new = create_wintun_adapter(vpninfo, open_tun, &ret); + if ( new ) { + /* add the new adapter to the beginning of the list so it can be freed */ + new->next = list; + list = new; } else { - ret = open_tun(vpninfo, ADAPTER_WINTUN, wintun_adapter_guid, vpninfo->ifname_w); - - if (ret == OPEN_TUN_SOFTFAIL) - ret = OPEN_TUN_HARDFAIL; + clear_ifname_w(vpninfo); + ret = OPEN_TUN_HARDFAIL; } + } + else if (ret == OPEN_TUN_HARDFAIL) { + clear_ifname_w(vpninfo); + } + } + else { + /* the user did not specify an interface name; try create a wintun default based on hostname */ + ret = create_ifname_w(vpninfo, vpninfo->hostname); + if (ret) + goto safe_return; + + /* check if the adapter already exists */ + struct oc_adapter_info * new = find_adapter_by_name(vpninfo, list, vpninfo->ifname_w); + + if (new) { + /* don't create a wintun adapter with a default name; + * unfortunately, wintun will rename an existing adapter when creating an adapter with the same name + * see https://git.zx2c4.com/wintun/tree/api/adapter.c?id=41624504341307f7f55afe72e86d5d8c76f81c0e#n292 + */ + vpn_progress(vpninfo, PRG_INFO, + _("Adapter %S already exists. Cannot use it as a default wintun adapter.\n"), + vpninfo->ifname_w); + } - if (ret == OPEN_TUN_HARDFAIL) { - os_shutdown_wintun(vpninfo); + if ( !new ) { + new = create_wintun_adapter(vpninfo, open_tun, &ret); + if ( new ) { + /* add the new adapter to the beginning of the list so it can be freed */ + new->next = list; + list = new; } - } else if (retw == -EPERM) { - ret = OPEN_TUN_HARDFAIL; - vpn_progress(vpninfo, PRG_ERR, - _("Access denied creating Wintun adapter. Are you running with Administrator privileges?\n")); + } + else { + new = NULL; + } + + if ( !new ) { + /* could not create wintun adapter; cleanup ifname_w and fallback to the first available tap */ + clear_ifname_w(vpninfo); + ret = search_taps(vpninfo, list, open_tun); } } diff --git a/wintun.c b/wintun.c index 9b13062f..8900d73f 100644 --- a/wintun.c +++ b/wintun.c @@ -143,7 +143,7 @@ intptr_t open_wintun(struct openconnect_info *vpninfo, char *guid, wchar_t *wnam intptr_t ret; if (init_wintun(vpninfo)) - return 0; + return OPEN_TUN_SOFTFAIL; if (!vpninfo->wintun_adapter) { vpninfo->wintun_adapter = WintunOpenAdapter(wname); @@ -153,7 +153,7 @@ intptr_t open_wintun(struct openconnect_info *vpninfo, char *guid, wchar_t *wnam wname, errstr); free(errstr); - ret = OPEN_TUN_SOFTFAIL; + ret = OPEN_TUN_HARDFAIL; goto out; } } -- 2.49.0