summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2013-06-18 09:32:53 -0500
committerDan Williams <dcbw@redhat.com>2013-07-31 08:16:40 -0500
commitcc924d8bab1ffd8770116fb822e5d864050d08f2 (patch)
tree0ebdd14b8ab6bde5ea224469019672c60e579f16
parenteacd4cf8f94b2ff883aa75847e1c118e81d01a86 (diff)
downloadNetworkManager-cc924d8bab1ffd8770116fb822e5d864050d08f2.tar.gz
libnm-glib-vpn: add support for interactive secrets requests
There are three additions to the D-Bus interface for VPN plugins as part of this patch: 1) ConnectInteractive(): called by NM instead of Connect() to let the plugin know that it can request additional secrets during the connection process using SecretsRequired 2) SecretsRequired: a new signal emitted by the plugin to indicate to NetworkManager that additional secrets are required to connect; can only be called if NetworkManager initiated the connection by calling the ConnectInteractive() method 3) NewSecrets(): a new method of the plugin that NetworkManager calls when new secrets requested by the SecretsRequired signal have been retrieved from secret agents We need new methods because agents need to be aware of the hints that the VPN plugins may send with the SecretsRequired signal (detailing the specific secrets that are required) and at this time, not all agents support passing those hints to the VPN plugin authentication dialogs.
-rw-r--r--introspection/nm-vpn-plugin.xml74
-rw-r--r--libnm-glib/libnm-glib-vpn.ver1
-rw-r--r--libnm-glib/nm-vpn-plugin.c182
-rw-r--r--libnm-glib/nm-vpn-plugin.h16
4 files changed, 257 insertions, 16 deletions
diff --git a/introspection/nm-vpn-plugin.xml b/introspection/nm-vpn-plugin.xml
index 5fb11622a5..79081f7b2e 100644
--- a/introspection/nm-vpn-plugin.xml
+++ b/introspection/nm-vpn-plugin.xml
@@ -5,9 +5,11 @@
<tp:docstring>
This interface is provided by plugins providing VPN services to the NetworkManager daemon.
</tp:docstring>
+
<method name="Connect">
<tp:docstring>
- Tells the plugin to connect.
+ Tells the plugin to connect. Interactive secrets requests (eg, emitting
+ the SecretsRequired signal) are not allowed.
</tp:docstring>
<annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_vpn_plugin_connect"/>
<arg name="connection" type="a{sa{sv}}" direction="in" tp:type="String_String_Variant_Map_Map">
@@ -21,6 +23,35 @@
<tp:error name="org.freedesktop.NetworkManager.VPN.Error.StoppingInProgress"/>
<tp:error name="org.freedesktop.NetworkManager.VPN.Error.BadArguments"/>
<tp:error name="org.freedesktop.NetworkManager.VPN.Error.LaunchFailed"/>
+ <tp:error name="org.freedesktop.NetworkManager.VPN.Error.WrongState"/>
+ </tp:possible-errors>
+ </method>
+
+ <method name="ConnectInteractive">
+ <tp:docstring>
+ 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).
+ </tp:docstring>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_vpn_plugin_connect_interactive"/>
+ <arg name="connection" type="a{sa{sv}}" direction="in" tp:type="String_String_Variant_Map_Map">
+ <tp:docstring>
+ Describes the connection to be established.
+ </tp:docstring>
+ </arg>
+ <arg name="details" type="a{sv}" direction="in" tp:type="String_Variant_Map">
+ <tp:docstring>
+ Additional details about the Connect process.
+ </tp:docstring>
+ </arg>
+ <tp:possible-errors>
+ <tp:error name="org.freedesktop.NetworkManager.VPN.Error.StartingInProgress"/>
+ <tp:error name="org.freedesktop.NetworkManager.VPN.Error.AlreadyStarted"/>
+ <tp:error name="org.freedesktop.NetworkManager.VPN.Error.StoppingInProgress"/>
+ <tp:error name="org.freedesktop.NetworkManager.VPN.Error.BadArguments"/>
+ <tp:error name="org.freedesktop.NetworkManager.VPN.Error.LaunchFailed"/>
+ <tp:error name="org.freedesktop.NetworkManager.VPN.Error.WrongState"/>
</tp:possible-errors>
</method>
@@ -122,6 +153,47 @@
</arg>
</signal>
+ <signal name="SecretsRequired">
+ <tp:docstring>
+ 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.
+ </tp:docstring>
+ <arg name="message" type="s" direction="out">
+ <tp:docstring>
+ 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.
+ </tp:docstring>
+ </arg>
+ <arg name="secrets" type="as" direction="out">
+ <tp:docstring>
+ 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.
+ </tp:docstring>
+ </arg>
+ </signal>
+
+ <method name="NewSecrets">
+ <tp:docstring>
+ Called in response to a SecretsRequired signal to deliver updated secrets
+ or other information to the plugin.
+ </tp:docstring>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_vpn_plugin_new_secrets"/>
+ <arg name="connection" type="a{sa{sv}}" direction="in" tp:type="String_String_Variant_Map_Map">
+ <tp:docstring>
+ Describes the connection including the new secrets.
+ </tp:docstring>
+ </arg>
+ <tp:possible-errors>
+ <tp:error name="org.freedesktop.NetworkManager.VPN.Error.WrongState"/>
+ <tp:error name="org.freedesktop.NetworkManager.VPN.Error.BadArguments"/>
+ <tp:error name="org.freedesktop.NetworkManager.VPN.Error.LaunchFailed"/>
+ </tp:possible-errors>
+ </method>
+
<signal name="Config">
<tp:docstring>
The plugin obtained generic configuration information.
diff --git a/libnm-glib/libnm-glib-vpn.ver b/libnm-glib/libnm-glib-vpn.ver
index 9d2bad5e2d..b2e763b2a6 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 c489fe01c9..1894c966c8 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 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
-impl_vpn_plugin_connect (NMVPNPlugin *plugin,
- GHashTable *properties,
- GError **error)
+_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,21 +459,27 @@ 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);
@@ -446,6 +487,25 @@ impl_vpn_plugin_connect (NMVPNPlugin *plugin,
}
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,
char **setting_name,
@@ -500,6 +560,94 @@ out:
}
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 dee726f7a4..dc237d5245 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);