1    	/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2    	/* NetworkManager -- Network link manager
3    	 *
4    	 * This program is free software; you can redistribute it and/or modify
5    	 * it under the terms of the GNU General Public License as published by
6    	 * the Free Software Foundation; either version 2 of the License, or
7    	 * (at your option) any later version.
8    	 *
9    	 * This program is distributed in the hope that it will be useful,
10   	 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   	 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   	 * GNU General Public License for more details.
13   	 *
14   	 * You should have received a copy of the GNU General Public License along
15   	 * with this program; if not, write to the Free Software Foundation, Inc.,
16   	 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17   	 *
18   	 * Copyright (C) 2010 - 2011 Red Hat, Inc.
19   	 */
20   	
21   	#include <config.h>
22   	#include <string.h>
23   	#include <pwd.h>
24   	
25   	#include <glib.h>
26   	#include <dbus/dbus-glib.h>
27   	#include <dbus/dbus-glib-lowlevel.h>
28   	
29   	#include "NetworkManager.h"
30   	#include "nm-logging.h"
31   	#include "nm-agent-manager.h"
32   	#include "nm-secret-agent.h"
33   	#include "nm-manager-auth.h"
34   	#include "nm-dbus-glib-types.h"
35   	#include "nm-manager-auth.h"
36   	#include "nm-setting-vpn.h"
37   	#include "nm-setting-connection.h"
38   	#include "nm-enum-types.h"
39   	
40   	G_DEFINE_TYPE (NMAgentManager, nm_agent_manager, G_TYPE_OBJECT)
41   	
42   	#define NM_AGENT_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
43   	                                         NM_TYPE_AGENT_MANAGER, \
44   	                                         NMAgentManagerPrivate))
45   	
46   	typedef struct {
47   		gboolean disposed;
48   	
49   		NMDBusManager *dbus_mgr;
50   		NMSessionMonitor *session_monitor;
51   	
52   		/* Auth chains for checking agent permissions */
53   		GSList *chains;
54   	
55   		/* Hashed by owner name, not identifier, since two agents in different
56   		 * sessions can use the same identifier.
57   		 */
58   		GHashTable *agents;
59   	
60   		GHashTable *requests;
61   	} NMAgentManagerPrivate;
62   	
63   	enum {
64   	        AGENT_REGISTERED,
65   	
66   	        LAST_SIGNAL
67   	};
68   	static guint signals[LAST_SIGNAL] = { 0 };
69   	
70   	
71   	typedef struct _Request Request;
72   	
73   	static void request_add_agent (Request *req,
74   	                               NMSecretAgent *agent,
75   	                               NMSessionMonitor *session_monitor);
76   	
77   	static void request_remove_agent (Request *req, NMSecretAgent *agent);
78   	
79   	static void impl_agent_manager_register (NMAgentManager *self,
80   	                                         const char *identifier,
81   	                                         DBusGMethodInvocation *context);
82   	
83   	static void impl_agent_manager_register_with_capabilities (NMAgentManager *self,
84   	                                                           const char *identifier,
85   	                                                           NMSecretAgentCapabilities capabilities,
86   	                                                           DBusGMethodInvocation *context);
87   	
88   	static void impl_agent_manager_unregister (NMAgentManager *self,
89   	                                           DBusGMethodInvocation *context);
90   	
91   	#include "nm-agent-manager-glue.h"
92   	
93   	/********************************************************************/
94   	
95   	#define NM_AGENT_MANAGER_ERROR         (nm_agent_manager_error_quark ())
96   	
97   	static GQuark
98   	nm_agent_manager_error_quark (void)
99   	{
100  		static GQuark ret = 0;
101  	
102  		if (G_UNLIKELY (ret == 0))
103  			ret = g_quark_from_static_string ("nm-agent-manager-error");
104  		return ret;
105  	}
106  	
107  	/*************************************************************/
108  	
109  	static gboolean
110  	remove_agent (NMAgentManager *self, const char *owner)
111  	{
112  		NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
113  		NMSecretAgent *agent;
114  		GHashTableIter iter;
115  		gpointer data;
116  	
117  		g_return_val_if_fail (owner != NULL, FALSE);
118  	
119  		/* Make sure this agent has already registered */
120  		agent = g_hash_table_lookup (priv->agents, owner);
121  		if (!agent)
122  			return FALSE;
123  	
124  		nm_log_dbg (LOGD_AGENTS, "(%s) agent unregistered or disappeared",
125  		            nm_secret_agent_get_description (agent));
126  	
127  		/* Remove this agent to any in-progress secrets requests */
128  		g_hash_table_iter_init (&iter, priv->requests);
129  		while (g_hash_table_iter_next (&iter, NULL, &data))
130  			request_remove_agent ((Request *) data, agent);
131  	
132  		/* And dispose of the agent */
133  		g_hash_table_remove (priv->agents, owner);
134  		return TRUE;
135  	}
136  	
137  	/*************************************************************/
138  	
139  	static gboolean
140  	validate_identifier (const char *identifier, GError **error)
141  	{
142  		const char *p = identifier;
143  		size_t id_len;
144  	
145  		if (!identifier) {
146  			g_set_error_literal (error,
147  			                     NM_AGENT_MANAGER_ERROR,
148  			                     NM_AGENT_MANAGER_ERROR_INVALID_IDENTIFIER,
149  			                     "No identifier was given");
150  			return FALSE;
151  		}
152  	
153  		/* Length between 3 and 255 characters inclusive */
154  		id_len = strlen (identifier);
155  		if (id_len < 3 || id_len > 255) {
156  			g_set_error_literal (error,
157  			                     NM_AGENT_MANAGER_ERROR,
158  			                     NM_AGENT_MANAGER_ERROR_INVALID_IDENTIFIER,
159  			                     "Identifier length not between 3 and 255 characters (inclusive)");
160  			return FALSE;
161  		}
162  	
163  		if ((identifier[0] == '.') || (identifier[id_len - 1] == '.')) {
164  			g_set_error_literal (error,
165  			                     NM_AGENT_MANAGER_ERROR,
166  			                     NM_AGENT_MANAGER_ERROR_INVALID_IDENTIFIER,
167  			                     "Identifier must not start or end with '.'");
168  			return FALSE;
169  		}
170  	
171  		/* FIXME: do complete validation here */
172  		while (p && *p) {
173  			if (!g_ascii_isalnum (*p) && (*p != '_') && (*p != '-') && (*p != '.')) {
174  				g_set_error (error,
175  				             NM_AGENT_MANAGER_ERROR,
176  					         NM_AGENT_MANAGER_ERROR_INVALID_IDENTIFIER,
177  					         "Identifier contains invalid character '%c'", *p);
178  				return FALSE;
179  			}
180  	
181  			if ((*p == '.') && (*(p + 1) == '.')) {
182  				g_set_error_literal (error,
183  				                     NM_AGENT_MANAGER_ERROR,
184  					                 NM_AGENT_MANAGER_ERROR_INVALID_IDENTIFIER,
185  					                 "Identifier contains two '.' characters in sequence");
186  				return FALSE;
187  			}
188  			p++;
189  		}
190  	
191  		return TRUE;
192  	}
193  	
194  	static void
195  	agent_register_permissions_done (NMAuthChain *chain,
196  	                                 GError *error,
197  	                                 DBusGMethodInvocation *context,
198  	                                 gpointer user_data)
199  	{
200  		NMAgentManager *self = NM_AGENT_MANAGER (user_data);
201  		NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
202  		NMSecretAgent *agent;
203  		const char *sender;
204  		GError *local = NULL;
205  		NMAuthCallResult result;
206  		GHashTableIter iter;
207  		Request *req;
208  	
209  		priv->chains = g_slist_remove (priv->chains, chain);
210  	
211  		if (error) {
212  			local = g_error_new (NM_AGENT_MANAGER_ERROR,
213  			                     NM_AGENT_MANAGER_ERROR_PERMISSION_DENIED,
214  			                     "Failed to request agent permissions: (%d) %s",
215  			                     error->code, error->message);
216  			dbus_g_method_return_error (context, local);
217  			g_error_free (local);
218  		} else {
219  			agent = nm_auth_chain_steal_data (chain, "agent");
220  			g_assert (agent);
221  	
222  			result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED);
223  			if (result == NM_AUTH_CALL_RESULT_YES)
224  				nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, TRUE);
225  	
226  			result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN);
227  			if (result == NM_AUTH_CALL_RESULT_YES)
228  				nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, TRUE);
229  	
230  			sender = nm_secret_agent_get_dbus_owner (agent);
231  			g_hash_table_insert (priv->agents, g_strdup (sender), agent);
232  			nm_log_dbg (LOGD_AGENTS, "(%s) agent registered",
233  			            nm_secret_agent_get_description (agent));
234  			dbus_g_method_return (context);
235  	
236  			/* Signal an agent was registered */
237  			g_signal_emit (self, signals[AGENT_REGISTERED], 0, agent);
238  	
239  			/* Add this agent to any in-progress secrets requests */
240  			g_hash_table_iter_init (&iter, priv->requests);
241  			while (g_hash_table_iter_next (&iter, NULL, (gpointer) &req))
242  				request_add_agent (req, agent, priv->session_monitor);
243  		}
244  	
245  		nm_auth_chain_unref (chain);
246  	}
247  	
248  	static NMSecretAgent *
249  	find_agent_by_identifier_and_uid (NMAgentManager *self,
250  	                                  const char *identifier,
251  	                                  gulong sender_uid)
252  	{
253  		NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
254  		GHashTableIter iter;
255  		NMSecretAgent *agent;
256  	
257  		g_hash_table_iter_init (&iter, priv->agents);
258  		while (g_hash_table_iter_next (&iter, NULL, (gpointer) &agent)) {
259  			if (   g_strcmp0 (nm_secret_agent_get_identifier (agent), identifier) == 0
260  			    && nm_secret_agent_get_owner_uid (agent) == sender_uid)
261  				return agent;
262  		}
263  		return NULL;
264  	}
265  	
266  	static void
267  	impl_agent_manager_register_with_capabilities (NMAgentManager *self,
268  	                                               const char *identifier,
269  	                                               NMSecretAgentCapabilities capabilities,
270  	                                               DBusGMethodInvocation *context)
271  	{
272  		NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
273  		char *sender = NULL;
274  		gulong sender_uid = G_MAXULONG;
275  		GError *error = NULL, *local = NULL;
276  		NMSecretAgent *agent;
277  		NMAuthChain *chain;
278  		const char *error_desc = NULL;
279  	
280  		if (!nm_dbus_manager_get_caller_info (priv->dbus_mgr,
281  		                                      context,
282  		                                      &sender,
283  		                                      &sender_uid)) {
284  			error = g_error_new_literal (NM_AGENT_MANAGER_ERROR,
285  			                             NM_AGENT_MANAGER_ERROR_SENDER_UNKNOWN,
286  			                             "Unable to determine request sender and UID.");
287  			goto done;
288  		}
289  	
290  		if (   0 != sender_uid
291  		    && !nm_session_monitor_uid_has_session (priv->session_monitor,
292  		                                            sender_uid,
293  		                                            NULL,
294  		                                            &local)) {
295  			error = g_error_new_literal (NM_AGENT_MANAGER_ERROR,
296  			                             NM_AGENT_MANAGER_ERROR_SESSION_NOT_FOUND,
297  			                             local && local->message ? local->message : "Session not found");
298  			goto done;
299  		}
300  	
301  		/* Validate the identifier */
302  		if (!validate_identifier (identifier, &error))
303  			goto done;
304  	
305  		/* Only one agent for each identifier is allowed per user */
306  		if (find_agent_by_identifier_and_uid (self, identifier, sender_uid)) {
307  			error = g_error_new_literal (NM_AGENT_MANAGER_ERROR,
308  			                             NM_AGENT_MANAGER_ERROR_PERMISSION_DENIED,
309  			                             "An agent with this ID is already registered for this user.");
310  			goto done;
311  		}
312  	
313  		/* Success, add the new agent */
314  		agent = nm_secret_agent_new (context, sender, identifier, sender_uid, capabilities);
315  		if (!agent) {
316  			error = g_error_new_literal (NM_AGENT_MANAGER_ERROR,
317  			                             NM_AGENT_MANAGER_ERROR_INTERNAL_ERROR,
318  			                             "Failed to initialize the agent");
319  			goto done;
320  		}
321  	
322  		nm_log_dbg (LOGD_AGENTS, "(%s) requesting permissions",
323  		            nm_secret_agent_get_description (agent));
324  	
325  		/* Kick off permissions requests for this agent */
326  		chain = nm_auth_chain_new (context, agent_register_permissions_done, self, &error_desc);
327  		if (chain) {
328  			nm_auth_chain_set_data (chain, "agent", agent, g_object_unref);
329  			nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, FALSE);
330  			nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, FALSE);
331  	
332  			priv->chains = g_slist_append (priv->chains, chain);
333  		} else {
334  			error = g_error_new_literal (NM_AGENT_MANAGER_ERROR,
335  			                             NM_AGENT_MANAGER_ERROR_SENDER_UNKNOWN,
336  			                             error_desc);
337  		}
338  	
339  	done:
340  		if (error)
341  			dbus_g_method_return_error (context, error);
342  		g_clear_error (&error);
343  		g_clear_error (&local);
344  		g_free (sender);
345  	}
346  	
347  	static void
348  	impl_agent_manager_register (NMAgentManager *self,
349  	                             const char *identifier,
350  	                             DBusGMethodInvocation *context)
351  	{
352  		impl_agent_manager_register_with_capabilities (self, identifier, 0, context);
353  	}
354  	
355  	static void
356  	impl_agent_manager_unregister (NMAgentManager *self,
357  	                               DBusGMethodInvocation *context)
358  	{
359  		NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
360  		GError *error = NULL;
361  		char *sender = NULL;
362  	
363  		if (!nm_dbus_manager_get_caller_info (priv->dbus_mgr,
364  		                                      context,
365  		                                      &sender,
366  		                                      NULL)) {
367  			error = g_error_new_literal (NM_AGENT_MANAGER_ERROR,
368  			                             NM_AGENT_MANAGER_ERROR_SENDER_UNKNOWN,
369  			                             "Unable to determine request sender.");
370  			goto done;
371  		}
372  	
373  		/* Found the agent, unregister and remove it */
374  		if (!remove_agent (self, sender)) {
375  			error = g_error_new_literal (NM_AGENT_MANAGER_ERROR,
376  			                             NM_AGENT_MANAGER_ERROR_NOT_REGISTERED,
377  			                             "Caller is not registered as an Agent");
378  			goto done;
379  		}
380  	
381  		dbus_g_method_return (context);
382  	
383  	done:
384  		if (error)
385  			dbus_g_method_return_error (context, error);
386  		g_clear_error (&error);
387  		g_free (sender);
388  	}
389  	
390  	/*************************************************************/
391  	
392  	typedef void (*RequestCompleteFunc) (Request *req,
393  	                                     GHashTable *secrets,
394  	                                     const char *agent_dbus_owner,
395  	                                     const char *agent_username,
396  	                                     GError *error,
397  	                                     gpointer user_data);
398  	typedef gboolean (*RequestAddAgentFunc) (Request *req,
399  	                                         NMSecretAgent *agent,
400  	                                         NMSessionMonitor *session_monitor);
401  	typedef void (*RequestNextFunc) (Request *req);
402  	typedef void (*RequestCancelFunc) (Request *req);
403  	
404  	/* Basic secrets request structure */
405  	struct _Request {
406  		guint32 reqid;
407  		char *detail;
408  		char *verb;
409  	
410  		gboolean filter_by_uid;
411  		gulong uid_filter;
412  	
413  		/* Current agent being asked for secrets */
414  		NMSecretAgent *current;
415  		gconstpointer current_call_id;
416  	
417  		/* Stores the sorted list of NMSecretAgents which will be asked for secrets */
418  		GSList *pending;
419  	
420  		/* Stores the list of NMSecretAgent hashes that we've already
421  		 * asked for secrets, so that we don't ask the same agent twice
422  		 * if it quits and re-registers during this secrets request.
423  		 */
424  		GSList *asked;
425  	
426  		guint32 idle_id;
427  	
428  		RequestAddAgentFunc add_agent_callback;
429  		RequestCancelFunc cancel_callback;
430  		RequestNextFunc next_callback;
431  		RequestCompleteFunc complete_callback;
432  		gpointer complete_callback_data;
433  		gboolean completed;
434  	
435  		GDestroyNotify free_func;
436  	};
437  	
438  	static guint32 next_req_id = 1;
439  	
440  	static Request *
441  	request_new (gsize struct_size,
442  	             const char *detail,
443  	             const char *verb,
444  	             gboolean filter_by_uid,
445  	             gulong uid_filter,
446  	             RequestCompleteFunc complete_callback,
447  	             gpointer complete_callback_data,
448  	             RequestAddAgentFunc add_agent_callback,
449  	             RequestNextFunc next_callback,
450  	             RequestCancelFunc cancel_callback,
451  	             GDestroyNotify free_func)
452  	{
453  		Request *req;
454  	
455  		req = g_malloc0 (struct_size);
456  		req->reqid = next_req_id++;
457  		req->detail = g_strdup (detail);
458  		req->verb = g_strdup (verb);
459  		req->filter_by_uid = filter_by_uid;
460  		req->uid_filter = uid_filter;
461  		req->complete_callback = complete_callback;
462  		req->complete_callback_data = complete_callback_data;
463  		req->add_agent_callback = add_agent_callback,
464  		req->next_callback = next_callback;
465  		req->cancel_callback = cancel_callback;
466  		req->free_func = free_func;
467  		return req;
468  	}
469  	
470  	static void
471  	request_free (Request *req)
472  	{
473  		if (req->free_func)
474  			req->free_func ((gpointer) req);
475  	
476  		if (req->idle_id)
477  			g_source_remove (req->idle_id);
478  	
479  		if (!req->completed && req->cancel_callback)
480  			req->cancel_callback (req);
481  	
482  		g_free (req->detail);
483  		g_free (req->verb);
484  		g_slist_free (req->pending);
485  		g_slist_free (req->asked);
486  		memset (req, 0, sizeof (Request));
487  		g_free (req);
488  	}
489  	
490  	static void
491  	req_complete_success (Request *req,
492  	                      GHashTable *secrets,
493  	                      const char *agent_dbus_owner,
494  	                      const char *agent_uname)
495  	{
496  		req->completed = TRUE;
497  		req->complete_callback (req,
498  		                        secrets,
499  		                        agent_dbus_owner,
500  		                        agent_uname,
501  		                        NULL,
502  		                        req->complete_callback_data);
503  	}
504  	
505  	static void
506  	req_complete_error (Request *req, GError *error)
507  	{
508  		req->completed = TRUE;
509  		req->complete_callback (req, NULL, NULL, NULL, error, req->complete_callback_data);
510  	}
511  	
512  	static gint
513  	agent_compare_func (NMSecretAgent *a, NMSecretAgent *b, gpointer user_data)
514  	{
515  		NMSessionMonitor *session_monitor = NM_SESSION_MONITOR (user_data);
516  		gboolean a_active, b_active;
517  	
518  		if (a && !b)
519  			return -1;
520  		else if (a == b)
521  			return 0;
522  		else if (!a && b)
523  			return 1;
524  	
525  		/* Prefer agents in active sessions */
526  		a_active = nm_session_monitor_uid_active (session_monitor,
527  		                                          nm_secret_agent_get_owner_uid (a),
528  		                                          NULL);
529  		b_active = nm_session_monitor_uid_active (session_monitor,
530  		                                          nm_secret_agent_get_owner_uid (b),
531  		                                          NULL);
532  		if (a_active && !b_active)
533  			return -1;
534  		else if (a_active == b_active)
535  			return 0;
536  		else if (!a_active && b_active)
537  			return 1;
538  	
539  		return 0;
540  	}
541  	
542  	static void
543  	request_add_agent (Request *req,
544  	                   NMSecretAgent *agent,
545  	                   NMSessionMonitor *session_monitor)
546  	{
547  		uid_t agent_uid;
548  	
549  		g_return_if_fail (req != NULL);
550  		g_return_if_fail (agent != NULL);
551  	
552  		if (g_slist_find (req->asked, GUINT_TO_POINTER (nm_secret_agent_get_hash (agent))))
553  			return;
554  	
555  		if (req->add_agent_callback && !req->add_agent_callback (req, agent, session_monitor))
556  			return;
557  	
558  		/* If the request should filter agents by UID, do that now */
559  		agent_uid = nm_secret_agent_get_owner_uid (agent);
560  		if (req->filter_by_uid && (agent_uid != req->uid_filter)) {
561  			nm_log_dbg (LOGD_AGENTS, "(%s) agent ignored for secrets request %p/%s "
562  			            "(uid %d not required %ld)",
563  			            nm_secret_agent_get_description (agent),
564  			            req, req->detail, agent_uid, req->uid_filter);
565  			return;
566  		}
567  	
568  		nm_log_dbg (LOGD_AGENTS, "(%s) agent allowed for secrets request %p/%s",
569  		            nm_secret_agent_get_description (agent),
570  		            req, req->detail);
571  	
572  		/* Add this agent to the list, preferring active sessions */
573  		req->pending = g_slist_insert_sorted_with_data (req->pending,
574  		                                                agent,
575  		                                                (GCompareDataFunc) agent_compare_func,
576  		                                                session_monitor);
577  	}
578  	
579  	static void
580  	request_add_agents (NMAgentManager *self, Request *req)
581  	{
582  		NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
583  		GHashTableIter iter;
584  		gpointer data;
585  	
586  		g_hash_table_iter_init (&iter, priv->agents);
587  		while (g_hash_table_iter_next (&iter, NULL, &data))
588  			request_add_agent (req, NM_SECRET_AGENT (data), priv->session_monitor);
589  	}
590  	
591  	static void
592  	request_next_agent (Request *req)
593  	{
594  		GError *error = NULL;
595  	
596  		if (req->pending) {
597  			/* Send the request to the next agent */
598  			req->current_call_id = NULL;
599  			req->current = req->pending->data;
600  			req->pending = g_slist_remove (req->pending, req->current);
601  	
602  			nm_log_dbg (LOGD_AGENTS, "(%s) agent %s secrets for request %p/%s",
603  			            nm_secret_agent_get_description (req->current),
604  			            req->verb, req, req->detail);
605  	
606  			req->next_callback (req);
607  		} else {
608  			req->current_call_id = NULL;
609  			req->current = NULL;
610  	
611  			/* No more secret agents are available to fulfill this secrets request */
612  			error = g_error_new_literal (NM_AGENT_MANAGER_ERROR,
613  			                             NM_AGENT_MANAGER_ERROR_NO_SECRETS,
614  			                             "No agents were available for this request.");
615  			req_complete_error (req, error);
616  			g_error_free (error);
617  		}
618  	}
619  	
620  	static void
621  	request_remove_agent (Request *req, NMSecretAgent *agent)
622  	{
623  		g_return_if_fail (req != NULL);
624  		g_return_if_fail (agent != NULL);
625  	
626  		req->pending = g_slist_remove (req->pending, agent);
627  	
628  		if (agent == req->current) {
629  			nm_log_dbg (LOGD_AGENTS, "(%s) current agent removed from secrets request %p/%s",
630  			            nm_secret_agent_get_description (agent), req, req->detail);
631  			request_next_agent (req);
632  		} else {
633  			nm_log_dbg (LOGD_AGENTS, "(%s) agent removed from secrets request %p/%s",
634  			            nm_secret_agent_get_description (agent), req, req->detail);
635  		}
636  	}
637  	
638  	static gboolean
639  	request_start (gpointer user_data)
640  	{
641  		Request *req = user_data;
642  	
643  		req->idle_id = 0;
644  		request_next_agent (req);
645  		return FALSE;
646  	}
647  	
648  	/*************************************************************/
649  	
650  	/* Request subclass for connection secrets */
651  	typedef struct {
652  		Request parent;
653  	
654  		NMSettingsGetSecretsFlags flags;
655  		NMConnection *connection;
656  		char *setting_name;
657  		char **hints;
658  	
659  		GHashTable *existing_secrets;
660  	
661  		NMAgentSecretsResultFunc callback;
662  		gpointer callback_data;
663  		gpointer other_data2;
664  		gpointer other_data3;
665  	
666  		NMAuthChain *chain;
667  	
668  		/* Whether the agent currently being asked for secrets
669  		 * has the system.modify privilege.
670  		 */
671  		gboolean current_has_modify;
672  	} ConnectionRequest;
673  	
674  	static void
675  	connection_request_free (gpointer data)
676  	{
677  		ConnectionRequest *req = data;
678  	
679  		g_object_unref (req->connection);
680  		g_free (req->setting_name);
681  		g_strfreev (req->hints);
682  		if (req->existing_secrets)
683  			g_hash_table_unref (req->existing_secrets);
684  		if (req->chain)
685  			nm_auth_chain_unref (req->chain);
686  	}
687  	
688  	static gboolean
689  	connection_request_add_agent (Request *parent,
690  	                              NMSecretAgent *agent,
691  	                              NMSessionMonitor *session_monitor)
692  	{
693  		ConnectionRequest *req = (ConnectionRequest *) parent;
694  		uid_t agent_uid = nm_secret_agent_get_owner_uid (agent);
695  	
696  		/* Ensure the caller's username exists in the connection's permissions,
697  		 * or that the permissions is empty (ie, visible by everyone).
698  		 */
699  		if (!nm_auth_uid_in_acl (req->connection, session_monitor, agent_uid, NULL)) {
700  			nm_log_dbg (LOGD_AGENTS, "(%s) agent ignored for secrets request %p/%s (not in ACL)",
701  			            nm_secret_agent_get_description (agent),
702  			            parent, parent->detail);
703  			/* Connection not visible to this agent's user */
704  			return FALSE;
705  		}
706  	
707  		return TRUE;
708  	}
709  	
710  	static ConnectionRequest *
711  	connection_request_new_get (NMConnection *connection,
712  	                            gboolean filter_by_uid,
713  	                            gulong uid_filter,
714  	                            GHashTable *existing_secrets,
715  	                            const char *setting_name,
716  	                            const char *verb,
717  	                            NMSettingsGetSecretsFlags flags,
718  	                            const char **hints,
719  	                            NMAgentSecretsResultFunc callback,
720  	                            gpointer callback_data,
721  	                            gpointer other_data2,
722  	                            gpointer other_data3,
723  	                            RequestCompleteFunc complete_callback,
724  	                            gpointer complete_callback_data,
725  	                            RequestNextFunc next_callback,
726  	                            RequestCancelFunc cancel_callback)
727  	{
728  		ConnectionRequest *req;
729  	
730  		req = (ConnectionRequest *) request_new (sizeof (ConnectionRequest),
731  		                                         nm_connection_get_id (connection),
732  		                                         verb,
733  		                                         filter_by_uid,
734  		                                         uid_filter,
735  		                                         complete_callback,
736  		                                         complete_callback_data,
737  		                                         connection_request_add_agent,
738  		                                         next_callback,
739  		                                         cancel_callback,
740  		                                         connection_request_free);
741  		g_assert (req);
742  	
743  		req->connection = g_object_ref (connection);
744  		if (existing_secrets)
745  			req->existing_secrets = g_hash_table_ref (existing_secrets);
746  		req->setting_name = g_strdup (setting_name);
747  		req->hints = g_strdupv ((char **) hints);
748  		req->flags = flags;
749  		req->callback = callback;
750  		req->callback_data = callback_data;
751  		req->other_data2 = other_data2;
752  		req->other_data3 = other_data3;
753  		return req;
754  	}
755  	
756  	static ConnectionRequest *
757  	connection_request_new_other (NMConnection *connection,
758  	                              gboolean filter_by_uid,
759  	                              gulong uid_filter,
760  	                              const char *verb,
761  	                              RequestCompleteFunc complete_callback,
762  	                              gpointer complete_callback_data,
763  	                              RequestNextFunc next_callback)
764  	{
765  		ConnectionRequest *req;
766  	
767  		req = (ConnectionRequest *) request_new (sizeof (ConnectionRequest),
768  		                                         nm_connection_get_id (connection),
769  		                                         verb,
770  		                                         filter_by_uid,
771  		                                         uid_filter,
772  		                                         complete_callback,
773  		                                         complete_callback_data,
774  		                                         NULL,
775  		                                         next_callback,
776  		                                         NULL,
777  		                                         connection_request_free);
778  		g_assert (req);
779  		req->connection = g_object_ref (connection);
780  		return req;
781  	}
782  	
783  	static void
784  	get_done_cb (NMSecretAgent *agent,
785  	             gconstpointer call_id,
786  	             GHashTable *secrets,
787  	             GError *error,
788  	             gpointer user_data)
789  	{
790  		Request *parent = user_data;
791  		ConnectionRequest *req = user_data;
792  		GHashTable *setting_secrets;
793  		const char *agent_dbus_owner;
794  		struct passwd *pw;
795  		char *agent_uname = NULL;
796  	
797  		g_return_if_fail (call_id == parent->current_call_id);
798  	
799  		if (error) {
800  			nm_log_dbg (LOGD_AGENTS, "(%s) agent failed secrets request %p/%s/%s: (%d) %s",
801  			            nm_secret_agent_get_description (agent),
802  			            req, parent->detail, req->setting_name,
803  			            error ? error->code : -1,
804  			            (error && error->message) ? error->message : "(unknown)");
805  			/* Try the next agent */
806  			request_next_agent (parent);
807  			return;
808  		}
809  	
810  		/* Ensure the setting we wanted secrets for got returned and has something in it */
811  		setting_secrets = g_hash_table_lookup (secrets, req->setting_name);
812  		if (!setting_secrets || !g_hash_table_size (setting_secrets)) {
813  			nm_log_dbg (LOGD_AGENTS, "(%s) agent returned no secrets for request %p/%s/%s",
814  			            nm_secret_agent_get_description (agent),
815  			            req, parent->detail, req->setting_name);
816  			/* Try the next agent */
817  			request_next_agent (parent);
818  			return;
819  		}
820  	
821  		nm_log_dbg (LOGD_AGENTS, "(%s) agent returned secrets for request %p/%s/%s",
822  		            nm_secret_agent_get_description (agent),
823  		            req, parent->detail, req->setting_name);
824  	
825  		/* Get the agent's username */
826  		pw = getpwuid (nm_secret_agent_get_owner_uid (agent));
827  		if (pw && strlen (pw->pw_name)) {
828  			/* Needs to be UTF-8 valid since it may be pushed through D-Bus */
829  			if (g_utf8_validate (pw->pw_name, -1, NULL))
830  				agent_uname = g_strdup (pw->pw_name);
831  		}
832  	
833  		agent_dbus_owner = nm_secret_agent_get_dbus_owner (agent);
834  		req_complete_success (parent, secrets, agent_dbus_owner, agent_uname);
835  		g_free (agent_uname);
836  	}
837  	
838  	static void
839  	set_secrets_not_required (NMConnection *connection, GHashTable *hash)
840  	{
841  		GHashTableIter iter, setting_iter;
842  		const char *setting_name = NULL;
843  		GHashTable *setting_hash = NULL;
844  	
845  		/* Iterate through the settings hashes */
846  		g_hash_table_iter_init (&iter, hash);
847  		while (g_hash_table_iter_next (&iter,
848  		                               (gpointer *) &setting_name,
849  		                               (gpointer *) &setting_hash)) {
850  			const char *key_name = NULL;
851  			NMSetting *setting;
852  			GValue *val;
853  	
854  			setting = nm_connection_get_setting_by_name (connection, setting_name);
855  			if (setting) {
856  				/* Now through each secret in the setting and mark it as not required */
857  				g_hash_table_iter_init (&setting_iter, setting_hash);
858  				while (g_hash_table_iter_next (&setting_iter, (gpointer *) &key_name, (gpointer *) &val)) {
859  					/* For each secret, set the flag that it's not required; VPN
860  					 * secrets need slightly different treatment here since the
861  					 * "secrets" property is actually a hash table of secrets.
862  					 */
863  					if (   strcmp (setting_name, NM_SETTING_VPN_SETTING_NAME) == 0
864  					    && strcmp (key_name, NM_SETTING_VPN_SECRETS) == 0
865  					    && G_VALUE_HOLDS (val, DBUS_TYPE_G_MAP_OF_STRING)) {
866  						GHashTableIter vpn_secret_iter;
867  						const char *secret_name;
868  	
869  						g_hash_table_iter_init (&vpn_secret_iter, g_value_get_boxed (val));
870  						while (g_hash_table_iter_next (&vpn_secret_iter, (gpointer *) &secret_name, NULL))
871  							nm_setting_set_secret_flags (setting, secret_name, NM_SETTING_SECRET_FLAG_NOT_REQUIRED, NULL);
872  					} else
873  						nm_setting_set_secret_flags (setting, key_name, NM_SETTING_SECRET_FLAG_NOT_REQUIRED, NULL);
874  				}
875  			}
876  		}
877  	}
878  	
879  	static void
880  	get_agent_request_secrets (ConnectionRequest *req, gboolean include_system_secrets)
881  	{
882  		Request *parent = (Request *) req;
883  		NMConnection *tmp;
884  	
885  		tmp = nm_connection_duplicate (req->connection);
886  		nm_connection_clear_secrets (tmp);
(1) Event cond_true: Condition "include_system_secrets", taking true branch
887  		if (include_system_secrets) {
(2) Event cond_true: Condition "req->existing_secrets", taking true branch
888  			if (req->existing_secrets)
(3) Event check_return: Calling function "nm_connection_update_secrets(NMConnection *, char const *, GHashTable *, GError **)" without checking return value (as is done elsewhere 12 out of 14 times).
(14) Event unchecked_value: No check of the return value of "nm_connection_update_secrets(tmp, req->setting_name, req->existing_secrets, NULL)".
Also see events: [example_assign][example_checked][example_assign][example_checked][example_assign][example_checked][example_assign][example_checked][example_assign][example_checked]
889  				nm_connection_update_secrets (tmp, req->setting_name, req->existing_secrets, NULL);
890  		} else {
891  			/* Update secret flags in the temporary connection to indicate that
892  			 * the system secrets we're not sending to the agent aren't required,
893  			 * so the agent can properly validate UI controls and such.
894  			 */
895  			if (req->existing_secrets)
896  				set_secrets_not_required (tmp, req->existing_secrets);
897  		}
898  	
899  		parent->current_call_id = nm_secret_agent_get_secrets (parent->current,
900  		                                                       tmp,
901  		                                                       req->setting_name,
902  		                                                       (const char **) req->hints,
903  		                                                       req->flags,
904  		                                                       get_done_cb,
905  		                                                       req);
906  		if (parent->current_call_id == NULL) {
907  			/* Shouldn't hit this, but handle it anyway */
908  			g_warn_if_fail (parent->current_call_id != NULL);
909  			request_next_agent (parent);
910  		}
911  	
912  		g_object_unref (tmp);
913  	}
914  	
915  	static void
916  	get_agent_modify_auth_cb (NMAuthChain *chain,
917  	                          GError *error,
918  	                          DBusGMethodInvocation *context,
919  	                          gpointer user_data)
920  	{
921  		Request *parent = user_data;
922  		ConnectionRequest *req = user_data;
923  		const char *perm;
924  	
925  		req->chain = NULL;
926  	
927  		if (error) {
928  			nm_log_dbg (LOGD_AGENTS, "(%s) agent %p/%s/%s MODIFY check error: (%d) %s",
929  			            nm_secret_agent_get_description (parent->current),
930  			            req, parent->detail, req->setting_name,
931  			            error->code, error->message ? error->message : "(unknown)");
932  			/* Try the next agent */
933  			request_next_agent (parent);
934  		} else {
935  			/* If the agent obtained the 'modify' permission, we send all system secrets
936  			 * to it.  If it didn't, we still ask it for secrets, but we don't send
937  			 * any system secrets.
938  			 */
939  			perm = nm_auth_chain_get_data (chain, "perm");
940  			g_assert (perm);
941  			if (nm_auth_chain_get_result (chain, perm) == NM_AUTH_CALL_RESULT_YES)
942  				req->current_has_modify = TRUE;
943  	
944  			nm_log_dbg (LOGD_AGENTS, "(%s) agent %p/%s/%s MODIFY check result %s",
945  			            nm_secret_agent_get_description (parent->current),
946  			            req, parent->detail, req->setting_name,
947  			            req->current_has_modify ? "YES" : "NO");
948  	
949  			get_agent_request_secrets (req, req->current_has_modify);
950  		}
951  	
952  		nm_auth_chain_unref (chain);
953  	}
954  	
955  	static void
956  	check_system_secrets_cb (NMSetting *setting,
957  	                         const char *key,
958  	                         const GValue *value,
959  	                         GParamFlags flags,
960  	                         gpointer user_data)
961  	{
962  		NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
963  		gboolean *has_system = user_data;
964  	
965  		if (!(flags & NM_SETTING_PARAM_SECRET))
966  			return;
967  	
968  		/* Clear out system-owned or always-ask secrets */
969  		if (NM_IS_SETTING_VPN (setting) && !strcmp (key, NM_SETTING_VPN_SECRETS)) {
970  			GHashTableIter iter;
971  			const char *secret_name = NULL;
972  	
973  			/* VPNs are special; need to handle each secret separately */
974  			g_hash_table_iter_init (&iter, (GHashTable *) g_value_get_boxed (value));
975  			while (g_hash_table_iter_next (&iter, (gpointer *) &secret_name, NULL)) {
976  				secret_flags = NM_SETTING_SECRET_FLAG_NONE;
977  				nm_setting_get_secret_flags (setting, secret_name, &secret_flags, NULL);
978  				if (secret_flags == NM_SETTING_SECRET_FLAG_NONE)
979  					*has_system = TRUE;
980  			}
981  		} else {
982  			nm_setting_get_secret_flags (setting, key, &secret_flags, NULL);
983  			if (secret_flags == NM_SETTING_SECRET_FLAG_NONE)
984  				*has_system = TRUE;
985  		}
986  	}
987  	
988  	static gboolean
989  	has_system_secrets (NMConnection *connection)
990  	{
991  		gboolean has_system = FALSE;
992  	
993  		nm_connection_for_each_setting_value (connection, check_system_secrets_cb, &has_system);
994  		return has_system;
995  	}
996  	
997  	static void
998  	get_next_cb (Request *parent)
999  	{
1000 		ConnectionRequest *req = (ConnectionRequest *) parent;
1001 		NMSettingConnection *s_con;
1002 		const char *agent_dbus_owner, *perm;
1003 	
1004 		req->current_has_modify = FALSE;
1005 	
1006 		agent_dbus_owner = nm_secret_agent_get_dbus_owner (parent->current);
1007 	
1008 		/* If the request flags allow user interaction, and there are existing
1009 		 * system secrets (or blank secrets that are supposed to be system-owned),
1010 		 * check whether the agent has the 'modify' permission before sending those
1011 		 * secrets to the agent.  We shouldn't leak system-owned secrets to
1012 		 * unprivileged users.
1013 		 */
1014 		if (   (req->flags != NM_SETTINGS_GET_SECRETS_FLAG_NONE)
1015 		    && (req->existing_secrets || has_system_secrets (req->connection))) {
1016 			nm_log_dbg (LOGD_AGENTS, "(%p/%s/%s) request has system secrets; checking agent %s for MODIFY",
1017 			            req, parent->detail, req->setting_name, agent_dbus_owner);
1018 	
1019 			req->chain = nm_auth_chain_new_dbus_sender (agent_dbus_owner,
1020 			                                            nm_secret_agent_get_owner_uid (parent->current),
1021 			                                            get_agent_modify_auth_cb,
1022 			                                            req);
1023 			g_assert (req->chain);
1024 	
1025 			/* If the caller is the only user in the connection's permissions, then
1026 			 * we use the 'modify.own' permission instead of 'modify.system'.  If the
1027 			 * request affects more than just the caller, require 'modify.system'.
1028 			 */
1029 			s_con = nm_connection_get_setting_connection (req->connection);
1030 			g_assert (s_con);
1031 			if (nm_setting_connection_get_num_permissions (s_con) == 1)
1032 				perm = NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN;
1033 			else
1034 				perm = NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM;
1035 			nm_auth_chain_set_data (req->chain, "perm", (gpointer) perm, NULL);
1036 	
1037 			nm_auth_chain_add_call (req->chain, perm, TRUE);
1038 		} else {
1039 			nm_log_dbg (LOGD_AGENTS, "(%p/%s/%s) requesting user-owned secrets from agent %s",
1040 			            req, parent->detail, req->setting_name, agent_dbus_owner);
1041 	
1042 			get_agent_request_secrets (req, FALSE);
1043 		}
1044 	}
1045 	
1046 	static gboolean
1047 	get_start (gpointer user_data)
1048 	{
1049 		Request *parent = user_data;
1050 		ConnectionRequest *req = user_data;
1051 		GHashTable *setting_secrets = NULL;
1052 	
1053 		parent->idle_id = 0;
1054 	
1055 		/* Check if there are any existing secrets */
1056 		if (req->existing_secrets)
1057 			setting_secrets = g_hash_table_lookup (req->existing_secrets, req->setting_name);
1058 	
1059 		if (setting_secrets && g_hash_table_size (setting_secrets)) {
1060 			NMConnection *tmp;
1061 			GError *error = NULL;
1062 			gboolean new_secrets = (req->flags & NM_SETTINGS_GET_SECRETS_FLAG_REQUEST_NEW);
1063 	
1064 			/* The connection already had secrets; check if any more are required.
1065 			 * If no more are required, we're done.  If secrets are still needed,
1066 			 * ask a secret agent for more.  This allows admins to provide generic
1067 			 * secrets but allow additional user-specific ones as well.
1068 			 */
1069 			tmp = nm_connection_duplicate (req->connection);
1070 			g_assert (tmp);
1071 	
1072 			if (!nm_connection_update_secrets (tmp, req->setting_name, req->existing_secrets, &error)) {
1073 				req_complete_error (parent, error);
1074 				g_clear_error (&error);
1075 			} else {
1076 				/* Do we have everything we need? */
1077 				if (   (req->flags & NM_SETTINGS_GET_SECRETS_FLAG_ONLY_SYSTEM)
1078 				    || ((nm_connection_need_secrets (tmp, NULL) == NULL) && (new_secrets == FALSE))) {
1079 					nm_log_dbg (LOGD_AGENTS, "(%p/%s/%s) system settings secrets sufficient",
1080 					            req, parent->detail, req->setting_name);
1081 	
1082 					/* Got everything, we're done */
1083 					req_complete_success (parent, req->existing_secrets, NULL, NULL);
1084 				} else {
1085 					nm_log_dbg (LOGD_AGENTS, "(%p/%s/%s) system settings secrets insufficient, asking agents",
1086 					            req, parent->detail, req->setting_name);
1087 	
1088 					/* We don't, so ask some agents for additional secrets */
1089 					request_next_agent (parent);
1090 				}
1091 			}
1092 			g_object_unref (tmp);
1093 		} else {
1094 			/* Couldn't get secrets from system settings, so now we ask the
1095 			 * agents for secrets.  Let the Agent Manager handle which agents
1096 			 * we'll ask and in which order.
1097 			 */
1098 			request_next_agent (parent);
1099 		}
1100 	
1101 		return FALSE;
1102 	}
1103 	
1104 	static void
1105 	get_complete_cb (Request *parent,
1106 	                 GHashTable *secrets,
1107 	                 const char *agent_dbus_owner,
1108 	                 const char *agent_username,
1109 	                 GError *error,
1110 	                 gpointer user_data)
1111 	{
1112 		NMAgentManager *self = NM_AGENT_MANAGER (user_data);
1113 		NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
1114 		ConnectionRequest *req = (ConnectionRequest *) parent;
1115 	
1116 		/* Send secrets back to the requesting object */
1117 		req->callback (self,
1118 		               parent->reqid,
1119 		               agent_dbus_owner,
1120 		               agent_username,
1121 		               req->current_has_modify,
1122 		               req->setting_name,
1123 		               req->flags,
1124 		               error ? NULL : secrets,
1125 		               error,
1126 		               req->callback_data,
1127 		               req->other_data2,
1128 		               req->other_data3);
1129 	
1130 		g_hash_table_remove (priv->requests, GUINT_TO_POINTER (parent->reqid));
1131 	}
1132 	
1133 	static void
1134 	get_cancel_cb (Request *parent)
1135 	{
1136 		ConnectionRequest *req = (ConnectionRequest *) parent;
1137 	
1138 		req->current_has_modify = FALSE;
1139 		if (parent->current && parent->current_call_id)
1140 			nm_secret_agent_cancel_secrets (parent->current, parent->current_call_id);
1141 	}
1142 	
1143 	guint32
1144 	nm_agent_manager_get_secrets (NMAgentManager *self,
1145 	                              NMConnection *connection,
1146 	                              gboolean filter_by_uid,
1147 	                              gulong uid_filter,
1148 	                              GHashTable *existing_secrets,
1149 	                              const char *setting_name,
1150 	                              NMSettingsGetSecretsFlags flags,
1151 	                              const char **hints,
1152 	                              NMAgentSecretsResultFunc callback,
1153 	                              gpointer callback_data,
1154 	                              gpointer other_data2,
1155 	                              gpointer other_data3)
1156 	{
1157 		NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
1158 		Request *parent;
1159 		ConnectionRequest *req;
1160 	
1161 		g_return_val_if_fail (self != NULL, 0);
1162 		g_return_val_if_fail (NM_IS_CONNECTION (connection), 0);
1163 		g_return_val_if_fail (callback != NULL, 0);
1164 	
1165 		nm_log_dbg (LOGD_SETTINGS,
1166 		            "Secrets requested for connection %s (%s/%s)",
1167 		            nm_connection_get_path (connection),
1168 		            nm_connection_get_id (connection),
1169 		            setting_name);
1170 	
1171 		/* NOTE: a few things in the Request handling depend on existing_secrets
1172 		 * being NULL if there aren't any system-owned secrets for this connection.
1173 		 * This in turn depends on nm_connection_to_hash() and nm_setting_to_hash()
1174 		 * both returning NULL if they didn't hash anything.
1175 		 */
1176 	
1177 		req = connection_request_new_get (connection,
1178 		                                  filter_by_uid,
1179 		                                  uid_filter,
1180 		                                  existing_secrets,
1181 		                                  setting_name,
1182 		                                  "getting",
1183 		                                  flags,
1184 		                                  hints,
1185 		                                  callback,
1186 		                                  callback_data,
1187 		                                  other_data2,
1188 		                                  other_data3,
1189 		                                  get_complete_cb,
1190 		                                  self,
1191 		                                  get_next_cb,
1192 		                                  get_cancel_cb);
1193 		parent = (Request *) req;
1194 		g_hash_table_insert (priv->requests, GUINT_TO_POINTER (parent->reqid), req);
1195 	
1196 		/* Kick off the request */
1197 		if (!(req->flags & NM_SETTINGS_GET_SECRETS_FLAG_ONLY_SYSTEM))
1198 			request_add_agents (self, parent);
1199 		parent->idle_id = g_idle_add (get_start, req);
1200 		return parent->reqid;
1201 	}
1202 	
1203 	void
1204 	nm_agent_manager_cancel_secrets (NMAgentManager *self,
1205 	                                 guint32 request_id)
1206 	{
1207 		g_return_if_fail (self != NULL);
1208 		g_return_if_fail (request_id > 0);
1209 	
1210 		g_hash_table_remove (NM_AGENT_MANAGER_GET_PRIVATE (self)->requests,
1211 		                     GUINT_TO_POINTER (request_id));
1212 	}
1213 	
1214 	/*************************************************************/
1215 	
1216 	static void
1217 	save_done_cb (NMSecretAgent *agent,
1218 	              gconstpointer call_id,
1219 	              GHashTable *secrets,
1220 	              GError *error,
1221 	              gpointer user_data)
1222 	{
1223 		Request *parent = user_data;
1224 		ConnectionRequest *req = user_data;
1225 		const char *agent_dbus_owner;
1226 	
1227 		g_return_if_fail (call_id == parent->current_call_id);
1228 	
1229 		if (error) {
1230 			nm_log_dbg (LOGD_AGENTS, "(%s) agent failed save secrets request %p/%s: (%d) %s",
1231 			            nm_secret_agent_get_description (agent),
1232 			            req, parent->detail,
1233 			            error ? error->code : -1,
1234 			            (error && error->message) ? error->message : "(unknown)");
1235 			/* Try the next agent */
1236 			request_next_agent (parent);
1237 			return;
1238 		}
1239 	
1240 		nm_log_dbg (LOGD_AGENTS, "(%s) agent saved secrets for request %p/%s",
1241 		            nm_secret_agent_get_description (agent),
1242 		            req, parent->detail);
1243 	
1244 		agent_dbus_owner = nm_secret_agent_get_dbus_owner (agent);
1245 		req_complete_success (parent, NULL, NULL, agent_dbus_owner);
1246 	}
1247 	
1248 	static void
1249 	save_next_cb (Request *parent)
1250 	{
1251 		ConnectionRequest *req = (ConnectionRequest *) parent;
1252 	
1253 		parent->current_call_id = nm_secret_agent_save_secrets (parent->current,
1254 		                                                        req->connection,
1255 		                                                        save_done_cb,
1256 		                                                        req);
1257 		if (parent->current_call_id == NULL) {
1258 			/* Shouldn't hit this, but handle it anyway */
1259 			g_warn_if_fail (parent->current_call_id != NULL);
1260 			request_next_agent (parent);
1261 		}
1262 	}
1263 	
1264 	static void
1265 	save_complete_cb (Request *req,
1266 	                  GHashTable *secrets,
1267 	                  const char *agent_dbus_owner,
1268 	                  const char *agent_username,
1269 	                  GError *error,
1270 	                  gpointer user_data)
1271 	{
1272 		g_hash_table_remove (NM_AGENT_MANAGER_GET_PRIVATE (user_data)->requests,
1273 		                     GUINT_TO_POINTER (req->reqid));
1274 	}
1275 	
1276 	guint32
1277 	nm_agent_manager_save_secrets (NMAgentManager *self,
1278 	                               NMConnection *connection,
1279 	                               gboolean filter_by_uid,
1280 	                               gulong uid_filter)
1281 	{
1282 		NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
1283 		ConnectionRequest *req;
1284 		Request *parent;
1285 	
1286 		g_return_val_if_fail (self != NULL, 0);
1287 		g_return_val_if_fail (NM_IS_CONNECTION (connection), 0);
1288 	
1289 		nm_log_dbg (LOGD_SETTINGS,
1290 		            "Saving secrets for connection %s (%s)",
1291 		            nm_connection_get_path (connection),
1292 		            nm_connection_get_id (connection));
1293 	
1294 		req = connection_request_new_other (connection,
1295 		                                    filter_by_uid,
1296 		                                    uid_filter,
1297 		                                    "saving",
1298 		                                    save_complete_cb,
1299 		                                    self,
1300 		                                    save_next_cb);
1301 		parent = (Request *) req;
1302 		g_hash_table_insert (priv->requests, GUINT_TO_POINTER (parent->reqid), req);
1303 	
1304 		/* Kick off the request */
1305 		request_add_agents (self, parent);
1306 		parent->idle_id = g_idle_add (request_start, req);
1307 		return parent->reqid;
1308 	}
1309 	
1310 	/*************************************************************/
1311 	
1312 	static void
1313 	delete_done_cb (NMSecretAgent *agent,
1314 	              gconstpointer call_id,
1315 	              GHashTable *secrets,
1316 	              GError *error,
1317 	              gpointer user_data)
1318 	{
1319 		Request *req = user_data;
1320 	
1321 		g_return_if_fail (call_id == req->current_call_id);
1322 	
1323 		if (error) {
1324 			nm_log_dbg (LOGD_AGENTS, "(%s) agent failed delete secrets request %p/%s: (%d) %s",
1325 			            nm_secret_agent_get_description (agent), req, req->detail,
1326 			            error ? error->code : -1,
1327 			            (error && error->message) ? error->message : "(unknown)");
1328 		} else {
1329 			nm_log_dbg (LOGD_AGENTS, "(%s) agent deleted secrets for request %p/%s",
1330 			            nm_secret_agent_get_description (agent), req, req->detail);
1331 		}
1332 	
1333 		/* Tell the next agent to delete secrets */
1334 		request_next_agent (req);
1335 	}
1336 	
1337 	static void
1338 	delete_next_cb (Request *parent)
1339 	{
1340 		ConnectionRequest *req = (ConnectionRequest *) parent;
1341 	
1342 		parent->current_call_id = nm_secret_agent_delete_secrets (parent->current,
1343 		                                                          req->connection,
1344 		                                                          delete_done_cb,
1345 		                                                          req);
1346 		if (parent->current_call_id == NULL) {
1347 			/* Shouldn't hit this, but handle it anyway */
1348 			g_warn_if_fail (parent->current_call_id != NULL);
1349 			request_next_agent (parent);
1350 		}
1351 	}
1352 	
1353 	static void
1354 	delete_complete_cb (Request *req,
1355 	                    GHashTable *secrets,
1356 	                    const char *agent_dbus_owner,
1357 	                    const char *agent_username,
1358 	                    GError *error,
1359 	                    gpointer user_data)
1360 	{
1361 		g_hash_table_remove (NM_AGENT_MANAGER_GET_PRIVATE (user_data)->requests,
1362 		                     GUINT_TO_POINTER (req->reqid));
1363 	}
1364 	
1365 	guint32
1366 	nm_agent_manager_delete_secrets (NMAgentManager *self,
1367 	                                 NMConnection *connection,
1368 	                                 gboolean filter_by_uid,
1369 	                                 gulong uid_filter)
1370 	{
1371 		NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
1372 		ConnectionRequest *req;
1373 		Request *parent;
1374 	
1375 		g_return_val_if_fail (self != NULL, 0);
1376 		g_return_val_if_fail (NM_IS_CONNECTION (connection), 0);
1377 	
1378 		nm_log_dbg (LOGD_SETTINGS,
1379 		            "Deleting secrets for connection %s (%s)",
1380 		            nm_connection_get_path (connection),
1381 		            nm_connection_get_id (connection));
1382 	
1383 		req = connection_request_new_other (connection,
1384 		                                    filter_by_uid,
1385 		                                    uid_filter,
1386 		                                    "deleting",
1387 		                                    delete_complete_cb,
1388 		                                    self,
1389 		                                    delete_next_cb);
1390 		parent = (Request *) req;
1391 		g_hash_table_insert (priv->requests, GUINT_TO_POINTER (parent->reqid), req);
1392 	
1393 		/* Kick off the request */
1394 		request_add_agents (self, parent);
1395 		parent->idle_id = g_idle_add (request_start, req);
1396 		return parent->reqid;
1397 	}
1398 	
1399 	/*************************************************************/
1400 	
1401 	NMSecretAgent *
1402 	nm_agent_manager_get_agent_by_user (NMAgentManager *self, const char *username)
1403 	{
1404 		NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
1405 		GHashTableIter iter;
1406 		NMSecretAgent *agent;
1407 	
1408 		g_hash_table_iter_init (&iter, priv->agents);
1409 		while (g_hash_table_iter_next (&iter, NULL, (gpointer) &agent)) {
1410 			if (g_strcmp0 (nm_secret_agent_get_owner_username (agent), username) == 0)
1411 				return agent;
1412 		}
1413 	
1414 		return NULL;
1415 	}
1416 	
1417 	/*************************************************************/
1418 	
1419 	gboolean
1420 	nm_agent_manager_all_agents_have_capability (NMAgentManager *manager,
1421 	                                             gboolean filter_by_uid,
1422 	                                             gulong owner_uid,
1423 	                                             NMSecretAgentCapabilities capability)
1424 	{
1425 		NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (manager);
1426 		GHashTableIter iter;
1427 		NMSecretAgent *agent;
1428 	
1429 		g_hash_table_iter_init (&iter, priv->agents);
1430 		while (g_hash_table_iter_next (&iter, NULL, (gpointer) &agent)) {
1431 			if (filter_by_uid && nm_secret_agent_get_owner_uid (agent) != owner_uid)
1432 				continue;
1433 	
1434 			if (!(nm_secret_agent_get_capabilities (agent) & capability))
1435 				return FALSE;
1436 		}
1437 	
1438 		return TRUE;
1439 	}
1440 	
1441 	/*************************************************************/
1442 	
1443 	static void
1444 	name_owner_changed_cb (NMDBusManager *dbus_mgr,
1445 	                       const char *name,
1446 	                       const char *old_owner,
1447 	                       const char *new_owner,
1448 	                       gpointer user_data)
1449 	{
1450 		if (old_owner) {
1451 			/* The agent quit, so remove it and let interested clients know */
1452 			remove_agent (NM_AGENT_MANAGER (user_data), old_owner);
1453 		}
1454 	}
1455 	
1456 	static void
1457 	agent_permissions_changed_done (NMAuthChain *chain,
1458 	                                GError *error,
1459 	                                DBusGMethodInvocation *context,
1460 	                                gpointer user_data)
1461 	{
1462 		NMAgentManager *self = NM_AGENT_MANAGER (user_data);
1463 		NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
1464 		NMSecretAgent *agent;
1465 		gboolean share_protected = FALSE, share_open = FALSE;
1466 	
1467 		priv->chains = g_slist_remove (priv->chains, chain);
1468 	
1469 		agent = nm_auth_chain_get_data (chain, "agent");
1470 		g_assert (agent);
1471 	
1472 		if (error) {
1473 			nm_log_dbg (LOGD_AGENTS, "(%s) failed to request updated agent permissions",
1474 			            nm_secret_agent_get_description (agent));
1475 		} else {
1476 			nm_log_dbg (LOGD_AGENTS, "(%s) updated agent permissions",
1477 			            nm_secret_agent_get_description (agent));
1478 	
1479 			if (nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED) == NM_AUTH_CALL_RESULT_YES)
1480 				share_protected = TRUE;
1481 			if (nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN) == NM_AUTH_CALL_RESULT_YES)
1482 				share_open = TRUE;
1483 		}
1484 	
1485 		nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, share_protected);
1486 		nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, share_open);
1487 	
1488 		nm_auth_chain_unref (chain);
1489 	}
1490 	
1491 	static void
1492 	authority_changed_cb (gpointer user_data)
1493 	{
1494 		NMAgentManager *self = NM_AGENT_MANAGER (user_data);
1495 		NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
1496 		GHashTableIter iter;
1497 		NMSecretAgent *agent;
1498 	
1499 		/* Recheck the permissions of all secret agents */
1500 		g_hash_table_iter_init (&iter, priv->agents);
1501 		while (g_hash_table_iter_next (&iter, NULL, (gpointer) &agent)) {
1502 			NMAuthChain *chain;
1503 	
1504 			/* Kick off permissions requests for this agent */
1505 			chain = nm_auth_chain_new_dbus_sender (nm_secret_agent_get_dbus_owner (agent),
1506 			                                       nm_secret_agent_get_owner_uid (agent),
1507 			                                       agent_permissions_changed_done,
1508 			                                       self);
1509 			g_assert (chain);
1510 			priv->chains = g_slist_append (priv->chains, chain);
1511 	
1512 			/* Make sure if the agent quits while the permissions call is in progress
1513 			 * that the object sticks around until our callback.
1514 			 */
1515 			nm_auth_chain_set_data (chain, "agent", g_object_ref (agent), g_object_unref);
1516 			nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, FALSE);
1517 			nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, FALSE);
1518 		}
1519 	}
1520 	
1521 	/*************************************************************/
1522 	
1523 	NMAgentManager *
1524 	nm_agent_manager_get (void)
1525 	{
1526 		static NMAgentManager *singleton = NULL;
1527 		NMAgentManagerPrivate *priv;
1528 	
1529 		if (singleton)
1530 			return g_object_ref (singleton);
1531 	
1532 		singleton = (NMAgentManager *) g_object_new (NM_TYPE_AGENT_MANAGER, NULL);
1533 		g_assert (singleton);
1534 	
1535 		priv = NM_AGENT_MANAGER_GET_PRIVATE (singleton);
1536 		priv->session_monitor = nm_session_monitor_get ();
1537 		priv->dbus_mgr = nm_dbus_manager_get ();
1538 	
1539 		nm_dbus_manager_register_object (priv->dbus_mgr, NM_DBUS_PATH_AGENT_MANAGER, singleton);
1540 	
1541 		g_signal_connect (priv->dbus_mgr,
1542 		                  NM_DBUS_MANAGER_NAME_OWNER_CHANGED,
1543 		                  G_CALLBACK (name_owner_changed_cb),
1544 		                  singleton);
1545 	
1546 		nm_auth_changed_func_register (authority_changed_cb, singleton);
1547 	
1548 		return singleton;
1549 	}
1550 	
1551 	static void
1552 	nm_agent_manager_init (NMAgentManager *self)
1553 	{
1554 		NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
1555 	
1556 		priv->agents = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
1557 		priv->requests = g_hash_table_new_full (g_direct_hash,
1558 		                                        g_direct_equal,
1559 		                                        NULL,
1560 		                                        (GDestroyNotify) request_free);
1561 	}
1562 	
1563 	static void
1564 	dispose (GObject *object)
1565 	{
1566 		NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (object);
1567 	
1568 		if (!priv->disposed) {
1569 			priv->disposed = TRUE;
1570 	
1571 			nm_auth_changed_func_unregister (authority_changed_cb, NM_AGENT_MANAGER (object));
1572 	
1573 			g_slist_free_full (priv->chains, (GDestroyNotify) nm_auth_chain_unref);
1574 	
1575 			g_hash_table_destroy (priv->agents);
1576 			g_hash_table_destroy (priv->requests);
1577 	
1578 			g_object_unref (priv->session_monitor);
1579 			priv->dbus_mgr = NULL;
1580 		}
1581 	
1582 		G_OBJECT_CLASS (nm_agent_manager_parent_class)->dispose (object);
1583 	}
1584 	
1585 	static void
1586 	nm_agent_manager_class_init (NMAgentManagerClass *agent_manager_class)
1587 	{
1588 		GObjectClass *object_class = G_OBJECT_CLASS (agent_manager_class);
1589 	
1590 		g_type_class_add_private (agent_manager_class, sizeof (NMAgentManagerPrivate));
1591 	
1592 		/* virtual methods */
1593 		object_class->dispose = dispose;
1594 	
1595 		/* Signals */
1596 		signals[AGENT_REGISTERED] =
1597 			g_signal_new ("agent-registered",
1598 			              G_OBJECT_CLASS_TYPE (object_class),
1599 			              G_SIGNAL_RUN_FIRST,
1600 			              G_STRUCT_OFFSET (NMAgentManagerClass, agent_registered),
1601 			              NULL, NULL,
1602 			              g_cclosure_marshal_VOID__OBJECT,
1603 			              G_TYPE_NONE, 1,
1604 			              G_TYPE_OBJECT);
1605 	
1606 		dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (agent_manager_class),
1607 		                                 &dbus_glib_nm_agent_manager_object_info);
1608 	
1609 		dbus_g_error_domain_register (NM_AGENT_MANAGER_ERROR,
1610 		                              NM_DBUS_INTERFACE_AGENT_MANAGER,
1611 		                              NM_TYPE_AGENT_MANAGER_ERROR);
1612 	}
1613