From 77239854f42deee38b9d5d3c0d48204ad1ee0279 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 2 Feb 2011 16:19:15 -0600 Subject: [PATCH] agents: send system-owned secrets to the agent if it has 'modify' permission If we can authenticate the agent for 'modify' permission, then send any existing system secrets to it as the user has permission to change those secrets. This means the agent doesn't have to call GetSecrets() itself, which means simpler code on the agent side for a slight LoC hit in NM itself. This also moves the permissions checking into the NMAgentManager to check each agent, which is sub-optimal since now the agent manager has to do PolicyKit stuff, but hey that's life. Agents need secrets, and we do need to authenticate every agent before we send secrets to them, and the NMSettingsConnection doesn't know about individual agents at all. --- introspection/nm-secret-agent.xml | 5 +- src/settings/nm-agent-manager.c | 206 +++++++++++++++++---- src/settings/nm-agent-manager.h | 1 + src/settings/nm-settings-connection.c | 253 ++++++++------------------ 4 files changed, 251 insertions(+), 214 deletions(-) diff --git a/introspection/nm-secret-agent.xml b/introspection/nm-secret-agent.xml index 8a651dd66c..9655c2b9df 100644 --- a/introspection/nm-secret-agent.xml +++ b/introspection/nm-secret-agent.xml @@ -18,7 +18,10 @@ Nested settings maps containing the connection for which - secrets are being requested. + secrets are being requested. This may contain system-owned + secrets if the agent has successfully authenticated to + modify system network settings and the GetSecrets request + flags allow user interaction. diff --git a/src/settings/nm-agent-manager.c b/src/settings/nm-agent-manager.c index 9979c4e889..9f10dc7b4f 100644 --- a/src/settings/nm-agent-manager.c +++ b/src/settings/nm-agent-manager.c @@ -32,6 +32,8 @@ #include "nm-secret-agent.h" #include "nm-manager-auth.h" #include "nm-dbus-glib-types.h" +#include "nm-polkit-helpers.h" +#include "nm-manager-auth.h" G_DEFINE_TYPE (NMAgentManager, nm_agent_manager, G_TYPE_OBJECT) @@ -44,6 +46,7 @@ typedef struct { NMDBusManager *dbus_mgr; NMSessionMonitor *session_monitor; + PolkitAuthority *authority; /* Hashed by owner name, not identifier, since two agents in different * sessions can use the same identifier. @@ -323,6 +326,7 @@ done: typedef void (*RequestCompleteFunc) (Request *req, GHashTable *secrets, const char *agent_dbus_owner, + gboolean agent_has_modify, GError *error, gpointer user_data); typedef void (*RequestNextFunc) (Request *req); @@ -330,6 +334,8 @@ typedef void (*RequestCancelFunc) (Request *req); struct _Request { guint32 reqid; + PolkitAuthority *authority; + NMAuthChain *chain; NMConnection *connection; gboolean filter_by_uid; @@ -341,6 +347,7 @@ struct _Request { /* Current agent being asked for secrets */ NMSecretAgent *current; gconstpointer current_call_id; + gboolean current_has_modify; /* Stores the sorted list of NMSecretAgents which will be asked for secrets */ GSList *pending; @@ -370,6 +377,7 @@ static guint32 next_req_id = 1; static Request * request_new_get (NMConnection *connection, + PolkitAuthority *authority, gboolean filter_by_uid, gulong uid_filter, GHashTable *existing_secrets, @@ -390,6 +398,7 @@ request_new_get (NMConnection *connection, req = g_malloc0 (sizeof (Request)); req->reqid = next_req_id++; req->connection = g_object_ref (connection); + req->authority = g_object_ref (authority); req->filter_by_uid = filter_by_uid; req->uid_filter = uid_filter; if (existing_secrets) @@ -446,6 +455,9 @@ request_free (Request *req) g_free (req->hint); if (req->existing_secrets) g_hash_table_unref (req->existing_secrets); + if (req->chain) + nm_auth_chain_unref (req->chain); + g_object_unref (req->authority); memset (req, 0, sizeof (Request)); g_free (req); } @@ -552,6 +564,7 @@ request_remove_agent (Request *req, NMSecretAgent *agent) /* If this agent is being asked right now, cancel the request */ if (agent == req->current) { req->cancel_callback (req); + req->current_has_modify = FALSE; req->current = NULL; req->current_call_id = NULL; try_next = TRUE; @@ -583,10 +596,11 @@ next_generic (Request *req, const char *detail) error = g_error_new_literal (NM_AGENT_MANAGER_ERROR, NM_AGENT_MANAGER_ERROR_NO_SECRETS, "No agents were available for this request."); - req->complete_callback (req, NULL, NULL, error, req->complete_callback_data); + req->complete_callback (req, NULL, NULL, FALSE, error, req->complete_callback_data); g_error_free (error); } else { /* Send a secrets request to the next agent */ + req->current_has_modify = FALSE; req->current = req->pending->data; req->pending = g_slist_remove (req->pending, req->current); @@ -622,18 +636,21 @@ get_done_cb (NMSecretAgent *agent, Request *req = user_data; GHashTable *setting_secrets; const char *agent_dbus_owner; + gboolean agent_has_modify; g_return_if_fail (call_id == req->current_call_id); + agent_has_modify = req->current_has_modify; + req->current_has_modify = FALSE; req->current = NULL; req->current_call_id = NULL; if (error) { nm_log_dbg (LOGD_AGENTS, "(%s) agent failed secrets request %p/%s: (%d) %s", - nm_secret_agent_get_description (agent), - req, req->setting_name, - error ? error->code : -1, - (error && error->message) ? error->message : "(unknown)"); + nm_secret_agent_get_description (agent), + req, req->setting_name, + error ? error->code : -1, + (error && error->message) ? error->message : "(unknown)"); /* Try the next agent */ req->next_callback (req); @@ -644,8 +661,8 @@ get_done_cb (NMSecretAgent *agent, setting_secrets = g_hash_table_lookup (secrets, req->setting_name); if (!setting_secrets || !g_hash_table_size (setting_secrets)) { nm_log_dbg (LOGD_AGENTS, "(%s) agent returned no secrets for request %p/%s", - nm_secret_agent_get_description (agent), - req, req->setting_name); + nm_secret_agent_get_description (agent), + req, req->setting_name); /* Try the next agent */ req->next_callback (req); @@ -653,21 +670,25 @@ get_done_cb (NMSecretAgent *agent, } nm_log_dbg (LOGD_AGENTS, "(%s) agent returned secrets for request %p/%s", - nm_secret_agent_get_description (agent), - req, req->setting_name); + nm_secret_agent_get_description (agent), + req, req->setting_name); agent_dbus_owner = nm_secret_agent_get_dbus_owner (agent); - req->complete_callback (req, secrets, agent_dbus_owner, NULL, req->complete_callback_data); + req->complete_callback (req, secrets, agent_dbus_owner, agent_has_modify, NULL, req->complete_callback_data); } static void -get_next_cb (Request *req) +get_agent_request_secrets (Request *req, gboolean include_system_secrets) { - if (!next_generic (req, "getting")) - return; + NMConnection *tmp; + + tmp = nm_connection_duplicate (req->connection); + nm_connection_clear_secrets (tmp); + if (include_system_secrets) + nm_connection_update_secrets (tmp, req->setting_name, req->existing_secrets, NULL); req->current_call_id = nm_secret_agent_get_secrets (NM_SECRET_AGENT (req->current), - req->connection, + tmp, req->setting_name, req->hint, req->flags, @@ -676,9 +697,103 @@ get_next_cb (Request *req) if (req->current_call_id == NULL) { /* Shouldn't hit this, but handle it anyway */ g_warn_if_fail (req->current_call_id != NULL); + req->current_has_modify = FALSE; req->current = NULL; req->next_callback (req); } + + g_object_unref (tmp); +} + +static void +get_agent_modify_auth_cb (NMAuthChain *chain, + GError *error, + DBusGMethodInvocation *context, + gpointer user_data) +{ + Request *req = user_data; + NMAuthCallResult result; + + req->chain = NULL; + nm_auth_chain_unref (chain); + + if (error) { + nm_log_dbg (LOGD_AGENTS, "(%p/%s) agent MODIFY check error: (%d) %s", + req, req->setting_name, + error->code, error->message ? error->message : "(unknown)"); + + /* Try the next agent */ + req->next_callback (req); + } else { + + /* If the agent obtained the 'modify' permission, we send all system secrets + * to it. If it didn't, we still ask it for secrets, but we don't send + * any system secrets. + */ + result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_SETTINGS_CONNECTION_MODIFY); + if (result == NM_AUTH_CALL_RESULT_YES) + req->current_has_modify = TRUE; + + nm_log_dbg (LOGD_AGENTS, "(%p/%s) agent MODIFY check result %d", + req, req->setting_name, result); + + get_agent_request_secrets (req, req->current_has_modify); + } +} + +static void +has_system_secrets (NMSetting *setting, + const char *key, + const GValue *value, + GParamFlags flags, + gpointer user_data) +{ + NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_SYSTEM_OWNED; + gboolean *has_system = user_data; + + if (flags & NM_SETTING_PARAM_SECRET) { + nm_setting_get_secret_flags (setting, key, &secret_flags, NULL); + if (secret_flags == NM_SETTING_SECRET_FLAG_SYSTEM_OWNED) + *has_system = TRUE; + } +} + +static void +get_next_cb (Request *req) +{ + const char *agent_dbus_owner; + + if (!next_generic (req, "getting")) + return; + + agent_dbus_owner = nm_secret_agent_get_dbus_owner (NM_SECRET_AGENT (req->current)); + + if (req->flags != 0) { + gboolean has_system = FALSE; + + /* Interaction with the user is allowed; if there are any system secrets, + * check whether the agent has the 'modify' permission before sending those + * secrets to the agent. + */ + nm_connection_for_each_setting_value (req->connection, has_system_secrets, &has_system); + if (has_system) { + nm_log_dbg (LOGD_AGENTS, "(%p/%s) request has system secrets; checking agent %s for MODIFY", + req, req->setting_name, agent_dbus_owner); + + req->chain = nm_auth_chain_new_dbus_sender (req->authority, + agent_dbus_owner, + get_agent_modify_auth_cb, + req); + g_assert (req->chain); + nm_auth_chain_add_call (req->chain, NM_AUTH_PERMISSION_SETTINGS_CONNECTION_MODIFY, TRUE); + return; + } + } + + nm_log_dbg (LOGD_AGENTS, "(%p/%s) requesting user-owned secrets from agent %s", + req, req->setting_name, agent_dbus_owner); + + get_agent_request_secrets (req, FALSE); } static gboolean @@ -706,20 +821,20 @@ get_start (gpointer user_data) g_assert (tmp); if (!nm_connection_update_secrets (tmp, req->setting_name, req->existing_secrets, &error)) { - req->complete_callback (req, NULL, NULL, error, req->complete_callback_data); + req->complete_callback (req, NULL, NULL, FALSE, error, req->complete_callback_data); g_clear_error (&error); } else { /* Do we have everything we need? */ /* FIXME: handle second check for VPN connections */ if (nm_connection_need_secrets (tmp, NULL) == NULL) { nm_log_dbg (LOGD_AGENTS, "(%p/%s) system settings secrets sufficient", - req, req->setting_name); + req, req->setting_name); /* Got everything, we're done */ - req->complete_callback (req, req->existing_secrets, NULL, NULL, req->complete_callback_data); + req->complete_callback (req, req->existing_secrets, NULL, FALSE, NULL, req->complete_callback_data); } else { nm_log_dbg (LOGD_AGENTS, "(%p/%s) system settings secrets insufficient, asking agents", - req, req->setting_name); + req, req->setting_name); /* We don't, so ask some agents for additional secrets */ req->next_callback (req); @@ -741,6 +856,7 @@ static void get_complete_cb (Request *req, GHashTable *secrets, const char *agent_dbus_owner, + gboolean agent_has_modify, GError *error, gpointer user_data) { @@ -751,6 +867,7 @@ get_complete_cb (Request *req, req->callback (self, req->reqid, agent_dbus_owner, + agent_has_modify, req->setting_name, req->flags, error ? NULL : secrets, @@ -797,6 +914,7 @@ nm_agent_manager_get_secrets (NMAgentManager *self, setting_name); req = request_new_get (connection, + priv->authority, filter_by_uid, uid_filter, existing_secrets, @@ -850,10 +968,10 @@ save_done_cb (NMSecretAgent *agent, if (error) { nm_log_dbg (LOGD_AGENTS, "(%s) agent failed save secrets request %p/%s: (%d) %s", - nm_secret_agent_get_description (agent), - req, req->setting_name, - error ? error->code : -1, - (error && error->message) ? error->message : "(unknown)"); + nm_secret_agent_get_description (agent), + req, req->setting_name, + error ? error->code : -1, + (error && error->message) ? error->message : "(unknown)"); /* Try the next agent */ req->next_callback (req); @@ -861,11 +979,11 @@ save_done_cb (NMSecretAgent *agent, } nm_log_dbg (LOGD_AGENTS, "(%s) agent saved secrets for request %p/%s", - nm_secret_agent_get_description (agent), - req, req->setting_name); + nm_secret_agent_get_description (agent), + req, req->setting_name); agent_dbus_owner = nm_secret_agent_get_dbus_owner (agent); - req->complete_callback (req, NULL, agent_dbus_owner, NULL, req->complete_callback_data); + req->complete_callback (req, NULL, agent_dbus_owner, FALSE, NULL, req->complete_callback_data); } static void @@ -890,6 +1008,7 @@ static void save_complete_cb (Request *req, GHashTable *secrets, const char *agent_dbus_owner, + gboolean agent_has_modify, GError *error, gpointer user_data) { @@ -949,14 +1068,14 @@ delete_done_cb (NMSecretAgent *agent, if (error) { nm_log_dbg (LOGD_AGENTS, "(%s) agent failed delete secrets request %p/%s: (%d) %s", - nm_secret_agent_get_description (agent), - req, req->setting_name, - error ? error->code : -1, - (error && error->message) ? error->message : "(unknown)"); + nm_secret_agent_get_description (agent), + req, req->setting_name, + error ? error->code : -1, + (error && error->message) ? error->message : "(unknown)"); } else { nm_log_dbg (LOGD_AGENTS, "(%s) agent deleted secrets for request %p/%s", - nm_secret_agent_get_description (agent), - req, req->setting_name); + nm_secret_agent_get_description (agent), + req, req->setting_name); } /* Tell the next agent to delete secrets */ @@ -985,6 +1104,7 @@ static void delete_complete_cb (Request *req, GHashTable *secrets, const char *agent_dbus_owner, + gboolean agent_has_modify, GError *error, gpointer user_data) { @@ -1077,6 +1197,15 @@ static void nm_agent_manager_init (NMAgentManager *self) { NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); + GError *error = NULL; + + priv->authority = polkit_authority_get_sync (NULL, &error); + if (!priv->authority) { + nm_log_warn (LOGD_SETTINGS, "failed to create PolicyKit authority: (%d) %s", + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); + } priv->agents = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); priv->requests = g_hash_table_new_full (g_direct_hash, @@ -1090,15 +1219,16 @@ dispose (GObject *object) { NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (object); - if (priv->disposed) - return; - priv->disposed = TRUE; + if (!priv->disposed) { + priv->disposed = TRUE; - g_object_unref (priv->session_monitor); - g_object_unref (priv->dbus_mgr); + g_hash_table_destroy (priv->agents); + g_hash_table_destroy (priv->requests); - g_hash_table_destroy (priv->agents); - g_hash_table_destroy (priv->requests); + g_object_unref (priv->session_monitor); + g_object_unref (priv->dbus_mgr); + g_object_unref (priv->authority); + } G_OBJECT_CLASS (nm_agent_manager_parent_class)->dispose (object); } diff --git a/src/settings/nm-agent-manager.h b/src/settings/nm-agent-manager.h index 6771f943a9..5e5dca42f6 100644 --- a/src/settings/nm-agent-manager.h +++ b/src/settings/nm-agent-manager.h @@ -48,6 +48,7 @@ NMAgentManager *nm_agent_manager_get (void); typedef void (*NMAgentSecretsResultFunc) (NMAgentManager *manager, guint32 call_id, const char *agent_dbus_owner, + gboolean agent_has_modify, const char *setting_name, guint32 flags, GHashTable *secrets, diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c index d82bb4414e..d3e1bada8c 100644 --- a/src/settings/nm-settings-connection.c +++ b/src/settings/nm-settings-connection.c @@ -426,140 +426,11 @@ new_secrets_commit_cb (NMSettingsConnection *connection, } } -static void -get_secrets_done (NMSettingsConnection *self, - guint32 call_id, - const char *setting_name, - GHashTable *secrets, - GError *error, - NMSettingsConnectionSecretsFunc callback, - gpointer callback_data) -{ - NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - NMSettingConnection *s_con; - GError *local = NULL; - GHashTable *hash = NULL; - - s_con = (NMSettingConnection *) nm_connection_get_setting (NM_CONNECTION (self), NM_TYPE_SETTING_CONNECTION); - g_assert (s_con); - - if (error) { - nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) secrets request completed; error: (%d) %s", - nm_setting_connection_get_uuid (s_con), - setting_name, - call_id, - error->code, - error->message ? error->message : "(unknown)"); - callback (self, call_id, setting_name, error, callback_data); - return; - } - - nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) secrets request completed successfully", - nm_setting_connection_get_uuid (s_con), - setting_name, - call_id); - - /* Update the connection with our existing secrets from backing storage */ - nm_connection_clear_secrets (NM_CONNECTION (self)); - hash = nm_connection_to_hash (priv->secrets, NM_SETTING_HASH_FLAG_ONLY_SECRETS); - if (nm_connection_update_secrets (NM_CONNECTION (self), setting_name, hash, &local)) { - /* Update the connection with the agent's secrets; by this point if any - * system-owned secrets exist in 'secrets' the agent that provided them - * will have been authenticated, so those secrets can replace the existing - * system secrets. - */ - if (nm_connection_update_secrets (NM_CONNECTION (self), setting_name, secrets, &local)) { - /* Now that all secrets are updated, copy and cache new secrets, - * then save them to backing storage. - */ - if (priv->secrets) - g_object_unref (priv->secrets); - priv->secrets = nm_connection_duplicate (NM_CONNECTION (self)); - - nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) saving new secrets to backing storage", - nm_setting_connection_get_uuid (s_con), - setting_name, - call_id); - - nm_settings_connection_commit_changes (self, new_secrets_commit_cb, NULL); - } else { - nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) failed to update with agent secrets: (%d) %s", - nm_setting_connection_get_uuid (s_con), - setting_name, - call_id, - local->code, - local->message ? local->message : "(unknown)"); - } - } else { - nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) failed to update with existing secrets: (%d) %s", - nm_setting_connection_get_uuid (s_con), - setting_name, - call_id, - local->code, - local->message ? local->message : "(unknown)"); - } - - callback (self, call_id, setting_name, local, callback_data); - g_clear_error (&local); - if (hash) - g_hash_table_destroy (hash); -} - -static void -agent_secrets_modify_auth_cb (NMAuthChain *chain, - GError *error, - DBusGMethodInvocation *context, - gpointer user_data) -{ - NMSettingsConnection *self = NM_SETTINGS_CONNECTION (user_data); - NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - NMSettingConnection *s_con; - NMAuthCallResult result; - GHashTable *secrets = nm_auth_chain_get_data (chain, "secrets"); - const char *setting_name = nm_auth_chain_get_data (chain, "setting-name"); - guint32 call_id = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "call-id")); - - s_con = (NMSettingConnection *) nm_connection_get_setting (NM_CONNECTION (self), NM_TYPE_SETTING_CONNECTION); - g_assert (s_con); - - priv->pending_auths = g_slist_remove (priv->pending_auths, chain); - - result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_SETTINGS_CONNECTION_MODIFY); - - nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) agent MODIFY check result %d", - nm_setting_connection_get_uuid (s_con), - setting_name, - call_id, - result); - - if (result == NM_AUTH_CALL_RESULT_YES) { - /* Agent can modify system connections; system-owned secrets it returned - * replace any secrets in backing storage. - */ - } else { - /* Agent didn't successfully authenticate; clear system-owned secrets - * from the secrets the agent returned. - */ - nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) agent failed to authenticate after providing system secrets", - nm_setting_connection_get_uuid (s_con), - setting_name, - call_id); - for_each_secret (NM_CONNECTION (self), secrets, clear_system_owned_secrets, NULL); - } - - get_secrets_done (self, - call_id, - setting_name, - secrets, - error, - nm_auth_chain_get_data (chain, "callback"), - nm_auth_chain_get_data (chain, "callback_data")); -} - static void agent_secrets_done_cb (NMAgentManager *manager, guint32 call_id, const char *agent_dbus_owner, + gboolean agent_has_modify, const char *setting_name, guint32 flags, GHashTable *secrets, @@ -574,17 +445,18 @@ agent_secrets_done_cb (NMAgentManager *manager, gpointer callback_data = other_data3; NMSettingConnection *s_con; GError *local = NULL; + GHashTable *hash; s_con = (NMSettingConnection *) nm_connection_get_setting (NM_CONNECTION (self), NM_TYPE_SETTING_CONNECTION); g_assert (s_con); if (error) { nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) secrets request error: (%d) %s", - nm_setting_connection_get_uuid (s_con), - setting_name, - call_id, - error->code, - error->message ? error->message : "(unknown)"); + nm_setting_connection_get_uuid (s_con), + setting_name, + call_id, + error->code, + error->message ? error->message : "(unknown)"); callback (self, call_id, setting_name, error, callback_data); return; @@ -604,10 +476,10 @@ agent_secrets_done_cb (NMAgentManager *manager, gboolean has_system = FALSE; nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) secrets returned from agent %s", - nm_setting_connection_get_uuid (s_con), - setting_name, - call_id, - agent_dbus_owner); + nm_setting_connection_get_uuid (s_con), + setting_name, + call_id, + agent_dbus_owner); /* If the agent returned any system-owned secrets (initial connect and no * secrets given when the connection was created, or something like that) @@ -621,51 +493,81 @@ agent_secrets_done_cb (NMAgentManager *manager, /* No user interaction was allowed when requesting secrets; the * agent is being bad. Remove system-owned secrets. */ - for_each_secret (NM_CONNECTION (self), secrets, clear_system_owned_secrets, NULL); - nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) interaction forbidden but agent %s returned system secrets", - nm_setting_connection_get_uuid (s_con), - setting_name, - call_id, - agent_dbus_owner); - } else { - NMAuthChain *chain; + nm_setting_connection_get_uuid (s_con), + setting_name, + call_id, + agent_dbus_owner); - nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) agent %s returned system secrets; checking for MODIFY", - nm_setting_connection_get_uuid (s_con), - setting_name, - call_id, - agent_dbus_owner); - - /* User interaction was allowed, check whether the agent's UID - * has the 'modify' privilege before using the system-owned - * secrets supplied by the agent. + for_each_secret (NM_CONNECTION (self), secrets, clear_system_owned_secrets, NULL); + } else if (agent_has_modify == FALSE) { + /* Agent didn't successfully authenticate; clear system-owned secrets + * from the secrets the agent returned. */ - chain = nm_auth_chain_new_dbus_sender (priv->authority, - agent_dbus_owner, - agent_secrets_modify_auth_cb, - self); - g_assert (chain); + nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) agent failed to authenticate but provided system secrets", + nm_setting_connection_get_uuid (s_con), + setting_name, + call_id); - nm_auth_chain_set_data (chain, "call-id", GUINT_TO_POINTER (call_id), NULL); - nm_auth_chain_set_data (chain, "setting-name", g_strdup (setting_name), g_free); - nm_auth_chain_set_data (chain, "secrets", g_hash_table_ref (secrets), (GDestroyNotify) g_hash_table_unref); - nm_auth_chain_set_data (chain, "callback", callback, NULL); - nm_auth_chain_set_data (chain, "callback-data", callback_data, NULL); - - nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SETTINGS_CONNECTION_MODIFY, TRUE); - priv->pending_auths = g_slist_append (priv->pending_auths, chain); - return; + for_each_secret (NM_CONNECTION (self), secrets, clear_system_owned_secrets, NULL); } } } else { nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) existing secrets returned", - nm_setting_connection_get_uuid (s_con), - setting_name, - call_id); + nm_setting_connection_get_uuid (s_con), + setting_name, + call_id); } - get_secrets_done (self, call_id, setting_name, secrets, error, callback, callback_data); + nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) secrets request completed", + nm_setting_connection_get_uuid (s_con), + setting_name, + call_id); + + /* Update the connection with our existing secrets from backing storage */ + nm_connection_clear_secrets (NM_CONNECTION (self)); + hash = nm_connection_to_hash (priv->secrets, NM_SETTING_HASH_FLAG_ONLY_SECRETS); + if (nm_connection_update_secrets (NM_CONNECTION (self), setting_name, hash, &local)) { + /* Update the connection with the agent's secrets; by this point if any + * system-owned secrets exist in 'secrets' the agent that provided them + * will have been authenticated, so those secrets can replace the existing + * system secrets. + */ + if (nm_connection_update_secrets (NM_CONNECTION (self), setting_name, secrets, &local)) { + /* Now that all secrets are updated, copy and cache new secrets, + * then save them to backing storage. + */ + if (priv->secrets) + g_object_unref (priv->secrets); + priv->secrets = nm_connection_duplicate (NM_CONNECTION (self)); + + nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) saving new secrets to backing storage", + nm_setting_connection_get_uuid (s_con), + setting_name, + call_id); + + nm_settings_connection_commit_changes (self, new_secrets_commit_cb, NULL); + } else { + nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) failed to update with agent secrets: (%d) %s", + nm_setting_connection_get_uuid (s_con), + setting_name, + call_id, + local->code, + local->message ? local->message : "(unknown)"); + } + } else { + nm_log_dbg (LOGD_SETTINGS, "(%s/%s:%u) failed to update with existing secrets: (%d) %s", + nm_setting_connection_get_uuid (s_con), + setting_name, + call_id, + local->code, + local->message ? local->message : "(unknown)"); + } + + callback (self, call_id, setting_name, local, callback_data); + g_clear_error (&local); + if (hash) + g_hash_table_destroy (hash); } /** @@ -1170,7 +1072,7 @@ nm_settings_connection_init (NMSettingsConnection *self) priv->dbus_mgr = nm_dbus_manager_get (); - priv->authority = polkit_authority_get_sync (NULL, NULL); + priv->authority = polkit_authority_get_sync (NULL, &error); if (!priv->authority) { nm_log_warn (LOGD_SETTINGS, "failed to create PolicyKit authority: (%d) %s", error ? error->code : -1, @@ -1222,6 +1124,7 @@ dispose (GObject *object) g_object_unref (priv->session_monitor); g_object_unref (priv->agent_mgr); g_object_unref (priv->dbus_mgr); + g_object_unref (priv->authority); out: G_OBJECT_CLASS (nm_settings_connection_parent_class)->dispose (object);