diff options
| author | Michael Biebl <biebl@debian.org> | 2015-01-22 00:29:39 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2015-01-22 00:29:39 +0100 |
| commit | 2c032d8f1c6292c1338a615e6ec40252889ba85c (patch) | |
| tree | 1f77182220b2b0264288ba4a476ab47e5bc48716 /libnm-core/nm-setting-vpn.c | |
| parent | 33491bc4279481db8ae47213e34a6d695a0e8830 (diff) | |
Imported Upstream version 1.0.0 upstream/1.0.0
Diffstat (limited to 'libnm-core/nm-setting-vpn.c')
| -rw-r--r-- | libnm-core/nm-setting-vpn.c | 913 |
1 files changed, 913 insertions, 0 deletions
diff --git a/libnm-core/nm-setting-vpn.c b/libnm-core/nm-setting-vpn.c new file mode 100644 index 00000000..e6fffa87 --- /dev/null +++ b/libnm-core/nm-setting-vpn.c @@ -0,0 +1,913 @@ +/* -*- 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. + * + * Copyright 2007 - 2013 Red Hat, Inc. + * Copyright 2007 - 2008 Novell, Inc. + */ + +#include "config.h" + +#include <string.h> +#include <errno.h> +#include <stdlib.h> +#include <glib/gi18n-lib.h> + +#include "nm-setting-vpn.h" +#include "nm-utils.h" +#include "nm-utils-private.h" +#include "nm-setting-private.h" + +/** + * SECTION:nm-setting-vpn + * @short_description: Describes connection properties for Virtual Private Networks + * + * 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. + **/ + +G_DEFINE_TYPE_WITH_CODE (NMSettingVpn, nm_setting_vpn, NM_TYPE_SETTING, + _nm_register_setting (VPN, 1)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_VPN) + +#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; + + /* Whether the VPN stays up across link changes, until the user + * explicitly disconnects it. + */ + gboolean persistent; + + /* 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; + +enum { + PROP_0, + PROP_SERVICE_TYPE, + PROP_USER_NAME, + PROP_PERSISTENT, + PROP_DATA, + PROP_SECRETS, + + LAST_PROP +}; + +/** + * nm_setting_vpn_new: + * + * Creates a new #NMSettingVpn object with default values. + * + * Returns: (transfer full): the new empty #NMSettingVpn object + **/ +NMSetting * +nm_setting_vpn_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_VPN, NULL); +} + +/** + * nm_setting_vpn_get_service_type: + * @setting: the #NMSettingVpn + * + * 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 + **/ +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; +} + +/** + * nm_setting_vpn_get_user_name: + * @setting: the #NMSettingVpn + * + * Returns: the #NMSettingVpn:user-name property of the setting + **/ +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; +} + +/** + * 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; +} + +/** + * nm_setting_vpn_get_num_data_items: + * @setting: the #NMSettingVpn + * + * Gets number of key/value pairs of VPN configuration data. + * + * Returns: the number of VPN plugin specific configuration data items + **/ +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); +} + +/** + * 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. + **/ +void +nm_setting_vpn_add_data_item (NMSettingVpn *setting, + const char *key, + const char *item) +{ + g_return_if_fail (NM_IS_SETTING_VPN (setting)); + 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); + + g_hash_table_insert (NM_SETTING_VPN_GET_PRIVATE (setting)->data, + g_strdup (key), g_strdup (item)); + g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_DATA); +} + +/** + * 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 + **/ +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); +} + +/** + * 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(). + * + * Returns: %TRUE if the data item was found and removed from the internal list, + * %FALSE if it was not. + **/ +gboolean +nm_setting_vpn_remove_data_item (NMSettingVpn *setting, const char *key) +{ + gboolean found; + + g_return_val_if_fail (NM_IS_SETTING_VPN (setting), FALSE); + + 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; +} + +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); + } + + g_slist_free_full (copied, g_free); +} + +/** + * nm_setting_vpn_foreach_data_item: + * @setting: a #NMSettingVpn + * @func: (scope call): an user provided function + * @user_data: data to be passed to @func + * + * 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. + */ +void +nm_setting_vpn_foreach_data_item (NMSettingVpn *setting, + NMVpnIterFunc func, + gpointer user_data) +{ + g_return_if_fail (NM_IS_SETTING_VPN (setting)); + + foreach_item_helper (NM_SETTING_VPN_GET_PRIVATE (setting)->data, func, user_data); +} + +/** + * nm_setting_vpn_get_num_secrets: + * @setting: the #NMSettingVpn + * + * Gets number of VPN plugin specific secrets in the setting. + * + * Returns: the number of VPN plugin specific secrets + **/ +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); +} + +/** + * 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. + **/ +void +nm_setting_vpn_add_secret (NMSettingVpn *setting, + const char *key, + const char *secret) +{ + g_return_if_fail (NM_IS_SETTING_VPN (setting)); + 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); + + g_hash_table_insert (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets, + g_strdup (key), g_strdup (secret)); + g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_SECRETS); +} + +/** + * 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 + **/ +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); +} + +/** + * 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(). + * + * Returns: %TRUE if the secret was found and removed from the internal list, + * %FALSE if it was not. + **/ +gboolean +nm_setting_vpn_remove_secret (NMSettingVpn *setting, const char *key) +{ + gboolean found; + + g_return_val_if_fail (NM_IS_SETTING_VPN (setting), FALSE); + + 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; +} + +/** + * nm_setting_vpn_foreach_secret: + * @setting: a #NMSettingVpn + * @func: (scope call): an user provided function + * @user_data: data to be passed to @func + * + * 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. + */ +void +nm_setting_vpn_foreach_secret (NMSettingVpn *setting, + NMVpnIterFunc func, + gpointer user_data) +{ + g_return_if_fail (NM_IS_SETTING_VPN (setting)); + + foreach_item_helper (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets, func, user_data); +} + +static gboolean +verify (NMSetting *setting, NMConnection *connection, GError **error) +{ + NMSettingVpnPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting); + + if (!priv->service_type) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_VPN_SETTING_NAME, NM_SETTING_VPN_SERVICE_TYPE); + return FALSE; + } + + if (!strlen (priv->service_type)) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_VPN_SETTING_NAME, NM_SETTING_VPN_SERVICE_TYPE); + return FALSE; + } + + /* default username can be NULL, but can't be zero-length */ + if (priv->user_name && !strlen (priv->user_name)) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_VPN_SETTING_NAME, NM_SETTING_VPN_USER_NAME); + return FALSE; + } + + return TRUE; +} + +static NMSettingUpdateSecretResult +update_secret_string (NMSetting *setting, + const char *key, + const char *value, + GError **error) +{ + NMSettingVpnPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting); + + g_return_val_if_fail (key != NULL, NM_SETTING_UPDATE_SECRET_ERROR); + g_return_val_if_fail (value != NULL, NM_SETTING_UPDATE_SECRET_ERROR); + + if (!value || !strlen (value)) { + g_set_error (error, NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("secret was empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_VPN_SETTING_NAME, key); + return NM_SETTING_UPDATE_SECRET_ERROR; + } + + if (g_strcmp0 (g_hash_table_lookup (priv->secrets, key), value) == 0) + return NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED; + + g_hash_table_insert (priv->secrets, g_strdup (key), g_strdup (value)); + return NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED; +} + +static NMSettingUpdateSecretResult +update_secret_dict (NMSetting *setting, + GVariant *secrets, + GError **error) +{ + NMSettingVpnPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting); + GVariantIter iter; + const char *name, *value; + NMSettingUpdateSecretResult result = NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED; + + g_return_val_if_fail (secrets != NULL, NM_SETTING_UPDATE_SECRET_ERROR); + + /* Make sure the items are valid */ + g_variant_iter_init (&iter, secrets); + while (g_variant_iter_next (&iter, "{&s&s}", &name, &value)) { + if (!name || !strlen (name)) { + g_set_error_literal (error, NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_SETTING, + _("setting contained a secret with an empty name")); + g_prefix_error (error, "%s: ", NM_SETTING_VPN_SETTING_NAME); + return NM_SETTING_UPDATE_SECRET_ERROR; + } + + if (!value || !strlen (value)) { + g_set_error (error, NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("secret value was empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_VPN_SETTING_NAME, name); + return NM_SETTING_UPDATE_SECRET_ERROR; + } + } + + /* Now add the items to the settings' secrets list */ + g_variant_iter_init (&iter, secrets); + while (g_variant_iter_next (&iter, "{&s&s}", &name, &value)) { + if (value == NULL) { + g_warn_if_fail (value != NULL); + continue; + } + if (strlen (value) == 0) { + g_warn_if_fail (strlen (value) > 0); + continue; + } + + if (g_strcmp0 (g_hash_table_lookup (priv->secrets, name), value) == 0) + continue; + + g_hash_table_insert (priv->secrets, g_strdup (name), g_strdup (value)); + result = NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED; + } + + return result; +} + +static int +update_one_secret (NMSetting *setting, const char *key, GVariant *value, GError **error) +{ + NMSettingUpdateSecretResult success = NM_SETTING_UPDATE_SECRET_ERROR; + + g_return_val_if_fail (key != NULL, NM_SETTING_UPDATE_SECRET_ERROR); + g_return_val_if_fail (value != NULL, NM_SETTING_UPDATE_SECRET_ERROR); + + if (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING)) { + /* 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_variant_get_string (value, NULL), error); + } else if (g_variant_is_of_type (value, G_VARIANT_TYPE ("a{ss}"))) { + if (strcmp (key, NM_SETTING_VPN_SECRETS) != 0) { + g_set_error_literal (error, NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_PROPERTY_NOT_SECRET, + _("not a secret property")); + g_prefix_error (error, "%s.%s ", NM_SETTING_VPN_SETTING_NAME, key); + } else + success = update_secret_dict (setting, value, error); + } else { + g_set_error_literal (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("secret is not of correct type")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_VPN_SETTING_NAME, key); + } + + if (success == NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED) + g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_SECRETS); + + return success; +} + +static gboolean +get_secret_flags (NMSetting *setting, + const char *secret_name, + gboolean verify_secret, + NMSettingSecretFlags *out_flags, + GError **error) +{ + NMSettingVpnPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting); + gboolean success = FALSE; + char *flags_key; + gpointer val; + unsigned long tmp; + NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE; + + 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)) { + flags = (NMSettingSecretFlags) tmp; + success = TRUE; + } else { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("failed to convert value '%s' to uint"), + (const char *) val); + g_prefix_error (error, "%s.%s: ", NM_SETTING_VPN_SETTING_NAME, flags_key); + } + } else { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_MISSING_PROPERTY, + _("secret flags property not found")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_VPN_SETTING_NAME, flags_key); + } + g_free (flags_key); + if (out_flags) + *out_flags = flags; + return success; +} + +static gboolean +set_secret_flags (NMSetting *setting, + const char *secret_name, + gboolean verify_secret, + 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)); + g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_SECRETS); + return TRUE; +} + +static GPtrArray * +need_secrets (NMSetting *setting) +{ + /* Assume that VPN connections need secrets since they almost always will */ + return g_ptr_array_sized_new (1); +} + +static gboolean +compare_one_secret (NMSettingVpn *a, + NMSettingVpn *b, + NMSettingCompareFlags flags) +{ + GHashTable *a_secrets, *b_secrets; + GHashTableIter iter; + const char *key, *val; + + a_secrets = NM_SETTING_VPN_GET_PRIVATE (a)->secrets; + b_secrets = NM_SETTING_VPN_GET_PRIVATE (b)->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; +} + +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; +} + +static gboolean +clear_secrets_with_flags (NMSetting *setting, + GParamSpec *pspec, + NMSettingClearSecretsWithFlagsFn func, + gpointer user_data) +{ + NMSettingVpnPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting); + GHashTableIter iter; + const char *secret; + gboolean changed = TRUE; + + if (priv->secrets == NULL) + return FALSE; + + /* 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); + if (func (setting, pspec->name, flags, user_data) == TRUE) { + g_hash_table_iter_remove (&iter); + changed = TRUE; + } + } + + if (changed) + g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_SECRETS); + + return changed; +} + +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); +} + +static void +nm_setting_vpn_init (NMSettingVpn *setting) +{ + NMSettingVpnPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting); + + 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); +} + +static void +finalize (GObject *object) +{ + NMSettingVpnPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (object); + + g_free (priv->service_type); + g_free (priv->user_name); + g_hash_table_destroy (priv->data); + g_hash_table_destroy (priv->secrets); + + G_OBJECT_CLASS (nm_setting_vpn_parent_class)->finalize (object); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingVpnPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_SERVICE_TYPE: + g_free (priv->service_type); + priv->service_type = g_value_dup_string (value); + break; + case PROP_USER_NAME: + g_free (priv->user_name); + priv->user_name = g_value_dup_string (value); + break; + case PROP_PERSISTENT: + priv->persistent = g_value_get_boolean (value); + break; + case PROP_DATA: + g_hash_table_unref (priv->data); + priv->data = _nm_utils_copy_strdict (g_value_get_boxed (value)); + break; + case PROP_SECRETS: + g_hash_table_unref (priv->secrets); + priv->secrets = _nm_utils_copy_strdict (g_value_get_boxed (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMSettingVpn *setting = NM_SETTING_VPN (object); + NMSettingVpnPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting); + + switch (prop_id) { + case PROP_SERVICE_TYPE: + g_value_set_string (value, nm_setting_vpn_get_service_type (setting)); + break; + case PROP_USER_NAME: + g_value_set_string (value, nm_setting_vpn_get_user_name (setting)); + break; + case PROP_PERSISTENT: + g_value_set_boolean (value, priv->persistent); + break; + case PROP_DATA: + g_value_take_boxed (value, _nm_utils_copy_strdict (priv->data)); + break; + case PROP_SECRETS: + g_value_take_boxed (value, _nm_utils_copy_strdict (priv->secrets)); + break; + 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); + + g_type_class_add_private (setting_class, sizeof (NMSettingVpnPrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + + parent_class->verify = verify; + parent_class->update_one_secret = update_one_secret; + parent_class->get_secret_flags = get_secret_flags; + parent_class->set_secret_flags = set_secret_flags; + parent_class->need_secrets = need_secrets; + parent_class->compare_property = compare_property; + parent_class->clear_secrets_with_flags = clear_secrets_with_flags; + + /* Properties */ + /** + * NMSettingVpn:service-type: + * + * 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 + * plugin. + **/ + g_object_class_install_property + (object_class, PROP_SERVICE_TYPE, + g_param_spec_string (NM_SETTING_VPN_SERVICE_TYPE, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingVpn:user-name: + * + * If the VPN connection requires a user name for authentication, that name + * 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 + * leave this property empty. If this property is empty, NetworkManager + * will automatically supply the username of the user which requested the + * VPN connection. + **/ + g_object_class_install_property + (object_class, PROP_USER_NAME, + g_param_spec_string (NM_SETTING_VPN_USER_NAME, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * 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)); + + /** + * NMSettingVpn:data: + * + * Dictionary of key/value pairs of VPN plugin specific data. Both keys and + * values must be strings. + * + * Type: GHashTable(utf8,utf8) + **/ + /* ---keyfile--- + * property: data + * variable: separate variables named after keys of the dictionary + * description: The keys of the data dictionary are used as variable names directly + * under [vpn] section. + * example: remote=ovpn.corp.com cipher=AES-256-CBC username=joe + * ---end--- + */ + g_object_class_install_property + (object_class, PROP_DATA, + g_param_spec_boxed (NM_SETTING_VPN_DATA, "", "", + G_TYPE_HASH_TABLE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + _nm_setting_class_transform_property (parent_class, NM_SETTING_VPN_DATA, + G_VARIANT_TYPE ("a{ss}"), + _nm_utils_strdict_to_dbus, + _nm_utils_strdict_from_dbus); + + /** + * NMSettingVpn:secrets: + * + * Dictionary of key/value pairs of VPN plugin specific secrets like + * passwords or private keys. Both keys and values must be strings. + * + * Type: GHashTable(utf8,utf8) + **/ + /* ---keyfile--- + * property: secrets + * variable: separate variables named after keys of the dictionary + * description: The keys of the secrets dictionary are used as variable names directly + * under [vpn-secrets] section. + * example: password=Popocatepetl + * ---end--- + */ + g_object_class_install_property + (object_class, PROP_SECRETS, + g_param_spec_boxed (NM_SETTING_VPN_SECRETS, "", "", + G_TYPE_HASH_TABLE, + G_PARAM_READWRITE | + NM_SETTING_PARAM_SECRET | + G_PARAM_STATIC_STRINGS)); + _nm_setting_class_transform_property (parent_class, NM_SETTING_VPN_SECRETS, + G_VARIANT_TYPE ("a{ss}"), + _nm_utils_strdict_to_dbus, + _nm_utils_strdict_from_dbus); +} |