diff --git a/introspection/nm-vpn-plugin.xml b/introspection/nm-vpn-plugin.xml index 5fb11622a..79081f7b2 100644 --- a/introspection/nm-vpn-plugin.xml +++ b/introspection/nm-vpn-plugin.xml @@ -5,9 +5,11 @@ This interface is provided by plugins providing VPN services to the NetworkManager daemon. + - Tells the plugin to connect. + Tells the plugin to connect. Interactive secrets requests (eg, emitting + the SecretsRequired signal) are not allowed. @@ -21,6 +23,35 @@ + + + + + + + Tells the plugin to connect, allowing interactive secrets requests (eg + the plugin is allowed to emit the SecretsRequired signal if the VPN + service indicates that it needs additional secrets during the connect + process). + + + + + Describes the connection to be established. + + + + + Additional details about the Connect process. + + + + + + + + + @@ -122,6 +153,47 @@ + + + Emitted during an ongoing ConnectInteractive() request when the plugin + has determined that new secrets are required. NetworkManager will then + call the NewSecrets() method with a connection hash including the new + secrets. + + + + Informational message, if any, about the request. For example, if + a second PIN is required, could indicate to the user to wait for + the token code to change until entering the next PIN. + + + + + Array of strings of VPN secret names which the plugin thinks + secrets may be required for, or other VPN-specific data to be + processed by the VPN's front-end. + + + + + + + Called in response to a SecretsRequired signal to deliver updated secrets + or other information to the plugin. + + + + + Describes the connection including the new secrets. + + + + + + + + + The plugin obtained generic configuration information. diff --git a/libnm-glib/libnm-glib-vpn.ver b/libnm-glib/libnm-glib-vpn.ver index 9d2bad5e2..b2e763b2a 100644 --- a/libnm-glib/libnm-glib-vpn.ver +++ b/libnm-glib/libnm-glib-vpn.ver @@ -7,6 +7,7 @@ global: nm_vpn_plugin_get_connection; nm_vpn_plugin_get_state; nm_vpn_plugin_get_type; + nm_vpn_plugin_secrets_required; nm_vpn_plugin_set_ip4_config; nm_vpn_plugin_set_login_banner; nm_vpn_plugin_set_state; diff --git a/libnm-glib/nm-vpn-plugin.c b/libnm-glib/nm-vpn-plugin.c index c489fe01c..1894c966c 100644 --- a/libnm-glib/nm-vpn-plugin.c +++ b/libnm-glib/nm-vpn-plugin.c @@ -31,13 +31,22 @@ static gboolean impl_vpn_plugin_connect (NMVPNPlugin *plugin, GHashTable *connection, - GError **err); + GError **error); + +static gboolean impl_vpn_plugin_connect_interactive (NMVPNPlugin *plugin, + GHashTable *connection, + GHashTable *details, + GError **error); static gboolean impl_vpn_plugin_need_secrets (NMVPNPlugin *plugin, GHashTable *connection, char **service_name, GError **err); +static gboolean impl_vpn_plugin_new_secrets (NMVPNPlugin *plugin, + GHashTable *connection, + GError **err); + static gboolean impl_vpn_plugin_disconnect (NMVPNPlugin *plugin, GError **err); @@ -74,6 +83,7 @@ typedef struct { guint connect_timer; guint quit_timer; guint fail_stop_id; + gboolean interactive; gboolean got_config; gboolean has_ip4, got_ip4; @@ -93,6 +103,7 @@ enum { LOGIN_BANNER, FAILURE, QUIT, + SECRETS_REQUIRED, LAST_SIGNAL }; @@ -275,6 +286,16 @@ fail_stop (gpointer data) return FALSE; } +static void +schedule_fail_stop (NMVPNPlugin *plugin) +{ + NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin); + + if (priv->fail_stop_id) + g_source_remove (priv->fail_stop_id); + priv->fail_stop_id = g_idle_add (fail_stop, plugin); +} + void nm_vpn_plugin_set_config (NMVPNPlugin *plugin, GHashTable *config) @@ -395,12 +416,26 @@ connect_timer_removed (gpointer data) NM_VPN_PLUGIN_GET_PRIVATE (data)->connect_timer = 0; } -static gboolean -impl_vpn_plugin_connect (NMVPNPlugin *plugin, - GHashTable *properties, - GError **error) +static void +connect_timer_start (NMVPNPlugin *plugin) { NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin); + + priv->connect_timer = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT, + 60, + connect_timer_expired, + plugin, + connect_timer_removed); +} + +static gboolean +_connect_generic (NMVPNPlugin *plugin, + GHashTable *properties, + GHashTable *details, + GError **error) +{ + NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin); + NMVPNPluginClass *vpn_class = NM_VPN_PLUGIN_GET_CLASS (plugin); NMConnection *connection; gboolean success = FALSE; GError *local = NULL; @@ -424,27 +459,52 @@ impl_vpn_plugin_connect (NMVPNPlugin *plugin, nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTING); - success = NM_VPN_PLUGIN_GET_CLASS (plugin)->connect (plugin, connection, error); + priv->interactive = FALSE; + if (details && !vpn_class->connect_interactive) { + g_set_error_literal (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_GENERAL, + "Plugin does not implement ConnectInteractive()"); + return FALSE; + } + + if (details) { + priv->interactive = TRUE; + success = vpn_class->connect_interactive (plugin, connection, details, error); + } else + success = vpn_class->connect (plugin, connection, error); + if (success) { /* Add a timer to make sure we do not wait indefinitely for the successful connect. */ - priv->connect_timer = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT, - 60, - connect_timer_expired, - plugin, - connect_timer_removed); + connect_timer_start (plugin); } else { /* Stop the plugin from an idle handler so that the Connect * method return gets sent before the STOP StateChanged signal. */ - if (priv->fail_stop_id) - g_source_remove (priv->fail_stop_id); - priv->fail_stop_id = g_idle_add (fail_stop, plugin); + schedule_fail_stop (plugin); } g_object_unref (connection); return success; } +static gboolean +impl_vpn_plugin_connect (NMVPNPlugin *plugin, + GHashTable *connection, + GError **error) +{ + return _connect_generic (plugin, connection, NULL, error); +} + +static gboolean +impl_vpn_plugin_connect_interactive (NMVPNPlugin *plugin, + GHashTable *connection, + GHashTable *details, + GError **error) +{ + return _connect_generic (plugin, connection, details, error); +} + +/***************************************************************/ + static gboolean impl_vpn_plugin_need_secrets (NMVPNPlugin *plugin, GHashTable *properties, @@ -499,6 +559,94 @@ out: return ret; } +static gboolean +impl_vpn_plugin_new_secrets (NMVPNPlugin *plugin, + GHashTable *properties, + GError **error) +{ + NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin); + NMConnection *connection; + GError *local = NULL; + gboolean success; + + if (priv->state != NM_VPN_SERVICE_STATE_STARTING) { + g_set_error (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_WRONG_STATE, + "Could not accept new secrets: wrong plugin state %d", + priv->state); + return FALSE; + } + + connection = nm_connection_new_from_hash (properties, &local); + if (!connection) { + g_set_error (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, + "Invalid connection: (%d) %s", + local->code, local->message); + g_clear_error (&local); + return FALSE; + } + + if (!NM_VPN_PLUGIN_GET_CLASS (plugin)->new_secrets) { + g_set_error_literal (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_GENERAL, + "Could not accept new secrets: plugin cannot process interactive secrets"); + g_object_unref (connection); + return FALSE; + } + + success = NM_VPN_PLUGIN_GET_CLASS (plugin)->new_secrets (plugin, connection, error); + if (success) { + /* Add a timer to make sure we do not wait indefinitely for the successful connect. */ + connect_timer_start (plugin); + } else { + /* Stop the plugin from and idle handler so that the NewSecrets + * method return gets sent before the STOP StateChanged signal. + */ + schedule_fail_stop (plugin); + } + + g_object_unref (connection); + return success; +} + +/** + * nm_vpn_plugin_secrets_required: + * @plugin: the #NMVPNPlugin + * @message: an information message about why secrets are required, if any + * @hints: VPN specific secret names for required new secrets + * + * Called by VPN plugin implementations to signal to NetworkManager that secrets + * are required during the connection process. This signal may be used to + * request new secrets when the secrets originally provided by NetworkManager + * are insufficient, or the VPN process indicates that it needs additional + * information to complete the request. + * + * Since: 0.9.10 + */ +void +nm_vpn_plugin_secrets_required (NMVPNPlugin *plugin, + const char *message, + const char **hints) +{ + NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin); + + /* Plugin must be able to accept the new secrets if it calls this method */ + g_return_if_fail (NM_VPN_PLUGIN_GET_CLASS (plugin)->new_secrets); + + /* Plugin cannot call this method if NetworkManager didn't originally call + * ConnectInteractive(). + */ + g_return_if_fail (priv->interactive == TRUE); + + /* Cancel the connect timer since secrets might take a while. It'll + * get restarted when the secrets come back via NewSecrets(). + */ + if (priv->connect_timer) + g_source_remove (priv->connect_timer); + + g_signal_emit (plugin, signals[SECRETS_REQUIRED], 0, message, hints); +} + +/***************************************************************/ + static gboolean impl_vpn_plugin_disconnect (NMVPNPlugin *plugin, GError **err) @@ -835,6 +983,14 @@ nm_vpn_plugin_class_init (NMVPNPluginClass *plugin_class) G_TYPE_NONE, 1, G_TYPE_UINT); + signals[SECRETS_REQUIRED] = + g_signal_new ("secrets-required", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + NULL, + G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRV); + signals[CONFIG] = g_signal_new ("config", G_OBJECT_CLASS_TYPE (object_class), diff --git a/libnm-glib/nm-vpn-plugin.h b/libnm-glib/nm-vpn-plugin.h index dee726f7a..dc237d524 100644 --- a/libnm-glib/nm-vpn-plugin.h +++ b/libnm-glib/nm-vpn-plugin.h @@ -121,11 +121,19 @@ typedef struct { void (*ip6_config) (NMVPNPlugin *plugin, GHashTable *config); + /* more methods */ + gboolean (*new_secrets) (NMVPNPlugin *plugin, + NMConnection *connection, + GError **error); + + gboolean (*connect_interactive) (NMVPNPlugin *plugin, + NMConnection *connection, + GHashTable *details, + GError **error); + /* Padding for future expansion */ void (*_reserved1) (void); void (*_reserved2) (void); - void (*_reserved3) (void); - void (*_reserved4) (void); } NMVPNPluginClass; GType nm_vpn_plugin_get_type (void); @@ -137,6 +145,10 @@ NMVPNServiceState nm_vpn_plugin_get_state (NMVPNPlugin *plugin); void nm_vpn_plugin_set_state (NMVPNPlugin *plugin, NMVPNServiceState state); +void nm_vpn_plugin_secrets_required (NMVPNPlugin *plugin, + const char *message, + const char **hints); + void nm_vpn_plugin_set_login_banner (NMVPNPlugin *plugin, const char *banner);