summary refs log tree commit diff
path: root/src/settings/nm-agent-manager.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2020-04-11 21:28:04 +0200
committerMichael Biebl <biebl@debian.org>2020-04-11 21:28:04 +0200
commit1e5977b62f896e844b548c3007ace9e1dfa7f9ed (patch)
tree7a7416ed410e72b6200f3d860fd315ec11cc106b /src/settings/nm-agent-manager.c
parentb012fa6e1d808e0736c009799c62d835cbfcc1dd (diff)
New upstream version 1.23.90 upstream/1.23.90
Diffstat (limited to 'src/settings/nm-agent-manager.c')
-rw-r--r--src/settings/nm-agent-manager.c399
1 files changed, 195 insertions, 204 deletions
diff --git a/src/settings/nm-agent-manager.c b/src/settings/nm-agent-manager.c
index ecca0eb3..363b1175 100644
--- a/src/settings/nm-agent-manager.c
+++ b/src/settings/nm-agent-manager.c
@@ -35,15 +35,9 @@ typedef struct {
 	NMAuthManager *auth_mgr;
 	NMSessionMonitor *session_monitor;
 
-	/* Auth chains for checking agent permissions */
-	GSList *chains;
+	CList agent_lst_head;
 
-	/* Hashed by owner name, not identifier, since two agents in different
-	 * sessions can use the same identifier.
-	 */
-	GHashTable *agents;
-
-	CList requests;
+	CList request_lst_head;
 
 	guint64 agent_version_id;
 } NMAgentManagerPrivate;
@@ -119,6 +113,8 @@ static void _con_del_request_start (Request *req);
 
 static gboolean _con_get_try_complete_early (Request *req);
 
+static void agent_disconnected_cb (NMSecretAgent *agent, gpointer user_data);
+
 /*****************************************************************************/
 
 guint64
@@ -152,7 +148,7 @@ _request_type_to_string (RequestType request_type, gboolean verbose)
 /*****************************************************************************/
 
 struct _NMAgentManagerCallId {
-	CList lst_request;
+	CList request_lst;
 
 	NMAgentManager *self;
 
@@ -201,40 +197,73 @@ struct _NMAgentManagerCallId {
 
 /*****************************************************************************/
 
-static gboolean
-remove_agent (NMAgentManager *self, const char *owner)
+static NMSecretAgent *
+_agent_find_by_owner (NMAgentManagerPrivate *priv,
+                      const char *owner)
 {
-	NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
 	NMSecretAgent *agent;
-	CList *iter, *safe;
 
-	g_return_val_if_fail (owner != NULL, FALSE);
+	c_list_for_each_entry (agent, &priv->agent_lst_head, agent_lst) {
+		if (nm_streq (nm_secret_agent_get_dbus_owner (agent), owner))
+			return agent;
+	}
+	return NULL;
+}
 
-	/* Make sure this agent has already registered */
-	agent = g_hash_table_lookup (priv->agents, owner);
-	if (!agent)
-		return FALSE;
+static NMSecretAgent *
+_agent_find_by_identifier_and_uid (NMAgentManagerPrivate *priv,
+                                   const char *identifier,
+                                   gulong sender_uid)
+{
+	NMSecretAgent *agent;
+
+	c_list_for_each_entry (agent, &priv->agent_lst_head, agent_lst) {
+		if (    nm_streq0 (nm_secret_agent_get_identifier (agent), identifier)
+		    && sender_uid == nm_secret_agent_get_owner_uid (agent))
+			return agent;
+	}
+	return NULL;
+}
+
+/*****************************************************************************/
+
+static void
+_agent_remove (NMAgentManager *self, NMSecretAgent *agent)
+{
+	NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
+	CList *iter, *safe;
+
+	nm_assert (NM_IS_SECRET_AGENT (agent));
+	nm_assert (c_list_contains (&priv->agent_lst_head, &agent->agent_lst));
 
 	_LOGD (agent, "agent unregistered or disappeared");
 
+	nm_clear_pointer (&agent->auth_chain, nm_auth_chain_destroy);
+
+	c_list_unlink (&agent->agent_lst);
+
+	g_signal_handlers_disconnect_by_func (agent, G_CALLBACK (agent_disconnected_cb), self);
+
 	/* Remove this agent from any in-progress secrets requests */
-	c_list_for_each_safe (iter, safe, &priv->requests)
-		request_remove_agent (c_list_entry (iter, Request, lst_request), agent);
+	c_list_for_each_safe (iter, safe, &priv->request_lst_head)
+		request_remove_agent (c_list_entry (iter, Request, request_lst), agent);
 
-	/* And dispose of the agent */
-	g_hash_table_remove (priv->agents, owner);
-	return TRUE;
+	g_object_unref (agent);
 }
 
 /* Call this *after* calling request_next_agent() */
 static void
-maybe_remove_agent_on_error (NMSecretAgent *agent,
+maybe_remove_agent_on_error (NMAgentManager *self,
+                             NMSecretAgent *agent,
                              GError *error)
 {
-	if (   g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED)
-	    || g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_DISCONNECTED)
-	    || g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER))
-		remove_agent (nm_agent_manager_get (), nm_secret_agent_get_dbus_owner (agent));
+	if (   !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED)
+	    && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_DISCONNECTED)
+	    && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER))
+		return;
+
+	if (!c_list_is_empty (&agent->agent_lst))
+		_agent_remove (self, agent);
 }
 
 /*****************************************************************************/
@@ -295,70 +324,92 @@ validate_identifier (const char *identifier, GError **error)
 }
 
 static void
-agent_register_permissions_done (NMAuthChain *chain,
-                                 GDBusMethodInvocation *context,
-                                 gpointer user_data)
+_agent_permissions_check_done (NMAuthChain *chain,
+                               GDBusMethodInvocation *context,
+                               gpointer user_data)
 {
 	NMAgentManager *self = NM_AGENT_MANAGER (user_data);
 	NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
 	NMSecretAgent *agent;
-	const char *sender;
-	NMAuthCallResult result;
-	CList *iter;
+	Request *request;
 
-	nm_assert (G_IS_DBUS_METHOD_INVOCATION (context));
-
-	priv->chains = g_slist_remove (priv->chains, chain);
+	nm_assert (!context || G_IS_DBUS_METHOD_INVOCATION (context));
 
 	agent = nm_auth_chain_steal_data (chain, "agent");
-	nm_assert (agent);
 
-	result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED);
-	if (result == NM_AUTH_CALL_RESULT_YES)
-		nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, TRUE);
+	nm_assert (NM_IS_SECRET_AGENT (agent));
+	nm_assert (agent->auth_chain == chain);
+	nm_assert (agent->fully_registered == (!context));
+	nm_assert (c_list_contains (&priv->agent_lst_head, &agent->agent_lst));
 
-	result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN);
-	if (result == NM_AUTH_CALL_RESULT_YES)
-		nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, TRUE);
+	agent->auth_chain = NULL;
+
+	nm_secret_agent_add_permission (agent,
+	                                NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED,
+	                                (nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED) == NM_AUTH_CALL_RESULT_YES));
+	nm_secret_agent_add_permission (agent,
+	                                NM_AUTH_PERMISSION_WIFI_SHARE_OPEN,
+	                                (nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN) == NM_AUTH_CALL_RESULT_YES));
+
+	if (agent->fully_registered) {
+		_LOGD (agent, "updated agent permissions");
+		return;
+	}
 
-	priv->agent_version_id += 1;
-	sender = nm_secret_agent_get_dbus_owner (agent);
-	g_hash_table_insert (priv->agents, g_strdup (sender), agent);
 	_LOGI (agent, "agent registered");
+
+	agent->fully_registered = TRUE;
+
+	priv->agent_version_id += 1;
+
 	g_dbus_method_invocation_return_value (context, NULL);
 
-	/* Signal an agent was registered */
-	g_signal_emit (self, signals[AGENT_REGISTERED], 0, agent);
+	c_list_for_each_entry (request, &priv->request_lst_head, request_lst)
+		request_add_agent (request, agent);
 
-	/* Add this agent to any in-progress secrets requests */
-	c_list_for_each (iter, &priv->requests)
-		request_add_agent (c_list_entry (iter, Request, lst_request), agent);
+	g_signal_emit (self, signals[AGENT_REGISTERED], 0, agent);
 }
 
-static NMSecretAgent *
-find_agent_by_identifier_and_uid (NMAgentManager *self,
-                                  const char *identifier,
-                                  gulong sender_uid)
+static NMAuthChain *
+_agent_create_auth_chain (NMAgentManager *self,
+                          NMSecretAgent *agent,
+                          GDBusMethodInvocation *context)
 {
-	NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
-	GHashTableIter iter;
-	NMSecretAgent *agent;
+	NMAuthChain *chain;
 
-	g_hash_table_iter_init (&iter, priv->agents);
-	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &agent)) {
-		if (   g_strcmp0 (nm_secret_agent_get_identifier (agent), identifier) == 0
-		    && nm_secret_agent_get_owner_uid (agent) == sender_uid)
-			return agent;
+	_LOGD (agent, "requesting permissions");
+
+	nm_assert (   !agent->auth_chain
+	           || (agent->fully_registered == (!nm_auth_chain_get_context (agent->auth_chain))));
+
+	if (   agent->auth_chain
+	    && !context
+	    && !agent->fully_registered) {
+		/* we restart the authorization check (without a @context), but the currently
+		 * pending auth-chain carries a context. We need to pass it on as we replace
+		 * the auth-chain. */
+		context = nm_auth_chain_get_context (agent->auth_chain);
+		nm_assert (context);
 	}
-	return NULL;
+
+	chain = nm_auth_chain_new_subject (nm_secret_agent_get_subject (agent),
+	                                   context,
+	                                   _agent_permissions_check_done,
+	                                   self);
+
+	nm_auth_chain_set_data (chain, "agent", agent, NULL);
+	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, FALSE);
+	nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, FALSE);
+
+	nm_clear_pointer (&agent->auth_chain, nm_auth_chain_destroy);
+	agent->auth_chain = chain;
+	return chain;
 }
 
 static void
 agent_disconnected_cb (NMSecretAgent *agent, gpointer user_data)
 {
-	/* The agent quit, so remove it and let interested clients know */
-	remove_agent (NM_AGENT_MANAGER (user_data),
-	              nm_secret_agent_get_dbus_owner (agent));
+	_agent_remove (NM_AGENT_MANAGER (user_data), agent);
 }
 
 static void
@@ -368,65 +419,44 @@ agent_manager_register_with_capabilities (NMAgentManager *self,
                                           guint32 capabilities)
 {
 	NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
-	NMAuthSubject *subject;
+	gs_unref_object NMAuthSubject *subject = NULL;
 	gulong sender_uid = G_MAXULONG;
 	GError *error = NULL;
 	NMSecretAgent *agent;
-	NMAuthChain *chain;
 
-	subject = nm_auth_subject_new_unix_process_from_context (context);
+	subject = nm_dbus_manager_new_auth_subject_from_context (context);
 	if (!subject) {
 		error = g_error_new_literal (NM_AGENT_MANAGER_ERROR,
 		                             NM_AGENT_MANAGER_ERROR_PERMISSION_DENIED,
 		                             NM_UTILS_ERROR_MSG_REQ_UID_UKNOWN);
-		goto done;
+		g_dbus_method_invocation_take_error (context, error);
+		return;
 	}
 	sender_uid = nm_auth_subject_get_unix_process_uid (subject);
 
 	/* Validate the identifier */
-	if (!validate_identifier (identifier, &error))
-		goto done;
+	if (!validate_identifier (identifier, &error)) {
+		g_dbus_method_invocation_take_error (context, error);
+		return;
+	}
 
 	/* Only one agent for each identifier is allowed per user */
-	if (find_agent_by_identifier_and_uid (self, identifier, sender_uid)) {
+	if (_agent_find_by_identifier_and_uid (priv, identifier, sender_uid)) {
 		error = g_error_new_literal (NM_AGENT_MANAGER_ERROR,
 		                             NM_AGENT_MANAGER_ERROR_PERMISSION_DENIED,
 		                             "An agent with this ID is already registered for this user.");
-		goto done;
+		g_dbus_method_invocation_take_error (context, error);
+		return;
 	}
 
-	/* Success, add the new agent */
 	agent = nm_secret_agent_new (context, subject, identifier, capabilities);
-	if (!agent) {
-		error = g_error_new_literal (NM_AGENT_MANAGER_ERROR,
-		                             NM_AGENT_MANAGER_ERROR_FAILED,
-		                             "Failed to initialize the agent");
-		goto done;
-	}
+
 	g_signal_connect (agent, NM_SECRET_AGENT_DISCONNECTED,
 	                  G_CALLBACK (agent_disconnected_cb), self);
 
-	_LOGD (agent, "requesting permissions");
-
-	/* Kick off permissions requests for this agent */
-	chain = nm_auth_chain_new_subject (subject, context, agent_register_permissions_done, self);
-	if (chain) {
-		nm_auth_chain_set_data (chain, "agent", agent, g_object_unref);
-		nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, FALSE);
-		nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, FALSE);
+	c_list_link_tail (&priv->agent_lst_head, &agent->agent_lst);
 
-		priv->chains = g_slist_append (priv->chains, chain);
-	} else {
-		g_object_unref (agent);
-		error = g_error_new_literal (NM_AGENT_MANAGER_ERROR,
-		                             NM_AGENT_MANAGER_ERROR_FAILED,
-		                             "Unable to start agent authentication.");
-	}
-
-done:
-	if (error)
-		g_dbus_method_invocation_take_error (context, error);
-	g_clear_object (&subject);
+	_agent_create_auth_chain (self, agent, context);
 }
 
 static void
@@ -470,8 +500,11 @@ impl_agent_manager_unregister (NMDBusObject *obj,
                                GVariant *parameters)
 {
 	NMAgentManager *self = NM_AGENT_MANAGER (obj);
+	NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
+	NMSecretAgent *agent;
 
-	if (!remove_agent (self, sender)) {
+	agent = _agent_find_by_owner (priv, sender);
+	if (!agent) {
 		g_dbus_method_invocation_return_error_literal (invocation,
 		                                               NM_AGENT_MANAGER_ERROR,
 		                                               NM_AGENT_MANAGER_ERROR_NOT_REGISTERED,
@@ -479,6 +512,8 @@ impl_agent_manager_unregister (NMDBusObject *obj,
 		return;
 	}
 
+	_agent_remove (self, agent);
+
 	g_dbus_method_invocation_return_value (invocation, NULL);
 }
 
@@ -497,7 +532,7 @@ request_new (NMAgentManager *self,
 	req->request_type = request_type;
 	req->detail = g_strdup (detail);
 	req->subject = g_object_ref (subject);
-	c_list_link_tail (&NM_AGENT_MANAGER_GET_PRIVATE (self)->requests, &req->lst_request);
+	c_list_link_tail (&NM_AGENT_MANAGER_GET_PRIVATE (self)->request_lst_head, &req->request_lst);
 	return req;
 }
 
@@ -583,7 +618,7 @@ req_complete_cancel (Request *req, gboolean is_disposing)
 	gs_free_error GError *error = NULL;
 
 	nm_assert (req && req->self);
-	nm_assert (!c_list_contains (&NM_AGENT_MANAGER_GET_PRIVATE (req->self)->requests, &req->lst_request));
+	nm_assert (!c_list_contains (&NM_AGENT_MANAGER_GET_PRIVATE (req->self)->request_lst_head, &req->request_lst));
 
 	nm_utils_error_set_cancelled (&error, is_disposing, "NMAgentManager");
 	req_complete_release (req, NULL, NULL, NULL, error);
@@ -598,9 +633,9 @@ req_complete (Request *req,
 {
 	NMAgentManager *self = req->self;
 
-	nm_assert (c_list_contains (&NM_AGENT_MANAGER_GET_PRIVATE (self)->requests, &req->lst_request));
+	nm_assert (c_list_contains (&NM_AGENT_MANAGER_GET_PRIVATE (self)->request_lst_head, &req->request_lst));
 
-	c_list_unlink (&req->lst_request);
+	c_list_unlink (&req->request_lst);
 
 	req_complete_release (req, secrets, agent_dbus_owner, agent_username, error);
 }
@@ -626,7 +661,7 @@ agent_compare_func (gconstpointer aa, gconstpointer bb, gpointer user_data)
 	b_pid = nm_secret_agent_get_pid (b);
 
 	/* Prefer agents in the process the request came from */
-	if (nm_auth_subject_is_unix_process (req->subject)) {
+	if (nm_auth_subject_get_subject_type (req->subject) == NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS) {
 		requester = nm_auth_subject_get_unix_process_pid (req->subject);
 
 		if (a_pid != b_pid) {
@@ -682,7 +717,7 @@ request_add_agent (Request *req, NMSecretAgent *agent)
 	}
 
 	/* If the request should filter agents by UID, do that now */
-	if (nm_auth_subject_is_unix_process (req->subject)) {
+	if (nm_auth_subject_get_subject_type (req->subject) == NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS) {
 		uid_t agent_uid, subject_uid;
 
 		agent_uid = nm_secret_agent_get_owner_uid (agent);
@@ -710,12 +745,12 @@ static void
 request_add_agents (NMAgentManager *self, Request *req)
 {
 	NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
-	GHashTableIter iter;
-	gpointer data;
+	NMSecretAgent *agent;
 
-	g_hash_table_iter_init (&iter, priv->agents);
-	while (g_hash_table_iter_next (&iter, NULL, &data))
-		request_add_agent (req, NM_SECRET_AGENT (data));
+	c_list_for_each_entry (agent, &priv->agent_lst_head, agent_lst) {
+		if (agent->fully_registered)
+			request_add_agent (req, agent);
+	}
 }
 
 static void
@@ -868,7 +903,7 @@ _con_get_request_done (NMSecretAgent *agent,
 
 			/* Try the next agent */
 			request_next_agent (req);
-			maybe_remove_agent_on_error (agent, error);
+			maybe_remove_agent_on_error (self, agent, error);
 		}
 		return;
 	}
@@ -1037,7 +1072,7 @@ _con_get_request_start (Request *req)
 		                                            NULL,
 		                                            _con_get_request_start_validated,
 		                                            req);
-		g_assert (req->con.chain);
+		nm_assert (req->con.chain);
 
 		/* If the caller is the only user in the connection's permissions, then
 		 * we use the 'modify.own' permission instead of 'modify.system'.  If the
@@ -1188,7 +1223,6 @@ nm_agent_manager_get_secrets (NMAgentManager *self,
 	req->con.get.callback = callback;
 	req->con.get.callback_data = callback_data;
 
-	/* Kick off the request */
 	if (!(req->con.get.flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ONLY_SYSTEM))
 		request_add_agents (self, req);
 	req->idle_id = g_idle_add (request_start, req);
@@ -1203,9 +1237,9 @@ nm_agent_manager_cancel_secrets (NMAgentManager *self,
 	g_return_if_fail (request_id);
 	g_return_if_fail (request_id->request_type == REQUEST_TYPE_CON_GET);
 
-	nm_assert (c_list_contains (&NM_AGENT_MANAGER_GET_PRIVATE (self)->requests, &request_id->lst_request));
+	nm_assert (c_list_contains (&NM_AGENT_MANAGER_GET_PRIVATE (self)->request_lst_head, &request_id->request_lst));
 
-	c_list_unlink (&request_id->lst_request);
+	c_list_unlink (&request_id->request_lst);
 
 	req_complete_cancel (request_id, FALSE);
 }
@@ -1242,7 +1276,7 @@ _con_save_request_done (NMSecretAgent *agent,
 		       LOG_REQ_ARG (req), error->message);
 		/* Try the next agent */
 		request_next_agent (req);
-		maybe_remove_agent_on_error (agent, error);
+		maybe_remove_agent_on_error (self, agent, error);
 		return;
 	}
 
@@ -1291,7 +1325,6 @@ nm_agent_manager_save_secrets (NMAgentManager *self,
 	req->con.path = g_strdup (path);
 	req->con.connection = g_object_ref (connection);
 
-	/* Kick off the request */
 	request_add_agents (self, req);
 	req->idle_id = g_idle_add (request_start, req);
 }
@@ -1333,7 +1366,7 @@ _con_del_request_done (NMSecretAgent *agent,
 	/* Tell the next agent to delete secrets */
 	request_next_agent (req);
 	if (error)
-		maybe_remove_agent_on_error (agent, error);
+		maybe_remove_agent_on_error (self, agent, error);
 }
 
 static void
@@ -1376,27 +1409,36 @@ nm_agent_manager_delete_secrets (NMAgentManager *self,
 	req->con.connection = g_object_ref (connection);
 	g_object_unref (subject);
 
-	/* Kick off the request */
 	request_add_agents (self, req);
 	req->idle_id = g_idle_add (request_start, req);
 }
 
 /*****************************************************************************/
 
-NMSecretAgent *
-nm_agent_manager_get_agent_by_user (NMAgentManager *self, const char *username)
+gboolean
+nm_agent_manager_has_agent_with_permission (NMAgentManager *self,
+                                            const char *username,
+                                            const char *permission)
 {
-	NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
-	GHashTableIter iter;
+	NMAgentManagerPrivate *priv;
 	NMSecretAgent *agent;
 
-	g_hash_table_iter_init (&iter, priv->agents);
-	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &agent)) {
-		if (g_strcmp0 (nm_secret_agent_get_owner_username (agent), username) == 0)
-			return agent;
+	g_return_val_if_fail (NM_IS_AGENT_MANAGER (self), FALSE);
+	g_return_val_if_fail (username, FALSE);
+	g_return_val_if_fail (permission, FALSE);
+
+	priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
+
+	c_list_for_each_entry (agent, &priv->agent_lst_head, agent_lst) {
+		if (!agent->fully_registered)
+			continue;
+		if (!nm_streq0 (nm_secret_agent_get_owner_username (agent), username))
+			continue;
+		if (nm_secret_agent_has_permission (agent, permission))
+			return TRUE;
 	}
 
-	return NULL;
+	return FALSE;
 }
 
 /*****************************************************************************/
@@ -1407,17 +1449,16 @@ nm_agent_manager_all_agents_have_capability (NMAgentManager *manager,
                                              NMSecretAgentCapabilities capability)
 {
 	NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (manager);
-	GHashTableIter iter;
 	NMSecretAgent *agent;
-	gboolean subject_is_unix_process = nm_auth_subject_is_unix_process (subject);
-	gulong subject_uid = subject_is_unix_process ? nm_auth_subject_get_unix_process_uid (subject) : 0;
+	gboolean subject_is_unix_process = (nm_auth_subject_get_subject_type (subject) == NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS);
+	gulong subject_uid = subject_is_unix_process ? nm_auth_subject_get_unix_process_uid (subject) : 0u;
 
-	g_hash_table_iter_init (&iter, priv->agents);
-	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &agent)) {
+	c_list_for_each_entry (agent, &priv->agent_lst_head, agent_lst) {
+		if (!agent->fully_registered)
+			continue;
 		if (   subject_is_unix_process
 		    && nm_secret_agent_get_owner_uid (agent) != subject_uid)
 			continue;
-
 		if (!(nm_secret_agent_get_capabilities (agent) & capability))
 			return FALSE;
 	}
@@ -1428,58 +1469,13 @@ nm_agent_manager_all_agents_have_capability (NMAgentManager *manager,
 /*****************************************************************************/
 
 static void
-agent_permissions_changed_done (NMAuthChain *chain,
-                                GDBusMethodInvocation *context,
-                                gpointer user_data)
-{
-	NMAgentManager *self = NM_AGENT_MANAGER (user_data);
-	NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
-	NMSecretAgent *agent;
-	gboolean share_protected = FALSE, share_open = FALSE;
-
-	priv->chains = g_slist_remove (priv->chains, chain);
-
-	agent = nm_auth_chain_get_data (chain, "agent");
-	g_assert (agent);
-
-	_LOGD (agent, "updated agent permissions");
-
-	if (nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED) == NM_AUTH_CALL_RESULT_YES)
-		share_protected = TRUE;
-	if (nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN) == NM_AUTH_CALL_RESULT_YES)
-		share_open = TRUE;
-
-	nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, share_protected);
-	nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, share_open);
-}
-
-static void
 authority_changed_cb (NMAuthManager *auth_manager, NMAgentManager *self)
 {
 	NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
-	GHashTableIter iter;
 	NMSecretAgent *agent;
 
-	/* Recheck the permissions of all secret agents */
-	g_hash_table_iter_init (&iter, priv->agents);
-	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &agent)) {
-		NMAuthChain *chain;
-
-		/* Kick off permissions requests for this agent */
-		chain = nm_auth_chain_new_subject (nm_secret_agent_get_subject (agent),
-		                                   NULL,
-		                                   agent_permissions_changed_done,
-		                                   self);
-		g_assert (chain);
-		priv->chains = g_slist_append (priv->chains, chain);
-
-		/* Make sure if the agent quits while the permissions call is in progress
-		 * that the object sticks around until our callback.
-		 */
-		nm_auth_chain_set_data (chain, "agent", g_object_ref (agent), g_object_unref);
-		nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, FALSE);
-		nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, FALSE);
-	}
+	c_list_for_each_entry (agent, &priv->agent_lst_head, agent_lst)
+		_agent_create_auth_chain (self, agent, NULL);
 }
 
 /*****************************************************************************/
@@ -1490,14 +1486,14 @@ nm_agent_manager_init (NMAgentManager *self)
 	NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
 
 	priv->agent_version_id = 1;
-	c_list_init (&priv->requests);
-	priv->agents = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref);
+	c_list_init (&priv->agent_lst_head);
+	c_list_init (&priv->request_lst_head);
 }
 
 static void
 constructed (GObject *object)
 {
-	NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE ((NMAgentManager *) object);
+	NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (object);
 
 	G_OBJECT_CLASS (nm_agent_manager_parent_class)->constructed (object);
 
@@ -1515,24 +1511,19 @@ constructed (GObject *object)
 static void
 dispose (GObject *object)
 {
-	NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE ((NMAgentManager *) object);
-	CList *iter;
-
-cancel_more:
-	c_list_for_each (iter, &priv->requests) {
-		c_list_unlink (iter);
-		req_complete_cancel (c_list_entry (iter, Request, lst_request), TRUE);
-		goto cancel_more;
-	}
-
-	g_slist_free_full (priv->chains, (GDestroyNotify) nm_auth_chain_destroy);
-	priv->chains = NULL;
+	NMAgentManager *self = NM_AGENT_MANAGER (object);
+	NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
+	Request *request;
+	NMSecretAgent *agent;
 
-	if (priv->agents) {
-		g_hash_table_destroy (priv->agents);
-		priv->agents = NULL;
+	while ((request = c_list_first_entry (&priv->request_lst_head, Request, request_lst))) {
+		c_list_unlink (&request->request_lst);
+		req_complete_cancel (request, TRUE);
 	}
 
+	while ((agent = c_list_first_entry (&priv->agent_lst_head, NMSecretAgent, agent_lst)))
+		_agent_remove (self, agent);
+
 	if (priv->auth_mgr) {
 		g_signal_handlers_disconnect_by_func (priv->auth_mgr,
 		                                      G_CALLBACK (authority_changed_cb),