From ab84a866788f46c556c9b66a0917738ef70694fa Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 20 Jun 2013 23:07:49 -0500 Subject: [PATCH] vpn: handle interactive plugin secrets requests If all agents can handle VPN hints, then we'll try to use ConnectInteractive() to let the VPN plugin ask for secrets interactively via the SecretsRequired signal. These hints are then passed to agents during the connection process if the plugin needs more secrets or different secrets, and when the new secrets are returned, they are passed back to the VPN plugin. If at least one agent does not have the VPN hints capability, we can't use ConnectInteractive(), but fall back to the old Connect call, because that agent won't be able to send the hints to the VPN plugin's authentication dialog, and thus we won't get back the secrets the VPN plugin is looking for. So, for interactive secrets to work correctly, you need: 1) A VPN plugin updated for interactive secrets requests 2) NM updated for interactive secrets requests 3) all agents to set the VPN_HINTS capability when registering with NetworkManager and to pass hints along to the VPN authentication dialog 4) a VPN authentication dialog updated to look for hints and only return secrets corresponding to the hints requested by the plugin --- introspection/nm-secret-agent.xml | 5 +- src/settings/nm-agent-manager.c | 24 +++ src/settings/nm-agent-manager.h | 5 + src/vpn-manager/nm-vpn-connection.c | 319 +++++++++++++++++++++------- 4 files changed, 276 insertions(+), 77 deletions(-) diff --git a/introspection/nm-secret-agent.xml b/introspection/nm-secret-agent.xml index 291f1bdd92..337d0a7fa8 100644 --- a/introspection/nm-secret-agent.xml +++ b/introspection/nm-secret-agent.xml @@ -58,7 +58,10 @@ required, so in some cases no hints may be given. The Agent should return any secrets it has, or that it thinks are required, regardless of what hints NetworkManager sends - in this request. + in this request. Some hints have special prefixes that + provide information to the agent; for example, VPN requests + may send server-specific messages prefixed with + "x-vpn-message:". diff --git a/src/settings/nm-agent-manager.c b/src/settings/nm-agent-manager.c index c6ab771e56..36512cde39 100644 --- a/src/settings/nm-agent-manager.c +++ b/src/settings/nm-agent-manager.c @@ -1422,6 +1422,30 @@ nm_agent_manager_get_agent_by_user (NMAgentManager *self, const char *username) /*************************************************************/ +gboolean +nm_agent_manager_all_agents_have_capability (NMAgentManager *manager, + gboolean filter_by_uid, + gulong owner_uid, + NMSecretAgentCapabilities capability) +{ + NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (manager); + GHashTableIter iter; + NMSecretAgent *agent; + + g_hash_table_iter_init (&iter, priv->agents); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &agent)) { + if (filter_by_uid && nm_secret_agent_get_owner_uid (agent) != owner_uid) + continue; + + if (!(nm_secret_agent_get_capabilities (agent) & capability)) + return FALSE; + } + + return TRUE; +} + +/*************************************************************/ + static void name_owner_changed_cb (NMDBusManager *dbus_mgr, const char *name, diff --git a/src/settings/nm-agent-manager.h b/src/settings/nm-agent-manager.h index 2145edd0f1..767bd2abf2 100644 --- a/src/settings/nm-agent-manager.h +++ b/src/settings/nm-agent-manager.h @@ -102,4 +102,9 @@ guint32 nm_agent_manager_delete_secrets (NMAgentManager *manager, NMSecretAgent *nm_agent_manager_get_agent_by_user (NMAgentManager *manager, const char *username); +gboolean nm_agent_manager_all_agents_have_capability (NMAgentManager *manager, + gboolean filter_by_uid, + gulong owner_uid, + NMSecretAgentCapabilities capability); + #endif /* NM_AGENT_MANAGER_H */ diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c index ea70e8ad81..29232fb37b 100644 --- a/src/vpn-manager/nm-vpn-connection.c +++ b/src/vpn-manager/nm-vpn-connection.c @@ -43,6 +43,7 @@ #include "nm-glib-compat.h" #include "settings/nm-settings-connection.h" #include "nm-dispatcher.h" +#include "nm-agent-manager.h" #include "nm-vpn-connection-glue.h" @@ -55,6 +56,8 @@ typedef enum { SECRETS_REQ_EXISTING = 1, /* New secrets required; ask an agent */ SECRETS_REQ_NEW = 2, + /* Plugin requests secrets interactively */ + SECRETS_REQ_INTERACTIVE = 3, /* Placeholder for bounds checking */ SECRETS_REQ_LAST } SecretsReq; @@ -76,7 +79,8 @@ typedef struct { NMVPNConnectionState vpn_state; NMVPNConnectionStateReason failure_reason; DBusGProxy *proxy; - guint ipconfig_timeout; + GHashTable *connect_hash; + guint connect_timeout; gboolean has_ip4; NMIP4Config *ip4_config; guint32 ip4_internal_gw; @@ -114,7 +118,14 @@ enum { LAST_PROP }; -static void get_secrets (NMVPNConnection *self, SecretsReq secrets_idx); +static void get_secrets (NMVPNConnection *self, + SecretsReq secrets_idx, + const char **hints); + +static void plugin_interactive_secrets_required (DBusGProxy *proxy, + const char *message, + const char **secrets, + gpointer user_data); static NMActiveConnectionState ac_state_from_vpn_state (NMVPNConnectionState vpn_state) @@ -226,7 +237,6 @@ nm_vpn_connection_set_vpn_state (NMVPNConnection *connection, nm_settings_connection_cancel_secrets (NM_SETTINGS_CONNECTION (priv->connection), priv->secrets_id); priv->secrets_id = 0; } - priv->secrets_idx = SECRETS_REQ_SYSTEM; /* The connection gets destroyed by the VPN manager when it enters the * disconnected/failed state, but we need to keep it around for a bit @@ -240,12 +250,10 @@ nm_vpn_connection_set_vpn_state (NMVPNConnection *connection, switch (vpn_state) { case NM_VPN_CONNECTION_STATE_NEED_AUTH: - /* Kick off the secrets requests; first we get existing system secrets - * and ask the plugin if these are sufficient, next we get all existing - * secrets from system and from user agents and ask the plugin again, - * and last we ask the user for new secrets if required. + /* Do nothing; not part of 'default' because we don't want to touch + * priv->secrets_req as NEED_AUTH is re-entered during interactive + * secrets. */ - get_secrets (connection, SECRETS_REQ_SYSTEM); break; case NM_VPN_CONNECTION_STATE_ACTIVATED: /* Secrets no longer needed now that we're connected */ @@ -278,8 +286,9 @@ nm_vpn_connection_set_vpn_state (NMVPNConnection *connection, /* Tear down and clean up the connection */ call_plugin_disconnect (connection); vpn_cleanup (connection); - break; + /* Fall through */ default: + priv->secrets_idx = SECRETS_REQ_SYSTEM; break; } @@ -748,7 +757,7 @@ nm_vpn_connection_config_maybe_complete (NMVPNConnection *connection, { NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); - if (priv->ipconfig_timeout == 0) { + if (priv->connect_timeout == 0) { /* config_complete() was already called with an error; * ignore further calls. */ @@ -763,8 +772,8 @@ nm_vpn_connection_config_maybe_complete (NMVPNConnection *connection, } } - g_source_remove (priv->ipconfig_timeout); - priv->ipconfig_timeout = 0; + g_source_remove (priv->connect_timeout); + priv->connect_timeout = 0; if (success) { print_vpn_config (connection); @@ -857,6 +866,12 @@ nm_vpn_connection_config_get (DBusGProxy *proxy, nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) reply received.", nm_connection_get_id (priv->connection)); + if (nm_vpn_connection_get_vpn_state (connection) == NM_VPN_CONNECTION_STATE_CONNECT) { + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_IP_CONFIG_GET, + NM_VPN_CONNECTION_STATE_REASON_NONE); + } + if (!process_generic_config (connection, config_hash)) return; @@ -882,6 +897,12 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy, GValue *val; int i; + if (nm_vpn_connection_get_vpn_state (connection) == NM_VPN_CONNECTION_STATE_CONNECT) { + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_IP_CONFIG_GET, + NM_VPN_CONNECTION_STATE_REASON_NONE); + } + if (priv->has_ip4) { nm_log_info (LOGD_VPN, "VPN connection '%s' (IP4 Config Get) reply received.", nm_connection_get_id (priv->connection)); @@ -1028,6 +1049,12 @@ nm_vpn_connection_ip6_config_get (DBusGProxy *proxy, nm_log_info (LOGD_VPN, "VPN connection '%s' (IP6 Config Get) reply received.", nm_connection_get_id (priv->connection)); + if (nm_vpn_connection_get_vpn_state (connection) == NM_VPN_CONNECTION_STATE_CONNECT) { + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_IP_CONFIG_GET, + NM_VPN_CONNECTION_STATE_REASON_NONE); + } + if (g_hash_table_size (config_hash) == 0) { priv->has_ip6 = FALSE; nm_vpn_connection_config_maybe_complete (connection, TRUE); @@ -1148,18 +1175,19 @@ nm_vpn_connection_ip6_config_get (DBusGProxy *proxy, } static gboolean -nm_vpn_connection_ip_config_timeout (gpointer user_data) +connect_timeout_cb (gpointer user_data) { NMVPNConnection *connection = NM_VPN_CONNECTION (user_data); NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + NMVPNConnectionState state; - priv->ipconfig_timeout = 0; + priv->connect_timeout = 0; - /* If the activation request's state is still IP_CONFIG_GET and we're - * in this timeout, cancel activation because it's taken too long. - */ - if (nm_vpn_connection_get_vpn_state (connection) == NM_VPN_CONNECTION_STATE_IP_CONFIG_GET) { - nm_log_warn (LOGD_VPN, "VPN connection '%s' (IP Config Get) timeout exceeded.", + /* Cancel activation if it's taken too long */ + state = nm_vpn_connection_get_vpn_state (connection); + if (state == NM_VPN_CONNECTION_STATE_CONNECT || + state == NM_VPN_CONNECTION_STATE_IP_CONFIG_GET) { + nm_log_warn (LOGD_VPN, "VPN connection '%s' connect timeout exceeded.", nm_connection_get_id (priv->connection)); nm_vpn_connection_set_vpn_state (connection, NM_VPN_CONNECTION_STATE_FAILED, @@ -1170,31 +1198,62 @@ nm_vpn_connection_ip_config_timeout (gpointer user_data) } static void -nm_vpn_connection_connect_cb (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data) +connect_success (NMVPNConnection *connection) { - NMVPNConnection *connection = NM_VPN_CONNECTION (user_data); NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + + /* 40 second timeout waiting for IP config signal from VPN service */ + priv->connect_timeout = g_timeout_add_seconds (40, connect_timeout_cb, connection); + + g_hash_table_destroy (priv->connect_hash); + priv->connect_hash = NULL; +} + +static void +connect_cb (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data) +{ + NMVPNConnection *self = NM_VPN_CONNECTION (user_data); + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); GError *err = NULL; nm_log_info (LOGD_VPN, "VPN connection '%s' (Connect) reply received.", nm_connection_get_id (priv->connection)); dbus_g_proxy_end_call (proxy, call, &err, G_TYPE_INVALID); - if (err) { - nm_log_warn (LOGD_VPN, "VPN connection '%s' failed to connect: '%s'.", - nm_connection_get_id (priv->connection), err->message); - g_error_free (err); - nm_vpn_connection_set_vpn_state (connection, - NM_VPN_CONNECTION_STATE_FAILED, - NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED); - } else { - nm_vpn_connection_set_vpn_state (connection, - NM_VPN_CONNECTION_STATE_IP_CONFIG_GET, - NM_VPN_CONNECTION_STATE_REASON_NONE); - - /* 40 second timeout waiting for IP config signal from VPN service */ - priv->ipconfig_timeout = g_timeout_add_seconds (40, nm_vpn_connection_ip_config_timeout, connection); + if (!err) { + connect_success (self); + return; } + + nm_log_warn (LOGD_VPN, "VPN connection '%s' failed to connect: '%s'.", + nm_connection_get_id (priv->connection), err->message); + g_error_free (err); + nm_vpn_connection_set_vpn_state (self, + NM_VPN_CONNECTION_STATE_FAILED, + NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED); +} + +static void +connect_interactive_cb (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data) +{ + NMVPNConnection *self = NM_VPN_CONNECTION (user_data); + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); + GError *err = NULL; + + nm_log_info (LOGD_VPN, "VPN connection '%s' (ConnectInteractive) reply received.", + nm_connection_get_id (priv->connection)); + + dbus_g_proxy_end_call (proxy, call, &err, G_TYPE_INVALID); + if (!err) { + connect_success (self); + return; + } + + /* Fall back to Connect() */ + dbus_g_proxy_begin_call (priv->proxy, "Connect", + connect_cb, self, NULL, + DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, priv->connect_hash, + G_TYPE_INVALID); } /* Add a username to a hashed connection */ @@ -1229,7 +1288,8 @@ static void really_activate (NMVPNConnection *connection, const char *username) { NMVPNConnectionPrivate *priv; - GHashTable *hash; + NMAgentManager *agent_mgr; + GHashTable *details; g_return_if_fail (NM_IS_VPN_CONNECTION (connection)); g_return_if_fail (nm_vpn_connection_get_vpn_state (connection) == NM_VPN_CONNECTION_STATE_NEED_AUTH); @@ -1237,38 +1297,56 @@ really_activate (NMVPNConnection *connection, const char *username) priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); dbus_g_object_register_marshaller (g_cclosure_marshal_VOID__BOXED, - G_TYPE_NONE, G_TYPE_VALUE, G_TYPE_INVALID); + G_TYPE_NONE, G_TYPE_VALUE, G_TYPE_INVALID); - /* Config signal */ - dbus_g_proxy_add_signal (priv->proxy, "Config", - DBUS_TYPE_G_MAP_OF_VARIANT, - G_TYPE_INVALID); + dbus_g_proxy_add_signal (priv->proxy, "Config", DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_INVALID); dbus_g_proxy_connect_signal (priv->proxy, "Config", - G_CALLBACK (nm_vpn_connection_config_get), - connection, NULL); + G_CALLBACK (nm_vpn_connection_config_get), + connection, NULL); /* Ip4Config signal */ - dbus_g_proxy_add_signal (priv->proxy, "Ip4Config", - DBUS_TYPE_G_MAP_OF_VARIANT, - G_TYPE_INVALID); + dbus_g_proxy_add_signal (priv->proxy, "Ip4Config", DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_INVALID); dbus_g_proxy_connect_signal (priv->proxy, "Ip4Config", - G_CALLBACK (nm_vpn_connection_ip4_config_get), - connection, NULL); + G_CALLBACK (nm_vpn_connection_ip4_config_get), + connection, NULL); /* Ip6Config signal */ - dbus_g_proxy_add_signal (priv->proxy, "Ip6Config", - DBUS_TYPE_G_MAP_OF_VARIANT, - G_TYPE_INVALID); + dbus_g_proxy_add_signal (priv->proxy, "Ip6Config", DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_INVALID); dbus_g_proxy_connect_signal (priv->proxy, "Ip6Config", - G_CALLBACK (nm_vpn_connection_ip6_config_get), - connection, NULL); + G_CALLBACK (nm_vpn_connection_ip6_config_get), + connection, NULL); - hash = _hash_with_username (priv->connection, username); - dbus_g_proxy_begin_call (priv->proxy, "Connect", - nm_vpn_connection_connect_cb, connection, NULL, - DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, hash, - G_TYPE_INVALID); - g_hash_table_destroy (hash); + if (priv->connect_hash) + g_hash_table_destroy (priv->connect_hash); + priv->connect_hash = _hash_with_username (priv->connection, username); + details = g_hash_table_new (g_str_hash, g_str_equal); + + /* If at least one agent doesn't support VPN hints, then we can't use + * ConnectInteractive(), because that agent won't be able to pass hints + * from the VPN plugin's interactive secrets requests to the VPN authentication + * dialog and we won't get the secrets we need. In this case fall back to + * the old Connect() call. + */ + agent_mgr = nm_agent_manager_get (); + if (nm_agent_manager_all_agents_have_capability (agent_mgr, + nm_active_connection_get_user_requested (NM_ACTIVE_CONNECTION (connection)), + nm_active_connection_get_user_uid (NM_ACTIVE_CONNECTION (connection)), + NM_SECRET_AGENT_CAPABILITY_VPN_HINTS)) { + nm_log_dbg (LOGD_VPN, "Allowing interactive secrets as all agents have that capability"); + dbus_g_proxy_begin_call (priv->proxy, "ConnectInteractive", + connect_interactive_cb, connection, NULL, + DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, priv->connect_hash, + DBUS_TYPE_G_MAP_OF_VARIANT, details, + G_TYPE_INVALID); + } else { + nm_log_dbg (LOGD_VPN, "Calling old Connect function as not all agents support interactive secrets"); + dbus_g_proxy_begin_call (priv->proxy, "Connect", + connect_cb, connection, NULL, + DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, priv->connect_hash, + G_TYPE_INVALID); + } + g_object_unref (agent_mgr); + g_hash_table_destroy (details); nm_vpn_connection_set_vpn_state (connection, NM_VPN_CONNECTION_STATE_CONNECT, @@ -1294,8 +1372,8 @@ nm_vpn_connection_activate (NMVPNConnection *connection) dbus_g_proxy_add_signal (priv->proxy, "Failure", G_TYPE_UINT, G_TYPE_INVALID); dbus_g_proxy_connect_signal (priv->proxy, "Failure", - G_CALLBACK (plugin_failed), - connection, NULL); + G_CALLBACK (plugin_failed), + connection, NULL); /* StateChanged signal */ dbus_g_proxy_add_signal (priv->proxy, "StateChanged", G_TYPE_UINT, G_TYPE_INVALID); @@ -1303,9 +1381,23 @@ nm_vpn_connection_activate (NMVPNConnection *connection) G_CALLBACK (plugin_state_changed), connection, NULL); + dbus_g_object_register_marshaller (g_cclosure_marshal_generic, + G_TYPE_NONE, G_TYPE_STRING, G_TYPE_STRV, G_TYPE_INVALID); + dbus_g_proxy_add_signal (priv->proxy, "SecretsRequired", G_TYPE_STRING, G_TYPE_STRV, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->proxy, "SecretsRequired", + G_CALLBACK (plugin_interactive_secrets_required), + connection, NULL); + nm_vpn_connection_set_vpn_state (connection, NM_VPN_CONNECTION_STATE_NEED_AUTH, NM_VPN_CONNECTION_STATE_REASON_NONE); + + /* Kick off the secrets requests; first we get existing system secrets + * and ask the plugin if these are sufficient, next we get all existing + * secrets from system and from user agents and ask the plugin again, + * and last we ask the user for new secrets if required. + */ + get_secrets (connection, SECRETS_REQ_SYSTEM, NULL); } NMConnection * @@ -1448,7 +1540,7 @@ plugin_need_secrets_cb (DBusGProxy *proxy, DBusGProxyCall *call, void *user_dat nm_connection_get_uuid (priv->connection), nm_connection_get_id (priv->connection)); - get_secrets (self, priv->secrets_idx + 1); + get_secrets (self, priv->secrets_idx + 1, NULL); } return; } @@ -1461,6 +1553,24 @@ plugin_need_secrets_cb (DBusGProxy *proxy, DBusGProxyCall *call, void *user_dat really_activate (self, priv->username); } +static void +plugin_new_secrets_cb (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data) +{ + NMVPNConnection *self = NM_VPN_CONNECTION (user_data); + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); + GError *error = NULL; + + if (!dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID)) { + nm_log_err (LOGD_VPN, "(%s/%s) sending new secrets to the plugin failed: %s %s", + nm_connection_get_uuid (priv->connection), + nm_connection_get_id (priv->connection), + g_quark_to_string (error->domain), + error->message); + nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS); + g_error_free (error); + } +} + static void get_secrets_cb (NMSettingsConnection *connection, guint32 call_id, @@ -1483,28 +1593,44 @@ get_secrets_cb (NMSettingsConnection *connection, priv->secrets_idx + 1, error->code, error->message); nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS); } else { - nm_log_dbg (LOGD_VPN, "(%s/%s) asking service if additional secrets are required", - nm_connection_get_uuid (priv->connection), - nm_connection_get_id (priv->connection)); - /* Cache the username for later */ if (agent_username) { g_free (priv->username); priv->username = g_strdup (agent_username); } - /* Ask the VPN service if more secrets are required */ hash = _hash_with_username (priv->connection, priv->username); - dbus_g_proxy_begin_call (priv->proxy, "NeedSecrets", - plugin_need_secrets_cb, self, NULL, - DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, hash, - G_TYPE_INVALID); + + if (priv->secrets_idx == SECRETS_REQ_INTERACTIVE) { + nm_log_dbg (LOGD_VPN, "(%s/%s) sending secrets to the plugin", + nm_connection_get_uuid (priv->connection), + nm_connection_get_id (priv->connection)); + + /* Send the secrets back to the plugin */ + dbus_g_proxy_begin_call (priv->proxy, "NewSecrets", + plugin_new_secrets_cb, self, NULL, + DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, hash, + G_TYPE_INVALID); + } else { + nm_log_dbg (LOGD_VPN, "(%s/%s) asking service if additional secrets are required", + nm_connection_get_uuid (priv->connection), + nm_connection_get_id (priv->connection)); + + /* Ask the VPN service if more secrets are required */ + dbus_g_proxy_begin_call (priv->proxy, "NeedSecrets", + plugin_need_secrets_cb, self, NULL, + DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, hash, + G_TYPE_INVALID); + } + g_hash_table_destroy (hash); } } static void -get_secrets (NMVPNConnection *self, SecretsReq secrets_idx) +get_secrets (NMVPNConnection *self, + SecretsReq secrets_idx, + const char **hints) { NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); NMSettingsGetSecretsFlags flags = NM_SETTINGS_GET_SECRETS_FLAG_NONE; @@ -1530,6 +1656,7 @@ get_secrets (NMVPNConnection *self, SecretsReq secrets_idx) flags = NM_SETTINGS_GET_SECRETS_FLAG_NONE; break; case SECRETS_REQ_NEW: + case SECRETS_REQ_INTERACTIVE: flags = NM_SETTINGS_GET_SECRETS_FLAG_ALLOW_INTERACTION; break; default: @@ -1544,7 +1671,7 @@ get_secrets (NMVPNConnection *self, SecretsReq secrets_idx) nm_active_connection_get_user_uid (NM_ACTIVE_CONNECTION (self)), NM_SETTING_VPN_SETTING_NAME, flags, - NULL, + hints, get_secrets_cb, self, &error); @@ -1558,12 +1685,49 @@ get_secrets (NMVPNConnection *self, SecretsReq secrets_idx) } } +static void +plugin_interactive_secrets_required (DBusGProxy *proxy, + const char *message, + const char **secrets, + gpointer user_data) +{ + NMVPNConnection *connection = NM_VPN_CONNECTION (user_data); + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + guint32 secrets_len = secrets ? g_strv_length ((char **) secrets) : 0; + char **hints; + guint32 i; + + nm_log_info (LOGD_VPN, "VPN plugin requested secrets; state %s (%d)", + vpn_state_to_string (priv->vpn_state), priv->vpn_state); + + g_return_if_fail (priv->vpn_state == NM_VPN_CONNECTION_STATE_CONNECT || + priv->vpn_state == NM_VPN_CONNECTION_STATE_NEED_AUTH); + + priv->secrets_idx = SECRETS_REQ_INTERACTIVE; + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_NEED_AUTH, + NM_VPN_CONNECTION_STATE_REASON_NONE); + + /* Copy hints and add message to the end */ + hints = g_malloc0 (sizeof (char *) * (secrets_len + 2)); + for (i = 0; i < secrets_len; i++) + hints[i] = g_strdup (secrets[i]); + if (message) + hints[i] = g_strdup_printf ("x-vpn-message:%s", message); + + get_secrets (connection, SECRETS_REQ_INTERACTIVE, (const char **) hints); + g_strfreev (hints); +} + /******************************************************************************/ static void nm_vpn_connection_init (NMVPNConnection *self) { - NM_VPN_CONNECTION_GET_PRIVATE (self)->vpn_state = NM_VPN_CONNECTION_STATE_PREPARE; + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); + + priv->vpn_state = NM_VPN_CONNECTION_STATE_PREPARE; + priv->secrets_idx = SECRETS_REQ_SYSTEM; } static void @@ -1609,6 +1773,9 @@ dispose (GObject *object) g_clear_pointer (&priv->ip4_gw_route, g_free); g_clear_pointer (&priv->ip6_gw_route, g_free); + if (priv->connect_hash) + g_hash_table_destroy (priv->connect_hash); + if (priv->ip6_internal_gw) g_free (priv->ip6_internal_gw); if (priv->ip6_external_gw) @@ -1629,8 +1796,8 @@ dispose (GObject *object) if (priv->ip6_config) g_object_unref (priv->ip6_config); - if (priv->ipconfig_timeout) - g_source_remove (priv->ipconfig_timeout); + if (priv->connect_timeout) + g_source_remove (priv->connect_timeout); if (priv->proxy) g_object_unref (priv->proxy);