mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-01-07 02:00:15 +01:00
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.
This commit is contained in:
parent
fa715e2387
commit
f8d82c7f10
4 changed files with 99 additions and 43 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue