summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Woodhouse <dwmw2@infradead.org>2023-05-10 16:31:44 +0100
committerDavid Woodhouse <dwmw2@infradead.org>2023-05-11 13:15:53 +0100
commitf8d82c7f104d86611eeb41c8ba7a1db28ccaf815 (patch)
tree12af872f71f50cb186c9bcbaf3dd5b8859f66aa8
parentfa715e2387165da5032b0500eac13f42430fe7fe (diff)
downloadNetworkManager-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.c74
-rw-r--r--src/libnmc-base/nm-vpn-helpers.h13
-rw-r--r--src/nmcli/common.c25
-rw-r--r--src/nmtui/nmtui-connect.c30
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);
}
}
}