diff options
author | Jiří Klimeš <jklimes@redhat.com> | 2015-12-12 22:33:51 +0100 |
---|---|---|
committer | Jiří Klimeš <jklimes@redhat.com> | 2015-12-12 22:33:51 +0100 |
commit | 5745e954a11eafc1f0fd5e59bfdea5df9e9ef5ff (patch) | |
tree | be80751c62186642b2d7ccf2cc274c74d2dc6b59 | |
parent | 088604f62ea77fcc492eb6cb4df70dcdbc318751 (diff) | |
parent | 71cfa4fb7948389de61408c53cb13d693d3c41a4 (diff) | |
download | NetworkManager-5745e954a11eafc1f0fd5e59bfdea5df9e9ef5ff.tar.gz |
merge: enable VPN secrets for nmtui/nmcli secret agent (rh #975185)
It allows nmcli and nmtui to ask for VPN passwords and thus successfully
activate VPN connections.
There is a special handling for OpenConnect, because the user does not know
the secrets. They have to be obtained by authenticating to OpenConnect server.
https://bugzilla.redhat.com/show_bug.cgi?id=975185
-rw-r--r-- | clients/cli/Makefile.am | 6 | ||||
-rw-r--r-- | clients/cli/common.c | 110 | ||||
-rw-r--r-- | clients/common/nm-secret-agent-simple.c | 184 | ||||
-rw-r--r-- | clients/common/nm-secret-agent-simple.h | 4 | ||||
-rw-r--r-- | clients/common/nm-vpn-helpers.c | 123 | ||||
-rw-r--r-- | clients/common/nm-vpn-helpers.h | 16 | ||||
-rw-r--r-- | clients/tui/Makefile.am | 4 | ||||
-rw-r--r-- | clients/tui/nmt-password-dialog.c | 2 | ||||
-rw-r--r-- | clients/tui/nmtui-connect.c | 92 | ||||
-rw-r--r-- | po/POTFILES.in | 1 |
10 files changed, 520 insertions, 22 deletions
diff --git a/clients/cli/Makefile.am b/clients/cli/Makefile.am index eef99cba71..2f394098a9 100644 --- a/clients/cli/Makefile.am +++ b/clients/cli/Makefile.am @@ -14,7 +14,9 @@ AM_CPPFLAGS = \ $(GLIB_CFLAGS) \ -DG_LOG_DOMAIN=\""nmcli"\" \ -DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \ - -DNMCLI_LOCALEDIR=\"$(datadir)/locale\" + -DNMCLI_LOCALEDIR=\"$(datadir)/locale\" \ + -DNMCONFDIR=\"$(nmconfdir)\" \ + -DNMLIBDIR=\"$(libdir)\" nmcli_SOURCES = \ agent.c \ @@ -38,6 +40,8 @@ nmcli_SOURCES = \ \ $(srcdir)/../common/nm-secret-agent-simple.c \ $(srcdir)/../common/nm-secret-agent-simple.h \ + $(srcdir)/../common/nm-vpn-helpers.c \ + $(srcdir)/../common/nm-vpn-helpers.h \ $(NULL) nmcli_LDADD = \ diff --git a/clients/cli/common.c b/clients/cli/common.c index 35676d0f2b..86f91d5869 100644 --- a/clients/cli/common.c +++ b/clients/cli/common.c @@ -32,6 +32,7 @@ #include "nm-glib-compat.h" +#include "nm-vpn-helpers.h" #include "common.h" #include "utils.h" @@ -927,15 +928,96 @@ nmc_find_connection (const GPtrArray *connections, } static gboolean +vpn_openconnect_get_secrets (NMConnection *connection, GPtrArray *secrets) +{ + GError *error = NULL; + NMSettingVpn *s_vpn; + const char *vpn_type, *gw, *port; + char *cookie = NULL; + char *gateway = NULL; + char *gwcert = NULL; + int status = 0; + int i; + gboolean ret; + + if (!connection) + return FALSE; + + if (!nm_connection_is_type (connection, NM_SETTING_VPN_SETTING_NAME)) + return FALSE; + + s_vpn = nm_connection_get_setting_vpn (connection); + vpn_type = nm_setting_vpn_get_service_type (s_vpn); + if (g_strcmp0 (vpn_type, NM_DBUS_INTERFACE ".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); + if (!ret) { + g_printerr (_("Error: openconnect failed: %s\n"), error->message); + g_clear_error (&error); + return FALSE; + } + + if (WIFEXITED (status)) { + if (WEXITSTATUS (status) != 0) + g_printerr (_("Error: openconnect failed with status %d\n"), WEXITSTATUS (status)); + } else if (WIFSIGNALED (status)) + g_printerr (_("Error: openconnect failed with signal %d\n"), WTERMSIG (status)); + + /* Append port to the host value */ + if (gateway && port) { + char *tmp = gateway; + gateway = g_strdup_printf ("%s%s", gateway, port); + g_free (tmp); + } + + /* Fill secrets to the array */ + for (i = 0; i < secrets->len; i++) { + NMSecretAgentSimpleSecret *secret = secrets->pdata[i]; + + if (!g_strcmp0 (secret->vpn_type, vpn_type)) { + if (!g_strcmp0 (secret->vpn_property, "cookie")) { + g_free (secret->value); + secret->value = cookie; + cookie = NULL; + } else if (!g_strcmp0 (secret->vpn_property, "gateway")) { + g_free (secret->value); + secret->value = gateway; + gateway = NULL; + } else if (!g_strcmp0 (secret->vpn_property, "gwcert")) { + g_free (secret->value); + secret->value = gwcert; + gwcert = NULL; + } + } + } + g_free (cookie); + g_free (gateway); + g_free (gwcert); + + return TRUE; +} + +static gboolean get_secrets_from_user (const char *request_id, const char *title, const char *msg, + NMConnection *connection, gboolean ask, GHashTable *pwds_hash, GPtrArray *secrets) { int i; + /* Check if there is a VPN OpenConnect secret to ask for */ + if (ask) + vpn_openconnect_get_secrets (connection, secrets); + for (i = 0; i < secrets->len; i++) { NMSecretAgentSimpleSecret *secret = secrets->pdata[i]; char *pwd = NULL; @@ -945,17 +1027,23 @@ get_secrets_from_user (const char *request_id, if (pwds_hash && (pwd = g_hash_table_lookup (pwds_hash, secret->prop_name))) { pwd = g_strdup (pwd); } else { - g_print ("%s\n", msg); if (ask) { if (secret->value) { - /* Prefill the password if we have it. */ - rl_startup_hook = nmc_rl_set_deftext; - nmc_rl_pre_input_deftext = g_strdup (secret->value); + if (!g_strcmp0 (secret->vpn_type, NM_DBUS_INTERFACE ".openconnect")) { + /* Do not present and ask user for openconnect secrets, we already have them */ + continue; + } else { + /* Prefill the password if we have it. */ + rl_startup_hook = nmc_rl_set_deftext; + nmc_rl_pre_input_deftext = g_strdup (secret->value); + } } + g_print ("%s\n", msg); pwd = nmc_readline ("%s (%s): ", secret->name, secret->prop_name); if (!pwd) pwd = g_strdup (""); } else { + g_print ("%s\n", msg); g_printerr (_("Warning: password for '%s' not given in 'passwd-file' " "and nmcli cannot ask without '--ask' option.\n"), secret->prop_name); @@ -993,12 +1081,24 @@ nmc_secrets_requested (NMSecretAgentSimple *agent, gpointer user_data) { NmCli *nmc = (NmCli *) user_data; + NMConnection *connection = NULL; + char *path, *p; gboolean success = FALSE; if (nmc->print_output == NMC_PRINT_PRETTY) nmc_terminal_erase_line (); - success = get_secrets_from_user (request_id, title, msg, nmc->in_editor || nmc->ask, + /* Find the connection for the request */ + path = g_strdup (request_id); + if (path) { + p = strrchr (path, '/'); + if (p) + *p = '\0'; + connection = nmc_find_connection (nmc->connections, "path", path, NULL); + g_free (path); + } + + success = get_secrets_from_user (request_id, title, msg, connection, nmc->in_editor || nmc->ask, nmc->pwds_hash, secrets); if (success) nm_secret_agent_simple_response (agent, request_id, secrets); diff --git a/clients/common/nm-secret-agent-simple.c b/clients/common/nm-secret-agent-simple.c index 8eddb4400e..6cb6c58db7 100644 --- a/clients/common/nm-secret-agent-simple.c +++ b/clients/common/nm-secret-agent-simple.c @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * - * Copyright 2011-2013 Red Hat, Inc. + * Copyright 2011-2015 Red Hat, Inc. * Copyright 2011 Giovanni Campagna <scampa.giovanni@gmail.com> */ @@ -32,8 +32,14 @@ #include "config.h" #include <string.h> +#include <stdlib.h> +#include <errno.h> #include <glib/gi18n-lib.h> +#include <NetworkManager.h> +#include <nm-core-internal.h> + +#include "nm-vpn-helpers.h" #include "nm-secret-agent-simple.h" G_DEFINE_TYPE (NMSecretAgentSimple, nm_secret_agent_simple, NM_TYPE_SECRET_AGENT_OLD) @@ -157,6 +163,8 @@ nm_secret_agent_simple_secret_free (NMSecretAgentSimpleSecret *secret) g_free (secret->name); g_free (secret->prop_name); g_free (secret->value); + g_free (secret->vpn_property); + g_free (secret->vpn_type); g_free (real->property); g_clear_object (&real->setting); @@ -167,20 +175,29 @@ static NMSecretAgentSimpleSecret * nm_secret_agent_simple_secret_new (const char *name, NMSetting *setting, const char *property, + const char *vpn_property, + const char *vpn_type, gboolean password) { NMSecretAgentSimpleSecretReal *real; real = g_slice_new0 (NMSecretAgentSimpleSecretReal); real->base.name = g_strdup (name); - real->base.prop_name = g_strdup_printf ("%s.%s", nm_setting_get_name (setting), property); + real->base.prop_name = vpn_property ? + g_strdup_printf ("%s.%s.%s", nm_setting_get_name (setting), property, vpn_property) : + g_strdup_printf ("%s.%s", nm_setting_get_name (setting), property); + real->base.vpn_property = g_strdup (vpn_property); + real->base.vpn_type = g_strdup (vpn_type); real->base.password = password; if (setting) { real->setting = g_object_ref (setting); real->property = g_strdup (property); - g_object_get (setting, property, &real->base.value, NULL); + if (vpn_property) + real->base.value = g_strdup (nm_setting_vpn_get_secret (NM_SETTING_VPN (setting), vpn_property)); + else + g_object_get (setting, property, &real->base.value, NULL); } return &real->base; @@ -209,11 +226,15 @@ add_8021x_secrets (NMSecretAgentSimpleRequest *request, secret = nm_secret_agent_simple_secret_new (_("Username"), NM_SETTING (s_8021x), NM_SETTING_802_1X_IDENTITY, + NULL, + NULL, FALSE); g_ptr_array_add (secrets, secret); secret = nm_secret_agent_simple_secret_new (_("Password"), NM_SETTING (s_8021x), NM_SETTING_802_1X_PASSWORD, + NULL, + NULL, TRUE); g_ptr_array_add (secrets, secret); return TRUE; @@ -223,11 +244,15 @@ add_8021x_secrets (NMSecretAgentSimpleRequest *request, secret = nm_secret_agent_simple_secret_new (_("Identity"), NM_SETTING (s_8021x), NM_SETTING_802_1X_IDENTITY, + NULL, + NULL, FALSE); g_ptr_array_add (secrets, secret); secret = nm_secret_agent_simple_secret_new (_("Private key password"), NM_SETTING (s_8021x), NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD, + NULL, + NULL, TRUE); g_ptr_array_add (secrets, secret); return TRUE; @@ -251,6 +276,8 @@ add_wireless_secrets (NMSecretAgentSimpleRequest *request, secret = nm_secret_agent_simple_secret_new (_("Password"), NM_SETTING (s_wsec), NM_SETTING_WIRELESS_SECURITY_PSK, + NULL, + NULL, TRUE); g_ptr_array_add (secrets, secret); return TRUE; @@ -265,6 +292,8 @@ add_wireless_secrets (NMSecretAgentSimpleRequest *request, secret = nm_secret_agent_simple_secret_new (_("Key"), NM_SETTING (s_wsec), key, + NULL, + NULL, TRUE); g_free (key); @@ -277,6 +306,8 @@ add_wireless_secrets (NMSecretAgentSimpleRequest *request, secret = nm_secret_agent_simple_secret_new (_("Password"), NM_SETTING (s_wsec), NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, + NULL, + NULL, TRUE); g_ptr_array_add (secrets, secret); return TRUE; @@ -300,21 +331,116 @@ add_pppoe_secrets (NMSecretAgentSimpleRequest *request, secret = nm_secret_agent_simple_secret_new (_("Username"), NM_SETTING (s_pppoe), NM_SETTING_PPPOE_USERNAME, + NULL, + NULL, FALSE); g_ptr_array_add (secrets, secret); secret = nm_secret_agent_simple_secret_new (_("Service"), NM_SETTING (s_pppoe), NM_SETTING_PPPOE_SERVICE, + NULL, + NULL, FALSE); g_ptr_array_add (secrets, secret); secret = nm_secret_agent_simple_secret_new (_("Password"), NM_SETTING (s_pppoe), NM_SETTING_PPPOE_PASSWORD, + NULL, + NULL, TRUE); g_ptr_array_add (secrets, secret); return TRUE; } +static NMSettingSecretFlags +get_vpn_secret_flags (NMSettingVpn *s_vpn, const char *secret_name) +{ + NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE; + GHashTable *vpn_data; + char *flag_name; + const char *val; + unsigned long tmp; + + g_object_get (s_vpn, NM_SETTING_VPN_DATA, &vpn_data, NULL); + + flag_name = g_strdup_printf ("%s-flags", secret_name); + + /* Try new flags value first */ + val = g_hash_table_lookup (vpn_data, flag_name); + if (val) { + errno = 0; + tmp = strtoul (val, NULL, 10); + if (errno == 0 && tmp <= NM_SETTING_SECRET_FLAGS_ALL) + flags = (NMSettingSecretFlags) tmp; + } + g_free (flag_name); + g_hash_table_unref (vpn_data); + + return flags; +} + +static void +add_vpn_secret_helper (GPtrArray *secrets, NMSettingVpn *s_vpn, const char *name, const char *ui_name) +{ + NMSecretAgentSimpleSecret *secret; + NMSettingSecretFlags flags; + int i; + + /* Check for duplicates */ + for (i = 0; i < secrets->len; i++) { + secret = secrets->pdata[i]; + + if (g_strcmp0 (secret->vpn_property, name) == 0) + return; + } + + flags = get_vpn_secret_flags (s_vpn, name); + if ( flags & NM_SETTING_SECRET_FLAG_AGENT_OWNED + || flags & NM_SETTING_SECRET_FLAG_NOT_SAVED) { + secret = nm_secret_agent_simple_secret_new (ui_name, + NM_SETTING (s_vpn), + NM_SETTING_VPN_SECRETS, + name, + nm_setting_vpn_get_service_type (s_vpn), + TRUE); + g_ptr_array_add (secrets, secret); + } +} + +#define VPN_MSG_TAG "x-vpn-message:" + +static gboolean +add_vpn_secrets (NMSecretAgentSimpleRequest *request, + GPtrArray *secrets, + char **msg) +{ + NMSettingVpn *s_vpn = nm_connection_get_setting_vpn (request->connection); + const VpnPasswordName *secret_names, *p; + char *tmp = NULL; + char **iter; + + /* If hints are given, then always ask for what the hints require */ + if (request->hints && g_strv_length (request->hints)) { + for (iter = request->hints; iter && *iter; iter++) { + if (!tmp && g_str_has_prefix (*iter, VPN_MSG_TAG)) + tmp = g_strdup (*iter + strlen (VPN_MSG_TAG)); + else + add_vpn_secret_helper (secrets, s_vpn, *iter, *iter); + } + } + if (msg) + *msg = g_strdup (tmp); + + /* Now add what client thinks might be required, because hints may be empty or incomplete */ + p = secret_names = nm_vpn_get_secret_names (nm_setting_vpn_get_service_type (s_vpn)); + while (p && p->name) { + add_vpn_secret_helper (secrets, s_vpn, p->name, _(p->ui_name)); + p++; + } + + return TRUE; +} + static void request_secrets_from_ui (NMSecretAgentSimpleRequest *request) { @@ -351,6 +477,8 @@ request_secrets_from_ui (NMSecretAgentSimpleRequest *request) secret = nm_secret_agent_simple_secret_new (_("Network name"), NM_SETTING (s_con), NM_SETTING_CONNECTION_ID, + NULL, + NULL, FALSE); g_ptr_array_add (secrets, secret); ok = add_8021x_secrets (request, secrets); @@ -369,6 +497,8 @@ request_secrets_from_ui (NMSecretAgentSimpleRequest *request) secret = nm_secret_agent_simple_secret_new (_("PIN"), NM_SETTING (s_gsm), NM_SETTING_GSM_PIN, + NULL, + NULL, FALSE); g_ptr_array_add (secrets, secret); } else { @@ -379,6 +509,8 @@ request_secrets_from_ui (NMSecretAgentSimpleRequest *request) secret = nm_secret_agent_simple_secret_new (_("Password"), NM_SETTING (s_gsm), NM_SETTING_GSM_PASSWORD, + NULL, + NULL, TRUE); g_ptr_array_add (secrets, secret); } @@ -392,6 +524,8 @@ request_secrets_from_ui (NMSecretAgentSimpleRequest *request) secret = nm_secret_agent_simple_secret_new (_("Password"), NM_SETTING (s_cdma), NM_SETTING_CDMA_PASSWORD, + NULL, + NULL, TRUE); g_ptr_array_add (secrets, secret); } else if (nm_connection_is_type (request->connection, NM_SETTING_BLUETOOTH_SETTING_NAME)) { @@ -408,8 +542,22 @@ request_secrets_from_ui (NMSecretAgentSimpleRequest *request) secret = nm_secret_agent_simple_secret_new (_("Password"), setting, "password", + NULL, + NULL, TRUE); g_ptr_array_add (secrets, secret); + } else if (nm_connection_is_type (request->connection, NM_SETTING_VPN_SETTING_NAME)) { + NMSettingConnection *s_con; + + s_con = nm_connection_get_setting_connection (request->connection); + + title = _("VPN password required"); + msg = NULL; + + ok = add_vpn_secrets (request, secrets, &msg); + if (!msg) + msg = g_strdup_printf (_("A password is required to connect to '%s'."), + nm_connection_get_id (request->connection)); } else ok = FALSE; @@ -455,13 +603,6 @@ nm_secret_agent_simple_get_secrets (NMSecretAgentOld *agent, s_con = nm_connection_get_setting_connection (connection); connection_type = nm_setting_connection_get_connection_type (s_con); - if (!strcmp (connection_type, NM_SETTING_VPN_SETTING_NAME)) { - /* We don't support VPN secrets yet */ - error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_NO_SECRETS, - "VPN secrets not supported"); - goto nope; - } - if (!(flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION)) { /* We don't do stored passwords */ error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_NO_SECRETS, @@ -515,9 +656,13 @@ nm_secret_agent_simple_response (NMSecretAgentSimple *self, if (secrets) { GVariantBuilder conn_builder, *setting_builder; + GVariantBuilder vpn_secrets_builder; GHashTable *settings; GHashTableIter iter; const char *name; + const char *vpn_secrets_base_name = NULL; + + g_variant_builder_init (&vpn_secrets_builder, G_VARIANT_TYPE ("a{ss}")); settings = g_hash_table_new (g_str_hash, g_str_equal); for (i = 0; i < secrets->len; i++) { @@ -530,9 +675,23 @@ nm_secret_agent_simple_response (NMSecretAgentSimple *self, setting_builder); } + if (secret->base.vpn_property) { + /* VPN secrets need slightly different treatment. + * "secrets" property is actually a hash table of secrets. */ + vpn_secrets_base_name = secret->property; + g_variant_builder_add (&vpn_secrets_builder, "{ss}", + secret->base.vpn_property, secret->base.value); + } else { + g_variant_builder_add (setting_builder, "{sv}", + secret->property, + g_variant_new_string (secret->base.value)); + } + } + + if (vpn_secrets_base_name) { g_variant_builder_add (setting_builder, "{sv}", - secret->property, - g_variant_new_string (secret->base.value)); + vpn_secrets_base_name, + g_variant_builder_end (&vpn_secrets_builder)); } g_variant_builder_init (&conn_builder, NM_VARIANT_TYPE_CONNECTION); @@ -691,5 +850,6 @@ nm_secret_agent_simple_new (const char *name) { return g_initable_new (NM_TYPE_SECRET_AGENT_SIMPLE, NULL, NULL, NM_SECRET_AGENT_OLD_IDENTIFIER, name, + NM_SECRET_AGENT_OLD_CAPABILITIES, NM_SECRET_AGENT_CAPABILITY_VPN_HINTS, NULL); } diff --git a/clients/common/nm-secret-agent-simple.h b/clients/common/nm-secret-agent-simple.h index 81fec65139..d2c58822a0 100644 --- a/clients/common/nm-secret-agent-simple.h +++ b/clients/common/nm-secret-agent-simple.h @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * - * Copyright 2013 - 2014 Red Hat, Inc. + * Copyright 2013 - 2015 Red Hat, Inc. */ #ifndef __NM_SECRET_AGENT_SIMPLE_H__ @@ -43,6 +43,8 @@ typedef struct { typedef struct { char *name, *prop_name, *value; + char *vpn_property; + char *vpn_type; gboolean password; } NMSecretAgentSimpleSecret; diff --git a/clients/common/nm-vpn-helpers.c b/clients/common/nm-vpn-helpers.c index eaa0917158..4a0cd23afc 100644 --- a/clients/common/nm-vpn-helpers.c +++ b/clients/common/nm-vpn-helpers.c @@ -28,10 +28,11 @@ #include <string.h> #include <glib.h> #include <gmodule.h> +#include <glib/gi18n-lib.h> #include <NetworkManager.h> -#include "nm-vpn-editor-plugin.h" +#include "nm-utils.h" #include "nm-vpn-helpers.h" @@ -225,3 +226,123 @@ nm_vpn_supports_ipv6 (NMConnection *connection) capabilities = nm_vpn_editor_plugin_get_capabilities (plugin); return (capabilities & NM_VPN_EDITOR_PLUGIN_CAPABILITY_IPV6) != 0; } + +const VpnPasswordName * +nm_vpn_get_secret_names (const char *vpn_type) +{ + const char *type; + static VpnPasswordName generic_vpn_secrets[] = { {"password", N_("Password")}, {NULL, NULL} }; + static VpnPasswordName vpnc_secrets[] = { {"Xauth password", N_("Password")}, + {"IPSec secret", N_("Group password")}, + {NULL, NULL} }; + static VpnPasswordName swan_secrets[] = { {"xauthpassword", N_("Password")}, + {"pskvalue", N_("Group password")}, + {NULL, NULL} }; + static VpnPasswordName openconnect_secrets[] = { {"gateway", N_("Gateway")}, + {"cookie", N_("Cookie")}, + {"gwcert", N_("Gateway certificate hash")}, + {NULL, NULL} }; + + if (!vpn_type) + return NULL; + + if (g_str_has_prefix (vpn_type, NM_DBUS_INTERFACE)) + type = vpn_type + strlen (NM_DBUS_INTERFACE) + 1; + else + type = vpn_type; + + if ( !g_strcmp0 (type, "openvpn") + || !g_strcmp0 (type, "pptp") + || !g_strcmp0 (type, "iodine") + || !g_strcmp0 (type, "ssh") + || !g_strcmp0 (type, "l2tp") + || !g_strcmp0 (type, "fortisslvpn")) + return generic_vpn_secrets; + else if (!g_strcmp0 (type, "vpnc")) + return vpnc_secrets; + else if ( !g_strcmp0 (type, "openswan") + || !g_strcmp0 (type, "libreswan") + || !g_strcmp0 (type, "strongswan")) + return swan_secrets; + else if (!g_strcmp0 (type, "openconnect")) + return openconnect_secrets; + return NULL; +} + +static gboolean +_extract_variable_value (char *line, const char *tag, char **value) +{ + char *p1, *p2; + + if (g_str_has_prefix (line, tag)) { + p1 = line + strlen (tag); + p2 = line + strlen (line) - 1; + if ((*p1 == '\'' || *p1 == '"') && (*p1 == *p2)) { + p1++; + *p2 = '\0'; + } + if (value) + *value = g_strdup (p1); + return TRUE; + } + return FALSE; +} + +gboolean +nm_vpn_openconnect_authenticate_helper (const char *host, + char **cookie, + char **gateway, + char **gwcert, + int *status, + GError **error) +{ + char *output = NULL; + gboolean ret; + char **strv = NULL, **iter; + char *argv[4]; + const char *path; + const char *const DEFAULT_PATHS[] = { + "/sbin/", + "/usr/sbin/", + "/usr/local/sbin/", + "/bin/", + "/usr/bin/", + "/usr/local/bin/", + NULL, + }; + + path = nm_utils_file_search_in_paths ("openconnect", "/usr/sbin/openconnect", DEFAULT_PATHS, + G_FILE_TEST_IS_EXECUTABLE, NULL, NULL, error); + if (!path) + return FALSE; + + argv[0] = (char *) path; + argv[1] = "--authenticate"; + argv[2] = (char *) host; + argv[3] = NULL; + + ret = g_spawn_sync (NULL, argv, NULL, + G_SPAWN_SEARCH_PATH | G_SPAWN_CHILD_INHERITS_STDIN, + NULL, NULL, &output, NULL, + status, error); + + if (!ret) + return FALSE; + + /* Parse output and set cookie, gateway and gwcert + * output example: + * COOKIE='loremipsum' + * HOST='1.2.3.4' + * FINGERPRINT='sha1:32bac90cf09a722e10ecc1942c67fe2ac8c21e2e' + */ + strv = g_strsplit_set (output ? output : "", "\r\n", 0); + for (iter = strv; iter && *iter; iter++) { + _extract_variable_value (*iter, "COOKIE=", cookie); + _extract_variable_value (*iter, "HOST=", gateway); + _extract_variable_value (*iter, "FINGERPRINT=", gwcert); + } + g_strfreev (strv); + + return TRUE; +} + diff --git a/clients/common/nm-vpn-helpers.h b/clients/common/nm-vpn-helpers.h index 7ec21a71e2..6b3396efb1 100644 --- a/clients/common/nm-vpn-helpers.h +++ b/clients/common/nm-vpn-helpers.h @@ -20,14 +20,28 @@ #define __NM_VPN_HELPERS_H__ #include <glib.h> -#include <nm-connection.h> +#include <NetworkManager.h> #include <nm-vpn-editor-plugin.h> +struct { + const char *name; + const char *ui_name; +} typedef VpnPasswordName; + GSList *nm_vpn_get_plugins (GError **error); NMVpnEditorPlugin *nm_vpn_get_plugin_by_service (const char *service); gboolean nm_vpn_supports_ipv6 (NMConnection *connection); +const VpnPasswordName * nm_vpn_get_secret_names (const char *vpn_type); + +gboolean nm_vpn_openconnect_authenticate_helper (const char *host, + char **cookie, + char **gateway, + char **gwcert, + int *status, + GError **error); + #endif /* __NM_VPN_HELPERS_H__ */ diff --git a/clients/tui/Makefile.am b/clients/tui/Makefile.am index f6d93be2b4..a84fcdffe3 100644 --- a/clients/tui/Makefile.am +++ b/clients/tui/Makefile.am @@ -18,6 +18,8 @@ AM_CPPFLAGS= \ -DG_LOG_DOMAIN=\""nmtui"\" \ -DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \ -DLOCALEDIR=\""$(localedir)"\" \ + -DNMCONFDIR=\"$(nmconfdir)\" \ + -DNMLIBDIR=\"$(libdir)\" \ $(NULL) bin_PROGRAMS = nmtui @@ -116,6 +118,8 @@ nmtui_SOURCES = \ nmt-widget-list.h \ $(srcdir)/../common/nm-secret-agent-simple.c \ $(srcdir)/../common/nm-secret-agent-simple.h \ + $(srcdir)/../common/nm-vpn-helpers.c \ + $(srcdir)/../common/nm-vpn-helpers.h \ $(NULL) nmtui_LDADD = \ diff --git a/clients/tui/nmt-password-dialog.c b/clients/tui/nmt-password-dialog.c index aeece1211f..bf29dcf5a1 100644 --- a/clients/tui/nmt-password-dialog.c +++ b/clients/tui/nmt-password-dialog.c @@ -154,6 +154,8 @@ nmt_password_dialog_constructed (GObject *object) if (secret->password) flags |= NMT_NEWT_ENTRY_PASSWORD; widget = nmt_newt_entry_new (30, flags); + if (secret->value) + nmt_newt_entry_set_text (NMT_NEWT_ENTRY (widget), secret->value); nmt_newt_grid_add (secret_grid, widget, 1, i); g_ptr_array_add (priv->entries, widget); diff --git a/clients/tui/nmtui-connect.c b/clients/tui/nmtui-connect.c index cacb0b44ea..9838b6fdfc 100644 --- a/clients/tui/nmtui-connect.c +++ b/clients/tui/nmtui-connect.c @@ -39,8 +39,60 @@ #include "nmt-connect-connection-list.h" #include "nmt-password-dialog.h" #include "nm-secret-agent-simple.h" +#include "nm-vpn-helpers.h" #include "nmt-utils.h" +/** + * Runs openconnect to authenticate. The current screen state is saved + * before starting the command and restored after it returns. + */ +static gboolean +openconnect_authenticate (NMConnection *connection, char **cookie, char **gateway, char **gwcert) +{ + 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); + + newtResume (); + + if (!ret) { + nmt_newt_message_dialog (_("Error: openconnect failed: %s"), error->message); + g_clear_error (&error); + return FALSE; + } + + if (WIFEXITED (status)) { + if (WEXITSTATUS (status) != 0) { + nmt_newt_message_dialog (_("openconnect failed with status %d"), WEXITSTATUS (status)); + return FALSE; + } + } else if (WIFSIGNALED (status)) { + nmt_newt_message_dialog (_("openconnect failed with signal %d"), WTERMSIG (status)); + return FALSE; + } + + if (gateway && *gateway && port) { + char *tmp = *gateway; + *gateway = g_strdup_printf ("%s%s", *gateway, port); + g_free (tmp); + } + + return TRUE; +} + static void secrets_requested (NMSecretAgentSimple *agent, const char *request_id, @@ -50,6 +102,44 @@ secrets_requested (NMSecretAgentSimple *agent, gpointer user_data) { NmtNewtForm *form; + NMConnection *connection = NM_CONNECTION (user_data); + char *cookie = NULL; + char *gateway = NULL; + char *gwcert = NULL; + int i; + + /* Get secrets for OpenConnect VPN */ + if (connection && nm_connection_is_type (connection, NM_SETTING_VPN_SETTING_NAME)) { + NMSettingVpn *s_vpn = nm_connection_get_setting_vpn (connection); + const char *vpn_type = nm_setting_vpn_get_service_type (s_vpn); + + if (!g_strcmp0 (vpn_type, NM_DBUS_INTERFACE ".openconnect")) { + openconnect_authenticate (connection, &cookie, &gateway, &gwcert); + + for (i = 0; i < secrets->len; i++) { + NMSecretAgentSimpleSecret *secret = secrets->pdata[i]; + + if (!g_strcmp0 (secret->vpn_type, NM_DBUS_INTERFACE ".openconnect")) { + if (!g_strcmp0 (secret->vpn_property, "cookie")) { + g_free (secret->value); + secret->value = cookie; + cookie = NULL; + } else if (!g_strcmp0 (secret->vpn_property, "gateway")) { + g_free (secret->value); + secret->value = gateway; + gateway = NULL; + } else if (!g_strcmp0 (secret->vpn_property, "gwcert")) { + g_free (secret->value); + secret->value = gwcert; + gwcert = NULL; + } + } + } + g_free (cookie); + g_free (gateway); + g_free (gwcert); + } + } form = nmt_password_dialog_new (request_id, title, msg, secrets); nmt_newt_form_run_sync (form); @@ -153,7 +243,7 @@ activate_connection (NMConnection *connection, nm_secret_agent_simple_enable (NM_SECRET_AGENT_SIMPLE (agent), nm_object_get_path (NM_OBJECT (connection))); } - g_signal_connect (agent, "request-secrets", G_CALLBACK (secrets_requested), NULL); + g_signal_connect (agent, "request-secrets", G_CALLBACK (secrets_requested), connection); } specific_object_path = specific_object ? nm_object_get_path (specific_object) : NULL; diff --git a/po/POTFILES.in b/po/POTFILES.in index 6c09b21ff0..19ef9df384 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -12,6 +12,7 @@ clients/cli/settings.c clients/cli/utils.c clients/common/nm-polkit-listener.c clients/common/nm-secret-agent-simple.c +clients/common/nm-vpn-helpers.c clients/nm-online.c clients/tui/newt/nmt-newt-utils.c clients/tui/nm-editor-utils.c |