diff options
author | David Woodhouse <dwmw2@infradead.org> | 2023-05-10 16:31:44 +0100 |
---|---|---|
committer | David Woodhouse <dwmw2@infradead.org> | 2023-05-11 13:15:53 +0100 |
commit | f8d82c7f104d86611eeb41c8ba7a1db28ccaf815 (patch) | |
tree | 12af872f71f50cb186c9bcbaf3dd5b8859f66aa8 | |
parent | fa715e2387165da5032b0500eac13f42430fe7fe (diff) | |
download | NetworkManager-f8d82c7f104d86611eeb41c8ba7a1db28ccaf815.tar.gz |
nmcli, nmtui: update authentication for OpenConnect
Since OpenConnect 8.20, 'openconnect --authenticate' will return the
full gateway URL, including the hostname and the path. This allows
servers behind SNI-based proxies to work. To ensure we end up at the
same IP address even behind round-robin DNS, there is a separate
--resolve argument.
Update nmcli/nmtui to use this, as NetworkManager-openconnect does.
Shift some of the logic into the nm_vpn_openconnect_authenticate_helper()
function instead of duplicating it in the callers.
Also, pass the correct protocol in rather than only supporting Cisco
AnyConnect.
-rw-r--r-- | src/libnmc-base/nm-vpn-helpers.c | 74 | ||||
-rw-r--r-- | src/libnmc-base/nm-vpn-helpers.h | 13 | ||||
-rw-r--r-- | src/nmcli/common.c | 25 | ||||
-rw-r--r-- | src/nmtui/nmtui-connect.c | 30 |
4 files changed, 99 insertions, 43 deletions
diff --git a/src/libnmc-base/nm-vpn-helpers.c b/src/libnmc-base/nm-vpn-helpers.c index 476fbe518e..e447ef047a 100644 --- a/src/libnmc-base/nm-vpn-helpers.c +++ b/src/libnmc-base/nm-vpn-helpers.c @@ -160,9 +160,10 @@ nm_vpn_get_secret_names(const char *service_type) }; if (NM_IN_STRSET(type, "openconnect")) { - return _VPN_PASSWORD_LIST({"gateway", N_("Gateway")}, + return _VPN_PASSWORD_LIST({"gateway", N_("Gateway URL")}, {"cookie", N_("Cookie")}, - {"gwcert", N_("Gateway certificate hash")}, ); + {"gwcert", N_("Gateway certificate hash")}, + {"resolve", N_("Gateway DNS resolution ('host:IP')")}, ); }; return NULL; @@ -186,18 +187,24 @@ _extract_variable_value(char *line, const char *tag, char **value) return TRUE; } +#define OC_ARGS_MAX 10 + gboolean -nm_vpn_openconnect_authenticate_helper(const char *host, - char **cookie, - char **gateway, - char **gwcert, - int *status, - GError **error) +nm_vpn_openconnect_authenticate_helper(NMSettingVpn *s_vpn, + char **cookie, + char **gateway, + char **gwcert, + char **resolve, + int *status, + GError **error) { - gs_free char *output = NULL; - gs_free const char **output_v = NULL; + gs_free char *output = NULL; + gs_free char *legacy_host = NULL; + gs_free char *connect_url = NULL; + gs_free const char **output_v = NULL; const char *const *iter; const char *path; + const char *opt; const char *const DEFAULT_PATHS[] = { "/sbin/", "/usr/sbin/", @@ -207,6 +214,13 @@ nm_vpn_openconnect_authenticate_helper(const char *host, "/usr/local/bin/", NULL, }; + const char *gw, *port; + const char *oc_argv[OC_ARGS_MAX]; + int oc_argc = 0; + + /* Get gateway and port */ + gw = nm_setting_vpn_get_data_item(s_vpn, "gateway"); + port = gw ? strrchr(gw, ':') : NULL; path = nm_utils_file_search_in_paths("openconnect", "/usr/sbin/openconnect", @@ -218,8 +232,21 @@ nm_vpn_openconnect_authenticate_helper(const char *host, if (!path) return FALSE; + oc_argv[oc_argc++] = path; + oc_argv[oc_argc++] = "--authenticate"; + oc_argv[oc_argc++] = gw; + + opt = nm_setting_vpn_get_data_item(s_vpn, "protocol"); + if (opt) { + oc_argv[oc_argc++] = "--protocol"; + oc_argv[oc_argc++] = opt; + } + + oc_argv[oc_argc++] = NULL; + g_return_val_if_fail(oc_argc <= OC_ARGS_MAX, FALSE); + if (!g_spawn_sync(NULL, - (char **) NM_MAKE_STRV(path, "--authenticate", host), + (char **) oc_argv, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_CHILD_INHERITS_STDIN, NULL, @@ -235,14 +262,37 @@ nm_vpn_openconnect_authenticate_helper(const char *host, * COOKIE='loremipsum' * HOST='1.2.3.4' * FINGERPRINT='sha1:32bac90cf09a722e10ecc1942c67fe2ac8c21e2e' + * + * Since OpenConnect v8.20 (2022-02-20) OpenConnect has also passed e.g.: + * + * CONNECT_URL='https://vpn.example.com:8443/ConnectPath' + * RESOLVE=vpn.example.com:1.2.3.4 */ output_v = nm_strsplit_set_with_empty(output, "\r\n"); for (iter = output_v; iter && *iter; iter++) { char *s_mutable = (char *) *iter; _extract_variable_value(s_mutable, "COOKIE=", cookie); - _extract_variable_value(s_mutable, "HOST=", gateway); + _extract_variable_value(s_mutable, "CONNECT_URL=", &connect_url); + _extract_variable_value(s_mutable, "HOST=", &legacy_host); _extract_variable_value(s_mutable, "FINGERPRINT=", gwcert); + _extract_variable_value(s_mutable, "RESOLVE=", resolve); + } + + if (connect_url) { + *gateway = g_steal_pointer(&connect_url); + } else { + if (!legacy_host) { + g_set_error(error, + NM_VPN_PLUGIN_ERROR, + NM_VPN_PLUGIN_ERROR_FAILED, + _("OpenConnect failed to return gateway URL")); + return FALSE; + } + if (port) + *gateway = g_strdup_printf("%s%s", legacy_host, port); + else + *gateway = g_steal_pointer(&legacy_host); } return TRUE; diff --git a/src/libnmc-base/nm-vpn-helpers.h b/src/libnmc-base/nm-vpn-helpers.h index 8bea846025..f2bdace57d 100644 --- a/src/libnmc-base/nm-vpn-helpers.h +++ b/src/libnmc-base/nm-vpn-helpers.h @@ -19,11 +19,12 @@ gboolean nm_vpn_supports_ipv6(NMConnection *connection); const NmcVpnPasswordName *nm_vpn_get_secret_names(const char *service_type); -gboolean nm_vpn_openconnect_authenticate_helper(const char *host, - char **cookie, - char **gateway, - char **gwcert, - int *status, - GError **error); +gboolean nm_vpn_openconnect_authenticate_helper(NMSettingVpn *s_vpn, + char **cookie, + char **gateway, + char **gwcert, + char **resolve, + int *status, + GError **error); #endif /* __NM_VPN_HELPERS_H__ */ diff --git a/src/nmcli/common.c b/src/nmcli/common.c index f31d09872d..24ea1b1448 100644 --- a/src/nmcli/common.c +++ b/src/nmcli/common.c @@ -635,10 +635,10 @@ vpn_openconnect_get_secrets(NMConnection *connection, GPtrArray *secrets) { GError *error = NULL; NMSettingVpn *s_vpn; - const char *gw, *port; gs_free char *cookie = NULL; gs_free char *gateway = NULL; gs_free char *gwcert = NULL; + gs_free char *resolve = NULL; int status = 0; int i; gboolean ret; @@ -653,12 +653,14 @@ vpn_openconnect_get_secrets(NMConnection *connection, GPtrArray *secrets) if (!nm_streq0(nm_setting_vpn_get_service_type(s_vpn), NM_SECRET_AGENT_VPN_TYPE_OPENCONNECT)) return FALSE; - /* Get gateway and port */ - gw = nm_setting_vpn_get_data_item(s_vpn, "gateway"); - port = gw ? strrchr(gw, ':') : NULL; - /* Interactively authenticate to OpenConnect server and get secrets */ - ret = nm_vpn_openconnect_authenticate_helper(gw, &cookie, &gateway, &gwcert, &status, &error); + ret = nm_vpn_openconnect_authenticate_helper(s_vpn, + &cookie, + &gateway, + &gwcert, + &resolve, + &status, + &error); if (!ret) { nmc_printerr(_("Error: openconnect failed: %s\n"), error->message); g_clear_error(&error); @@ -671,13 +673,6 @@ vpn_openconnect_get_secrets(NMConnection *connection, GPtrArray *secrets) } else if (WIFSIGNALED(status)) nmc_printerr(_("Error: openconnect failed with signal %d\n"), WTERMSIG(status)); - /* Append port to the host value */ - if (gateway && port) { - gs_free char *tmp = gateway; - - gateway = g_strdup_printf("%s%s", tmp, port); - } - /* Fill secrets to the array */ for (i = 0; i < secrets->len; i++) { NMSecretAgentSimpleSecret *secret = secrets->pdata[i]; @@ -698,6 +693,10 @@ vpn_openconnect_get_secrets(NMConnection *connection, GPtrArray *secrets) NM_SECRET_AGENT_ENTRY_ID_PREFX_VPN_SECRETS "gwcert")) { g_free(secret->value); secret->value = g_steal_pointer(&gwcert); + } else if (nm_streq0(secret->entry_id, + NM_SECRET_AGENT_ENTRY_ID_PREFX_VPN_SECRETS "resolve")) { + g_free(secret->value); + secret->value = g_steal_pointer(&resolve); } } diff --git a/src/nmtui/nmtui-connect.c b/src/nmtui/nmtui-connect.c index 8c4625ec6a..ba9fffcaec 100644 --- a/src/nmtui/nmtui-connect.c +++ b/src/nmtui/nmtui-connect.c @@ -31,25 +31,32 @@ * before starting the command and restored after it returns. */ static gboolean -openconnect_authenticate(NMConnection *connection, char **cookie, char **gateway, char **gwcert) +openconnect_authenticate(NMConnection *connection, + char **cookie, + char **gateway, + char **gwcert, + char **resolve) { GError *error = NULL; NMSettingVpn *s_vpn; gboolean ret; int status = 0; - const char *gw, *port; nmt_newt_message_dialog( _("openconnect will be run to authenticate.\nIt will return to nmtui when completed.")); /* Get port */ s_vpn = nm_connection_get_setting_vpn(connection); - gw = nm_setting_vpn_get_data_item(s_vpn, "gateway"); - port = gw ? strrchr(gw, ':') : NULL; newtSuspend(); - ret = nm_vpn_openconnect_authenticate_helper(gw, cookie, gateway, gwcert, &status, &error); + ret = nm_vpn_openconnect_authenticate_helper(s_vpn, + cookie, + gateway, + gwcert, + resolve, + &status, + &error); newtResume(); @@ -69,12 +76,6 @@ openconnect_authenticate(NMConnection *connection, char **cookie, char **gateway return FALSE; } - if (gateway && *gateway && port) { - char *tmp = *gateway; - *gateway = g_strdup_printf("%s%s", *gateway, port); - g_free(tmp); - } - return TRUE; } @@ -99,8 +100,9 @@ secrets_requested(NMSecretAgentSimple *agent, gs_free char *cookie = NULL; gs_free char *gateway = NULL; gs_free char *gwcert = NULL; + gs_free char *resolve = NULL; - openconnect_authenticate(connection, &cookie, &gateway, &gwcert); + openconnect_authenticate(connection, &cookie, &gateway, &gwcert, &resolve); for (i = 0; i < secrets->len; i++) { NMSecretAgentSimpleSecret *secret = secrets->pdata[i]; @@ -121,6 +123,10 @@ secrets_requested(NMSecretAgentSimple *agent, NM_SECRET_AGENT_ENTRY_ID_PREFX_VPN_SECRETS "gwcert")) { g_free(secret->value); secret->value = g_steal_pointer(&gwcert); + } else if (nm_streq0(secret->entry_id, + NM_SECRET_AGENT_ENTRY_ID_PREFX_VPN_SECRETS "resolve")) { + g_free(secret->value); + secret->value = g_steal_pointer(&resolve); } } } |