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)
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