diff options
Diffstat (limited to 'clients')
| -rw-r--r-- | clients/cli/Makefile.am | 6 | ||||
| -rw-r--r-- | clients/cli/Makefile.in | 28 | ||||
| -rw-r--r-- | clients/cli/common.c | 110 | ||||
| -rw-r--r-- | clients/cli/connections.c | 12 | ||||
| -rw-r--r-- | clients/cli/settings.c | 30 | ||||
| -rw-r--r-- | clients/common/nm-polkit-listener.c | 8 | ||||
| -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 | 348 | ||||
| -rw-r--r-- | clients/common/nm-vpn-helpers.h | 47 | ||||
| -rw-r--r-- | clients/tui/Makefile.am | 4 | ||||
| -rw-r--r-- | clients/tui/Makefile.in | 26 | ||||
| -rw-r--r-- | clients/tui/nm-editor-utils.c | 12 | ||||
| -rw-r--r-- | clients/tui/nmt-password-dialog.c | 2 | ||||
| -rw-r--r-- | clients/tui/nmtui-connect.c | 92 |
15 files changed, 863 insertions, 50 deletions
diff --git a/clients/cli/Makefile.am b/clients/cli/Makefile.am index eef99cba..2f394098 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/Makefile.in b/clients/cli/Makefile.in index d79f0e70..2abbf580 100644 --- a/clients/cli/Makefile.in +++ b/clients/cli/Makefile.in @@ -122,6 +122,8 @@ am__nmcli_SOURCES_DIST = agent.c agent.h common.c common.h \ utils.h polkit-agent.c polkit-agent.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 \ $(srcdir)/../common/nm-polkit-listener.c \ $(srcdir)/../common/nm-polkit-listener.h @WITH_POLKIT_AGENT_TRUE@am__objects_1 = nm-polkit-listener.$(OBJEXT) @@ -129,7 +131,7 @@ am_nmcli_OBJECTS = agent.$(OBJEXT) common.$(OBJEXT) \ connections.$(OBJEXT) devices.$(OBJEXT) general.$(OBJEXT) \ settings.$(OBJEXT) nmcli.$(OBJEXT) utils.$(OBJEXT) \ polkit-agent.$(OBJEXT) nm-secret-agent-simple.$(OBJEXT) \ - $(am__objects_1) + nm-vpn-helpers.$(OBJEXT) $(am__objects_1) nmcli_OBJECTS = $(am_nmcli_OBJECTS) am__DEPENDENCIES_1 = @WITH_POLKIT_AGENT_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) @@ -472,14 +474,17 @@ AM_CPPFLAGS = -I${top_srcdir} -I${top_builddir} \ -I${top_srcdir}/clients/common $(GLIB_CFLAGS) \ -DG_LOG_DOMAIN=\""nmcli"\" \ -DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \ - -DNMCLI_LOCALEDIR=\"$(datadir)/locale\" $(am__append_1) + -DNMCLI_LOCALEDIR=\"$(datadir)/locale\" \ + -DNMCONFDIR=\"$(nmconfdir)\" -DNMLIBDIR=\"$(libdir)\" \ + $(am__append_1) nmcli_SOURCES = agent.c agent.h common.c common.h connections.c \ connections.h devices.c devices.h general.c general.h \ settings.c settings.h nmcli.c nmcli.h utils.c utils.h \ polkit-agent.c polkit-agent.h \ $(srcdir)/../common/nm-secret-agent-simple.c \ - $(srcdir)/../common/nm-secret-agent-simple.h $(NULL) \ - $(am__append_2) + $(srcdir)/../common/nm-secret-agent-simple.h \ + $(srcdir)/../common/nm-vpn-helpers.c \ + $(srcdir)/../common/nm-vpn-helpers.h $(NULL) $(am__append_2) nmcli_LDADD = $(GLIB_LIBS) $(READLINE_LIBS) \ $(top_builddir)/libnm/libnm.la $(am__append_3) @BUILD_SETTING_DOCS_TRUE@BUILT_SOURCES = settings-docs.c @@ -587,6 +592,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/general.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-polkit-listener.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-secret-agent-simple.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-vpn-helpers.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmcli.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/polkit-agent.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/settings.Po@am__quote@ @@ -627,6 +633,20 @@ nm-secret-agent-simple.obj: $(srcdir)/../common/nm-secret-agent-simple.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-secret-agent-simple.obj `if test -f '$(srcdir)/../common/nm-secret-agent-simple.c'; then $(CYGPATH_W) '$(srcdir)/../common/nm-secret-agent-simple.c'; else $(CYGPATH_W) '$(srcdir)/$(srcdir)/../common/nm-secret-agent-simple.c'; fi` +nm-vpn-helpers.o: $(srcdir)/../common/nm-vpn-helpers.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-vpn-helpers.o -MD -MP -MF $(DEPDIR)/nm-vpn-helpers.Tpo -c -o nm-vpn-helpers.o `test -f '$(srcdir)/../common/nm-vpn-helpers.c' || echo '$(srcdir)/'`$(srcdir)/../common/nm-vpn-helpers.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nm-vpn-helpers.Tpo $(DEPDIR)/nm-vpn-helpers.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(srcdir)/../common/nm-vpn-helpers.c' object='nm-vpn-helpers.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-vpn-helpers.o `test -f '$(srcdir)/../common/nm-vpn-helpers.c' || echo '$(srcdir)/'`$(srcdir)/../common/nm-vpn-helpers.c + +nm-vpn-helpers.obj: $(srcdir)/../common/nm-vpn-helpers.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-vpn-helpers.obj -MD -MP -MF $(DEPDIR)/nm-vpn-helpers.Tpo -c -o nm-vpn-helpers.obj `if test -f '$(srcdir)/../common/nm-vpn-helpers.c'; then $(CYGPATH_W) '$(srcdir)/../common/nm-vpn-helpers.c'; else $(CYGPATH_W) '$(srcdir)/$(srcdir)/../common/nm-vpn-helpers.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nm-vpn-helpers.Tpo $(DEPDIR)/nm-vpn-helpers.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(srcdir)/../common/nm-vpn-helpers.c' object='nm-vpn-helpers.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-vpn-helpers.obj `if test -f '$(srcdir)/../common/nm-vpn-helpers.c'; then $(CYGPATH_W) '$(srcdir)/../common/nm-vpn-helpers.c'; else $(CYGPATH_W) '$(srcdir)/$(srcdir)/../common/nm-vpn-helpers.c'; fi` + nm-polkit-listener.o: $(srcdir)/../common/nm-polkit-listener.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-polkit-listener.o -MD -MP -MF $(DEPDIR)/nm-polkit-listener.Tpo -c -o nm-polkit-listener.o `test -f '$(srcdir)/../common/nm-polkit-listener.c' || echo '$(srcdir)/'`$(srcdir)/../common/nm-polkit-listener.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nm-polkit-listener.Tpo $(DEPDIR)/nm-polkit-listener.Po diff --git a/clients/cli/common.c b/clients/cli/common.c index 35676d0f..86f91d58 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/cli/connections.c b/clients/cli/connections.c index 55bde29a..e5aaa658 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -9087,12 +9087,6 @@ do_connection_reload (NmCli *nmc, int argc, char **argv) nmc->return_value = NMC_RESULT_SUCCESS; nmc->should_wait = FALSE; - if (!nm_client_get_nm_running (nmc->client)) { - g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); - nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; - return nmc->return_value; - } - if (!nm_client_reload_connections (nmc->client, NULL, &error)) { g_string_printf (nmc->return_text, _("Error: failed to reload connections: %s."), error->message); @@ -9113,12 +9107,6 @@ do_connection_load (NmCli *nmc, int argc, char **argv) nmc->return_value = NMC_RESULT_SUCCESS; nmc->should_wait = FALSE; - if (!nm_client_get_nm_running (nmc->client)) { - g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); - nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; - return nmc->return_value; - } - if (argc == 0) { g_string_printf (nmc->return_text, _("Error: No connection specified.")); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; diff --git a/clients/cli/settings.c b/clients/cli/settings.c index 8a6b5fa8..6a3c90c2 100644 --- a/clients/cli/settings.c +++ b/clients/cli/settings.c @@ -4150,13 +4150,26 @@ nmc_property_vlan_remove_egress_priority_map (NMSetting *setting, } /* --- NM_SETTING_VPN_SETTING_NAME property setter functions --- */ +/* Validate value of vpn 'data' and 'secret' options */ +static const char * +_validate_vpn_hash_value (const char *option, const char *value, GError **error) +{ + /* nm_setting_vpn_add_data_item() and nm_setting_vpn_add_secret() does not + * allow empty strings */ + if (!value || !*value) { + g_set_error (error, 1, 0, _("'%s' cannot be empty"), option); + return NULL; + } + return value; +} + /* 'data' */ DEFINE_SETTER_OPTIONS (nmc_property_vpn_set_data, NM_SETTING_VPN, NMSettingVpn, nm_setting_vpn_add_data_item, NULL, - NULL) + _validate_vpn_hash_value) DEFINE_REMOVER_OPTION (nmc_property_vpn_remove_option_data, NM_SETTING_VPN, nm_setting_vpn_remove_data_item) @@ -4167,7 +4180,7 @@ DEFINE_SETTER_OPTIONS (nmc_property_vpn_set_secrets, NMSettingVpn, nm_setting_vpn_add_secret, NULL, - NULL) + _validate_vpn_hash_value) DEFINE_REMOVER_OPTION (nmc_property_vpn_remove_option_secret, NM_SETTING_VPN, nm_setting_vpn_remove_secret) @@ -4273,12 +4286,23 @@ nmc_property_wired_set_s390_nettype (NMSetting *setting, const char *prop, const DEFINE_ALLOWED_VAL_FUNC (nmc_property_wired_allowed_s390_nettype, wired_valid_s390_nettypes) /* 's390-options' */ +/* Validate value of 's390-options' */ +static const char * +_validate_s390_option_value (const char *option, const char *value, GError **error) +{ + /* nm_setting_wired_add_s390_option() requires value len in <1,199> interval */ + if (!value || !*value || strlen (value) >= 200) { + g_set_error (error, 1, 0, _("'%s' string value should consist of 1 - 199 characters"), option); + return NULL; + } + return value; +} DEFINE_SETTER_OPTIONS (nmc_property_wired_set_s390_options, NM_SETTING_WIRED, NMSettingWired, nm_setting_wired_add_s390_option, nm_setting_wired_get_valid_s390_options, - NULL) + _validate_s390_option_value) DEFINE_REMOVER_OPTION (nmc_property_wired_remove_option_s390_options, NM_SETTING_WIRED, nm_setting_wired_remove_s390_option) diff --git a/clients/common/nm-polkit-listener.c b/clients/common/nm-polkit-listener.c index 82df1b2d..c1c7c908 100644 --- a/clients/common/nm-polkit-listener.c +++ b/clients/common/nm-polkit-listener.c @@ -323,9 +323,11 @@ nm_polkit_listener_new (gboolean for_session, GError **error) listener = g_object_new (NM_TYPE_POLKIT_LISTENER, NULL); priv = NM_POLKIT_LISTENER_GET_PRIVATE (listener); - if (for_session) - session = polkit_unix_session_new_for_process_sync (getpid (), NULL, NULL); - else + if (for_session) { + session = polkit_unix_session_new_for_process_sync (getpid (), NULL, error); + if (!session) + return NULL; + } else session = polkit_unix_process_new_for_owner (getpid (), 0, getuid ()); priv->reg_handle = polkit_agent_listener_register (listener, POLKIT_AGENT_REGISTER_FLAGS_NONE, diff --git a/clients/common/nm-secret-agent-simple.c b/clients/common/nm-secret-agent-simple.c index 8eddb440..6cb6c58d 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 81fec651..d2c58822 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 new file mode 100644 index 00000000..370e128c --- /dev/null +++ b/clients/common/nm-vpn-helpers.c @@ -0,0 +1,348 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * 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 - 2015 Red Hat, Inc. + */ + +/** + * SECTION:nm-vpn-helpers + * @short_description: VPN-related utilities + * + * Some functions should probably eventually move into libnm. + */ + +#include "config.h" + +#include <string.h> +#include <glib.h> +#include <gmodule.h> +#include <glib/gi18n-lib.h> + +#include <NetworkManager.h> + +#include "nm-utils.h" +#include "nm-vpn-helpers.h" +#include "nm-glib-compat.h" + +#define VPN_NAME_FILES_DIR NMCONFDIR "/VPN" +#define DEFAULT_DIR_LIB NMLIBDIR"/VPN" + +static gboolean plugins_loaded = FALSE; +static GHashTable *plugins_hash = NULL; +static GSList *plugins_list = NULL; + + +GQuark nm_vpn_error_quark (void); +G_DEFINE_QUARK (NM_VPN_ERROR, nm_vpn_error) +#define NM_VPN_ERROR nm_vpn_error_quark () +#define NM_VPN_ERROR_GENERIC 0 + +NMVpnEditorPlugin * +nm_vpn_get_plugin_by_service (const char *service) +{ + NMVpnEditorPlugin *plugin; + const char *str; + char *tmp = NULL; + + g_return_val_if_fail (service != NULL, NULL); + + if (G_UNLIKELY (!plugins_loaded)) + nm_vpn_get_plugins (NULL); + + if (!plugins_hash) + return NULL; + + if (g_str_has_prefix (service, NM_DBUS_SERVICE)) + str = service; + else + str = tmp = g_strdup_printf ("%s.%s", NM_DBUS_SERVICE, service); + + plugin = g_hash_table_lookup (plugins_hash, str); + g_free (tmp); + + return plugin; +} + +GSList * +nm_vpn_get_plugins (GError **error) +{ + GDir *dir; + const char *f; + GHashTableIter iter; + NMVpnEditorPlugin *plugin; + + if (error) + g_return_val_if_fail (*error == NULL, NULL); + + if (G_LIKELY (plugins_loaded)) + return plugins_list; + + plugins_loaded = TRUE; + + dir = g_dir_open (VPN_NAME_FILES_DIR, 0, NULL); + if (!dir) { + g_set_error (error, NM_VPN_ERROR, NM_VPN_ERROR_GENERIC, "Couldn't read VPN .name files directory " VPN_NAME_FILES_DIR "."); + return NULL; + } + + plugins_hash = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, (GDestroyNotify) g_object_unref); + + while ((f = g_dir_read_name (dir))) { + char *path = NULL, *service = NULL; + char *so_path = NULL, *so_name = NULL; + GKeyFile *keyfile = NULL; + GModule *module = NULL; + NMVpnEditorPluginFactory factory = NULL; + + if (!g_str_has_suffix (f, ".name")) + continue; + + path = g_strdup_printf ("%s/%s", VPN_NAME_FILES_DIR, f); + + keyfile = g_key_file_new (); + if (!g_key_file_load_from_file (keyfile, path, 0, NULL)) + goto next; + + service = g_key_file_get_string (keyfile, "VPN Connection", "service", NULL); + if (!service) + goto next; + + so_path = g_key_file_get_string (keyfile, "libnm", "plugin", NULL); + if (!so_path) + goto next; + + if (g_path_is_absolute (so_path)) + module = g_module_open (so_path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL); + + if (!module) { + /* Remove any path and extension components, then reconstruct path + * to the SO in LIBDIR + */ + so_name = g_path_get_basename (so_path); + g_free (so_path); + so_path = g_strdup_printf ("%s/NetworkManager/%s", NMLIBDIR, so_name); + g_free (so_name); + + module = g_module_open (so_path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL); + if (!module) { + g_clear_error (error); + g_set_error (error, NM_VPN_ERROR, NM_VPN_ERROR_GENERIC, "Cannot load the VPN plugin which provides the " + "service '%s'.", service); + goto next; + } + } + + if (g_module_symbol (module, "nm_vpn_editor_plugin_factory", (gpointer) &factory)) { + GError *factory_error = NULL; + gboolean success = FALSE; + + plugin = factory (&factory_error); + if (plugin) { + char *plug_name = NULL, *plug_service = NULL; + + /* Validate plugin properties */ + g_object_get (G_OBJECT (plugin), + NM_VPN_EDITOR_PLUGIN_NAME, &plug_name, + NM_VPN_EDITOR_PLUGIN_SERVICE, &plug_service, + NULL); + if (!plug_name || !strlen (plug_name)) { + g_clear_error (error); + g_set_error (error, NM_VPN_ERROR, NM_VPN_ERROR_GENERIC, "cannot load VPN plugin in '%s': missing plugin name", + g_module_name (module)); + } else if (!plug_service || strcmp (plug_service, service)) { + g_clear_error (error); + g_set_error (error, NM_VPN_ERROR, NM_VPN_ERROR_GENERIC, "cannot load VPN plugin in '%s': invalid service name", + g_module_name (module)); + } else { + /* Success! */ + g_object_set_data_full (G_OBJECT (plugin), "gmodule", module, + (GDestroyNotify) g_module_close); + g_hash_table_insert (plugins_hash, g_strdup (service), plugin); + success = TRUE; + } + g_free (plug_name); + g_free (plug_service); + } else { + g_clear_error (error); + g_set_error (error, NM_VPN_ERROR, NM_VPN_ERROR_GENERIC, "cannot load VPN plugin in '%s': %s", + g_module_name (module), g_module_error ()); + } + + if (!success) + g_module_close (module); + } else { + g_clear_error (error); + g_set_error (error, NM_VPN_ERROR, NM_VPN_ERROR_GENERIC, "cannot locate nm_vpn_editor_plugin_factory() in '%s': %s", + g_module_name (module), g_module_error ()); + g_module_close (module); + } + + next: + g_free (so_path); + g_free (service); + g_key_file_free (keyfile); + g_free (path); + } + g_dir_close (dir); + + /* Copy hash to list */ + g_hash_table_iter_init (&iter, plugins_hash); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &plugin)) + plugins_list = g_slist_prepend (plugins_list, plugin); + + return plugins_list; +} + +gboolean +nm_vpn_supports_ipv6 (NMConnection *connection) +{ + NMSettingVpn *s_vpn; + const char *service_type; + NMVpnEditorPlugin *plugin; + guint32 capabilities; + + s_vpn = nm_connection_get_setting_vpn (connection); + g_return_val_if_fail (s_vpn != NULL, FALSE); + + service_type = nm_setting_vpn_get_service_type (s_vpn); + g_return_val_if_fail (service_type != NULL, FALSE); + + plugin = nm_vpn_get_plugin_by_service (service_type); + g_return_val_if_fail (plugin != NULL, FALSE); + + 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 new file mode 100644 index 00000000..6b3396ef --- /dev/null +++ b/clients/common/nm-vpn-helpers.h @@ -0,0 +1,47 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * 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 - 2015 Red Hat, Inc. + */ + +#ifndef __NM_VPN_HELPERS_H__ +#define __NM_VPN_HELPERS_H__ + +#include <glib.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 f6d93be2..a84fcdff 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/Makefile.in b/clients/tui/Makefile.in index 26f00406..866ae462 100644 --- a/clients/tui/Makefile.in +++ b/clients/tui/Makefile.in @@ -140,7 +140,9 @@ am__nmtui_SOURCES_DIST = nmtui.c nmtui.h nmtui-connect.c \ nmt-route-table.h nmt-slave-list.c nmt-slave-list.h \ nmt-utils.c nmt-utils.h nmt-widget-list.c nmt-widget-list.h \ $(srcdir)/../common/nm-secret-agent-simple.c \ - $(srcdir)/../common/nm-secret-agent-simple.h + $(srcdir)/../common/nm-secret-agent-simple.h \ + $(srcdir)/../common/nm-vpn-helpers.c \ + $(srcdir)/../common/nm-vpn-helpers.h @BUILD_NMTUI_TRUE@am_nmtui_OBJECTS = nmtui.$(OBJEXT) \ @BUILD_NMTUI_TRUE@ nmtui-connect.$(OBJEXT) nmtui-edit.$(OBJEXT) \ @BUILD_NMTUI_TRUE@ nmtui-hostname.$(OBJEXT) \ @@ -177,7 +179,8 @@ am__nmtui_SOURCES_DIST = nmtui.c nmtui.h nmtui-connect.c \ @BUILD_NMTUI_TRUE@ nmt-route-table.$(OBJEXT) \ @BUILD_NMTUI_TRUE@ nmt-slave-list.$(OBJEXT) nmt-utils.$(OBJEXT) \ @BUILD_NMTUI_TRUE@ nmt-widget-list.$(OBJEXT) \ -@BUILD_NMTUI_TRUE@ nm-secret-agent-simple.$(OBJEXT) +@BUILD_NMTUI_TRUE@ nm-secret-agent-simple.$(OBJEXT) \ +@BUILD_NMTUI_TRUE@ nm-vpn-helpers.$(OBJEXT) nmtui_OBJECTS = $(am_nmtui_OBJECTS) am__DEPENDENCIES_1 = @BUILD_NMTUI_TRUE@nmtui_DEPENDENCIES = $(top_builddir)/libnm/libnm.la \ @@ -573,6 +576,8 @@ with_valgrind = @with_valgrind@ @BUILD_NMTUI_TRUE@ -DG_LOG_DOMAIN=\""nmtui"\" \ @BUILD_NMTUI_TRUE@ -DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \ @BUILD_NMTUI_TRUE@ -DLOCALEDIR=\""$(localedir)"\" \ +@BUILD_NMTUI_TRUE@ -DNMCONFDIR=\"$(nmconfdir)\" \ +@BUILD_NMTUI_TRUE@ -DNMLIBDIR=\"$(libdir)\" \ @BUILD_NMTUI_TRUE@ $(NULL) @BUILD_NMTUI_TRUE@links = nmtui-edit nmtui-connect nmtui-hostname @@ -659,6 +664,8 @@ with_valgrind = @with_valgrind@ @BUILD_NMTUI_TRUE@ nmt-widget-list.h \ @BUILD_NMTUI_TRUE@ $(srcdir)/../common/nm-secret-agent-simple.c \ @BUILD_NMTUI_TRUE@ $(srcdir)/../common/nm-secret-agent-simple.h \ +@BUILD_NMTUI_TRUE@ $(srcdir)/../common/nm-vpn-helpers.c \ +@BUILD_NMTUI_TRUE@ $(srcdir)/../common/nm-vpn-helpers.h \ @BUILD_NMTUI_TRUE@ $(NULL) @BUILD_NMTUI_TRUE@nmtui_LDADD = \ @@ -765,6 +772,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-editor-bindings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-editor-utils.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-secret-agent-simple.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-vpn-helpers.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-address-list.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-connect-connection-list.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-device-entry.Po@am__quote@ @@ -838,6 +846,20 @@ nm-secret-agent-simple.obj: $(srcdir)/../common/nm-secret-agent-simple.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-secret-agent-simple.obj `if test -f '$(srcdir)/../common/nm-secret-agent-simple.c'; then $(CYGPATH_W) '$(srcdir)/../common/nm-secret-agent-simple.c'; else $(CYGPATH_W) '$(srcdir)/$(srcdir)/../common/nm-secret-agent-simple.c'; fi` +nm-vpn-helpers.o: $(srcdir)/../common/nm-vpn-helpers.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-vpn-helpers.o -MD -MP -MF $(DEPDIR)/nm-vpn-helpers.Tpo -c -o nm-vpn-helpers.o `test -f '$(srcdir)/../common/nm-vpn-helpers.c' || echo '$(srcdir)/'`$(srcdir)/../common/nm-vpn-helpers.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nm-vpn-helpers.Tpo $(DEPDIR)/nm-vpn-helpers.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(srcdir)/../common/nm-vpn-helpers.c' object='nm-vpn-helpers.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-vpn-helpers.o `test -f '$(srcdir)/../common/nm-vpn-helpers.c' || echo '$(srcdir)/'`$(srcdir)/../common/nm-vpn-helpers.c + +nm-vpn-helpers.obj: $(srcdir)/../common/nm-vpn-helpers.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-vpn-helpers.obj -MD -MP -MF $(DEPDIR)/nm-vpn-helpers.Tpo -c -o nm-vpn-helpers.obj `if test -f '$(srcdir)/../common/nm-vpn-helpers.c'; then $(CYGPATH_W) '$(srcdir)/../common/nm-vpn-helpers.c'; else $(CYGPATH_W) '$(srcdir)/$(srcdir)/../common/nm-vpn-helpers.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nm-vpn-helpers.Tpo $(DEPDIR)/nm-vpn-helpers.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(srcdir)/../common/nm-vpn-helpers.c' object='nm-vpn-helpers.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-vpn-helpers.obj `if test -f '$(srcdir)/../common/nm-vpn-helpers.c'; then $(CYGPATH_W) '$(srcdir)/../common/nm-vpn-helpers.c'; else $(CYGPATH_W) '$(srcdir)/$(srcdir)/../common/nm-vpn-helpers.c'; fi` + mostlyclean-libtool: -rm -f *.lo diff --git a/clients/tui/nm-editor-utils.c b/clients/tui/nm-editor-utils.c index f936c959..fa2a2752 100644 --- a/clients/tui/nm-editor-utils.c +++ b/clients/tui/nm-editor-utils.c @@ -32,20 +32,20 @@ #include "nm-editor-utils.h" #if 0 -#include "vpn-helpers.h" +#include "nm-vpn-helpers.h" static GSList *vpn_plugins; static gint sort_vpn_plugins (gconstpointer a, gconstpointer b) { - NMVpnPluginUiInterface *aa = NM_VPN_PLUGIN_UI_INTERFACE (a); - NMVpnPluginUiInterface *bb = NM_VPN_PLUGIN_UI_INTERFACE (b); + NMVpnEditorPlugin *aa = NM_VPN_EDITOR_PLUGIN (a); + NMVpnEditorPlugin *bb = NM_VPN_EDITOR_PLUGIN (b); char *aa_desc = NULL, *bb_desc = NULL; int ret; - g_object_get (aa, NM_VPN_PLUGIN_UI_INTERFACE_NAME, &aa_desc, NULL); - g_object_get (bb, NM_VPN_PLUGIN_UI_INTERFACE_NAME, &bb_desc, NULL); + g_object_get (aa, NM_VPN_EDITOR_PLUGIN_NAME, &aa_desc, NULL); + g_object_get (bb, NM_VPN_EDITOR_PLUGIN_NAME, &bb_desc, NULL); ret = g_strcmp0 (aa_desc, bb_desc); @@ -224,7 +224,7 @@ nm_editor_utils_get_connection_type_list (void) #if 0 /* Add "VPN" only if there are plugins */ - vpn_plugins_hash = vpn_get_plugins (NULL); + vpn_plugins_hash = nm_vpn_get_plugins (NULL); have_vpn_plugins = vpn_plugins_hash && g_hash_table_size (vpn_plugins_hash); if (have_vpn_plugins) { GHashTableIter iter; diff --git a/clients/tui/nmt-password-dialog.c b/clients/tui/nmt-password-dialog.c index aeece121..bf29dcf5 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 cacb0b44..9838b6fd 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; |