device: renew dhcp leases on awake for software devices
[NetworkManager.git] / libnm-util / nm-setting-vpn.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 2007 - 2013 Red Hat, Inc.
19  * Copyright 2007 - 2008 Novell, Inc.
20  */
21
22 #include "nm-default.h"
23
24 #include <string.h>
25 #include <errno.h>
26 #include <stdlib.h>
27 #include <dbus/dbus-glib.h>
28
29 #include "nm-setting-vpn.h"
30 #include "nm-param-spec-specialized.h"
31 #include "nm-utils.h"
32 #include "nm-dbus-glib-types.h"
33 #include "nm-setting-private.h"
34
35 /**
36  * SECTION:nm-setting-vpn
37  * @short_description: Describes connection properties for Virtual Private Networks
38  * @include: nm-setting-vpn.h
39  *
40  * The #NMSettingVPN object is a #NMSetting subclass that describes properties
41  * necessary for connection to Virtual Private Networks.  NetworkManager uses
42  * a plugin architecture to allow easier use of new VPN types, and this
43  * setting abstracts the configuration for those plugins.  Since the configuration
44  * options are only known to the VPN plugins themselves, the VPN configuration
45  * options are stored as key/value pairs of strings rather than GObject
46  * properties.
47  **/
48
49 /**
50  * nm_setting_vpn_error_quark:
51  *
52  * Registers an error quark for #NMSettingVPN if necessary.
53  *
54  * Returns: the error quark used for #NMSettingVPN errors.
55  **/
56 GQuark
57 nm_setting_vpn_error_quark (void)
58 {
59         static GQuark quark;
60
61         if (G_UNLIKELY (!quark))
62                 quark = g_quark_from_static_string ("nm-setting-vpn-error-quark");
63         return quark;
64 }
65
66
67 G_DEFINE_TYPE_WITH_CODE (NMSettingVPN, nm_setting_vpn, NM_TYPE_SETTING,
68                          _nm_register_setting (NM_SETTING_VPN_SETTING_NAME,
69                                                g_define_type_id,
70                                                1,
71                                                NM_SETTING_VPN_ERROR))
72 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_VPN)
73
74 #define NM_SETTING_VPN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_VPN, NMSettingVPNPrivate))
75
76 typedef struct {
77         char *service_type;
78
79         /* username of the user requesting this connection, thus
80          * it's really only valid for user connections, and it also
81          * should never be saved out to persistent config.
82          */
83         char *user_name;
84
85         /* Whether the VPN stays up across link changes, until the user
86          * explicitly disconnects it.
87          */
88         gboolean persistent;
89
90         /* The hash table is created at setting object
91          * init time and should not be replaced.  It is
92          * a char * -> char * mapping, and both the key
93          * and value are owned by the hash table, and should
94          * be allocated with functions whose value can be
95          * freed with g_free().  Should not contain secrets.
96          */
97         GHashTable *data;
98
99         /* The hash table is created at setting object
100          * init time and should not be replaced.  It is
101          * a char * -> char * mapping, and both the key
102          * and value are owned by the hash table, and should
103          * be allocated with functions whose value can be
104          * freed with g_free().  Should contain secrets only.
105          */
106         GHashTable *secrets;
107 } NMSettingVPNPrivate;
108
109 enum {
110         PROP_0,
111         PROP_SERVICE_TYPE,
112         PROP_USER_NAME,
113         PROP_PERSISTENT,
114         PROP_DATA,
115         PROP_SECRETS,
116
117         LAST_PROP
118 };
119
120 /**
121  * nm_setting_vpn_new:
122  *
123  * Creates a new #NMSettingVPN object with default values.
124  *
125  * Returns: (transfer full): the new empty #NMSettingVPN object
126  **/
127 NMSetting *
128 nm_setting_vpn_new (void)
129 {
130         return (NMSetting *) g_object_new (NM_TYPE_SETTING_VPN, NULL);
131 }
132
133 /**
134  * nm_setting_vpn_get_service_type:
135  * @setting: the #NMSettingVPN
136  *
137  * Returns the service name of the VPN, which identifies the specific VPN
138  * plugin that should be used to connect to this VPN.
139  *
140  * Returns: the VPN plugin's service name
141  **/
142 const char *
143 nm_setting_vpn_get_service_type (NMSettingVPN *setting)
144 {
145         g_return_val_if_fail (NM_IS_SETTING_VPN (setting), NULL);
146
147         return NM_SETTING_VPN_GET_PRIVATE (setting)->service_type;
148 }
149
150 /**
151  * nm_setting_vpn_get_user_name:
152  * @setting: the #NMSettingVPN
153  *
154  * Returns: the #NMSettingVPN:user-name property of the setting
155  **/
156 const char *
157 nm_setting_vpn_get_user_name (NMSettingVPN *setting)
158 {
159         g_return_val_if_fail (NM_IS_SETTING_VPN (setting), NULL);
160
161         return NM_SETTING_VPN_GET_PRIVATE (setting)->user_name;
162 }
163
164 /**
165  * nm_setting_vpn_get_persistent:
166  * @setting: the #NMSettingVPN
167  *
168  * Returns: the #NMSettingVPN:persistent property of the setting
169  **/
170 gboolean
171 nm_setting_vpn_get_persistent (NMSettingVPN *setting)
172 {
173         g_return_val_if_fail (NM_IS_SETTING_VPN (setting), FALSE);
174
175         return NM_SETTING_VPN_GET_PRIVATE (setting)->persistent;
176 }
177
178 /**
179  * nm_setting_vpn_get_num_data_items:
180  * @setting: the #NMSettingVPN
181  *
182  * Gets number of key/value pairs of VPN configuration data.
183  *
184  * Returns: the number of VPN plugin specific configuration data items
185  **/
186 guint32
187 nm_setting_vpn_get_num_data_items (NMSettingVPN *setting)
188 {
189         g_return_val_if_fail (NM_IS_SETTING_VPN (setting), 0);
190
191         return g_hash_table_size (NM_SETTING_VPN_GET_PRIVATE (setting)->data);
192 }
193
194 /**
195  * nm_setting_vpn_add_data_item:
196  * @setting: the #NMSettingVPN
197  * @key: a name that uniquely identifies the given value @item
198  * @item: the value to be referenced by @key
199  *
200  * Establishes a relationship between @key and @item internally in the
201  * setting which may be retrieved later.  Should not be used to store passwords
202  * or other secrets, which is what nm_setting_vpn_add_secret() is for.
203  **/
204 void
205 nm_setting_vpn_add_data_item (NMSettingVPN *setting,
206                               const char *key,
207                               const char *item)
208 {
209         g_return_if_fail (NM_IS_SETTING_VPN (setting));
210         g_return_if_fail (key != NULL);
211         g_return_if_fail (strlen (key) > 0);
212         g_return_if_fail (item != NULL);
213         g_return_if_fail (strlen (item) > 0);
214
215         g_hash_table_insert (NM_SETTING_VPN_GET_PRIVATE (setting)->data,
216                              g_strdup (key), g_strdup (item));
217         g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_DATA);
218 }
219
220 /**
221  * nm_setting_vpn_get_data_item:
222  * @setting: the #NMSettingVPN
223  * @key: the name of the data item to retrieve
224  *
225  * Retrieves the data item of a key/value relationship previously established
226  * by nm_setting_vpn_add_data_item().
227  *
228  * Returns: the data item, if any
229  **/
230 const char *
231 nm_setting_vpn_get_data_item (NMSettingVPN *setting, const char *key)
232 {
233         g_return_val_if_fail (NM_IS_SETTING_VPN (setting), NULL);
234
235         return (const char *) g_hash_table_lookup (NM_SETTING_VPN_GET_PRIVATE (setting)->data, key);
236 }
237
238 /**
239  * nm_setting_vpn_remove_data_item:
240  * @setting: the #NMSettingVPN
241  * @key: the name of the data item to remove
242  *
243  * Deletes a key/value relationship previously established by
244  * nm_setting_vpn_add_data_item().
245  *
246  * Returns: %TRUE if the data item was found and removed from the internal list,
247  * %FALSE if it was not.
248  **/
249 gboolean
250 nm_setting_vpn_remove_data_item (NMSettingVPN *setting, const char *key)
251 {
252         gboolean found;
253
254         g_return_val_if_fail (NM_IS_SETTING_VPN (setting), FALSE);
255
256         found = g_hash_table_remove (NM_SETTING_VPN_GET_PRIVATE (setting)->data, key);
257         if (found)
258                 g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_DATA);
259         return found;
260 }
261
262 static void
263 foreach_item_helper (GHashTable *hash,
264                      NMVPNIterFunc func,
265                      gpointer user_data)
266 {
267         GList *keys, *liter;
268         GSList *copied = NULL, *siter;
269
270         g_return_if_fail (hash != NULL);
271
272         /* Grab keys and copy them so that the callback func can modify
273          * the hash table items if it wants to.
274          */
275         keys = g_hash_table_get_keys (hash);
276         for (liter = keys; liter; liter = g_list_next (liter))
277                 copied = g_slist_prepend (copied, g_strdup (liter->data));
278         copied = g_slist_reverse (copied);
279         g_list_free (keys);
280
281         for (siter = copied; siter; siter = g_slist_next (siter)) {
282                 gpointer value;
283
284                 value = g_hash_table_lookup (hash, siter->data);
285                 func (siter->data, value, user_data);
286         }
287
288         g_slist_free_full (copied, g_free);
289 }
290
291 /**
292  * nm_setting_vpn_foreach_data_item:
293  * @setting: a #NMSettingVPN
294  * @func: (scope call): an user provided function
295  * @user_data: data to be passed to @func
296  *
297  * Iterates all data items stored in this setting.  It is safe to add, remove,
298  * and modify data items inside @func, though any additions or removals made
299  * during iteration will not be part of the iteration.
300  */
301 void
302 nm_setting_vpn_foreach_data_item (NMSettingVPN *setting,
303                                   NMVPNIterFunc func,
304                                   gpointer user_data)
305 {
306         g_return_if_fail (NM_IS_SETTING_VPN (setting));
307
308         foreach_item_helper (NM_SETTING_VPN_GET_PRIVATE (setting)->data, func, user_data);
309 }
310
311 /**
312  * nm_setting_vpn_get_num_secrets:
313  * @setting: the #NMSettingVPN
314  *
315  * Gets number of VPN plugin specific secrets in the setting.
316  *
317  * Returns: the number of VPN plugin specific secrets
318  **/
319 guint32
320 nm_setting_vpn_get_num_secrets (NMSettingVPN *setting)
321 {
322         g_return_val_if_fail (NM_IS_SETTING_VPN (setting), 0);
323
324         return g_hash_table_size (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets);
325 }
326
327 /**
328  * nm_setting_vpn_add_secret:
329  * @setting: the #NMSettingVPN
330  * @key: a name that uniquely identifies the given secret @secret
331  * @secret: the secret to be referenced by @key
332  *
333  * Establishes a relationship between @key and @secret internally in the
334  * setting which may be retrieved later.
335  **/
336 void
337 nm_setting_vpn_add_secret (NMSettingVPN *setting,
338                            const char *key,
339                            const char *secret)
340 {
341         g_return_if_fail (NM_IS_SETTING_VPN (setting));
342         g_return_if_fail (key != NULL);
343         g_return_if_fail (strlen (key) > 0);
344         g_return_if_fail (secret != NULL);
345         g_return_if_fail (strlen (secret) > 0);
346
347         g_hash_table_insert (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets,
348                              g_strdup (key), g_strdup (secret));
349         g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_SECRETS);
350 }
351
352 /**
353  * nm_setting_vpn_get_secret:
354  * @setting: the #NMSettingVPN
355  * @key: the name of the secret to retrieve
356  *
357  * Retrieves the secret of a key/value relationship previously established
358  * by nm_setting_vpn_add_secret().
359  *
360  * Returns: the secret, if any
361  **/
362 const char *
363 nm_setting_vpn_get_secret (NMSettingVPN *setting, const char *key)
364 {
365         g_return_val_if_fail (NM_IS_SETTING_VPN (setting), NULL);
366
367         return (const char *) g_hash_table_lookup (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets, key);
368 }
369
370 /**
371  * nm_setting_vpn_remove_secret:
372  * @setting: the #NMSettingVPN
373  * @key: the name of the secret to remove
374  *
375  * Deletes a key/value relationship previously established by
376  * nm_setting_vpn_add_secret().
377  *
378  * Returns: %TRUE if the secret was found and removed from the internal list,
379  * %FALSE if it was not.
380  **/
381 gboolean
382 nm_setting_vpn_remove_secret (NMSettingVPN *setting, const char *key)
383 {
384         gboolean found;
385
386         g_return_val_if_fail (NM_IS_SETTING_VPN (setting), FALSE);
387
388         found = g_hash_table_remove (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets, key);
389         if (found)
390                 g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_SECRETS);
391         return found;
392 }
393
394 /**
395  * nm_setting_vpn_foreach_secret:
396  * @setting: a #NMSettingVPN
397  * @func: (scope call): an user provided function
398  * @user_data: data to be passed to @func
399  *
400  * Iterates all secrets stored in this setting.  It is safe to add, remove,
401  * and modify secrets inside @func, though any additions or removals made during
402  * iteration will not be part of the iteration.
403  */
404 void
405 nm_setting_vpn_foreach_secret (NMSettingVPN *setting,
406                                NMVPNIterFunc func,
407                                gpointer user_data)
408 {
409         g_return_if_fail (NM_IS_SETTING_VPN (setting));
410
411         foreach_item_helper (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets, func, user_data);
412 }
413
414 static gboolean
415 verify (NMSetting *setting, GSList *all_settings, GError **error)
416 {
417         NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
418
419         if (!priv->service_type) {
420                 g_set_error_literal (error,
421                                      NM_SETTING_VPN_ERROR,
422                                      NM_SETTING_VPN_ERROR_MISSING_PROPERTY,
423                                      _("property is missing"));
424                 g_prefix_error (error, "%s.%s: ", NM_SETTING_VPN_SETTING_NAME, NM_SETTING_VPN_SERVICE_TYPE);
425                 return FALSE;
426         }
427
428         if (!strlen (priv->service_type)) {
429                 g_set_error_literal (error,
430                                      NM_SETTING_VPN_ERROR,
431                                      NM_SETTING_VPN_ERROR_INVALID_PROPERTY,
432                                      _("property is empty"));
433                 g_prefix_error (error, "%s.%s: ", NM_SETTING_VPN_SETTING_NAME, NM_SETTING_VPN_SERVICE_TYPE);
434                 return FALSE;
435         }
436
437         /* default username can be NULL, but can't be zero-length */
438         if (priv->user_name && !strlen (priv->user_name)) {
439                 g_set_error_literal (error,
440                                      NM_SETTING_VPN_ERROR,
441                                      NM_SETTING_VPN_ERROR_INVALID_PROPERTY,
442                                      _("property is empty"));
443                 g_prefix_error (error, "%s.%s: ", NM_SETTING_VPN_SETTING_NAME, NM_SETTING_VPN_USER_NAME);
444                 return FALSE;
445         }
446
447         return TRUE;
448 }
449
450 static NMSettingUpdateSecretResult
451 update_secret_string (NMSetting *setting,
452                       const char *key,
453                       const char *value,
454                       GError **error)
455 {
456         NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
457
458         g_return_val_if_fail (key != NULL, NM_SETTING_UPDATE_SECRET_ERROR);
459         g_return_val_if_fail (value != NULL, NM_SETTING_UPDATE_SECRET_ERROR);
460
461         if (!value || !strlen (value)) {
462                 g_set_error (error, NM_SETTING_ERROR,
463                              NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH,
464                              "Secret %s was empty", key);
465                 return NM_SETTING_UPDATE_SECRET_ERROR;
466         }
467
468         if (g_strcmp0 (g_hash_table_lookup (priv->secrets, key), value) == 0)
469                 return NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED;
470
471         g_hash_table_insert (priv->secrets, g_strdup (key), g_strdup (value));
472         return NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED;
473 }
474
475 static NMSettingUpdateSecretResult
476 update_secret_hash (NMSetting *setting,
477                     GHashTable *secrets,
478                     GError **error)
479 {
480         NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
481         GHashTableIter iter;
482         const char *name, *value;
483         NMSettingUpdateSecretResult result = NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED;
484
485         g_return_val_if_fail (secrets != NULL, NM_SETTING_UPDATE_SECRET_ERROR);
486
487         /* Make sure the items are valid */
488         g_hash_table_iter_init (&iter, secrets);
489         while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &value)) {
490                 if (!name || !strlen (name)) {
491                         g_set_error_literal (error, NM_SETTING_ERROR,
492                                              NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH,
493                                              "Secret name was empty");
494                         return NM_SETTING_UPDATE_SECRET_ERROR;
495                 }
496
497                 if (!value || !strlen (value)) {
498                         g_set_error (error, NM_SETTING_ERROR,
499                                      NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH,
500                                      "Secret %s value was empty", name);
501                         return NM_SETTING_UPDATE_SECRET_ERROR;
502                 }
503         }
504
505         /* Now add the items to the settings' secrets list */
506         g_hash_table_iter_init (&iter, secrets);
507         while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &value)) {
508                 if (value == NULL) {
509                         g_warn_if_fail (value != NULL);
510                         continue;
511                 }
512                 if (strlen (value) == 0) {
513                         g_warn_if_fail (strlen (value) > 0);
514                         continue;
515                 }
516
517                 if (g_strcmp0 (g_hash_table_lookup (priv->secrets, name), value) == 0)
518                         continue;
519
520                 g_hash_table_insert (priv->secrets, g_strdup (name), g_strdup (value));
521                 result = NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED;
522         }
523
524         return result;
525 }
526
527 static int
528 update_one_secret (NMSetting *setting, const char *key, GValue *value, GError **error)
529 {
530         NMSettingUpdateSecretResult success = NM_SETTING_UPDATE_SECRET_ERROR;
531
532         g_return_val_if_fail (key != NULL, NM_SETTING_UPDATE_SECRET_ERROR);
533         g_return_val_if_fail (value != NULL, NM_SETTING_UPDATE_SECRET_ERROR);
534
535         if (G_VALUE_HOLDS_STRING (value)) {
536                 /* Passing the string properties individually isn't correct, and won't
537                  * produce the correct result, but for some reason that's how it used
538                  * to be done.  So even though it's not correct, keep the code around
539                  * for compatibility's sake.
540                  */
541                 success = update_secret_string (setting, key, g_value_get_string (value), error);
542         } else if (G_VALUE_HOLDS (value, DBUS_TYPE_G_MAP_OF_STRING)) {
543                 if (strcmp (key, NM_SETTING_VPN_SECRETS) != 0) {
544                         g_set_error (error, NM_SETTING_ERROR, NM_SETTING_ERROR_PROPERTY_NOT_SECRET,
545                                      "Property %s not a secret property", key);
546                 } else
547                         success = update_secret_hash (setting, g_value_get_boxed (value), error);
548         } else
549                 g_set_error_literal (error, NM_SETTING_ERROR, NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH, key);
550
551         if (success == NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED)
552                 g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_SECRETS);
553
554         return success;
555 }
556
557 static gboolean
558 get_secret_flags (NMSetting *setting,
559                   const char *secret_name,
560                   gboolean verify_secret,
561                   NMSettingSecretFlags *out_flags,
562                   GError **error)
563 {
564         NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
565         gboolean success = FALSE;
566         char *flags_key;
567         gpointer val;
568         unsigned long tmp;
569         NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
570
571         flags_key = g_strdup_printf ("%s-flags", secret_name);
572         if (g_hash_table_lookup_extended (priv->data, flags_key, NULL, &val)) {
573                 errno = 0;
574                 tmp = strtoul ((const char *) val, NULL, 10);
575                 if ((errno == 0) && (tmp <= NM_SETTING_SECRET_FLAGS_ALL)) {
576                         flags = (NMSettingSecretFlags) tmp;
577                         success = TRUE;
578                 } else {
579                         g_set_error (error,
580                                      NM_SETTING_ERROR,
581                                      NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH,
582                                      _("Failed to convert '%s' value '%s' to uint"),
583                                      flags_key, (const char *) val);
584                 }
585         } else {
586                 g_set_error (error,
587                              NM_SETTING_ERROR,
588                              NM_SETTING_ERROR_PROPERTY_NOT_FOUND,
589                              _("Secret flags property '%s' not found"), flags_key);
590         }
591         g_free (flags_key);
592         if (out_flags)
593                 *out_flags = flags;
594         return success;
595 }
596
597 static gboolean
598 set_secret_flags (NMSetting *setting,
599                   const char *secret_name,
600                   gboolean verify_secret,
601                   NMSettingSecretFlags flags,
602                   GError **error)
603 {
604         g_hash_table_insert (NM_SETTING_VPN_GET_PRIVATE (setting)->data,
605                              g_strdup_printf ("%s-flags", secret_name),
606                              g_strdup_printf ("%u", flags));
607         g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_SECRETS);
608         return TRUE;
609 }
610
611 static GPtrArray *
612 need_secrets (NMSetting *setting)
613 {
614         /* Assume that VPN connections need secrets since they almost always will */
615         return g_ptr_array_sized_new (1);
616 }
617
618 static gboolean
619 compare_one_secret (NMSettingVPN *a,
620                     NMSettingVPN *b,
621                     NMSettingCompareFlags flags)
622 {
623         GHashTable *a_secrets, *b_secrets;
624         GHashTableIter iter;
625         const char *key, *val;
626
627         a_secrets = NM_SETTING_VPN_GET_PRIVATE (a)->secrets;
628         b_secrets = NM_SETTING_VPN_GET_PRIVATE (b)->secrets;
629
630         g_hash_table_iter_init (&iter, a_secrets);
631         while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &val)) {
632                 NMSettingSecretFlags a_secret_flags = NM_SETTING_SECRET_FLAG_NONE;
633                 NMSettingSecretFlags b_secret_flags = NM_SETTING_SECRET_FLAG_NONE;
634
635                 nm_setting_get_secret_flags (NM_SETTING (a), key, &a_secret_flags, NULL);
636                 nm_setting_get_secret_flags (NM_SETTING (b), key, &b_secret_flags, NULL);
637
638                 /* If the secret flags aren't the same, the settings aren't the same */
639                 if (a_secret_flags != b_secret_flags)
640                         return FALSE;
641
642                 if (   (flags & NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS)
643                     && (a_secret_flags & NM_SETTING_SECRET_FLAG_AGENT_OWNED))
644                         continue;
645
646                 if (   (flags & NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)
647                     && (a_secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED))
648                         continue;
649
650                 /* Now compare the values themselves */
651                 if (g_strcmp0 (val, nm_setting_vpn_get_secret (b, key)) != 0)
652                         return FALSE;
653         }
654
655         return TRUE;
656 }
657
658 static gboolean
659 compare_property (NMSetting *setting,
660                   NMSetting *other,
661                   const GParamSpec *prop_spec,
662                   NMSettingCompareFlags flags)
663 {
664         gboolean same;
665
666         /* We only need to treat the 'secrets' property specially */
667         if (g_strcmp0 (prop_spec->name, NM_SETTING_VPN_SECRETS) != 0)
668                 return NM_SETTING_CLASS (nm_setting_vpn_parent_class)->compare_property (setting, other, prop_spec, flags);
669
670         /* Compare A to B to ensure everything in A is found in B */
671         same = compare_one_secret (NM_SETTING_VPN (setting), NM_SETTING_VPN (other), flags);
672         if (same) {
673                 /* And then B to A to ensure everything in B is also found in A */
674                 same = compare_one_secret (NM_SETTING_VPN (other), NM_SETTING_VPN (setting), flags);
675         }
676
677         return same;
678 }
679
680 static gboolean
681 clear_secrets_with_flags (NMSetting *setting,
682                           GParamSpec *pspec,
683                           NMSettingClearSecretsWithFlagsFn func,
684                           gpointer user_data)
685 {
686         NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
687         GHashTableIter iter;
688         const char *secret;
689         gboolean changed = TRUE;
690
691         if (priv->secrets == NULL)
692                 return FALSE;
693
694         /* Iterate through secrets hash and check each entry */
695         g_hash_table_iter_init (&iter, priv->secrets);
696         while (g_hash_table_iter_next (&iter, (gpointer) &secret, NULL)) {
697                 NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
698
699                 nm_setting_get_secret_flags (setting, secret, &flags, NULL);
700                 if (func (setting, pspec->name, flags, user_data) == TRUE) {
701                         g_hash_table_iter_remove (&iter);
702                         changed = TRUE;
703                 }
704         }
705
706         if (changed)
707                 g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_SECRETS);
708
709         return changed;
710 }
711
712 static void
713 destroy_one_secret (gpointer data)
714 {
715         char *secret = (char *) data;
716
717         /* Don't leave the secret lying around in memory */
718         memset (secret, 0, strlen (secret));
719         g_free (secret);
720 }
721
722 static void
723 nm_setting_vpn_init (NMSettingVPN *setting)
724 {
725         NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
726
727         priv->data = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
728         priv->secrets = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, destroy_one_secret);
729 }
730
731 static void
732 finalize (GObject *object)
733 {
734         NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (object);
735
736         g_free (priv->service_type);
737         g_free (priv->user_name);
738         g_hash_table_destroy (priv->data);
739         g_hash_table_destroy (priv->secrets);
740
741         G_OBJECT_CLASS (nm_setting_vpn_parent_class)->finalize (object);
742 }
743
744 static void
745 copy_hash (gpointer key, gpointer value, gpointer user_data)
746 {
747         g_return_if_fail (value != NULL);
748         g_return_if_fail (strlen (value));
749         g_hash_table_insert ((GHashTable *) user_data, g_strdup (key), g_strdup (value));
750 }
751
752 static void
753 set_property (GObject *object, guint prop_id,
754               const GValue *value, GParamSpec *pspec)
755 {
756         NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (object);
757         GHashTable *new_hash;
758
759         switch (prop_id) {
760         case PROP_SERVICE_TYPE:
761                 g_free (priv->service_type);
762                 priv->service_type = g_value_dup_string (value);
763                 break;
764         case PROP_USER_NAME:
765                 g_free (priv->user_name);
766                 priv->user_name = g_value_dup_string (value);
767                 break;
768         case PROP_PERSISTENT:
769                 priv->persistent = g_value_get_boolean (value);
770                 break;
771         case PROP_DATA:
772                 /* Must make a deep copy of the hash table here... */
773                 g_hash_table_remove_all (priv->data);
774                 new_hash = g_value_get_boxed (value);
775                 if (new_hash)
776                         g_hash_table_foreach (new_hash, copy_hash, priv->data);
777                 break;
778         case PROP_SECRETS:
779                 /* Must make a deep copy of the hash table here... */
780                 g_hash_table_remove_all (priv->secrets);
781                 new_hash = g_value_get_boxed (value);
782                 if (new_hash)
783                         g_hash_table_foreach (new_hash, copy_hash, priv->secrets);
784                 break;
785         default:
786                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
787                 break;
788         }
789 }
790
791 static void
792 get_property (GObject *object, guint prop_id,
793               GValue *value, GParamSpec *pspec)
794 {
795         NMSettingVPN *setting = NM_SETTING_VPN (object);
796         NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
797
798         switch (prop_id) {
799         case PROP_SERVICE_TYPE:
800                 g_value_set_string (value, nm_setting_vpn_get_service_type (setting));
801                 break;
802         case PROP_USER_NAME:
803                 g_value_set_string (value, nm_setting_vpn_get_user_name (setting));
804                 break;
805         case PROP_PERSISTENT:
806                 g_value_set_boolean (value, priv->persistent);
807                 break;
808         case PROP_DATA:
809                 g_value_set_boxed (value, priv->data);
810                 break;
811         case PROP_SECRETS:
812                 g_value_set_boxed (value, priv->secrets);
813                 break;
814         default:
815                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
816                 break;
817         }
818 }
819
820 static void
821 nm_setting_vpn_class_init (NMSettingVPNClass *setting_class)
822 {
823         GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
824         NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
825
826         g_type_class_add_private (setting_class, sizeof (NMSettingVPNPrivate));
827
828         /* virtual methods */
829         object_class->set_property = set_property;
830         object_class->get_property = get_property;
831         object_class->finalize     = finalize;
832
833         parent_class->verify            = verify;
834         parent_class->update_one_secret = update_one_secret;
835         parent_class->get_secret_flags  = get_secret_flags;
836         parent_class->set_secret_flags  = set_secret_flags;
837         parent_class->need_secrets      = need_secrets;
838         parent_class->compare_property  = compare_property;
839         parent_class->clear_secrets_with_flags = clear_secrets_with_flags;
840
841         /* Properties */
842         /**
843          * NMSettingVPN:service-type:
844          *
845          * D-Bus service name of the VPN plugin that this setting uses to connect to
846          * its network.  i.e. org.freedesktop.NetworkManager.vpnc for the vpnc
847          * plugin.
848          **/
849         g_object_class_install_property
850                 (object_class, PROP_SERVICE_TYPE,
851                  g_param_spec_string (NM_SETTING_VPN_SERVICE_TYPE, "", "",
852                                       NULL,
853                                       G_PARAM_READWRITE |
854                                       G_PARAM_STATIC_STRINGS));
855
856         /**
857          * NMSettingVPN:user-name:
858          *
859          * If the VPN connection requires a user name for authentication, that name
860          * should be provided here.  If the connection is available to more than one
861          * user, and the VPN requires each user to supply a different name, then
862          * leave this property empty.  If this property is empty, NetworkManager
863          * will automatically supply the username of the user which requested the
864          * VPN connection.
865          **/
866         g_object_class_install_property
867                 (object_class, PROP_USER_NAME,
868                  g_param_spec_string (NM_SETTING_VPN_USER_NAME, "", "",
869                                       NULL,
870                                       G_PARAM_READWRITE |
871                                       G_PARAM_STATIC_STRINGS));
872
873         /**
874          * NMSettingVPN:persistent:
875          *
876          * If the VPN service supports persistence, and this property is %TRUE,
877          * the VPN will attempt to stay connected across link changes and outages,
878          * until explicitly disconnected.
879          **/
880         g_object_class_install_property
881                 (object_class, PROP_PERSISTENT,
882                  g_param_spec_boolean (NM_SETTING_VPN_PERSISTENT, "", "",
883                                        FALSE,
884                                        G_PARAM_READWRITE |
885                                        G_PARAM_STATIC_STRINGS));
886
887         /**
888          * NMSettingVPN:data:
889          *
890          * Dictionary of key/value pairs of VPN plugin specific data.  Both keys and
891          * values must be strings.
892          **/
893         g_object_class_install_property
894                 (object_class, PROP_DATA,
895                  _nm_param_spec_specialized (NM_SETTING_VPN_DATA, "", "",
896                                              DBUS_TYPE_G_MAP_OF_STRING,
897                                              G_PARAM_READWRITE |
898                                              G_PARAM_STATIC_STRINGS));
899
900         /**
901          * NMSettingVPN:secrets:
902          *
903          * Dictionary of key/value pairs of VPN plugin specific secrets like
904          * passwords or private keys.  Both keys and values must be strings.
905          **/
906         g_object_class_install_property
907                 (object_class, PROP_SECRETS,
908                  _nm_param_spec_specialized (NM_SETTING_VPN_SECRETS, "", "",
909                                              DBUS_TYPE_G_MAP_OF_STRING,
910                                              G_PARAM_READWRITE |
911                                              NM_SETTING_PARAM_SECRET |
912                                              G_PARAM_STATIC_STRINGS));
913 }