summaryrefslogtreecommitdiff
path: root/libnm-glib/nm-vpn-plugin.c
diff options
context:
space:
mode:
Diffstat (limited to 'libnm-glib/nm-vpn-plugin.c')
-rw-r--r--libnm-glib/nm-vpn-plugin.c311
1 files changed, 214 insertions, 97 deletions
diff --git a/libnm-glib/nm-vpn-plugin.c b/libnm-glib/nm-vpn-plugin.c
index dd4d7b3a6..d52416969 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);
@@ -59,7 +68,6 @@ static gboolean impl_vpn_plugin_set_failure (NMVPNPlugin *plugin,
#include "nm-vpn-plugin-glue.h"
-#define NM_VPN_PLUGIN_CONNECT_TIMER 60
#define NM_VPN_PLUGIN_QUIT_TIMER 20
G_DEFINE_ABSTRACT_TYPE (NMVPNPlugin, nm_vpn_plugin, G_TYPE_OBJECT)
@@ -75,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;
@@ -94,6 +103,7 @@ enum {
LOGIN_BANNER,
FAILURE,
QUIT,
+ SECRETS_REQUIRED,
LAST_SIGNAL
};
@@ -276,60 +286,28 @@ fail_stop (gpointer data)
return FALSE;
}
-static gboolean
-nm_vpn_plugin_connect (NMVPNPlugin *plugin,
- NMConnection *connection,
- GError **err)
+static void
+schedule_fail_stop (NMVPNPlugin *plugin)
{
NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
- gboolean ret = FALSE;
- NMVPNServiceState state;
-
- g_return_val_if_fail (NM_IS_VPN_PLUGIN (plugin), FALSE);
-
- state = nm_vpn_plugin_get_state (plugin);
- switch (state) {
- case NM_VPN_SERVICE_STATE_STARTING:
- g_set_error (err,
- NM_VPN_PLUGIN_ERROR,
- NM_VPN_PLUGIN_ERROR_STARTING_IN_PROGRESS,
- "%s",
- "Could not process the request because the VPN connection is already being started.");
- break;
- case NM_VPN_SERVICE_STATE_STARTED:
- g_set_error (err,
- NM_VPN_PLUGIN_ERROR,
- NM_VPN_PLUGIN_ERROR_ALREADY_STARTED,
- "%s",
- "Could not process the request because a VPN connection was already active.");
- break;
- case NM_VPN_SERVICE_STATE_STOPPING:
- g_set_error (err,
- NM_VPN_PLUGIN_ERROR,
- NM_VPN_PLUGIN_ERROR_STOPPING_IN_PROGRESS,
- "%s",
- "Could not process the request because the VPN connection is being stopped.");
- break;
- case NM_VPN_SERVICE_STATE_STOPPED:
- case NM_VPN_SERVICE_STATE_INIT:
- nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTING);
- ret = NM_VPN_PLUGIN_GET_CLASS (plugin)->connect (plugin, connection, err);
- if (!ret) {
- /* Stop the plugin from and 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);
- }
- break;
- default:
- g_assert_not_reached ();
- break;
- }
+ if (priv->fail_stop_id)
+ g_source_remove (priv->fail_stop_id);
+ priv->fail_stop_id = g_idle_add (fail_stop, plugin);
+}
- return ret;
+static void
+_g_value_set (GValue *dst, GValue *src)
+{
+ if (src) {
+ GType type = G_VALUE_TYPE (src);
+
+ if (G_IS_VALUE (dst))
+ g_value_unset (dst);
+ g_value_init (dst, type);
+ g_value_copy (src, dst);
+ } else if (G_IS_VALUE (dst))
+ g_value_unset (dst);
}
void
@@ -356,26 +334,10 @@ nm_vpn_plugin_set_config (NMVPNPlugin *plugin,
/* Record the items that need to also be inserted into the
* ip4config, for compatibility with older daemons.
*/
- val = g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_BANNER);
- if (val) {
- g_value_init (&priv->banner, G_VALUE_TYPE (val));
- g_value_copy (val, &priv->banner);
- }
- val = g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_TUNDEV);
- if (val) {
- g_value_init (&priv->tundev, G_VALUE_TYPE (val));
- g_value_copy (val, &priv->tundev);
- }
- val = g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY);
- if (val) {
- g_value_init (&priv->gateway, G_VALUE_TYPE (val));
- g_value_copy (val, &priv->gateway);
- }
- val = g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_MTU);
- if (val) {
- g_value_init (&priv->mtu, G_VALUE_TYPE (val));
- g_value_copy (val, &priv->mtu);
- }
+ _g_value_set (&priv->banner, g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_BANNER));
+ _g_value_set (&priv->tundev, g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_TUNDEV));
+ _g_value_set (&priv->gateway, g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY));
+ _g_value_set (&priv->mtu, g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_MTU));
g_signal_emit (plugin, signals[CONFIG], 0, config);
}
@@ -446,31 +408,103 @@ nm_vpn_plugin_set_ip6_config (NMVPNPlugin *plugin,
nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTED);
}
+static void
+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;
+
+ if (priv->state != NM_VPN_SERVICE_STATE_STOPPED &&
+ priv->state != NM_VPN_SERVICE_STATE_INIT) {
+ g_set_error (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_WRONG_STATE,
+ "Could not start connection: wrong plugin state %d",
+ priv->state);
+ return FALSE;
+ }
- connection = nm_connection_new_from_hash (properties, error);
+ connection = nm_connection_new_from_hash (properties, &local);
if (!connection) {
- g_warning ("%s: Invalid connection: '%s' / '%s' invalid: %d",
- __func__,
- g_type_name (nm_connection_lookup_setting_type_by_quark ((*error)->domain)),
- (*error)->message,
- (*error)->code);
+ 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;
+ }
+
+
+ priv->interactive = FALSE;
+ if (details && !vpn_class->connect_interactive) {
+ g_set_error_literal (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED,
+ "Plugin does not implement ConnectInteractive()");
+ return FALSE;
+ }
+
+ nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTING);
+
+ 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. */
+ connect_timer_start (plugin);
} else {
- success = nm_vpn_plugin_connect (plugin, connection, error);
- g_object_unref (connection);
+ /* Stop the plugin from an idle handler so that the Connect
+ * method return gets sent before the STOP StateChanged signal.
+ */
+ 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,
char **setting_name,
@@ -525,6 +559,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_INTERACTIVE_NOT_SUPPORTED,
+ "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)
{
@@ -767,12 +889,6 @@ finalize (GObject *object)
}
static void
-connect_timer_removed (gpointer data)
-{
- NM_VPN_PLUGIN_GET_PRIVATE (data)->connect_timer = 0;
-}
-
-static void
quit_timer_removed (gpointer data)
{
NM_VPN_PLUGIN_GET_PRIVATE (data)->quit_timer = 0;
@@ -793,13 +909,6 @@ state_changed (NMVPNPlugin *plugin, NMVPNServiceState state)
g_source_remove (priv->fail_stop_id);
priv->fail_stop_id = 0;
}
-
- /* 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,
- NM_VPN_PLUGIN_CONNECT_TIMER,
- connect_timer_expired,
- plugin,
- connect_timer_removed);
break;
case NM_VPN_SERVICE_STATE_STOPPED:
priv->quit_timer = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT,
@@ -873,6 +982,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),
@@ -934,7 +1051,7 @@ nm_vpn_plugin_class_init (NMVPNPluginClass *plugin_class)
G_TYPE_NONE);
dbus_g_error_domain_register (NM_VPN_PLUGIN_ERROR,
- NULL,
+ NM_DBUS_VPN_ERROR_PREFIX,
NM_TYPE_VPN_PLUGIN_ERROR);
setup_unix_signal_handler ();