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);