From 68812f61d9e7a6b62ff1d61534cc3bdfbaa9a710 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 26 Jan 2011 17:13:15 -0600 Subject: [PATCH] settings: request secrets from agents when clients call GetSecrets A client calling GetSecrets on the connection should also request secrets from agents in that client's session. ie, a connection editor should be able to call GetSecrets, and get the secrets stored by the agent in that session (the applet). --- marshallers/nm-marshal.list | 1 + src/nm-manager.c | 67 +++++++- src/settings/nm-settings-connection.c | 214 +++++++++++++------------- src/settings/nm-settings-connection.h | 18 +++ 4 files changed, 191 insertions(+), 109 deletions(-) diff --git a/marshallers/nm-marshal.list b/marshallers/nm-marshal.list index 359fbed2d5..3aefac93b6 100644 --- a/marshallers/nm-marshal.list +++ b/marshallers/nm-marshal.list @@ -26,4 +26,5 @@ BOOLEAN:VOID VOID:STRING,BOOLEAN VOID:STRING,OBJECT,POINTER VOID:BOOLEAN,UINT +UINT:STRING,POINTER,POINTER diff --git a/src/nm-manager.c b/src/nm-manager.c index 2f8cbdf4a4..f28a405ee4 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -924,6 +924,71 @@ connections_changed (NMSettings *settings, bluez_manager_resync_devices (manager); } +static void +secrets_result_cb (NMAgentManager *manager, + guint32 call_id, + NMConnection *connection, + const char *setting_name, + GError *error, + gpointer user_data, + gpointer user_data2, + gpointer user_data3) +{ + NMSettingsConnectionSecretsUpdatedFunc callback = user_data2; + gpointer callback_data = user_data3; + + callback (NM_SETTINGS_CONNECTION (connection), setting_name, call_id, error, callback_data); +} + +static guint32 +system_connection_get_secrets_cb (NMSettingsConnection *connection, + const char *setting_name, + NMSettingsConnectionSecretsUpdatedFunc callback, + gpointer callback_data, + gpointer user_data) +{ + NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + gboolean call_id; + + call_id = nm_agent_manager_get_secrets (priv->agent_mgr, + NM_CONNECTION (connection), + setting_name, + NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE, + NULL, + secrets_result_cb, + self, + callback, + callback_data); + return call_id; +} + +static void +system_connection_cancel_secrets_cb (NMSettingsConnection *connection, + guint32 call_id, + gpointer user_data) +{ + NMManager *self = NM_MANAGER (user_data); + + nm_agent_manager_cancel_secrets (NM_MANAGER_GET_PRIVATE (self)->agent_mgr, call_id); +} + +static void +connection_added (NMSettings *settings, + NMSettingsConnection *connection, + NMManager *manager) +{ + /* Hook up secrets request listeners */ + g_signal_connect (connection, NM_SETTINGS_CONNECTION_GET_SECRETS, + G_CALLBACK (system_connection_get_secrets_cb), + manager); + g_signal_connect (connection, NM_SETTINGS_CONNECTION_CANCEL_SECRETS, + G_CALLBACK (system_connection_cancel_secrets_cb), + manager); + + connections_changed (settings, connection, manager); +} + static void system_unmanaged_devices_changed_cb (NMSettings *settings, GParamSpec *pspec, @@ -3098,7 +3163,7 @@ nm_manager_get (NMSettings *settings, g_signal_connect (priv->settings, "notify::" NM_SETTINGS_HOSTNAME, G_CALLBACK (system_hostname_changed_cb), singleton); g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_ADDED, - G_CALLBACK (connections_changed), singleton); + G_CALLBACK (connection_added), singleton); g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_UPDATED, G_CALLBACK (connections_changed), singleton); g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_REMOVED, diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c index 2743f841ea..7bf4b7daf4 100644 --- a/src/settings/nm-settings-connection.c +++ b/src/settings/nm-settings-connection.c @@ -34,6 +34,7 @@ #include "nm-polkit-helpers.h" #include "nm-logging.h" #include "nm-manager-auth.h" +#include "nm-marshal.h" static void impl_settings_connection_get_settings (NMSettingsConnection *connection, DBusGMethodInvocation *context); @@ -65,16 +66,22 @@ enum { enum { UPDATED, REMOVED, + GET_SECRETS, + CANCEL_SECRETS, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; typedef struct { + gboolean disposed; + PolkitAuthority *authority; GSList *pending_auths; /* List of pending authentication requests */ NMConnection *secrets; gboolean visible; /* Is this connection is visible by some session? */ + GSList *reqs; /* in-progress secrets requests */ + NMSessionMonitor *session_monitor; guint session_changed_id; } NMSettingsConnectionPrivate; @@ -333,80 +340,6 @@ supports_secrets (NMSettingsConnection *connection, const char *setting_name) return TRUE; } -static GValue * -string_to_gvalue (const char *str) -{ - GValue *val = g_slice_new0 (GValue); - - g_value_init (val, G_TYPE_STRING); - g_value_set_string (val, str); - return val; -} - -static GValue * -byte_array_to_gvalue (const GByteArray *array) -{ - GValue *val; - - val = g_slice_new0 (GValue); - g_value_init (val, DBUS_TYPE_G_UCHAR_ARRAY); - g_value_set_boxed (val, array); - - return val; -} - -static void -copy_one_secret (gpointer key, gpointer value, gpointer user_data) -{ - const char *value_str = (const char *) value; - - if (value_str) { - g_hash_table_insert ((GHashTable *) user_data, - g_strdup ((char *) key), - string_to_gvalue (value_str)); - } -} - -static void -add_secrets (NMSetting *setting, - const char *key, - const GValue *value, - GParamFlags flags, - gpointer user_data) -{ - GHashTable *secrets = user_data; - - if (!(flags & NM_SETTING_PARAM_SECRET)) - return; - - /* Copy secrets into the returned hash table */ - if (G_VALUE_HOLDS_STRING (value)) { - const char *tmp; - - tmp = g_value_get_string (value); - if (tmp) - g_hash_table_insert (secrets, g_strdup (key), string_to_gvalue (tmp)); - } else if (G_VALUE_HOLDS (value, DBUS_TYPE_G_MAP_OF_STRING)) { - /* Flatten the string hash by pulling its keys/values out; for VPN secrets */ - g_hash_table_foreach (g_value_get_boxed (value), copy_one_secret, secrets); - } else if (G_VALUE_TYPE (value) == DBUS_TYPE_G_UCHAR_ARRAY) { - GByteArray *array; - - array = g_value_get_boxed (value); - if (array) - g_hash_table_insert (secrets, g_strdup (key), byte_array_to_gvalue (array)); - } -} - -static void -destroy_gvalue (gpointer data) -{ - GValue *value = (GValue *) data; - - g_value_unset (value); - g_slice_free (GValue, value); -} - /** * nm_settings_connection_get_secrets: * @connection: the #NMSettingsConnection @@ -415,6 +348,9 @@ destroy_gvalue (gpointer data) * * Return secrets in persistent storage, if any. Does not query any Secret * Agents for secrets. + * + * Returns: a hash mapping setting names to hash tables, each inner hash + * containing string:value mappings of secrets **/ GHashTable * nm_settings_connection_get_secrets (NMSettingsConnection *connection, @@ -422,8 +358,6 @@ nm_settings_connection_get_secrets (NMSettingsConnection *connection, GError **error) { NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection); - GHashTable *settings = NULL; - GHashTable *secrets = NULL; NMSetting *setting; /* Use priv->secrets to work around the fact that nm_connection_clear_secrets() @@ -448,19 +382,7 @@ nm_settings_connection_get_secrets (NMSettingsConnection *connection, return NULL; } - /* Returned secrets are a{sa{sv}}; this is the outer a{s...} hash that - * will contain all the individual settings hashes. - */ - settings = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, (GDestroyNotify) g_hash_table_destroy); - - /* Add the secrets from this setting to the inner secrets hash for this setting */ - secrets = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, destroy_gvalue); - nm_setting_enumerate_values (setting, add_secrets, secrets); - - g_hash_table_insert (settings, g_strdup (setting_name), secrets); - - return secrets; + return nm_connection_to_hash (priv->secrets, NM_SETTING_HASH_FLAG_ONLY_SECRETS); } /**** User authorization **************************************/ @@ -736,31 +658,79 @@ impl_settings_connection_delete (NMSettingsConnection *self, auth_start (self, context, TRUE, delete_auth_cb, NULL); } -static void -secrets_auth_cb (NMSettingsConnection *self, - DBusGMethodInvocation *context, - GError *error, - gpointer user_data) +/**************************************************************/ + +static gboolean +get_secrets_accumulator (GSignalInvocationHint *ihint, + GValue *return_accu, + const GValue *handler_return, + gpointer data) { + guint handler_call_id = g_value_get_uint (handler_return); + + if (handler_call_id > 0) + g_value_set_uint (return_accu, handler_call_id); + + /* Abort signal emission if a valid call ID got returned */ + return handler_call_id ? FALSE : TRUE; +} + +static void +dbus_get_agent_secrets_cb (NMSettingsConnection *self, + const char *setting_name, + guint32 call_id, + GError *error, + gpointer user_data) +{ + NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + DBusGMethodInvocation *context = user_data; + GHashTable *hash; + + priv->reqs = g_slist_remove (priv->reqs, GUINT_TO_POINTER (call_id)); + + /* The connection's secrets will have been updated by the agent manager, + * so we want to refresh the secrets cache. + */ + if (priv->secrets) + g_object_unref (priv->secrets); + priv->secrets = nm_connection_duplicate (NM_CONNECTION (self)); + + if (error) + dbus_g_method_return_error (context, error); + else { + hash = nm_connection_to_hash (NM_CONNECTION (self), NM_SETTING_HASH_FLAG_ONLY_SECRETS); + dbus_g_method_return (context, hash); + g_hash_table_destroy (hash); + } +} + +static void +dbus_secrets_auth_cb (NMSettingsConnection *self, + DBusGMethodInvocation *context, + GError *error, + gpointer user_data) +{ + NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); char *setting_name = user_data; - GHashTable *secrets; + guint32 call_id = 0; GError *local = NULL; - if (error) { + if (error) dbus_g_method_return_error (context, error); - goto out; + else { + g_signal_emit (self, signals[GET_SECRETS], 0, setting_name, dbus_get_agent_secrets_cb, context, &call_id); + if (call_id > 0) { + /* track the request and wait for the callback */ + priv->reqs = g_slist_append (priv->reqs, GUINT_TO_POINTER (call_id)); + } else { + local = g_error_new_literal (NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_SECRETS_UNAVAILABLE, + "No secrets were available"); + dbus_g_method_return_error (context, local); + g_error_free (local); + } } - secrets = nm_settings_connection_get_secrets (self, setting_name, &error); - if (secrets) { - dbus_g_method_return (context, secrets); - g_hash_table_destroy (secrets); - } else { - dbus_g_method_return_error (context, local); - g_clear_error (&local); - } - -out: g_free (setting_name); } @@ -769,7 +739,7 @@ impl_settings_connection_get_secrets (NMSettingsConnection *self, const gchar *setting_name, DBusGMethodInvocation *context) { - auth_start (self, context, TRUE, secrets_auth_cb, g_strdup (setting_name)); + auth_start (self, context, TRUE, dbus_secrets_auth_cb, g_strdup (setting_name)); } /**************************************************************/ @@ -809,6 +779,10 @@ dispose (GObject *object) NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); GSList *iter; + if (priv->disposed) + goto out; + priv->disposed = TRUE; + if (priv->secrets) g_object_unref (priv->secrets); @@ -818,10 +792,16 @@ dispose (GObject *object) g_slist_free (priv->pending_auths); priv->pending_auths = NULL; + /* Cancel in-progress secrets requests */ + for (iter = priv->reqs; iter; iter = g_slist_next (iter)) + g_signal_emit (self, signals[CANCEL_SECRETS], 0, GPOINTER_TO_UINT (iter->data)); + g_slist_free (priv->reqs); + set_visible (self, FALSE); g_object_unref (priv->session_monitor); +out: G_OBJECT_CLASS (nm_settings_connection_parent_class)->dispose (object); } @@ -890,6 +870,24 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *class) g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + /* not exported over D-Bus */ + signals[GET_SECRETS] = + g_signal_new (NM_SETTINGS_CONNECTION_GET_SECRETS, + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMSettingsConnectionClass, get_secrets), + get_secrets_accumulator, NULL, + _nm_marshal_UINT__STRING_POINTER_POINTER, + G_TYPE_UINT, 3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER); + + signals[CANCEL_SECRETS] = + g_signal_new (NM_SETTINGS_CONNECTION_CANCEL_SECRETS, + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 0); + dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (class), &dbus_glib_nm_settings_connection_object_info); } diff --git a/src/settings/nm-settings-connection.h b/src/settings/nm-settings-connection.h index e62aa4b97d..6f7a2ed921 100644 --- a/src/settings/nm-settings-connection.h +++ b/src/settings/nm-settings-connection.h @@ -36,6 +36,9 @@ G_BEGIN_DECLS #define NM_SETTINGS_CONNECTION_UPDATED "updated" #define NM_SETTINGS_CONNECTION_REMOVED "removed" +#define NM_SETTINGS_CONNECTION_GET_SECRETS "get-secrets" +#define NM_SETTINGS_CONNECTION_CANCEL_SECRETS "cancel-secrets" + #define NM_SETTINGS_CONNECTION_VISIBLE "visible" typedef struct _NMSettingsConnection NMSettingsConnection; @@ -50,6 +53,12 @@ typedef void (*NMSettingsConnectionDeleteFunc) (NMSettingsConnection *connection GError *error, gpointer user_data); +typedef void (*NMSettingsConnectionSecretsUpdatedFunc) (NMSettingsConnection *connection, + const char *setting_name, + guint32 call_id, + GError *error, + gpointer user_data); + struct _NMSettingsConnection { NMConnection parent; }; @@ -57,6 +66,7 @@ struct _NMSettingsConnection { struct _NMSettingsConnectionClass { NMConnectionClass parent; + /* virtual methods */ void (*commit_changes) (NMSettingsConnection *connection, NMSettingsConnectionCommitFunc callback, gpointer user_data); @@ -67,6 +77,14 @@ struct _NMSettingsConnectionClass { gboolean (*supports_secrets) (NMSettingsConnection *connection, const char *setting_name); + + /* signals */ + guint32 (*get_secrets) (NMSettingsConnection *connection, + const char *setting_name, + NMSettingsConnectionSecretsUpdatedFunc callback, + gpointer callback_data); + + void (*cancel_secrets) (NMSettingsConnection *connection, guint32 call_id); }; GType nm_settings_connection_get_type (void);