libnm-glib: don't fail creating connection in NMSecretAgent
[NetworkManager.git] / libnm-glib / nm-secret-agent.c
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /*
3  * This library is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation; either
6  * version 2 of the License, or (at your option) any later version.
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with this library; if not, write to the
15  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16  * Boston, MA 02110-1301 USA.
17  *
18  * Copyright 2010 - 2011 Red Hat, Inc.
19  */
20
21 #include "nm-default.h"
22
23 #include <string.h>
24 #include <dbus/dbus-glib-lowlevel.h>
25
26 #include "NetworkManager.h"
27 #include "nm-secret-agent.h"
28 #include "nm-glib-enum-types.h"
29 #include "nm-dbus-helpers-private.h"
30 #include "nm-setting-private.h"
31
32 static void impl_secret_agent_get_secrets (NMSecretAgent *self,
33                                            GHashTable *connection_hash,
34                                            const char *connection_path,
35                                            const char *setting_name,
36                                            const char **hints,
37                                            guint32 flags,
38                                            DBusGMethodInvocation *context);
39
40 static void impl_secret_agent_cancel_get_secrets (NMSecretAgent *self,
41                                                   const char *connection_path,
42                                                   const char *setting_name,
43                                                   DBusGMethodInvocation *context);
44
45 static void impl_secret_agent_save_secrets (NMSecretAgent *self,
46                                             GHashTable *connection_hash,
47                                             const char *connection_path,
48                                             DBusGMethodInvocation *context);
49
50 static void impl_secret_agent_delete_secrets (NMSecretAgent *self,
51                                               GHashTable *connection_hash,
52                                               const char *connection_path,
53                                               DBusGMethodInvocation *context);
54
55 #include "nm-secret-agent-glue.h"
56
57 G_DEFINE_ABSTRACT_TYPE (NMSecretAgent, nm_secret_agent, G_TYPE_OBJECT)
58
59 #define NM_SECRET_AGENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SECRET_AGENT, NMSecretAgentPrivate))
60
61 static gboolean auto_register_cb (gpointer user_data);
62
63 typedef struct {
64         gboolean registered;
65         NMSecretAgentCapabilities capabilities;
66
67         DBusGConnection *bus;
68         DBusGProxy *dbus_proxy;
69         DBusGProxy *manager_proxy;
70         DBusGProxyCall *reg_call;
71
72         /* GetSecretsInfo structs of in-flight GetSecrets requests */
73         GSList *pending_gets;
74
75         char *nm_owner;
76
77         char *identifier;
78         gboolean auto_register;
79         gboolean suppress_auto;
80         gboolean auto_register_id;
81 } NMSecretAgentPrivate;
82
83 enum {
84         PROP_0,
85         PROP_IDENTIFIER,
86         PROP_AUTO_REGISTER,
87         PROP_REGISTERED,
88         PROP_CAPABILITIES,
89
90         LAST_PROP
91 };
92
93 enum {
94         REGISTRATION_RESULT,
95
96         LAST_SIGNAL
97 };
98
99 static guint signals[LAST_SIGNAL] = { 0 };
100
101
102 /********************************************************************/
103
104 GQuark
105 nm_secret_agent_error_quark (void)
106 {
107         static GQuark ret = 0;
108
109         if (G_UNLIKELY (ret == 0))
110                 ret = g_quark_from_static_string ("nm-secret-agent-error");
111         return ret;
112 }
113
114 /*************************************************************/
115
116 static const char *
117 get_nm_owner (NMSecretAgent *self)
118 {
119         NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
120         GError *error = NULL;
121         char *owner;
122
123         if (!priv->nm_owner) {
124                 if (!dbus_g_proxy_call_with_timeout (priv->dbus_proxy,
125                                                      "GetNameOwner", 2000, &error,
126                                                      G_TYPE_STRING, NM_DBUS_SERVICE,
127                                                      G_TYPE_INVALID,
128                                                      G_TYPE_STRING, &owner,
129                                                      G_TYPE_INVALID))
130                         return NULL;
131
132                 priv->nm_owner = g_strdup (owner);
133                 g_free (owner);
134         }
135
136         return priv->nm_owner;
137 }
138
139 static void
140 _internal_unregister (NMSecretAgent *self)
141 {
142         NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
143
144         if (priv->registered) {
145                 dbus_g_connection_unregister_g_object (priv->bus, G_OBJECT (self));
146                 priv->registered = FALSE;
147                 g_object_notify (G_OBJECT (self), NM_SECRET_AGENT_REGISTERED);
148         }
149 }
150
151 typedef struct {
152         char *path;
153         char *setting_name;
154         DBusGMethodInvocation *context;
155 } GetSecretsInfo;
156
157 static void
158 get_secrets_info_finalize (NMSecretAgent *self, GetSecretsInfo *info)
159 {
160         NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
161
162         g_return_if_fail (info != NULL);
163
164         priv->pending_gets = g_slist_remove (priv->pending_gets, info);
165
166         g_free (info->path);
167         g_free (info->setting_name);
168         memset (info, 0, sizeof (*info));
169         g_free (info);
170 }
171
172 static void
173 name_owner_changed (DBusGProxy *proxy,
174                     const char *name,
175                     const char *old_owner,
176                     const char *new_owner,
177                     gpointer user_data)
178 {
179         NMSecretAgent *self = NM_SECRET_AGENT (user_data);
180         NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
181         gboolean old_owner_good = (old_owner && strlen (old_owner));
182         gboolean new_owner_good = (new_owner && strlen (new_owner));
183         GSList *iter;
184
185         if (strcmp (name, NM_DBUS_SERVICE) == 0) {
186                 g_free (priv->nm_owner);
187                 priv->nm_owner = g_strdup (new_owner);
188
189                 if (!old_owner_good && new_owner_good) {
190                         /* NM appeared */
191                         auto_register_cb (self);
192                 } else if (old_owner_good && !new_owner_good) {
193                         /* Cancel any pending secrets requests */
194                         for (iter = priv->pending_gets; iter; iter = g_slist_next (iter)) {
195                                 GetSecretsInfo *info = iter->data;
196
197                                 NM_SECRET_AGENT_GET_CLASS (self)->cancel_get_secrets (self,
198                                                                                       info->path,
199                                                                                       info->setting_name);
200                         }
201                         g_slist_free (priv->pending_gets);
202                         priv->pending_gets = NULL;
203
204                         /* NM disappeared */
205                         _internal_unregister (self);
206                 } else if (old_owner_good && new_owner_good && strcmp (old_owner, new_owner)) {
207                         /* Hmm, NM magically restarted */
208                         _internal_unregister (self);
209                         auto_register_cb (self);
210                 }
211         }
212 }
213
214 static gboolean
215 verify_sender (NMSecretAgent *self,
216                DBusGMethodInvocation *context,
217                GError **error)
218 {
219         NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
220         DBusConnection *bus;
221         char *sender;
222         const char *nm_owner;
223         DBusError dbus_error;
224         uid_t sender_uid = G_MAXUINT;
225         gboolean allowed = FALSE;
226
227         g_return_val_if_fail (context != NULL, FALSE);
228
229         /* Verify the sender's UID is 0, and that the sender is the same as
230          * NetworkManager's bus name owner.
231          */
232
233         nm_owner = get_nm_owner (self);
234         if (!nm_owner) {
235                 g_set_error_literal (error,
236                                      NM_SECRET_AGENT_ERROR,
237                                      NM_SECRET_AGENT_ERROR_NOT_AUTHORIZED,
238                                      "NetworkManager bus name owner unknown.");
239                 return FALSE;
240         }
241
242         bus = dbus_g_connection_get_connection (priv->bus);
243         if (!bus) {
244                 g_set_error_literal (error,
245                                      NM_SECRET_AGENT_ERROR,
246                                      NM_SECRET_AGENT_ERROR_NOT_AUTHORIZED,
247                                      "Failed to get DBus connection.");
248                 return FALSE;
249         }
250
251         sender = dbus_g_method_get_sender (context);
252         if (!sender) {
253                 g_set_error_literal (error,
254                                      NM_SECRET_AGENT_ERROR,
255                                      NM_SECRET_AGENT_ERROR_NOT_AUTHORIZED,
256                                      "Failed to get request sender.");
257                 return FALSE;
258         }
259
260         /* Check that the sender matches the current NM bus name owner */
261         if (strcmp (sender, nm_owner) != 0) {
262                 g_set_error_literal (error,
263                                      NM_SECRET_AGENT_ERROR,
264                                      NM_SECRET_AGENT_ERROR_NOT_AUTHORIZED,
265                                      "Request sender does not match NetworkManager bus name owner.");
266                 goto out;
267         }
268
269         dbus_error_init (&dbus_error);
270         sender_uid = dbus_bus_get_unix_user (bus, sender, &dbus_error);
271         if (dbus_error_is_set (&dbus_error)) {
272                 g_set_error (error,
273                              NM_SECRET_AGENT_ERROR,
274                              NM_SECRET_AGENT_ERROR_NOT_AUTHORIZED,
275                              "Failed to get request unix user: (%s) %s.",
276                              dbus_error.name, dbus_error.message);
277                 dbus_error_free (&dbus_error);
278                 goto out;
279         }
280
281         /* We only accept requests from NM, which always runs as root */
282         if (0 != sender_uid) {
283                 g_set_error_literal (error,
284                                      NM_SECRET_AGENT_ERROR,
285                                      NM_SECRET_AGENT_ERROR_NOT_AUTHORIZED,
286                                      "Request sender is not root.");
287                 goto out;
288         }
289
290         allowed = TRUE;
291
292 out:
293         g_free (sender);
294         return allowed;
295 }
296
297 static gboolean
298 verify_request (NMSecretAgent *self,
299                 DBusGMethodInvocation *context,
300                 GHashTable *connection_hash,
301                 const char *connection_path,
302                 NMConnection **out_connection,
303                 GError **error)
304 {
305         NMConnection *connection = NULL;
306
307         g_return_val_if_fail (out_connection, FALSE);
308
309         if (!verify_sender (self, context, error))
310                 return FALSE;
311
312         /* No connection?  If the sender verified, then we allow the request */
313         if (connection_hash == NULL)
314                 return TRUE;
315
316         /* If we have a connection hash, we require a path too */
317         if (connection_path == NULL) {
318                 g_set_error_literal (error,
319                                      NM_SECRET_AGENT_ERROR,
320                                      NM_SECRET_AGENT_ERROR_INVALID_CONNECTION,
321                                      "Invalid connection: no connection path given.");
322                 return FALSE;
323         }
324
325         /* Make sure the given connection is valid */
326         connection = _nm_connection_new_from_hash (connection_hash);
327         nm_connection_set_path (connection, connection_path);
328         *out_connection = connection;
329
330         return TRUE;
331 }
332
333 static void
334 get_secrets_cb (NMSecretAgent *self,
335                 NMConnection *connection,
336                 GHashTable *secrets,
337                 GError *error,
338                 gpointer user_data)
339 {
340         GetSecretsInfo *info = user_data;
341
342         if (error)
343                 dbus_g_method_return_error (info->context, error);
344         else
345                 dbus_g_method_return (info->context, secrets);
346
347         /* Remove the request from internal tracking */
348         get_secrets_info_finalize (self, info);
349 }
350
351 static void
352 impl_secret_agent_get_secrets (NMSecretAgent *self,
353                                GHashTable *connection_hash,
354                                const char *connection_path,
355                                const char *setting_name,
356                                const char **hints,
357                                guint32 flags,
358                                DBusGMethodInvocation *context)
359 {
360         NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
361         GError *error = NULL;
362         NMConnection *connection = NULL;
363         GetSecretsInfo *info;
364
365         /* Make sure the request comes from NetworkManager and is valid */
366         if (!verify_request (self, context, connection_hash, connection_path, &connection, &error)) {
367                 dbus_g_method_return_error (context, error);
368                 g_clear_error (&error);
369                 return;
370         }
371
372         info = g_malloc0 (sizeof (GetSecretsInfo));
373         info->path = g_strdup (connection_path);
374         info->setting_name = g_strdup (setting_name);
375         info->context = context;
376         priv->pending_gets = g_slist_append (priv->pending_gets, info);
377
378         NM_SECRET_AGENT_GET_CLASS (self)->get_secrets (self,
379                                                        connection,
380                                                        connection_path,
381                                                        setting_name,
382                                                        hints,
383                                                        flags,
384                                                        get_secrets_cb,
385                                                        info);
386         g_object_unref (connection);
387 }
388
389 static GetSecretsInfo *
390 find_get_secrets_info (GSList *list, const char *path, const char *setting_name)
391 {
392         GSList *iter;
393
394         for (iter = list; iter; iter = g_slist_next (iter)) {
395                 GetSecretsInfo *candidate = iter->data;
396
397                 if (   g_strcmp0 (path, candidate->path) == 0
398                     && g_strcmp0 (setting_name, candidate->setting_name) == 0)
399                         return candidate;
400         }
401         return NULL;
402 }
403
404 static void
405 impl_secret_agent_cancel_get_secrets (NMSecretAgent *self,
406                                       const char *connection_path,
407                                       const char *setting_name,
408                                       DBusGMethodInvocation *context)
409 {
410         NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
411         GError *error = NULL;
412         GetSecretsInfo *info;
413
414         /* Make sure the request comes from NetworkManager and is valid */
415         if (!verify_request (self, context, NULL, NULL, NULL, &error)) {
416                 dbus_g_method_return_error (context, error);
417                 g_clear_error (&error);
418                 return;
419         }
420
421         info = find_get_secrets_info (priv->pending_gets, connection_path, setting_name);
422         if (!info) {
423                 g_set_error_literal (&error,
424                                      NM_SECRET_AGENT_ERROR,
425                                      NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
426                                      "No secrets request in progress for this connection.");
427                 dbus_g_method_return_error (context, error);
428                 g_clear_error (&error);
429                 return;
430         }
431
432         /* Send the cancel request up to the subclass and finalize it */
433         NM_SECRET_AGENT_GET_CLASS (self)->cancel_get_secrets (self,
434                                                               info->path,
435                                                               info->setting_name);
436         dbus_g_method_return (context);
437 }
438
439 static void
440 save_secrets_cb (NMSecretAgent *self,
441                  NMConnection *connection,
442                  GError *error,
443                  gpointer user_data)
444 {
445         DBusGMethodInvocation *context = user_data;
446
447         if (error)
448                 dbus_g_method_return_error (context, error);
449         else
450                 dbus_g_method_return (context);
451 }
452
453 static void
454 impl_secret_agent_save_secrets (NMSecretAgent *self,
455                                 GHashTable *connection_hash,
456                                 const char *connection_path,
457                                 DBusGMethodInvocation *context)
458 {
459         GError *error = NULL;
460         NMConnection *connection = NULL;
461
462         /* Make sure the request comes from NetworkManager and is valid */
463         if (!verify_request (self, context, connection_hash, connection_path, &connection, &error)) {
464                 dbus_g_method_return_error (context, error);
465                 g_clear_error (&error);
466                 return;
467         }
468
469         NM_SECRET_AGENT_GET_CLASS (self)->save_secrets (self,
470                                                         connection,
471                                                         connection_path,
472                                                         save_secrets_cb,
473                                                         context);
474         g_object_unref (connection);
475 }
476
477 static void
478 delete_secrets_cb (NMSecretAgent *self,
479                    NMConnection *connection,
480                    GError *error,
481                    gpointer user_data)
482 {
483         DBusGMethodInvocation *context = user_data;
484
485         if (error)
486                 dbus_g_method_return_error (context, error);
487         else
488                 dbus_g_method_return (context);
489 }
490
491 static void
492 impl_secret_agent_delete_secrets (NMSecretAgent *self,
493                                   GHashTable *connection_hash,
494                                   const char *connection_path,
495                                   DBusGMethodInvocation *context)
496 {
497         GError *error = NULL;
498         NMConnection *connection = NULL;
499
500         /* Make sure the request comes from NetworkManager and is valid */
501         if (!verify_request (self, context, connection_hash, connection_path, &connection, &error)) {
502                 dbus_g_method_return_error (context, error);
503                 g_clear_error (&error);
504                 return;
505         }
506
507         NM_SECRET_AGENT_GET_CLASS (self)->delete_secrets (self,
508                                                           connection,
509                                                           connection_path,
510                                                           delete_secrets_cb,
511                                                           context);
512         g_object_unref (connection);
513 }
514
515 /**************************************************************/
516
517 static void
518 reg_result (NMSecretAgent *self, GError *error)
519 {
520         NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
521
522         if (error) {
523                 /* If registration failed we shouldn't expose ourselves on the bus */
524                 _internal_unregister (self);
525         } else {
526                 priv->registered = TRUE;
527                 g_object_notify (G_OBJECT (self), NM_SECRET_AGENT_REGISTERED);
528         }
529
530         g_signal_emit (self, signals[REGISTRATION_RESULT], 0, error);
531 }
532
533 static void
534 reg_request_cb (DBusGProxy *proxy,
535                 DBusGProxyCall *call,
536                 gpointer user_data)
537 {
538         NMSecretAgent *self = NM_SECRET_AGENT (user_data);
539         NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
540         GError *error = NULL;
541
542         priv->reg_call = NULL;
543
544         dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID);
545         reg_result (self, error);
546         g_clear_error (&error);
547 }
548
549 static void
550 reg_with_caps_cb (DBusGProxy *proxy,
551                   DBusGProxyCall *call,
552                   gpointer user_data)
553 {
554         NMSecretAgent *self = NM_SECRET_AGENT (user_data);
555         NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
556
557         priv->reg_call = NULL;
558
559         if (dbus_g_proxy_end_call (proxy, call, NULL, G_TYPE_INVALID)) {
560                 reg_result (self, NULL);
561                 return;
562         }
563
564         /* Might be an old NetworkManager that doesn't support capabilities;
565          * fall back to old Register() method instead.
566          */
567         priv->reg_call = dbus_g_proxy_begin_call_with_timeout (priv->manager_proxy,
568                                                                "Register",
569                                                                reg_request_cb,
570                                                                self,
571                                                                NULL,
572                                                                5000,
573                                                                G_TYPE_STRING, priv->identifier,
574                                                                G_TYPE_INVALID);
575 }
576
577 /**
578  * nm_secret_agent_register:
579  * @self: a #NMSecretAgent
580  *
581  * Registers the #NMSecretAgent with the NetworkManager secret manager,
582  * indicating to NetworkManager that the agent is able to provide and save
583  * secrets for connections on behalf of its user.  Registration is an
584  * asynchronous operation and its success or failure is indicated via the
585  * 'registration-result' signal.
586  *
587  * Returns: a new %TRUE if registration was successfully requested (this does
588  * not mean registration itself was successful), %FALSE if registration was not
589  * successfully requested.
590  **/
591 gboolean
592 nm_secret_agent_register (NMSecretAgent *self)
593 {
594         NMSecretAgentPrivate *priv;
595         NMSecretAgentClass *class;
596
597         g_return_val_if_fail (NM_IS_SECRET_AGENT (self), FALSE);
598
599         priv = NM_SECRET_AGENT_GET_PRIVATE (self);
600
601         g_return_val_if_fail (priv->registered == FALSE, FALSE);
602         g_return_val_if_fail (priv->reg_call == NULL, FALSE);
603         g_return_val_if_fail (priv->bus != NULL, FALSE);
604         g_return_val_if_fail (priv->manager_proxy != NULL, FALSE);
605
606         /* Also make sure the subclass can actually respond to secrets requests */
607         class = NM_SECRET_AGENT_GET_CLASS (self);
608         g_return_val_if_fail (class->get_secrets != NULL, FALSE);
609         g_return_val_if_fail (class->save_secrets != NULL, FALSE);
610         g_return_val_if_fail (class->delete_secrets != NULL, FALSE);
611
612         if (!priv->nm_owner)
613                 return FALSE;
614
615         priv->suppress_auto = FALSE;
616
617         /* Export our secret agent interface before registering with the manager */
618         dbus_g_connection_register_g_object (priv->bus,
619                                              NM_DBUS_PATH_SECRET_AGENT,
620                                              G_OBJECT (self));
621
622         priv->reg_call = dbus_g_proxy_begin_call_with_timeout (priv->manager_proxy,
623                                                                "RegisterWithCapabilities",
624                                                                reg_with_caps_cb,
625                                                                self,
626                                                                NULL,
627                                                                5000,
628                                                                G_TYPE_STRING, priv->identifier,
629                                                                G_TYPE_UINT, priv->capabilities,
630                                                                G_TYPE_INVALID);
631         return TRUE;
632 }
633
634 /**
635  * nm_secret_agent_unregister:
636  * @self: a #NMSecretAgent
637  *
638  * Unregisters the #NMSecretAgent with the NetworkManager secret manager,
639  * indicating to NetworkManager that the agent is will no longer provide or
640  * store secrets on behalf of this user.
641  *
642  * Returns: a new %TRUE if unregistration was successful, %FALSE if it was not.
643  **/
644 gboolean
645 nm_secret_agent_unregister (NMSecretAgent *self)
646 {
647         NMSecretAgentPrivate *priv;
648
649         g_return_val_if_fail (NM_IS_SECRET_AGENT (self), FALSE);
650
651         priv = NM_SECRET_AGENT_GET_PRIVATE (self);
652
653         g_return_val_if_fail (priv->registered == TRUE, FALSE);
654         g_return_val_if_fail (priv->bus != NULL, FALSE);
655         g_return_val_if_fail (priv->manager_proxy != NULL, FALSE);
656
657         if (!priv->nm_owner)
658                 return FALSE;
659
660         dbus_g_proxy_call_no_reply (priv->manager_proxy, "Unregister", G_TYPE_INVALID);
661
662         _internal_unregister (self);
663         priv->suppress_auto = TRUE;
664
665         return TRUE;
666 }
667
668 /**
669  * nm_secret_agent_get_registered:
670  * @self: a #NMSecretAgent
671  *
672  * Returns: a %TRUE if the agent is registered, %FALSE if it is not.
673  **/
674 gboolean
675 nm_secret_agent_get_registered (NMSecretAgent *self)
676 {
677         g_return_val_if_fail (NM_IS_SECRET_AGENT (self), FALSE);
678
679         return NM_SECRET_AGENT_GET_PRIVATE (self)->registered;
680 }
681
682 static gboolean
683 auto_register_cb (gpointer user_data)
684 {
685         NMSecretAgent *self = NM_SECRET_AGENT (user_data);
686         NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
687
688         priv->auto_register_id = 0;
689         if (priv->auto_register && !priv->suppress_auto &&
690            (priv->reg_call == NULL && !priv->registered))
691                 nm_secret_agent_register (self);
692         return FALSE;
693 }
694
695 /**************************************************************/
696
697 /**
698  * nm_secret_agent_get_secrets:
699  * @self: a #NMSecretAgent
700  * @connection: the #NMConnection for which we're asked secrets
701  * @setting_name: the name of the secret setting
702  * @hints: (array zero-terminated=1): hints to the agent
703  * @flags: flags that modify the behavior of the request
704  * @callback: (scope async): a callback, to be invoked when the operation is done
705  * @user_data: (closure): caller-specific data to be passed to @callback
706  *
707  * Asyncronously retrieve secrets belonging to @connection for the
708  * setting @setting_name.  @flags indicate specific behavior that the secret
709  * agent should use when performing the request, for example returning only
710  * existing secrets without user interaction, or requesting entirely new
711  * secrets from the user.
712  *
713  * Virtual: get_secrets
714  */
715 void
716 nm_secret_agent_get_secrets (NMSecretAgent *self,
717                              NMConnection *connection,
718                              const char *setting_name,
719                              const char **hints,
720                              NMSecretAgentGetSecretsFlags flags,
721                              NMSecretAgentGetSecretsFunc callback,
722                              gpointer user_data)
723 {
724         g_return_if_fail (NM_IS_SECRET_AGENT (self));
725         g_return_if_fail (NM_IS_CONNECTION (connection));
726         g_return_if_fail (nm_connection_get_path (connection));
727         g_return_if_fail (setting_name != NULL);
728         g_return_if_fail (strlen (setting_name) > 0);
729         g_return_if_fail (callback != NULL);
730
731         NM_SECRET_AGENT_GET_CLASS (self)->get_secrets (self,
732                                                        connection,
733                                                        nm_connection_get_path (connection),
734                                                        setting_name,
735                                                        hints,
736                                                        flags,
737                                                        callback,
738                                                        user_data);
739 }
740
741 /**
742  * nm_secret_agent_save_secrets:
743  * @self: a #NMSecretAgent
744  * @connection: a #NMConnection
745  * @callback: (scope async): a callback, to be invoked when the operation is done
746  * @user_data: (closure): caller-specific data to be passed to @callback
747  *
748  * Asyncronously ensure that all secrets inside @connection
749  * are stored to disk.
750  *
751  * Virtual: save_secrets
752  */
753 void
754 nm_secret_agent_save_secrets (NMSecretAgent *self,
755                               NMConnection *connection,
756                               NMSecretAgentSaveSecretsFunc callback,
757                               gpointer user_data)
758 {
759         g_return_if_fail (NM_IS_SECRET_AGENT (self));
760         g_return_if_fail (NM_IS_CONNECTION (connection));
761         g_return_if_fail (nm_connection_get_path (connection));
762
763         NM_SECRET_AGENT_GET_CLASS (self)->save_secrets (self,
764                                                         connection,
765                                                         nm_connection_get_path (connection),
766                                                         callback,
767                                                         user_data);
768 }
769
770 /**
771  * nm_secret_agent_delete_secrets:
772  * @self: a #NMSecretAgent
773  * @connection: a #NMConnection
774  * @callback: (scope async): a callback, to be invoked when the operation is done
775  * @user_data: (closure): caller-specific data to be passed to @callback
776  *
777  * Asynchronously ask the agent to delete all saved secrets belonging to
778  * @connection.
779  *
780  * Virtual: delete_secrets
781  */
782 void
783 nm_secret_agent_delete_secrets (NMSecretAgent *self,
784                                 NMConnection *connection,
785                                 NMSecretAgentDeleteSecretsFunc callback,
786                                 gpointer user_data)
787 {
788         g_return_if_fail (NM_IS_SECRET_AGENT (self));
789         g_return_if_fail (NM_IS_CONNECTION (connection));
790         g_return_if_fail (nm_connection_get_path (connection));
791
792         NM_SECRET_AGENT_GET_CLASS (self)->delete_secrets (self,
793                                                           connection,
794                                                           nm_connection_get_path (connection),
795                                                           callback,
796                                                           user_data);
797 }
798
799 /**************************************************************/
800
801 static gboolean
802 validate_identifier (const char *identifier)
803 {
804         const char *p = identifier;
805         size_t id_len;
806
807         /* Length between 3 and 255 characters inclusive */
808         id_len = strlen (identifier);
809         if (id_len < 3 || id_len > 255)
810                 return FALSE;
811
812         if ((identifier[0] == '.') || (identifier[id_len - 1] == '.'))
813                 return FALSE;
814
815         /* FIXME: do complete validation here */
816         while (p && *p) {
817                 if (!g_ascii_isalnum (*p) && (*p != '_') && (*p != '-') && (*p != '.'))
818                         return FALSE;
819                 if ((*p == '.') && (*(p + 1) == '.'))
820                         return FALSE;
821                 p++;
822         }
823
824         return TRUE;
825 }
826
827 static void
828 nm_secret_agent_init (NMSecretAgent *self)
829 {
830         NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
831         GError *error = NULL;
832
833         priv->bus = _nm_dbus_new_connection (&error);
834         if (!priv->bus) {
835                 g_warning ("Couldn't connect to system bus: %s", error->message);
836                 g_error_free (error);
837                 return;
838         }
839
840         priv->dbus_proxy = dbus_g_proxy_new_for_name (priv->bus,
841                                                       DBUS_SERVICE_DBUS,
842                                                       DBUS_PATH_DBUS,
843                                                       DBUS_INTERFACE_DBUS);
844         g_assert (priv->dbus_proxy);
845
846         dbus_g_object_register_marshaller (g_cclosure_marshal_generic,
847                                            G_TYPE_NONE,
848                                            G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
849                                            G_TYPE_INVALID);
850         dbus_g_proxy_add_signal (priv->dbus_proxy, "NameOwnerChanged",
851                                  G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
852                                  G_TYPE_INVALID);
853         dbus_g_proxy_connect_signal (priv->dbus_proxy,
854                                      "NameOwnerChanged",
855                                      G_CALLBACK (name_owner_changed),
856                                      self, NULL);
857
858         get_nm_owner (self);
859
860         priv->manager_proxy = _nm_dbus_new_proxy_for_connection (priv->bus,
861                                                                  NM_DBUS_PATH_AGENT_MANAGER,
862                                                                  NM_DBUS_INTERFACE_AGENT_MANAGER);
863         if (!priv->manager_proxy) {
864                 g_warning ("Couldn't create NM agent manager proxy.");
865                 return;
866         }
867
868         if (priv->nm_owner)
869                 priv->auto_register_id = g_idle_add (auto_register_cb, self);
870 }
871
872 static void
873 get_property (GObject *object,
874               guint prop_id,
875               GValue *value,
876               GParamSpec *pspec)
877 {
878         NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (object);
879
880         switch (prop_id) {
881         case PROP_IDENTIFIER:
882                 g_value_set_string (value, priv->identifier);
883                 break;
884         case PROP_AUTO_REGISTER:
885                 g_value_set_boolean (value, priv->auto_register);
886                 break;
887         case PROP_REGISTERED:
888                 g_value_set_boolean (value, priv->registered);
889                 break;
890         case PROP_CAPABILITIES:
891                 g_value_set_flags (value, priv->capabilities);
892                 break;
893         default:
894                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
895                 break;
896         }
897 }
898
899 static void
900 set_property (GObject *object,
901               guint prop_id,
902               const GValue *value,
903               GParamSpec *pspec)
904 {
905         NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (object);
906         const char *identifier;
907
908         switch (prop_id) {
909         case PROP_IDENTIFIER:
910                 identifier = g_value_get_string (value);
911
912                 g_return_if_fail (validate_identifier (identifier));
913
914                 g_free (priv->identifier);
915                 priv->identifier = g_strdup (identifier);
916                 break;
917         case PROP_AUTO_REGISTER:
918                 priv->auto_register = g_value_get_boolean (value);
919                 break;
920         case PROP_CAPABILITIES:
921                 priv->capabilities = g_value_get_flags (value);
922                 break;
923         default:
924                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
925                 break;
926         }
927 }
928
929 static void
930 dispose (GObject *object)
931 {
932         NMSecretAgent *self = NM_SECRET_AGENT (object);
933         NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
934
935         if (priv->registered)
936                 nm_secret_agent_unregister (self);
937
938         if (priv->auto_register_id) {
939                 g_source_remove (priv->auto_register_id);
940                 priv->auto_register_id = 0;
941         }
942
943         g_free (priv->identifier);
944         priv->identifier = NULL;
945         g_free (priv->nm_owner);
946         priv->nm_owner = NULL;
947
948         while (priv->pending_gets)
949                 get_secrets_info_finalize (self, priv->pending_gets->data);
950
951         g_clear_object (&priv->dbus_proxy);
952         g_clear_object (&priv->manager_proxy);
953
954         if (priv->bus) {
955                 dbus_g_connection_unref (priv->bus);
956                 priv->bus = NULL;
957         }
958
959         G_OBJECT_CLASS (nm_secret_agent_parent_class)->dispose (object);
960 }
961
962 static void
963 nm_secret_agent_class_init (NMSecretAgentClass *class)
964 {
965         GObjectClass *object_class = G_OBJECT_CLASS (class);
966
967         g_type_class_add_private (class, sizeof (NMSecretAgentPrivate));
968
969         /* Virtual methods */
970         object_class->dispose = dispose;
971         object_class->get_property = get_property;
972         object_class->set_property = set_property;
973
974         /**
975          * NMSecretAgent:identifier:
976          *
977          * Identifies this agent; only one agent in each user session may use the
978          * same identifier.  Identifier formatting follows the same rules as
979          * D-Bus bus names with the exception that the ':' character is not
980          * allowed.  The valid set of characters is "[A-Z][a-z][0-9]_-." and the
981          * identifier is limited in length to 255 characters with a minimum
982          * of 3 characters.  An example valid identifier is 'org.gnome.nm-applet'
983          * (without quotes).
984          **/
985         g_object_class_install_property
986                 (object_class, PROP_IDENTIFIER,
987                  g_param_spec_string (NM_SECRET_AGENT_IDENTIFIER, "", "",
988                                       NULL,
989                                       G_PARAM_READWRITE |
990                                       G_PARAM_CONSTRUCT_ONLY |
991                                       G_PARAM_STATIC_STRINGS));
992
993         /**
994          * NMSecretAgent:auto-register:
995          *
996          * If TRUE, the agent will attempt to automatically register itself after
997          * it is created (via an idle handler) and to re-register itself if
998          * NetworkManager restarts.  If FALSE, the agent does not automatically
999          * register with NetworkManager, and nm_secret_agent_register() must be
1000          * called.  If 'auto-register' is TRUE, calling nm_secret_agent_unregister()
1001          * will suppress auto-registration until nm_secret_agent_register() is
1002          * called, which re-enables auto-registration.
1003          **/
1004         g_object_class_install_property
1005                 (object_class, PROP_AUTO_REGISTER,
1006                  g_param_spec_boolean (NM_SECRET_AGENT_AUTO_REGISTER, "", "",
1007                                        TRUE,
1008                                        G_PARAM_READWRITE |
1009                                        G_PARAM_CONSTRUCT |
1010                                        G_PARAM_STATIC_STRINGS));
1011
1012         /**
1013          * NMSecretAgent:registered:
1014          *
1015          * %TRUE if the agent is registered with NetworkManager, %FALSE if not.
1016          **/
1017         g_object_class_install_property
1018                 (object_class, PROP_REGISTERED,
1019                  g_param_spec_boolean (NM_SECRET_AGENT_REGISTERED, "", "",
1020                                        FALSE,
1021                                        G_PARAM_READABLE |
1022                                        G_PARAM_STATIC_STRINGS));
1023
1024         /**
1025          * NMSecretAgent:capabilities:
1026          *
1027          * A bitfield of %NMSecretAgentCapabilities.
1028          **/
1029         g_object_class_install_property
1030                 (object_class, PROP_CAPABILITIES,
1031                  g_param_spec_flags (NM_SECRET_AGENT_CAPABILITIES, "", "",
1032                                      NM_TYPE_SECRET_AGENT_CAPABILITIES,
1033                                      NM_SECRET_AGENT_CAPABILITY_NONE,
1034                                      G_PARAM_READWRITE |
1035                                      G_PARAM_CONSTRUCT |
1036                                      G_PARAM_STATIC_STRINGS));
1037
1038         /**
1039          * NMSecretAgent::registration-result:
1040          * @agent: the agent that received the signal
1041          * @error: the error, if any, that occured while registering
1042          *
1043          * Indicates the result of a registration request; if @error is NULL the
1044          * request was successful.
1045          **/
1046         signals[REGISTRATION_RESULT] =
1047                 g_signal_new (NM_SECRET_AGENT_REGISTRATION_RESULT,
1048                               G_OBJECT_CLASS_TYPE (object_class),
1049                               G_SIGNAL_RUN_FIRST,
1050                               0, NULL, NULL, NULL,
1051                               G_TYPE_NONE, 1, G_TYPE_POINTER);
1052
1053         dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (class),
1054                                          &dbus_glib_nm_secret_agent_object_info);
1055
1056         dbus_g_error_domain_register (NM_SECRET_AGENT_ERROR,
1057                                       NM_DBUS_INTERFACE_SECRET_AGENT,
1058                                       NM_TYPE_SECRET_AGENT_ERROR);
1059 }