From ddce34054e5145c88367da42ce1e0e73fa00a10b Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 11 May 2023 12:34:43 +0100 Subject: [PATCH] libnmc-base: fix port extraction for openconnect auth With old versions of openconnect we need to extract the port# from the initial URL and then append it to the hostname we eventually get back. Using strrchr(gw, ':') isn't going to work right with IPv6 literals, ad we should also be dropping any path element. So switch to using an int for the port instead of a string, and import a cut-down variant of openconnect's internal_parse_url() which does *largely* the same thing with strrchr() but is saved by using the 'end' value returned from strtol() and insisting that the port is the very end of the host part of the URL. --- src/libnmc-base/nm-vpn-helpers.c | 47 ++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/src/libnmc-base/nm-vpn-helpers.c b/src/libnmc-base/nm-vpn-helpers.c index 1a43fbc1a0..10e2e0e696 100644 --- a/src/libnmc-base/nm-vpn-helpers.c +++ b/src/libnmc-base/nm-vpn-helpers.c @@ -233,6 +233,46 @@ struct { #define NR_OC_STRING_PROPS (sizeof(oc_property_args) / sizeof(oc_property_args[0])) #define OC_ARGS_MAX (12 + 2 * NR_OC_STRING_PROPS) +/* + * For old versions of openconnect we need to extract the port# and + * append it to the hostname that is returned to us. Use a cut-down + * version of openconnect's own internal_parse_url() function. + */ +static int +extract_url_port(const char *url) +{ + const char *host, *port_str, *path; + char *end; + int port_nr; + + /* Skip the scheme, if present */ + host = strstr(url, "://"); + if (host) + host += 3; + else + host = url; + + port_str = strrchr(host, ':'); + if (!port_str) + return 0; + + /* + * If the host is an IPv6 literal, port_str may point somewhere + * inside it rather than to an actual port#. But IPv6 literals + * are always enclosed in [], e.g. '[fec0::1]:443'. So we check + * that the end pointer returned by strtol points exactly to the + * end of the hostname (either the end of the string, or to the + * first '/' of the path element if there is one). + */ + path = strchr(host, '/'); + port_nr = strtol(port_str + 1, &end, 10); + + if (end == path || (!path && !*end)) + return port_nr; + + return 0; +} + gboolean nm_vpn_openconnect_authenticate_helper(NMSettingVpn *s_vpn, GPtrArray *secrets, GError **error) { @@ -256,7 +296,8 @@ nm_vpn_openconnect_authenticate_helper(NMSettingVpn *s_vpn, GPtrArray *secrets, "/usr/local/bin/", NULL, }; - const char *gw, *port; + int port = 0; + const char *gw; const char *oc_argv[OC_ARGS_MAX]; int i, oc_argc = 0; @@ -270,7 +311,7 @@ nm_vpn_openconnect_authenticate_helper(NMSettingVpn *s_vpn, GPtrArray *secrets, return FALSE; } - port = strrchr(gw, ':'); + port = extract_url_port(gw); path = nm_utils_file_search_in_paths("openconnect", "/usr/sbin/openconnect", @@ -407,7 +448,7 @@ nm_vpn_openconnect_authenticate_helper(NMSettingVpn *s_vpn, GPtrArray *secrets, if (connect_url) secret->value = g_steal_pointer(&connect_url); else if (port) - secret->value = g_strdup_printf("%s%s", legacy_host, port); + secret->value = g_strdup_printf("%s:%d", legacy_host, port); else secret->value = g_steal_pointer(&legacy_host); } else if (nm_streq0(secret->entry_id,