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