nm-setting-vpn.c 28 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/*
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
18 19
 * Copyright 2007 - 2013 Red Hat, Inc.
 * Copyright 2007 - 2008 Novell, Inc.
20
 */
21

22
#include "nm-default.h"
23

24
#include <string.h>
25
#include <stdlib.h>
26
#include <dbus/dbus-glib.h>
27

28 29 30
#include "nm-setting-vpn.h"
#include "nm-param-spec-specialized.h"
#include "nm-utils.h"
31
#include "nm-dbus-glib-types.h"
32
#include "nm-setting-private.h"
33

34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
/**
 * SECTION:nm-setting-vpn
 * @short_description: Describes connection properties for Virtual Private Networks
 * @include: nm-setting-vpn.h
 *
 * The #NMSettingVPN object is a #NMSetting subclass that describes properties
 * necessary for connection to Virtual Private Networks.  NetworkManager uses
 * a plugin architecture to allow easier use of new VPN types, and this
 * setting abstracts the configuration for those plugins.  Since the configuration
 * options are only known to the VPN plugins themselves, the VPN configuration
 * options are stored as key/value pairs of strings rather than GObject
 * properties.
 **/

/**
 * nm_setting_vpn_error_quark:
 *
 * Registers an error quark for #NMSettingVPN if necessary.
 *
 * Returns: the error quark used for #NMSettingVPN errors.
 **/
55 56 57 58 59 60 61 62 63 64
GQuark
nm_setting_vpn_error_quark (void)
{
	static GQuark quark;

	if (G_UNLIKELY (!quark))
		quark = g_quark_from_static_string ("nm-setting-vpn-error-quark");
	return quark;
}

65 66 67 68 69 70
G_DEFINE_TYPE_WITH_CODE (NMSettingVPN, nm_setting_vpn, NM_TYPE_SETTING,
                         _nm_register_setting (NM_SETTING_VPN_SETTING_NAME,
                                               g_define_type_id,
                                               1,
                                               NM_SETTING_VPN_ERROR))
NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_VPN)
71

72 73 74 75 76 77 78 79 80 81 82
#define NM_SETTING_VPN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_VPN, NMSettingVPNPrivate))

typedef struct {
	char *service_type;

	/* username of the user requesting this connection, thus
	 * it's really only valid for user connections, and it also
	 * should never be saved out to persistent config.
	 */
	char *user_name;

83 84 85 86 87
	/* Whether the VPN stays up across link changes, until the user
	 * explicitly disconnects it.
	 */
	gboolean persistent;

88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
	/* The hash table is created at setting object
	 * init time and should not be replaced.  It is
	 * a char * -> char * mapping, and both the key
	 * and value are owned by the hash table, and should
	 * be allocated with functions whose value can be
	 * freed with g_free().  Should not contain secrets.
	 */
	GHashTable *data;

	/* The hash table is created at setting object
	 * init time and should not be replaced.  It is
	 * a char * -> char * mapping, and both the key
	 * and value are owned by the hash table, and should
	 * be allocated with functions whose value can be
	 * freed with g_free().  Should contain secrets only.
	 */
	GHashTable *secrets;
} NMSettingVPNPrivate;

107 108 109 110
enum {
	PROP_0,
	PROP_SERVICE_TYPE,
	PROP_USER_NAME,
111
	PROP_PERSISTENT,
112
	PROP_DATA,
113
	PROP_SECRETS,
114 115 116 117

	LAST_PROP
};

118 119 120 121 122 123 124
/**
 * nm_setting_vpn_new:
 *
 * Creates a new #NMSettingVPN object with default values.
 *
 * Returns: (transfer full): the new empty #NMSettingVPN object
 **/
125 126 127 128 129 130
NMSetting *
nm_setting_vpn_new (void)
{
	return (NMSetting *) g_object_new (NM_TYPE_SETTING_VPN, NULL);
}

131 132
/**
 * nm_setting_vpn_get_service_type:
Dan Williams's avatar
Dan Williams committed
133
 * @setting: the #NMSettingVPN
134 135 136 137 138 139
 *
 * Returns the service name of the VPN, which identifies the specific VPN
 * plugin that should be used to connect to this VPN.
 *
 * Returns: the VPN plugin's service name
 **/
140 141 142 143 144 145 146 147
const char *
nm_setting_vpn_get_service_type (NMSettingVPN *setting)
{
	g_return_val_if_fail (NM_IS_SETTING_VPN (setting), NULL);

	return NM_SETTING_VPN_GET_PRIVATE (setting)->service_type;
}

148 149 150 151 152 153
/**
 * nm_setting_vpn_get_user_name:
 * @setting: the #NMSettingVPN
 *
 * Returns: the #NMSettingVPN:user-name property of the setting
 **/
154 155 156 157 158 159 160 161
const char *
nm_setting_vpn_get_user_name (NMSettingVPN *setting)
{
	g_return_val_if_fail (NM_IS_SETTING_VPN (setting), NULL);

	return NM_SETTING_VPN_GET_PRIVATE (setting)->user_name;
}

162 163 164 165 166 167 168 169 170 171 172 173 174 175
/**
 * nm_setting_vpn_get_persistent:
 * @setting: the #NMSettingVPN
 *
 * Returns: the #NMSettingVPN:persistent property of the setting
 **/
gboolean
nm_setting_vpn_get_persistent (NMSettingVPN *setting)
{
	g_return_val_if_fail (NM_IS_SETTING_VPN (setting), FALSE);

	return NM_SETTING_VPN_GET_PRIVATE (setting)->persistent;
}

176 177 178 179 180
/**
 * nm_setting_vpn_get_num_data_items:
 * @setting: the #NMSettingVPN
 *
 * Gets number of key/value pairs of VPN configuration data.
181 182
 *
 * Returns: the number of VPN plugin specific configuration data items
183 184 185 186 187 188 189 190 191
 **/
guint32
nm_setting_vpn_get_num_data_items (NMSettingVPN *setting)
{
	g_return_val_if_fail (NM_IS_SETTING_VPN (setting), 0);

	return g_hash_table_size (NM_SETTING_VPN_GET_PRIVATE (setting)->data);
}

192 193 194 195 196 197 198 199 200 201
/**
 * nm_setting_vpn_add_data_item:
 * @setting: the #NMSettingVPN
 * @key: a name that uniquely identifies the given value @item
 * @item: the value to be referenced by @key
 *
 * Establishes a relationship between @key and @item internally in the
 * setting which may be retrieved later.  Should not be used to store passwords
 * or other secrets, which is what nm_setting_vpn_add_secret() is for.
 **/
202 203 204 205 206 207
void
nm_setting_vpn_add_data_item (NMSettingVPN *setting,
                              const char *key,
                              const char *item)
{
	g_return_if_fail (NM_IS_SETTING_VPN (setting));
208 209 210 211
	g_return_if_fail (key != NULL);
	g_return_if_fail (strlen (key) > 0);
	g_return_if_fail (item != NULL);
	g_return_if_fail (strlen (item) > 0);
212 213 214

	g_hash_table_insert (NM_SETTING_VPN_GET_PRIVATE (setting)->data,
	                     g_strdup (key), g_strdup (item));
215
	g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_DATA);
216 217
}

218 219 220 221 222 223 224 225 226 227
/**
 * nm_setting_vpn_get_data_item:
 * @setting: the #NMSettingVPN
 * @key: the name of the data item to retrieve
 *
 * Retrieves the data item of a key/value relationship previously established
 * by nm_setting_vpn_add_data_item().
 *
 * Returns: the data item, if any
 **/
228 229 230 231 232 233 234 235
const char *
nm_setting_vpn_get_data_item (NMSettingVPN *setting, const char *key)
{
	g_return_val_if_fail (NM_IS_SETTING_VPN (setting), NULL);

	return (const char *) g_hash_table_lookup (NM_SETTING_VPN_GET_PRIVATE (setting)->data, key);
}

236 237 238 239 240 241 242
/**
 * nm_setting_vpn_remove_data_item:
 * @setting: the #NMSettingVPN
 * @key: the name of the data item to remove
 *
 * Deletes a key/value relationship previously established by
 * nm_setting_vpn_add_data_item().
243 244 245
 *
 * Returns: %TRUE if the data item was found and removed from the internal list,
 * %FALSE if it was not.
246
 **/
247
gboolean
248 249
nm_setting_vpn_remove_data_item (NMSettingVPN *setting, const char *key)
{
250 251
	gboolean found;

252
	g_return_val_if_fail (NM_IS_SETTING_VPN (setting), FALSE);
253

254 255 256 257
	found = g_hash_table_remove (NM_SETTING_VPN_GET_PRIVATE (setting)->data, key);
	if (found)
		g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_DATA);
	return found;
258 259
}

260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
static void
foreach_item_helper (GHashTable *hash,
                     NMVPNIterFunc func,
                     gpointer user_data)
{
	GList *keys, *liter;
	GSList *copied = NULL, *siter;

	g_return_if_fail (hash != NULL);

	/* Grab keys and copy them so that the callback func can modify
	 * the hash table items if it wants to.
	 */
	keys = g_hash_table_get_keys (hash);
	for (liter = keys; liter; liter = g_list_next (liter))
		copied = g_slist_prepend (copied, g_strdup (liter->data));
	copied = g_slist_reverse (copied);
	g_list_free (keys);

	for (siter = copied; siter; siter = g_slist_next (siter)) {
		gpointer value;

		value = g_hash_table_lookup (hash, siter->data);
		func (siter->data, value, user_data);
	}

286
	g_slist_free_full (copied, g_free);
287 288
}

289 290 291 292
/**
 * nm_setting_vpn_foreach_data_item:
 * @setting: a #NMSettingVPN
 * @func: (scope call): an user provided function
293
 * @user_data: data to be passed to @func
294
 *
295 296 297
 * Iterates all data items stored in this setting.  It is safe to add, remove,
 * and modify data items inside @func, though any additions or removals made
 * during iteration will not be part of the iteration.
298
 */
299 300
void
nm_setting_vpn_foreach_data_item (NMSettingVPN *setting,
301
                                  NMVPNIterFunc func,
302 303 304 305
                                  gpointer user_data)
{
	g_return_if_fail (NM_IS_SETTING_VPN (setting));

306
	foreach_item_helper (NM_SETTING_VPN_GET_PRIVATE (setting)->data, func, user_data);
307 308
}

309 310 311 312 313
/**
 * nm_setting_vpn_get_num_secrets:
 * @setting: the #NMSettingVPN
 *
 * Gets number of VPN plugin specific secrets in the setting.
314 315
 *
 * Returns: the number of VPN plugin specific secrets
316 317 318 319 320 321 322 323 324
 **/
guint32
nm_setting_vpn_get_num_secrets (NMSettingVPN *setting)
{
	g_return_val_if_fail (NM_IS_SETTING_VPN (setting), 0);

	return g_hash_table_size (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets);
}

325 326 327 328 329 330 331 332 333
/**
 * nm_setting_vpn_add_secret:
 * @setting: the #NMSettingVPN
 * @key: a name that uniquely identifies the given secret @secret
 * @secret: the secret to be referenced by @key
 *
 * Establishes a relationship between @key and @secret internally in the
 * setting which may be retrieved later.
 **/
334 335 336 337 338 339
void
nm_setting_vpn_add_secret (NMSettingVPN *setting,
                           const char *key,
                           const char *secret)
{
	g_return_if_fail (NM_IS_SETTING_VPN (setting));
340 341 342 343
	g_return_if_fail (key != NULL);
	g_return_if_fail (strlen (key) > 0);
	g_return_if_fail (secret != NULL);
	g_return_if_fail (strlen (secret) > 0);
344 345 346

	g_hash_table_insert (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets,
	                     g_strdup (key), g_strdup (secret));
347
	g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_SECRETS);
348 349
}

350 351 352 353 354 355 356 357 358 359
/**
 * nm_setting_vpn_get_secret:
 * @setting: the #NMSettingVPN
 * @key: the name of the secret to retrieve
 *
 * Retrieves the secret of a key/value relationship previously established
 * by nm_setting_vpn_add_secret().
 *
 * Returns: the secret, if any
 **/
360 361 362 363 364 365 366 367
const char *
nm_setting_vpn_get_secret (NMSettingVPN *setting, const char *key)
{
	g_return_val_if_fail (NM_IS_SETTING_VPN (setting), NULL);

	return (const char *) g_hash_table_lookup (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets, key);
}

368 369 370 371 372 373 374
/**
 * nm_setting_vpn_remove_secret:
 * @setting: the #NMSettingVPN
 * @key: the name of the secret to remove
 *
 * Deletes a key/value relationship previously established by
 * nm_setting_vpn_add_secret().
375 376 377
 *
 * Returns: %TRUE if the secret was found and removed from the internal list,
 * %FALSE if it was not.
378
 **/
379
gboolean
380 381
nm_setting_vpn_remove_secret (NMSettingVPN *setting, const char *key)
{
382 383
	gboolean found;

384
	g_return_val_if_fail (NM_IS_SETTING_VPN (setting), FALSE);
385

386 387 388 389
	found = g_hash_table_remove (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets, key);
	if (found)
		g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_SECRETS);
	return found;
390 391
}

392 393 394 395
/**
 * nm_setting_vpn_foreach_secret:
 * @setting: a #NMSettingVPN
 * @func: (scope call): an user provided function
396
 * @user_data: data to be passed to @func
397
 *
398 399 400
 * Iterates all secrets stored in this setting.  It is safe to add, remove,
 * and modify secrets inside @func, though any additions or removals made during
 * iteration will not be part of the iteration.
401
 */
402 403
void
nm_setting_vpn_foreach_secret (NMSettingVPN *setting,
404
                               NMVPNIterFunc func,
405 406 407 408
                               gpointer user_data)
{
	g_return_if_fail (NM_IS_SETTING_VPN (setting));

409
	foreach_item_helper (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets, func, user_data);
410 411
}

412
static gboolean
413
verify (NMSetting *setting, GSList *all_settings, GError **error)
414
{
415
	NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
416

417
	if (!priv->service_type) {
418 419 420 421
		g_set_error_literal (error,
		                     NM_SETTING_VPN_ERROR,
		                     NM_SETTING_VPN_ERROR_MISSING_PROPERTY,
		                     _("property is missing"));
422
		g_prefix_error (error, "%s.%s: ", NM_SETTING_VPN_SETTING_NAME, NM_SETTING_VPN_SERVICE_TYPE);
423 424 425
		return FALSE;
	}

426
	if (!strlen (priv->service_type)) {
427 428 429 430
		g_set_error_literal (error,
		                     NM_SETTING_VPN_ERROR,
		                     NM_SETTING_VPN_ERROR_INVALID_PROPERTY,
		                     _("property is empty"));
431
		g_prefix_error (error, "%s.%s: ", NM_SETTING_VPN_SETTING_NAME, NM_SETTING_VPN_SERVICE_TYPE);
432
		return FALSE;
433
	}
434 435

	/* default username can be NULL, but can't be zero-length */
436
	if (priv->user_name && !strlen (priv->user_name)) {
437 438 439 440
		g_set_error_literal (error,
		                     NM_SETTING_VPN_ERROR,
		                     NM_SETTING_VPN_ERROR_INVALID_PROPERTY,
		                     _("property is empty"));
441
		g_prefix_error (error, "%s.%s: ", NM_SETTING_VPN_SETTING_NAME, NM_SETTING_VPN_USER_NAME);
442
		return FALSE;
443
	}
444 445 446 447

	return TRUE;
}

448
static NMSettingUpdateSecretResult
449 450 451 452
update_secret_string (NMSetting *setting,
                      const char *key,
                      const char *value,
                      GError **error)
453
{
454
	NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
455

456 457
	g_return_val_if_fail (key != NULL, NM_SETTING_UPDATE_SECRET_ERROR);
	g_return_val_if_fail (value != NULL, NM_SETTING_UPDATE_SECRET_ERROR);
458

459
	if (!value || !strlen (value)) {
460 461
		g_set_error (error, NM_SETTING_ERROR,
		             NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH,
462
		             "Secret %s was empty", key);
463
		return NM_SETTING_UPDATE_SECRET_ERROR;
464
	}
465

466 467 468
	if (g_strcmp0 (g_hash_table_lookup (priv->secrets, key), value) == 0)
		return NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED;

469
	g_hash_table_insert (priv->secrets, g_strdup (key), g_strdup (value));
470
	return NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED;
471 472
}

473
static NMSettingUpdateSecretResult
474 475 476 477 478 479 480
update_secret_hash (NMSetting *setting,
                    GHashTable *secrets,
                    GError **error)
{
	NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
	GHashTableIter iter;
	const char *name, *value;
481
	NMSettingUpdateSecretResult result = NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED;
482

483
	g_return_val_if_fail (secrets != NULL, NM_SETTING_UPDATE_SECRET_ERROR);
484 485 486 487 488 489 490 491

	/* Make sure the items are valid */
	g_hash_table_iter_init (&iter, secrets);
	while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &value)) {
		if (!name || !strlen (name)) {
			g_set_error_literal (error, NM_SETTING_ERROR,
			                     NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH,
			                     "Secret name was empty");
492
			return NM_SETTING_UPDATE_SECRET_ERROR;
493 494 495 496 497
		}

		if (!value || !strlen (value)) {
			g_set_error (error, NM_SETTING_ERROR,
			             NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH,
498
			             "Secret %s value was empty", name);
499
			return NM_SETTING_UPDATE_SECRET_ERROR;
500
		}
501 502
	}

503 504
	/* Now add the items to the settings' secrets list */
	g_hash_table_iter_init (&iter, secrets);
505 506 507 508 509 510 511 512 513 514
	while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &value)) {
		if (value == NULL) {
			g_warn_if_fail (value != NULL);
			continue;
		}
		if (strlen (value) == 0) {
			g_warn_if_fail (strlen (value) > 0);
			continue;
		}

515 516 517
		if (g_strcmp0 (g_hash_table_lookup (priv->secrets, name), value) == 0)
			continue;

518
		g_hash_table_insert (priv->secrets, g_strdup (name), g_strdup (value));
519
		result = NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED;
520
	}
521

522
	return result;
523 524
}

525
static int
526 527
update_one_secret (NMSetting *setting, const char *key, GValue *value, GError **error)
{
528
	NMSettingUpdateSecretResult success = NM_SETTING_UPDATE_SECRET_ERROR;
529

530 531
	g_return_val_if_fail (key != NULL, NM_SETTING_UPDATE_SECRET_ERROR);
	g_return_val_if_fail (value != NULL, NM_SETTING_UPDATE_SECRET_ERROR);
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548

	if (G_VALUE_HOLDS_STRING (value)) {
		/* Passing the string properties individually isn't correct, and won't
		 * produce the correct result, but for some reason that's how it used
		 * to be done.  So even though it's not correct, keep the code around
		 * for compatibility's sake.
		 */
		success = update_secret_string (setting, key, g_value_get_string (value), error);
	} else if (G_VALUE_HOLDS (value, DBUS_TYPE_G_MAP_OF_STRING)) {
		if (strcmp (key, NM_SETTING_VPN_SECRETS) != 0) {
			g_set_error (error, NM_SETTING_ERROR, NM_SETTING_ERROR_PROPERTY_NOT_SECRET,
			             "Property %s not a secret property", key);
		} else
			success = update_secret_hash (setting, g_value_get_boxed (value), error);
	} else
		g_set_error_literal (error, NM_SETTING_ERROR, NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH, key);

549
	if (success == NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED)
550 551
		g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_SECRETS);

552 553 554
	return success;
}

555 556 557
static gboolean
get_secret_flags (NMSetting *setting,
                  const char *secret_name,
558
                  gboolean verify_secret,
559 560 561 562 563 564 565 566
                  NMSettingSecretFlags *out_flags,
                  GError **error)
{
	NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
	gboolean success = FALSE;
	char *flags_key;
	gpointer val;
	unsigned long tmp;
567
	NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
568 569 570 571 572 573

	flags_key = g_strdup_printf ("%s-flags", secret_name);
	if (g_hash_table_lookup_extended (priv->data, flags_key, NULL, &val)) {
		errno = 0;
		tmp = strtoul ((const char *) val, NULL, 10);
		if ((errno == 0) && (tmp <= NM_SETTING_SECRET_FLAGS_ALL)) {
574
			flags = (NMSettingSecretFlags) tmp;
575 576 577 578 579
			success = TRUE;
		} else {
			g_set_error (error,
			             NM_SETTING_ERROR,
			             NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH,
580
			             _("Failed to convert '%s' value '%s' to uint"),
581 582 583 584 585 586
			             flags_key, (const char *) val);
		}
	} else {
		g_set_error (error,
		             NM_SETTING_ERROR,
		             NM_SETTING_ERROR_PROPERTY_NOT_FOUND,
587
		             _("Secret flags property '%s' not found"), flags_key);
588 589
	}
	g_free (flags_key);
590 591
	if (out_flags)
		*out_flags = flags;
592 593 594 595 596 597
	return success;
}

static gboolean
set_secret_flags (NMSetting *setting,
                  const char *secret_name,
598
                  gboolean verify_secret,
599 600 601 602 603 604
                  NMSettingSecretFlags flags,
                  GError **error)
{
	g_hash_table_insert (NM_SETTING_VPN_GET_PRIVATE (setting)->data,
	                     g_strdup_printf ("%s-flags", secret_name),
	                     g_strdup_printf ("%u", flags));
605
	g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_SECRETS);
606 607 608
	return TRUE;
}

609 610 611 612 613 614 615
static GPtrArray *
need_secrets (NMSetting *setting)
{
	/* Assume that VPN connections need secrets since they almost always will */
	return g_ptr_array_sized_new (1);
}

616
static gboolean
617 618 619
_compare_secrets (NMSettingVPN *a,
                  NMSettingVPN *b,
                  NMSettingCompareFlags flags)
620
{
621
	GHashTable *a_secrets;
622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653
	GHashTableIter iter;
	const char *key, *val;

	a_secrets = NM_SETTING_VPN_GET_PRIVATE (a)->secrets;
	g_hash_table_iter_init (&iter, a_secrets);
	while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &val)) {
		NMSettingSecretFlags a_secret_flags = NM_SETTING_SECRET_FLAG_NONE;
		NMSettingSecretFlags b_secret_flags = NM_SETTING_SECRET_FLAG_NONE;

		nm_setting_get_secret_flags (NM_SETTING (a), key, &a_secret_flags, NULL);
		nm_setting_get_secret_flags (NM_SETTING (b), key, &b_secret_flags, NULL);

		/* If the secret flags aren't the same, the settings aren't the same */
		if (a_secret_flags != b_secret_flags)
			return FALSE;

		if (   (flags & NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS)
		    && (a_secret_flags & NM_SETTING_SECRET_FLAG_AGENT_OWNED))
			continue;

		if (   (flags & NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)
		    && (a_secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED))
			continue;

		/* Now compare the values themselves */
		if (g_strcmp0 (val, nm_setting_vpn_get_secret (b, key)) != 0)
			return FALSE;
	}

	return TRUE;
}

654 655 656 657 658 659 660 661 662 663 664 665 666
static gboolean
compare_one_secret (NMSettingVPN *a,
                    NMSettingVPN *b,
                    NMSettingCompareFlags flags)
{
	if (!_compare_secrets (a, b, flags))
		return FALSE;
	if (!_compare_secrets (b, a, flags))
		return FALSE;

	return TRUE;
}

667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688
static gboolean
compare_property (NMSetting *setting,
                  NMSetting *other,
                  const GParamSpec *prop_spec,
                  NMSettingCompareFlags flags)
{
	gboolean same;

	/* We only need to treat the 'secrets' property specially */
	if (g_strcmp0 (prop_spec->name, NM_SETTING_VPN_SECRETS) != 0)
		return NM_SETTING_CLASS (nm_setting_vpn_parent_class)->compare_property (setting, other, prop_spec, flags);

	/* Compare A to B to ensure everything in A is found in B */
	same = compare_one_secret (NM_SETTING_VPN (setting), NM_SETTING_VPN (other), flags);
	if (same) {
		/* And then B to A to ensure everything in B is also found in A */
		same = compare_one_secret (NM_SETTING_VPN (other), NM_SETTING_VPN (setting), flags);
	}

	return same;
}

689
static gboolean
690
clear_secrets_with_flags (NMSetting *setting,
691 692 693
                          GParamSpec *pspec,
                          NMSettingClearSecretsWithFlagsFn func,
                          gpointer user_data)
694 695 696 697
{
	NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
	GHashTableIter iter;
	const char *secret;
698
	gboolean changed = TRUE;
699 700

	if (priv->secrets == NULL)
701
		return FALSE;
702 703 704 705 706 707 708

	/* Iterate through secrets hash and check each entry */
	g_hash_table_iter_init (&iter, priv->secrets);
	while (g_hash_table_iter_next (&iter, (gpointer) &secret, NULL)) {
		NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;

		nm_setting_get_secret_flags (setting, secret, &flags, NULL);
709
		if (func (setting, pspec->name, flags, user_data) == TRUE) {
710
			g_hash_table_iter_remove (&iter);
711 712
			changed = TRUE;
		}
713
	}
714 715 716

	if (changed)
		g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_SECRETS);
717 718

	return changed;
719 720
}

721 722 723 724 725 726 727 728
static void
destroy_one_secret (gpointer data)
{
	char *secret = (char *) data;

	/* Don't leave the secret lying around in memory */
	memset (secret, 0, strlen (secret));
	g_free (secret);
729 730
}

731 732 733
static void
nm_setting_vpn_init (NMSettingVPN *setting)
{
734
	NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
735

736 737
	priv->data = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
	priv->secrets = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, destroy_one_secret);
738 739 740 741 742
}

static void
finalize (GObject *object)
{
743
	NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (object);
744

745 746 747 748
	g_free (priv->service_type);
	g_free (priv->user_name);
	g_hash_table_destroy (priv->data);
	g_hash_table_destroy (priv->secrets);
749 750 751 752

	G_OBJECT_CLASS (nm_setting_vpn_parent_class)->finalize (object);
}

753 754 755
static void
copy_hash (gpointer key, gpointer value, gpointer user_data)
{
756 757
	g_return_if_fail (value != NULL);
	g_return_if_fail (strlen (value));
758 759 760
	g_hash_table_insert ((GHashTable *) user_data, g_strdup (key), g_strdup (value));
}

761 762
static void
set_property (GObject *object, guint prop_id,
763
              const GValue *value, GParamSpec *pspec)
764
{
765
	NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (object);
766
	GHashTable *new_hash;
767 768 769

	switch (prop_id) {
	case PROP_SERVICE_TYPE:
770 771
		g_free (priv->service_type);
		priv->service_type = g_value_dup_string (value);
772 773
		break;
	case PROP_USER_NAME:
774 775
		g_free (priv->user_name);
		priv->user_name = g_value_dup_string (value);
776
		break;
777 778 779
	case PROP_PERSISTENT:
		priv->persistent = g_value_get_boolean (value);
		break;
780 781
	case PROP_DATA:
		/* Must make a deep copy of the hash table here... */
782
		g_hash_table_remove_all (priv->data);
783 784
		new_hash = g_value_get_boxed (value);
		if (new_hash)
785
			g_hash_table_foreach (new_hash, copy_hash, priv->data);
786 787 788
		break;
	case PROP_SECRETS:
		/* Must make a deep copy of the hash table here... */
789
		g_hash_table_remove_all (priv->secrets);
790 791
		new_hash = g_value_get_boxed (value);
		if (new_hash)
792
			g_hash_table_foreach (new_hash, copy_hash, priv->secrets);
793
		break;
794 795 796 797 798 799 800 801
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}

static void
get_property (GObject *object, guint prop_id,
802
              GValue *value, GParamSpec *pspec)
803 804
{
	NMSettingVPN *setting = NM_SETTING_VPN (object);
805
	NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
806 807 808

	switch (prop_id) {
	case PROP_SERVICE_TYPE:
809
		g_value_set_string (value, nm_setting_vpn_get_service_type (setting));
810 811
		break;
	case PROP_USER_NAME:
812
		g_value_set_string (value, nm_setting_vpn_get_user_name (setting));
813
		break;
814 815 816
	case PROP_PERSISTENT:
		g_value_set_boolean (value, priv->persistent);
		break;
817
	case PROP_DATA:
818
		g_value_set_boxed (value, priv->data);
819
		break;
820
	case PROP_SECRETS:
821
		g_value_set_boxed (value, priv->secrets);
822
		break;
823 824 825 826 827 828 829 830 831 832 833 834
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}

static void
nm_setting_vpn_class_init (NMSettingVPNClass *setting_class)
{
	GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
	NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);

835 836
	g_type_class_add_private (setting_class, sizeof (NMSettingVPNPrivate));

837 838 839 840
	/* virtual methods */
	object_class->set_property = set_property;
	object_class->get_property = get_property;
	object_class->finalize     = finalize;
841 842

	parent_class->verify            = verify;
843
	parent_class->update_one_secret = update_one_secret;
844 845
	parent_class->get_secret_flags  = get_secret_flags;
	parent_class->set_secret_flags  = set_secret_flags;
846
	parent_class->need_secrets      = need_secrets;
847
	parent_class->compare_property  = compare_property;
848
	parent_class->clear_secrets_with_flags = clear_secrets_with_flags;
849 850

	/* Properties */
851 852 853
	/**
	 * NMSettingVPN:service-type:
	 *
854 855
	 * D-Bus service name of the VPN plugin that this setting uses to connect to
	 * its network.  i.e. org.freedesktop.NetworkManager.vpnc for the vpnc
856
	 * plugin.
857
	 **/
858 859
	g_object_class_install_property
		(object_class, PROP_SERVICE_TYPE,
860 861 862 863
		 g_param_spec_string (NM_SETTING_VPN_SERVICE_TYPE, "", "",
		                      NULL,
		                      G_PARAM_READWRITE |
		                      G_PARAM_STATIC_STRINGS));
864

865
	/**
866
	 * NMSettingVPN:user-name:
867
	 *
868
	 * If the VPN connection requires a user name for authentication, that name
869 870
	 * should be provided here.  If the connection is available to more than one
	 * user, and the VPN requires each user to supply a different name, then
871 872 873
	 * leave this property empty.  If this property is empty, NetworkManager
	 * will automatically supply the username of the user which requested the
	 * VPN connection.
874
	 **/
875 876
	g_object_class_install_property
		(object_class, PROP_USER_NAME,
877
		 g_param_spec_string (NM_SETTING_VPN_USER_NAME, "", "",
878
		                      NULL,
879 880
		                      G_PARAM_READWRITE |
		                      G_PARAM_STATIC_STRINGS));
881

882 883 884 885 886 887 888 889 890 891 892 893 894 895
	/**
	 * NMSettingVPN:persistent:
	 *
	 * If the VPN service supports persistence, and this property is %TRUE,
	 * the VPN will attempt to stay connected across link changes and outages,
	 * until explicitly disconnected.
	 **/
	g_object_class_install_property
		(object_class, PROP_PERSISTENT,
		 g_param_spec_boolean (NM_SETTING_VPN_PERSISTENT, "", "",
		                       FALSE,
		                       G_PARAM_READWRITE |
		                       G_PARAM_STATIC_STRINGS));

896 897 898
	/**
	 * NMSettingVPN:data:
	 *
899 900
	 * Dictionary of key/value pairs of VPN plugin specific data.  Both keys and
	 * values must be strings.
901
	 **/
902 903
	g_object_class_install_property
		(object_class, PROP_DATA,
904 905 906 907
		 _nm_param_spec_specialized (NM_SETTING_VPN_DATA, "", "",
		                             DBUS_TYPE_G_MAP_OF_STRING,
		                             G_PARAM_READWRITE |
		                             G_PARAM_STATIC_STRINGS));
908

909 910 911 912 913 914
	/**
	 * NMSettingVPN:secrets:
	 *
	 * Dictionary of key/value pairs of VPN plugin specific secrets like
	 * passwords or private keys.  Both keys and values must be strings.
	 **/
915 916
	g_object_class_install_property
		(object_class, PROP_SECRETS,
917 918
		 _nm_param_spec_specialized (NM_SETTING_VPN_SECRETS, "", "",
		                             DBUS_TYPE_G_MAP_OF_STRING,
919 920
		                             G_PARAM_READWRITE |
		                             NM_SETTING_PARAM_SECRET |
921
		                             G_PARAM_STATIC_STRINGS));
922
}