From 04df4edf48e55478d0f360ea566f5f398aa76268 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 18 Feb 2015 18:59:35 +0100 Subject: [PATCH] libnm: add keyfile support to libnm-core https://bugzilla.gnome.org/show_bug.cgi?id=744699 --- libnm-core/Makefile.libnm-core | 6 + libnm-core/nm-keyfile-reader.c | 627 ++++++++++++++++++++------------- libnm-core/nm-keyfile-reader.h | 67 +++- libnm-core/nm-keyfile-utils.c | 125 +------ libnm-core/nm-keyfile-utils.h | 20 +- libnm-core/nm-keyfile-writer.c | 472 ++++++------------------- libnm-core/nm-keyfile-writer.h | 78 +++- po/POTFILES.in | 2 + 8 files changed, 637 insertions(+), 760 deletions(-) diff --git a/libnm-core/Makefile.libnm-core b/libnm-core/Makefile.libnm-core index fc6d779b5..798714600 100644 --- a/libnm-core/Makefile.libnm-core +++ b/libnm-core/Makefile.libnm-core @@ -48,6 +48,9 @@ libnm_core_private_headers = \ $(core)/crypto.h \ $(core)/nm-connection-private.h \ $(core)/nm-core-internal.h \ + $(core)/nm-keyfile-reader.h \ + $(core)/nm-keyfile-utils.h \ + $(core)/nm-keyfile-writer.h \ $(core)/nm-property-compare.h \ $(core)/nm-setting-private.h \ $(core)/nm-utils-private.h @@ -57,6 +60,9 @@ libnm_core_sources = \ $(core)/crypto.c \ $(core)/nm-connection.c \ $(core)/nm-errors.c \ + $(core)/nm-keyfile-reader.c \ + $(core)/nm-keyfile-utils.c \ + $(core)/nm-keyfile-writer.c \ $(core)/nm-property-compare.c \ $(core)/nm-setting-8021x.c \ $(core)/nm-setting-adsl.c \ diff --git a/libnm-core/nm-keyfile-reader.c b/libnm-core/nm-keyfile-reader.c index 97b656702..9da3e1eca 100644 --- a/libnm-core/nm-keyfile-reader.c +++ b/libnm-core/nm-keyfile-reader.c @@ -16,7 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Copyright (C) 2008 - 2009 Novell, Inc. - * Copyright (C) 2008 - 2011 Red Hat, Inc. + * Copyright (C) 2008 - 2015 Red Hat, Inc. */ #include "config.h" @@ -28,17 +28,59 @@ #include #include #include +#include #include "nm-core-internal.h" -#include "nm-dbus-glib-types.h" +#include "gsystem-local-alloc.h" #include "nm-glib-compat.h" -#include "nm-system-config-interface.h" -#include "nm-logging.h" -#include "reader.h" -#include "common.h" -#include "utils.h" -#include "nm-core-internal.h" -#include "NetworkManagerUtils.h" +#include "nm-keyfile-reader.h" +#include "nm-keyfile-utils.h" + + +typedef struct { + NMConnection *connection; + GKeyFile *keyfile; + const char *base_dir; + NMKeyfileReadHandler handler; + void *user_data; + GError *error; + const char *group; + NMSetting *setting; +} KeyfileReaderInfo; + + +static void +_handle_warn (KeyfileReaderInfo *info, + const char *property_name, + NMKeyfileWarnSeverity severity, + char *message) +{ + NMKeyfileReadTypeDataWarn type_data = { + .group = info->group, + .setting = info->setting, + .property_name = property_name, + .severity = severity, + .message = message, + }; + + info->handler (info->keyfile, + info->connection, + NM_KEYFILE_READ_TYPE_WARN, + &type_data, + info->user_data, + &info->error); + g_free (message); +} +#define handle_warn(arg_info, arg_property_name, arg_severity, ...) \ + ({ \ + KeyfileReaderInfo *_info = (arg_info); \ + \ + if (_info->handler) { \ + _handle_warn (_info, (arg_property_name), (arg_severity), \ + g_strdup_printf (__VA_ARGS__)); \ + } \ + _info->error == NULL; \ + }) /* Some setting properties also contain setting names, such as * NMSettingConnection's 'type' property (which specifies the base type of the @@ -47,13 +89,13 @@ * properties' values to the real setting name if they are an alias. */ static void -setting_alias_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path) +setting_alias_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) { const char *setting_name = nm_setting_get_name (setting); char *s; const char *key_setting_name; - s = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, NULL); + s = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL); if (s) { key_setting_name = nm_keyfile_plugin_get_setting_name_for_alias (s); g_object_set (G_OBJECT (setting), @@ -63,7 +105,7 @@ setting_alias_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, co } } -static gboolean +static void read_array_of_uint (GKeyFile *file, NMSetting *setting, const char *key) @@ -75,34 +117,36 @@ read_array_of_uint (GKeyFile *file, tmp = nm_keyfile_plugin_kf_get_integer_list (file, nm_setting_get_name (setting), key, &length, NULL); array = g_array_sized_new (FALSE, FALSE, sizeof (guint32), length); - g_return_val_if_fail (array != NULL, FALSE); for (i = 0; i < length; i++) g_array_append_val (array, tmp[i]); g_object_set (setting, key, array, NULL); g_array_unref (array); - - return TRUE; } static gboolean -get_one_int (const char *str, guint32 max_val, const char *key_name, guint32 *out) +get_one_int (KeyfileReaderInfo *info, const char *property_name, const char *str, guint32 max_val, guint32 *out) { long tmp; char *endptr; + g_return_val_if_fail (!info == !property_name, FALSE); + if (!str || !str[0]) { - if (key_name) - nm_log_warn (LOGD_SETTINGS, "%s: ignoring missing number %s", __func__, key_name); + if (property_name) + handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring missing number")); return FALSE; } errno = 0; tmp = strtol (str, &endptr, 10); if (errno || (tmp < 0) || (tmp > max_val) || *endptr != 0) { - if (key_name) - nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid number %s '%s'", __func__, key_name, str); + if (property_name) + handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid number '%s'"), + str); return FALSE; } @@ -111,7 +155,7 @@ get_one_int (const char *str, guint32 max_val, const char *key_name, guint32 *ou } static gpointer -build_address (int family, const char *address_str, guint32 plen) +build_address (KeyfileReaderInfo *info, int family, const char *address_str, guint32 plen, const char *property_name) { NMIPAddress *addr; GError *error = NULL; @@ -120,9 +164,9 @@ build_address (int family, const char *address_str, guint32 plen) addr = nm_ip_address_new (family, address_str, plen, &error); if (!addr) { - nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid %s address: %s", __func__, - family == AF_INET ? "IPv4" : "IPv6", - error->message); + handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid %s addresss: %s"), + family == AF_INET ? "IPv4" : "IPv6", error->message); g_error_free (error); } @@ -130,10 +174,11 @@ build_address (int family, const char *address_str, guint32 plen) } static gpointer -build_route (int family, +build_route (KeyfileReaderInfo *info, + const char *property_name, + int family, const char *dest_str, guint32 plen, - const char *gateway_str, const char *metric_str, - const char *key_name) + const char *gateway_str, const char *metric_str) { NMIPRoute *route; guint32 metric = 0; @@ -155,10 +200,14 @@ build_route (int family, **/ if ( family == AF_INET6 && !metric_str - && get_one_int (gateway_str, G_MAXUINT32, NULL, &metric)) + && get_one_int (NULL, NULL, gateway_str, G_MAXUINT32, &metric)) gateway_str = NULL; else { - nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid gateway '%s'", __func__, gateway_str); + if (!info->error) { + handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid gateway '%s' for %s route"), + gateway_str, family == AF_INET ? "IPv4" : "IPv6"); + } return NULL; } } @@ -167,7 +216,7 @@ build_route (int family, /* parse metric, default to 0 */ if (metric_str) { - if (!get_one_int (metric_str, G_MAXUINT32, key_name, &metric)) + if (!get_one_int (info, property_name, metric_str, G_MAXUINT32, &metric)) return NULL; } @@ -175,7 +224,8 @@ build_route (int family, metric ? (gint64) metric : -1, &error); if (!route) { - nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid %s route: %s", __func__, + handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid %s route: %s"), family == AF_INET ? "IPv4" : "IPv6", error->message); g_error_free (error); @@ -269,46 +319,52 @@ read_field (char **current, char **error, const char *characters, const char *de * match the iproute2 defaults (32 for IPv4 and 128 for IPv6). */ static gpointer -read_one_ip_address_or_route (GKeyFile *file, +read_one_ip_address_or_route (KeyfileReaderInfo *info, + const char *property_name, const char *setting_name, const char *key_name, gboolean ipv6, gboolean route, - char **out_gateway) + char **out_gateway, + NMSetting *setting) { guint32 plen = G_MAXUINT32; gpointer result; - char *address_str, *plen_str, *gateway_str, *metric_str, *value, *current, *error; + char *address_str, *plen_str, *gateway_str, *metric_str, *current, *error; + gs_free char *value = NULL, *value_orig = NULL; - current = value = nm_keyfile_plugin_kf_get_string (file, setting_name, key_name, NULL); +#define VALUE_ORIG() (value_orig ? value_orig : (value_orig = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key_name, NULL))) + + current = value = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key_name, NULL); if (!value) return NULL; /* get address field */ address_str = read_field (¤t, &error, IP_ADDRESS_CHARS, DELIMITERS); if (error) { - nm_log_warn (LOGD_SETTINGS, "keyfile: Unexpected character '%c' in '%s.%s' address (position %td of '%s').", - *error, setting_name, key_name, error - current, current); - goto error; + handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN, + _("unexpected character '%c' for address %s: '%s' (position %td)"), + *error, key_name, VALUE_ORIG (), error - current); + return NULL; } /* get prefix length field (skippable) */ plen_str = read_field (¤t, &error, DIGITS, DELIMITERS); /* get gateway field */ gateway_str = read_field (¤t, &error, IP_ADDRESS_CHARS, DELIMITERS); if (error) { - nm_log_warn (LOGD_SETTINGS, "keyfile: Unexpected character '%c' in '%s.%s' %s (position %td of '%s').", - *error, setting_name, key_name, - plen_str ? "gateway" : "gateway or prefix length", - error - current, current); - goto error; + handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN, + _("unexpected character '%c' for %s: '%s' (position %td)"), + *error, key_name, VALUE_ORIG (), error - current); + return NULL; } /* for routes, get metric */ if (route) { metric_str = read_field (¤t, &error, DIGITS, DELIMITERS); if (error) { - nm_log_warn (LOGD_SETTINGS, "keyfile: Unexpected character '%c' in '%s.%s' prefix length (position %td of '%s').", - *error, setting_name, key_name, error - current, current); - goto error; + handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN, + _("unexpected character '%c' in prefix length for %s: '%s' (position %td)"), + *error, key_name, VALUE_ORIG (), error - current); + return NULL; } } else metric_str = NULL; @@ -316,13 +372,16 @@ read_one_ip_address_or_route (GKeyFile *file, /* there is still some data */ if (*current) { /* another field follows */ - nm_log_warn (LOGD_SETTINGS, "keyfile: %s.%s: Garbage at the and of the line: %s", - setting_name, key_name, current); - goto error; + handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN, + _("garbage at the end of value %s: '%s'"), + key_name, VALUE_ORIG ()); + return NULL; } else { /* semicolon at the end of input */ - nm_log_info (LOGD_SETTINGS, "keyfile: %s.%s: Deprecated semicolon at the end of value.", - setting_name, key_name); + if (!handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_INFO, + _("deprecated semicolon at the end of value %s: '%s'"), + key_name, VALUE_ORIG ())) + return NULL; } } @@ -330,39 +389,44 @@ read_one_ip_address_or_route (GKeyFile *file, /* parse plen, fallback to defaults */ if (plen_str) { - if (!get_one_int (plen_str, ipv6 ? 128 : 32, key_name, &plen) + if (!get_one_int (info, property_name, plen_str, ipv6 ? 128 : 32, &plen) || (route && plen == 0)) { plen = DEFAULT_PREFIX (route, ipv6); - nm_log_warn (LOGD_SETTINGS, "keyfile: invalid prefix length '%s' in '%s.%s', defaulting to %d", - plen_str, setting_name, key_name, plen); + if ( info->error + || !handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid prefix length for %s '%s', defaulting to %d"), + key_name, VALUE_ORIG (), plen)) + return NULL; } } else { plen = DEFAULT_PREFIX (route, ipv6); - nm_log_warn (LOGD_SETTINGS, "keyfile: Missing prefix length in '%s.%s', defaulting to %d", - setting_name, key_name, plen); + if (!handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN, + _("missing prefix length for %s '%s', defaulting to %d"), + key_name, VALUE_ORIG (), plen)) + return NULL; } /* build the appropriate data structure for NetworkManager settings */ if (route) { - result = build_route (ipv6 ? AF_INET6 : AF_INET, - address_str, plen, gateway_str, metric_str, - key_name); + result = build_route (info, property_name, + ipv6 ? AF_INET6 : AF_INET, + address_str, plen, gateway_str, metric_str); } else { - result = build_address (ipv6 ? AF_INET6 : AF_INET, - address_str, plen); + result = build_address (info, ipv6 ? AF_INET6 : AF_INET, + address_str, plen, property_name); + if (!result) + return NULL; if (out_gateway && gateway_str) *out_gateway = g_strdup (gateway_str); } - g_free (value); +#undef VALUE_ORIG + return result; -error: - g_free (value); - return NULL; } static void -ip_address_or_route_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path) +ip_address_or_route_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) { const char *setting_name = nm_setting_get_name (setting); gboolean ipv6 = !strcmp (setting_name, "ipv6"); @@ -383,7 +447,7 @@ ip_address_or_route_parser (NMSetting *setting, const char *key, GKeyFile *keyfi for (i = -1; i < 1000; i++) { const char **key_basename; - + for (key_basename = key_names; *key_basename; key_basename++) { char *key_name; gpointer item; @@ -394,12 +458,18 @@ ip_address_or_route_parser (NMSetting *setting, const char *key, GKeyFile *keyfi else key_name = g_strdup (*key_basename); - item = read_one_ip_address_or_route (keyfile, setting_name, key_name, ipv6, routes, - gateway ? NULL : &gateway); + item = read_one_ip_address_or_route (info, key, setting_name, key_name, ipv6, routes, + gateway ? NULL : &gateway, setting); + g_free (key_name); + + if (info->error) { + g_ptr_array_unref (list); + g_free (gateway); + return; + } if (item) g_ptr_array_add (list, item); - g_free (key_name); } } @@ -415,7 +485,7 @@ ip_address_or_route_parser (NMSetting *setting, const char *key, GKeyFile *keyfi } static void -ip4_dns_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path) +ip4_dns_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) { const char *setting_name = nm_setting_get_name (setting); GPtrArray *array; @@ -423,7 +493,7 @@ ip4_dns_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const ch char **list, **iter; int ret; - list = nm_keyfile_plugin_kf_get_string_list (keyfile, setting_name, key, &length, NULL); + list = nm_keyfile_plugin_kf_get_string_list (info->keyfile, setting_name, key, &length, NULL); if (!list || !g_strv_length (list)) return; @@ -433,7 +503,13 @@ ip4_dns_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const ch ret = inet_pton (AF_INET, *iter, &addr); if (ret <= 0) { - nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid DNS server address '%s'", __func__, *iter); + if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid DNS server IPv4 address '%s'"), + *iter)) { + g_ptr_array_unref (array); + g_strfreev (list); + return; + } continue; } @@ -447,7 +523,7 @@ ip4_dns_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const ch } static void -ip6_dns_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path) +ip6_dns_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) { const char *setting_name = nm_setting_get_name (setting); GPtrArray *array = NULL; @@ -455,7 +531,7 @@ ip6_dns_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const ch char **list, **iter; int ret; - list = nm_keyfile_plugin_kf_get_string_list (keyfile, setting_name, key, &length, NULL); + list = nm_keyfile_plugin_kf_get_string_list (info->keyfile, setting_name, key, &length, NULL); if (!list || !g_strv_length (list)) return; @@ -466,7 +542,13 @@ ip6_dns_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const ch ret = inet_pton (AF_INET6, *iter, &addr); if (ret <= 0) { - nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid DNS server IPv6 address '%s'", __func__, *iter); + if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid DNS server IPv6 address '%s'"), + *iter)) { + g_ptr_array_unref (array); + g_strfreev (list); + return; + } continue; } @@ -480,7 +562,7 @@ ip6_dns_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const ch } static void -mac_address_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path, gsize enforce_length) +mac_address_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key, gsize enforce_length) { const char *setting_name = nm_setting_get_name (setting); char *tmp_string = NULL, *p, *mac_str; @@ -488,7 +570,7 @@ mac_address_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, cons GByteArray *array = NULL; gsize length; - p = tmp_string = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, NULL); + p = tmp_string = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL); if (tmp_string && tmp_string[0]) { /* Look for enough ':' characters to signify a MAC address */ guint i = 0; @@ -513,7 +595,7 @@ mac_address_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, cons if (array == NULL) { /* Old format; list of ints */ - tmp_list = nm_keyfile_plugin_kf_get_integer_list (keyfile, setting_name, key, &length, NULL); + tmp_list = nm_keyfile_plugin_kf_get_integer_list (info->keyfile, setting_name, key, &length, NULL); if (length > 0 && (enforce_length == 0 || enforce_length == length)) { gsize i; @@ -523,12 +605,12 @@ mac_address_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, cons const guint8 v = (guint8) (val & 0xFF); if (val < 0 || val > 255) { - nm_log_warn (LOGD_SETTINGS, "%s: %s / %s ignoring invalid byte element '%d' (not " - " between 0 and 255 inclusive)", __func__, setting_name, - key, val); + handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid byte element '%d' (not between 0 and 255 inclusive)"), + val); g_byte_array_free (array, TRUE); - array = NULL; - break; + g_free (tmp_list); + return; } g_byte_array_append (array, &v, 1); } @@ -537,8 +619,8 @@ mac_address_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, cons } if (!array) { - nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid MAC address for %s / %s", - __func__, setting_name, key); + handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid MAC address")); return; } @@ -549,15 +631,15 @@ mac_address_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, cons } static void -mac_address_parser_ETHER (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path) +mac_address_parser_ETHER (KeyfileReaderInfo *info, NMSetting *setting, const char *key) { - mac_address_parser (setting, key, keyfile, keyfile_path, ETH_ALEN); + mac_address_parser (info, setting, key, ETH_ALEN); } static void -mac_address_parser_INFINIBAND (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path) +mac_address_parser_INFINIBAND (KeyfileReaderInfo *info, NMSetting *setting, const char *key) { - mac_address_parser (setting, key, keyfile, keyfile_path, INFINIBAND_ALEN); + mac_address_parser (info, setting, key, INFINIBAND_ALEN); } static void @@ -606,7 +688,7 @@ unescape_semicolons (char *str) } static GBytes * -get_bytes (GKeyFile *keyfile, +get_bytes (KeyfileReaderInfo *info, const char *setting_name, const char *key, gboolean zero_terminate, @@ -618,13 +700,13 @@ get_bytes (GKeyFile *keyfile, gsize length; int i; - if (!nm_keyfile_plugin_kf_has_key (keyfile, setting_name, key, NULL)) + if (!nm_keyfile_plugin_kf_has_key (info->keyfile, setting_name, key, NULL)) return NULL; /* New format: just a string * Old format: integer list; e.g. 11;25;38; */ - tmp_string = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, NULL); + tmp_string = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL); if (tmp_string) { GRegex *regex; GMatchInfo *match_info; @@ -648,11 +730,13 @@ get_bytes (GKeyFile *keyfile, } if (!array) { + gboolean already_warned = FALSE; + /* Old format; list of ints */ - tmp_list = nm_keyfile_plugin_kf_get_integer_list (keyfile, setting_name, key, &length, NULL); + tmp_list = nm_keyfile_plugin_kf_get_integer_list (info->keyfile, setting_name, key, &length, NULL); if (!tmp_list) { - nm_log_warn (LOGD_SETTINGS, "%s: %s / %s ignoring invalid binary property", - __func__, setting_name, key); + handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid binary property")); return NULL; } array = g_byte_array_sized_new (length); @@ -661,9 +745,15 @@ get_bytes (GKeyFile *keyfile, unsigned char v = (unsigned char) (val & 0xFF); if (val < 0 || val > 255) { - nm_log_warn (LOGD_SETTINGS, "%s: %s / %s ignoring invalid byte element '%d' (not " - " between 0 and 255 inclusive)", __func__, setting_name, - key, val); + if ( !already_warned + && !handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid byte element '%d' (not between 0 and 255 inclusive)"), + val)) { + g_free (tmp_list); + g_byte_array_free (array, TRUE); + return NULL; + } + already_warned = TRUE; } else g_byte_array_append (array, (const unsigned char *) &v, sizeof (v)); } @@ -678,44 +768,44 @@ get_bytes (GKeyFile *keyfile, } static void -ssid_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path) +ssid_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) { const char *setting_name = nm_setting_get_name (setting); GBytes *bytes; - bytes = get_bytes (keyfile, setting_name, key, FALSE, TRUE); + bytes = get_bytes (info, setting_name, key, FALSE, TRUE); if (bytes) { g_object_set (setting, key, bytes, NULL); g_bytes_unref (bytes); - } else { - nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid SSID for %s / %s", - __func__, setting_name, key); + } else if (!info->error) { + handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid SSID")); } } static void -password_raw_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path) +password_raw_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) { const char *setting_name = nm_setting_get_name (setting); GBytes *bytes; - bytes = get_bytes (keyfile, setting_name, key, FALSE, TRUE); + bytes = get_bytes (info, setting_name, key, FALSE, TRUE); if (bytes) { g_object_set (setting, key, bytes, NULL); g_bytes_unref (bytes); - } else { - nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid raw password for %s / %s", - __func__, setting_name, key); + } else if (!info->error) { + handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid raw password")); } } static char * -get_cert_path (const char *keyfile_path, const guint8 *cert_path, gsize cert_path_len) +get_cert_path (const char *base_dir, const guint8 *cert_path, gsize cert_path_len) { const char *base; - char *p = NULL, *path, *dirname, *tmp; + char *p = NULL, *path, *tmp; - g_return_val_if_fail (keyfile_path != NULL, NULL); + g_return_val_if_fail (base_dir != NULL, NULL); g_return_val_if_fail (cert_path != NULL, NULL); base = path = g_malloc0 (cert_path_len + 1); @@ -728,9 +818,7 @@ get_cert_path (const char *keyfile_path, const guint8 *cert_path, gsize cert_pat if (p) base = p + 1; - dirname = g_path_get_dirname (keyfile_path); - tmp = g_build_path ("/", dirname, base, NULL); - g_free (dirname); + tmp = g_build_path ("/", base_dir, base, NULL); g_free (path); return tmp; } @@ -770,10 +858,10 @@ handle_as_scheme (GBytes *bytes, NMSetting *setting, const char *key) } static gboolean -handle_as_path (GBytes *bytes, +handle_as_path (KeyfileReaderInfo *info, + GBytes *bytes, NMSetting *setting, - const char *key, - const char *keyfile_path) + const char *key) { const guint8 *data; gsize data_len; @@ -802,7 +890,7 @@ handle_as_path (GBytes *bytes, * relative path to the current directory. */ - path = get_cert_path (keyfile_path, data, data_len); + path = get_cert_path (info->base_dir, data, data_len); exists = g_file_test (path, G_FILE_TEST_EXISTS); if ( exists || memchr (data, '/', data_len) @@ -822,7 +910,9 @@ handle_as_path (GBytes *bytes, /* Warn if the certificate didn't exist */ if (exists == FALSE) - nm_log_warn (LOGD_SETTINGS, "certificate or key %s does not exist", path); + handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, + _("certificate or key '%s' does not exist"), + path); } g_free (path); @@ -830,48 +920,51 @@ handle_as_path (GBytes *bytes, } static void -cert_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path) +cert_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) { const char *setting_name = nm_setting_get_name (setting); GBytes *bytes; gboolean success = FALSE; - bytes = get_bytes (keyfile, setting_name, key, TRUE, FALSE); + bytes = get_bytes (info, setting_name, key, TRUE, FALSE); if (bytes) { /* Try as a path + scheme (ie, starts with "file://") */ success = handle_as_scheme (bytes, setting, key); /* If not, it might be a plain path */ if (success == FALSE) - success = handle_as_path (bytes, setting, key, keyfile_path); + success = handle_as_path (info, bytes, setting, key); + if (info->error) + goto out_error; /* If neither of those two, assume blob with certificate data */ if (success == FALSE) g_object_set (setting, key, bytes, NULL); - } else { - nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid key/cert value for %s / %s", - __func__, setting_name, key); + } else if (!info->error) { + handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid key/cert value")); } +out_error: if (bytes) g_bytes_unref (bytes); } static void -parity_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path) +parity_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) { const char *setting_name = nm_setting_get_name (setting); NMSettingSerialParity parity; int int_val; - char *str_val; + gs_free char *str_val = NULL; /* Keyfile traditionally stored this as the ASCII value for 'E', 'o', or 'n'. * We now accept either that or the (case-insensitive) character itself (but * still always write it the old way, for backward compatibility). */ - int_val = nm_keyfile_plugin_kf_get_integer (keyfile, setting_name, key, NULL); + int_val = nm_keyfile_plugin_kf_get_integer (info->keyfile, setting_name, key, NULL); if (!int_val) { - str_val = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, NULL); + str_val = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL); if (str_val) { if (str_val[0] && !str_val[1]) int_val = str_val[0]; @@ -880,7 +973,6 @@ parity_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const cha int_val = 'X'; } } - g_free (str_val); } if (!int_val) @@ -900,8 +992,9 @@ parity_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const cha parity = NM_SETTING_SERIAL_PARITY_NONE; break; default: - nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid value for %s / %s", - __func__, setting_name, key); + handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid parity value '%s'"), + str_val ? str_val : ""); return; } @@ -912,7 +1005,7 @@ typedef struct { const char *setting_name; const char *key; gboolean check_for_key; - void (*parser) (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path); + void (*parser) (KeyfileReaderInfo *info, NMSetting *setting, const char *key); } KeyParser; /* A table of keys that require further parsing/conversion because they are @@ -1025,11 +1118,6 @@ static KeyParser key_parsers[] = { { NULL, NULL, FALSE } }; -typedef struct { - GKeyFile *keyfile; - const char *keyfile_path; -} ReadInfo; - static void read_one_setting_value (NMSetting *setting, const char *key, @@ -1037,14 +1125,18 @@ read_one_setting_value (NMSetting *setting, GParamFlags flags, gpointer user_data) { - ReadInfo *info = user_data; + KeyfileReaderInfo *info = user_data; + GKeyFile *keyfile = info->keyfile; const char *setting_name; int errsv; GType type; - GError *err = NULL; + gs_free_error GError *err = NULL; gboolean check_for_key = TRUE; KeyParser *parser = &key_parsers[0]; + if (info->error) + return; + /* Property is not writable */ if (!(flags & G_PARAM_WRITABLE)) return; @@ -1082,11 +1174,13 @@ read_one_setting_value (NMSetting *setting, * like IP addresses and routes where more than one value is actually * encoded by the setting property, this won't be true. */ - if (check_for_key && !nm_keyfile_plugin_kf_has_key (info->keyfile, setting_name, key, &err)) { + if (check_for_key && !nm_keyfile_plugin_kf_has_key (keyfile, setting_name, key, &err)) { /* Key doesn't exist or an error ocurred, thus nothing to do. */ if (err) { - nm_log_warn (LOGD_SETTINGS, "Error loading setting '%s' value: %s", setting_name, err->message); - g_error_free (err); + if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, + _("error loading setting value: %s"), + err->message)) + goto out_error; } return; } @@ -1095,7 +1189,7 @@ read_one_setting_value (NMSetting *setting, * parsers below. */ if (parser->setting_name) { - (*parser->parser) (setting, key, info->keyfile, info->keyfile_path); + (*parser->parser) (info, setting, key); return; } @@ -1104,62 +1198,73 @@ read_one_setting_value (NMSetting *setting, if (type == G_TYPE_STRING) { char *str_val; - str_val = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL); + str_val = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, NULL); g_object_set (setting, key, str_val, NULL); g_free (str_val); } else if (type == G_TYPE_UINT) { int int_val; - int_val = nm_keyfile_plugin_kf_get_integer (info->keyfile, setting_name, key, NULL); - if (int_val < 0) - nm_log_warn (LOGD_SETTINGS, "Casting negative value (%i) to uint", int_val); + int_val = nm_keyfile_plugin_kf_get_integer (keyfile, setting_name, key, NULL); + if (int_val < 0) { + if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid negative value (%i)"), + int_val)) + goto out_error; + } g_object_set (setting, key, int_val, NULL); } else if (type == G_TYPE_INT) { int int_val; - int_val = nm_keyfile_plugin_kf_get_integer (info->keyfile, setting_name, key, NULL); + int_val = nm_keyfile_plugin_kf_get_integer (keyfile, setting_name, key, NULL); g_object_set (setting, key, int_val, NULL); } else if (type == G_TYPE_BOOLEAN) { gboolean bool_val; - bool_val = nm_keyfile_plugin_kf_get_boolean (info->keyfile, setting_name, key, NULL); + bool_val = nm_keyfile_plugin_kf_get_boolean (keyfile, setting_name, key, NULL); g_object_set (setting, key, bool_val, NULL); } else if (type == G_TYPE_CHAR) { int int_val; - int_val = nm_keyfile_plugin_kf_get_integer (info->keyfile, setting_name, key, NULL); - if (int_val < G_MININT8 || int_val > G_MAXINT8) - nm_log_warn (LOGD_SETTINGS, "Casting value (%i) to char", int_val); + int_val = nm_keyfile_plugin_kf_get_integer (keyfile, setting_name, key, NULL); + if (int_val < G_MININT8 || int_val > G_MAXINT8) { + if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid char value (%i)"), + int_val)) + goto out_error; + } g_object_set (setting, key, int_val, NULL); } else if (type == G_TYPE_UINT64) { char *tmp_str; guint64 uint_val; - tmp_str = nm_keyfile_plugin_kf_get_value (info->keyfile, setting_name, key, NULL); + tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_name, key, NULL); uint_val = g_ascii_strtoull (tmp_str, NULL, 10); g_free (tmp_str); g_object_set (setting, key, uint_val, NULL); } else if (type == G_TYPE_INT64) { - char *tmp_str; + gs_free char *tmp_str = NULL; gint64 int_val; - tmp_str = nm_keyfile_plugin_kf_get_value (info->keyfile, setting_name, key, NULL); + tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_name, key, NULL); int_val = _nm_utils_ascii_str_to_int64 (tmp_str, 10, G_MININT64, G_MAXINT64, 0); errsv = errno; - if (errsv) - nm_log_warn (LOGD_SETTINGS, "Invalid int64 value (%s)", tmp_str); - else + if (errsv) { + if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid int64 value (%s)"), + tmp_str)) + goto out_error; + } else g_object_set (setting, key, int_val, NULL); - g_free (tmp_str); - } else if (type == G_TYPE_BYTES) { + } else if (type == G_TYPE_BYTES) { gint *tmp; GByteArray *array; GBytes *bytes; gsize length; int i; + gboolean already_warned = FALSE; - tmp = nm_keyfile_plugin_kf_get_integer_list (info->keyfile, setting_name, key, &length, NULL); + tmp = nm_keyfile_plugin_kf_get_integer_list (keyfile, setting_name, key, &length, NULL); array = g_byte_array_sized_new (length); for (i = 0; i < length; i++) { @@ -1167,9 +1272,15 @@ read_one_setting_value (NMSetting *setting, unsigned char v = (unsigned char) (val & 0xFF); if (val < 0 || val > 255) { - nm_log_warn (LOGD_SETTINGS, "%s: %s / %s ignoring invalid byte element '%d' (not " - " between 0 and 255 inclusive)", __func__, setting_name, - key, val); + if ( !already_warned + && !handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid byte element '%d' (not between 0 and 255 inclusive)"), + val)) { + g_byte_array_unref (array); + g_free (tmp); + goto out_error; + } + already_warned = TRUE; } else g_byte_array_append (array, (const unsigned char *) &v, sizeof (v)); } @@ -1182,75 +1293,83 @@ read_one_setting_value (NMSetting *setting, gchar **sa; gsize length; - sa = nm_keyfile_plugin_kf_get_string_list (info->keyfile, setting_name, key, &length, NULL); + sa = nm_keyfile_plugin_kf_get_string_list (keyfile, setting_name, key, &length, NULL); g_object_set (setting, key, sa, NULL); g_strfreev (sa); } else if (type == G_TYPE_HASH_TABLE) { - read_hash_of_string (info->keyfile, setting, key); + read_hash_of_string (keyfile, setting, key); } else if (type == G_TYPE_ARRAY) { - if (!read_array_of_uint (info->keyfile, setting, key)) { - nm_log_warn (LOGD_SETTINGS, "Unhandled setting property type (read): '%s/%s' : '%s'", - setting_name, key, G_VALUE_TYPE_NAME (value)); - } + read_array_of_uint (keyfile, setting, key); } else if (G_VALUE_HOLDS_FLAGS (value)) { guint64 uint_val; /* Flags are guint but GKeyFile has no uint reader, just uint64 */ - uint_val = nm_keyfile_plugin_kf_get_uint64 (info->keyfile, setting_name, key, &err); + uint_val = nm_keyfile_plugin_kf_get_uint64 (keyfile, setting_name, key, &err); if (!err) { if (uint_val <= G_MAXUINT) g_object_set (setting, key, (guint) uint_val, NULL); else { - nm_log_warn (LOGD_SETTINGS, "Too large FLAGS property (read): '%s/%s' : '%s'", - setting_name, key, G_VALUE_TYPE_NAME (value)); + if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, + _("too large FLAGS property '%s' (%lld)"), + G_VALUE_TYPE_NAME (value), (long long unsigned) uint_val)) + goto out_error; } } - g_clear_error (&err); } else if (G_VALUE_HOLDS_ENUM (value)) { gint int_val; - int_val = nm_keyfile_plugin_kf_get_integer (info->keyfile, setting_name, key, &err); + int_val = nm_keyfile_plugin_kf_get_integer (keyfile, setting_name, key, &err); if (!err) g_object_set (setting, key, (gint) int_val, NULL); - g_clear_error (&err); } else { - nm_log_warn (LOGD_SETTINGS, "Unhandled setting property type (read): '%s/%s' : '%s'", - setting_name, key, G_VALUE_TYPE_NAME (value)); + if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, + _("unhandled setting property type '%s'"), + G_VALUE_TYPE_NAME (value))) + goto out_error; } +out_error: + return; } static NMSetting * -read_setting (GKeyFile *file, const char *keyfile_path, const char *group) +read_setting (KeyfileReaderInfo *info) { - NMSetting *setting = NULL; - ReadInfo info = { file, keyfile_path }; const char *alias; GType type; - alias = nm_keyfile_plugin_get_setting_name_for_alias (group); - if (alias) - group = alias; + alias = nm_keyfile_plugin_get_setting_name_for_alias (info->group); + if (!alias) + alias = info->group; - type = nm_setting_lookup_type (group); + type = nm_setting_lookup_type (alias); if (type) { - setting = g_object_new (type, NULL); - nm_setting_enumerate_values (setting, read_one_setting_value, &info); - } else - nm_log_warn (LOGD_SETTINGS, "Invalid setting name '%s'", group); + NMSetting *setting = g_object_new (type, NULL); - return setting; + info->setting = setting; + nm_setting_enumerate_values (setting, read_one_setting_value, info); + info->setting = NULL; + if (!info->error) + return setting; + + g_object_unref (setting); + } else { + handle_warn (info, NULL, NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid setting name '%s'"), info->group); + } + + return NULL; } static void -read_vpn_secrets (GKeyFile *file, NMSettingVpn *s_vpn) +read_vpn_secrets (KeyfileReaderInfo *info, NMSettingVpn *s_vpn) { char **keys, **iter; - keys = nm_keyfile_plugin_kf_get_keys (file, VPN_SECRETS_GROUP, NULL, NULL); + keys = nm_keyfile_plugin_kf_get_keys (info->keyfile, VPN_SECRETS_GROUP, NULL, NULL); for (iter = keys; *iter; iter++) { char *secret; - secret = nm_keyfile_plugin_kf_get_string (file, VPN_SECRETS_GROUP, *iter, NULL); + secret = nm_keyfile_plugin_kf_get_string (info->keyfile, VPN_SECRETS_GROUP, *iter, NULL); if (secret) { nm_setting_vpn_add_secret (s_vpn, *iter, secret); g_free (secret); @@ -1259,12 +1378,37 @@ read_vpn_secrets (GKeyFile *file, NMSettingVpn *s_vpn) g_strfreev (keys); } +/** + * nm_keyfile_read: + * @keyfile: the keyfile from which to create the connection + * @keyfile_name: keyfile allows missing connection id and uuid + * and NetworkManager will create those when reading a connection + * from file. By providing a filename you can reproduce that behavior, + * but of course, it can only recreate the same UUID if you provide the + * same filename as NetworkManager core daemon would. + * @keyfile_name has only a relevance for setting the id or uuid if it + * is missing and as fallback for @base_dir. + * @base_dir: when reading certificates from files with relative name, + * the relative path is made absolute using @base_dir. + * If @base_dir is missing, first try to get the pathname from @keyfile_name + * (if it is given as absolute path). As last, fallback to the current path. + * @handler: read handler + * @user_data: user data for read handler + * @error: error + * + * Tries to create a NMConnection from a keyfile. The resulting keyfile is + * not normalized and might not even verify. + * + * Returns: (transfer full): on success, returns the created connection. + */ NMConnection * -nm_keyfile_plugin_connection_from_file (const char *filename, GError **error) +nm_keyfile_read (GKeyFile *keyfile, + const char *keyfile_name, + const char *base_dir, + NMKeyfileReadHandler handler, + void *user_data, + GError **error) { - GKeyFile *key_file; - struct stat statbuf; - gboolean bad_permissions; NMConnection *connection = NULL; NMSettingConnection *s_con; NMSetting *setting; @@ -1272,30 +1416,33 @@ nm_keyfile_plugin_connection_from_file (const char *filename, GError **error) gsize length; int i; gboolean vpn_secrets = FALSE; - GError *verify_error = NULL; + KeyfileReaderInfo info = { 0 }; + gs_free char *base_dir_free = NULL; - if (stat (filename, &statbuf) != 0 || !S_ISREG (statbuf.st_mode)) { - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "File did not exist or was not a regular file"); - return NULL; - } + g_return_val_if_fail (keyfile, NULL); + g_return_val_if_fail (!error || !*error, NULL); - bad_permissions = statbuf.st_mode & 0077; - - if (bad_permissions) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "File permissions (%o) were insecure", - statbuf.st_mode); - return NULL; - } - - key_file = g_key_file_new (); - if (!g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, error)) - goto out; + if (!base_dir) { + /* basedir is not given. Prefer it from the keyfile_name */ + if (keyfile_name && keyfile_name[0] == '/') { + base_dir = base_dir_free = g_path_get_dirname (keyfile_name); + } else { + /* if keyfile is not given or not an absolute path, fallback + * to current working directory. */ + base_dir = base_dir_free = g_get_current_dir (); + } + } else + g_return_val_if_fail ("/", NULL); connection = nm_simple_connection_new (); - groups = g_key_file_get_groups (key_file, &length); + info.connection = connection; + info.keyfile = (GKeyFile *) keyfile; + info.base_dir = base_dir; + info.handler = handler; + info.user_data = user_data; + + groups = g_key_file_get_groups (keyfile, &length); for (i = 0; i < length; i++) { /* Only read out secrets when needed */ if (!strcmp (groups[i], VPN_SECRETS_GROUP)) { @@ -1303,10 +1450,15 @@ nm_keyfile_plugin_connection_from_file (const char *filename, GError **error) continue; } - setting = read_setting (key_file, filename, groups[i]); + info.group = groups[i]; + setting = read_setting (&info); + info.group = NULL; + if (info.error) + goto out_error; if (setting) nm_connection_add_setting (connection, setting); } + g_strfreev (groups); s_con = nm_connection_get_setting_connection (connection); if (!s_con) { @@ -1315,19 +1467,21 @@ nm_keyfile_plugin_connection_from_file (const char *filename, GError **error) } /* Make sure that we have 'id' even if not explictly specified in the keyfile */ - if (!nm_setting_connection_get_id (s_con)) { + if ( keyfile_name + && !nm_setting_connection_get_id (s_con)) { char *base_name; - base_name = g_path_get_basename (filename); + base_name = g_path_get_basename (keyfile_name); g_object_set (s_con, NM_SETTING_CONNECTION_ID, base_name, NULL); g_free (base_name); } /* Make sure that we have 'uuid' even if not explictly specified in the keyfile */ - if (!nm_setting_connection_get_uuid (s_con)) { + if ( keyfile_name + && !nm_setting_connection_get_uuid (s_con)) { char *hashed_uuid; - hashed_uuid = _nm_utils_uuid_generate_from_strings ("keyfile", filename, NULL); + hashed_uuid = _nm_utils_uuid_generate_from_strings ("keyfile", keyfile_name, NULL); g_object_set (s_con, NM_SETTING_CONNECTION_UUID, hashed_uuid, NULL); g_free (hashed_uuid); } @@ -1339,7 +1493,7 @@ nm_keyfile_plugin_connection_from_file (const char *filename, GError **error) && nm_setting_connection_get_connection_type (s_con)) { char *interface_name; - interface_name = g_key_file_get_string (key_file, + interface_name = g_key_file_get_string (keyfile, nm_setting_connection_get_connection_type (s_con), "interface-name", NULL); @@ -1354,23 +1508,16 @@ nm_keyfile_plugin_connection_from_file (const char *filename, GError **error) NMSettingVpn *s_vpn; s_vpn = nm_connection_get_setting_vpn (connection); - if (s_vpn) - read_vpn_secrets (key_file, s_vpn); + if (s_vpn) { + read_vpn_secrets (&info, s_vpn); + if (info.error) + goto out_error; + } } - g_strfreev (groups); - - /* Normalize and verify the connection */ - if (!nm_connection_normalize (connection, NULL, NULL, &verify_error)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "invalid connection: %s", - verify_error->message); - g_clear_error (&verify_error); - g_object_unref (connection); - connection = NULL; - } - -out: - g_key_file_free (key_file); return connection; +out_error: + g_propagate_error (error, info.error); + g_free (connection); + return NULL; } diff --git a/libnm-core/nm-keyfile-reader.h b/libnm-core/nm-keyfile-reader.h index 55819630e..73752ff22 100644 --- a/libnm-core/nm-keyfile-reader.h +++ b/libnm-core/nm-keyfile-reader.h @@ -16,15 +16,70 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Copyright (C) 2008 Novell, Inc. - * Copyright (C) 2008 Red Hat, Inc. + * Copyright (C) 2015 Red Hat, Inc. */ -#ifndef _KEYFILE_PLUGIN_READER_H -#define _KEYFILE_PLUGIN_READER_H +#ifndef __NM_KEYFILE_READER_H__ +#define __NM_KEYFILE_READER_H__ #include -#include -NMConnection *nm_keyfile_plugin_connection_from_file (const char *filename, GError **error); +#include "nm-connection.h" -#endif /* _KEYFILE_PLUGIN_READER_H */ + +typedef enum { + NM_KEYFILE_READ_TYPE_WARN = 1, +} NMKeyfileReadType; + +/** + * NMKeyfileReadHandler: + * + * Hook to nm_keyfile_read(). The user might fail the reading by setting + * @error. + * + * Returns: should return TRUE, if the reading was handled. Otherwise, + * a default action will be performed that depends on the @type. + * For %NM_KEYFILE_READ_TYPE_WARN type, the default action is doing nothing. + */ +typedef gboolean (*NMKeyfileReadHandler) (GKeyFile *keyfile, + NMConnection *connection, + NMKeyfileReadType type, + void *type_data, + void *user_data, + GError **error); + +typedef enum { + NM_KEYFILE_WARN_SEVERITY_DEBUG = 1000, + NM_KEYFILE_WARN_SEVERITY_INFO = 2000, + NM_KEYFILE_WARN_SEVERITY_WARN = 3000, +} NMKeyfileWarnSeverity; + +/** + * NMKeyfileReadTypeDataWarn: + * + * this struct is passed as @type_data for the @NMKeyfileReadHandler of + * type %NM_KEYFILE_READ_TYPE_WARN. + */ +typedef struct { + /* might be %NULL, if the warning is not about a group. */ + const char *group; + + /* might be %NULL, if the warning is not about a setting. */ + NMSetting *setting; + + /* might be %NULL, if the warning is not about a property. */ + const char *property_name; + + NMKeyfileWarnSeverity severity; + const char *message; +} NMKeyfileReadTypeDataWarn; + + +NMConnection *nm_keyfile_read (GKeyFile *keyfile, + const char *keyfile_name, + const char *base_dir, + NMKeyfileReadHandler handler, + void *user_data, + GError **error); + +#endif /* __NM_KEYFILE_READER_H__ */ diff --git a/libnm-core/nm-keyfile-utils.c b/libnm-core/nm-keyfile-utils.c index 30400b1bc..e2198712b 100644 --- a/libnm-core/nm-keyfile-utils.c +++ b/libnm-core/nm-keyfile-utils.c @@ -23,128 +23,11 @@ #include #include #include -#include "gsystem-local-alloc.h" -#include "utils.h" -#include -#include -#include - -static const char temp_letters[] = -"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - -/* - * Check '.[a-zA-Z0-9]{6}' file suffix used for temporary files by g_file_set_contents() (mkstemp()). - */ -static gboolean -check_mkstemp_suffix (const char *path) -{ - const char *ptr; - - g_return_val_if_fail (path != NULL, FALSE); - - /* Matches *.[a-zA-Z0-9]{6} suffix of mkstemp()'s temporary files */ - ptr = strrchr (path, '.'); - if (ptr && (strspn (ptr + 1, temp_letters) == 6) && (! ptr[7])) - return TRUE; - return FALSE; -} - -static gboolean -check_prefix (const char *base, const char *tag) -{ - int len, tag_len; - - g_return_val_if_fail (base != NULL, TRUE); - g_return_val_if_fail (tag != NULL, TRUE); - - len = strlen (base); - tag_len = strlen (tag); - if ((len > tag_len) && !g_ascii_strncasecmp (base, tag, tag_len)) - return TRUE; - return FALSE; -} - -static gboolean -check_suffix (const char *base, const char *tag) -{ - int len, tag_len; - - g_return_val_if_fail (base != NULL, TRUE); - g_return_val_if_fail (tag != NULL, TRUE); - - len = strlen (base); - tag_len = strlen (tag); - if ((len > tag_len) && !g_ascii_strcasecmp (base + len - tag_len, tag)) - return TRUE; - return FALSE; -} - -#define SWP_TAG ".swp" -#define SWPX_TAG ".swpx" -#define PEM_TAG ".pem" -#define DER_TAG ".der" - -gboolean -nm_keyfile_plugin_utils_should_ignore_file (const char *filename) -{ - gs_free char *base = NULL; - - g_return_val_if_fail (filename != NULL, TRUE); - - base = g_path_get_basename (filename); - g_return_val_if_fail (base != NULL, TRUE); - - /* Ignore hidden and backup files */ - /* should_ignore_file() must mirror escape_filename() */ - if (check_prefix (base, ".") || check_suffix (base, "~")) - return TRUE; - /* Ignore temporary files */ - if (check_mkstemp_suffix (base)) - return TRUE; - /* Ignore 802.1x certificates and keys */ - if (check_suffix (base, PEM_TAG) || check_suffix (base, DER_TAG)) - return TRUE; - - return FALSE; -} - -char * -nm_keyfile_plugin_utils_escape_filename (const char *filename) -{ - GString *str; - const char *f = filename; - const char ESCAPE_CHAR = '*'; - - /* keyfile used to escape with '*', do not change that behavior. - * But for newly added escapings, use '_' instead. */ - const char ESCAPE_CHAR2 = '_'; - - g_return_val_if_fail (filename && filename[0], NULL); - - str = g_string_sized_new (60); - - /* Convert '/' to ESCAPE_CHAR */ - for (f = filename; f[0]; f++) { - if (f[0] == '/') - g_string_append_c (str, ESCAPE_CHAR); - else - g_string_append_c (str, f[0]); - } - - /* escape_filename() must avoid anything that should_ignore_file() would reject. - * We can escape here more aggressivly then what we would read back. */ - if (check_prefix (str->str, ".")) - str->str[0] = ESCAPE_CHAR2; - if (check_suffix (str->str, "~")) - str->str[str->len - 1] = ESCAPE_CHAR2; - if ( check_mkstemp_suffix (str->str) - || check_suffix (str->str, PEM_TAG) - || check_suffix (str->str, DER_TAG)) - g_string_append_c (str, ESCAPE_CHAR2); - - return g_string_free (str, FALSE);; -} +#include "nm-keyfile-utils.h" +#include "nm-setting-wired.h" +#include "nm-setting-wireless.h" +#include "nm-setting-wireless-security.h" typedef struct { diff --git a/libnm-core/nm-keyfile-utils.h b/libnm-core/nm-keyfile-utils.h index 456cd1a88..fd4334d11 100644 --- a/libnm-core/nm-keyfile-utils.h +++ b/libnm-core/nm-keyfile-utils.h @@ -15,25 +15,15 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * (C) Copyright 2010 Red Hat, Inc. + * (C) Copyright 2010-2015 Red Hat, Inc. */ -#ifndef _UTILS_H_ -#define _UTILS_H_ +#ifndef __NM_KEYFILE_UTILS_H__ +#define __NM_KEYFILE_UTILS_H__ #include -#include "common.h" -#include "NetworkManagerUtils.h" -#define NM_KEYFILE_CONNECTION_LOG_PATH(path) str_if_set (path,"in-memory") -#define NM_KEYFILE_CONNECTION_LOG_FMT "%s (%s,\"%s\")" -#define NM_KEYFILE_CONNECTION_LOG_ARG(con) NM_KEYFILE_CONNECTION_LOG_PATH (nm_settings_connection_get_filename ((NMSettingsConnection *) (con))), nm_connection_get_uuid ((NMConnection *) (con)), nm_connection_get_id ((NMConnection *) (con)) -#define NM_KEYFILE_CONNECTION_LOG_FMTD "%s (%s,\"%s\",%p)" -#define NM_KEYFILE_CONNECTION_LOG_ARGD(con) NM_KEYFILE_CONNECTION_LOG_PATH (nm_settings_connection_get_filename ((NMSettingsConnection *) (con))), nm_connection_get_uuid ((NMConnection *) (con)), nm_connection_get_id ((NMConnection *) (con)), (con) - -gboolean nm_keyfile_plugin_utils_should_ignore_file (const char *filename); - -char *nm_keyfile_plugin_utils_escape_filename (const char *filename); +#define VPN_SECRETS_GROUP "vpn-secrets" const char *nm_keyfile_plugin_get_alias_for_setting_name (const char *setting_name); @@ -85,5 +75,5 @@ gboolean nm_keyfile_plugin_kf_has_key (GKeyFile *kf, const char *key, GError **error); -#endif /* _UTILS_H_ */ +#endif /* __NM_KEYFILE_UTILS_H__ */ diff --git a/libnm-core/nm-keyfile-writer.c b/libnm-core/nm-keyfile-writer.c index a707ac328..ec934fafd 100644 --- a/libnm-core/nm-keyfile-writer.c +++ b/libnm-core/nm-keyfile-writer.c @@ -16,7 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Copyright (C) 2008 Novell, Inc. - * Copyright (C) 2008 - 2012 Red Hat, Inc. + * Copyright (C) 2008 - 2015 Red Hat, Inc. */ #include "config.h" @@ -26,26 +26,34 @@ #include #include #include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include +#include + +#include "nm-setting.h" +#include "nm-setting-connection.h" +#include "nm-setting-ip4-config.h" +#include "nm-setting-ip6-config.h" +#include "nm-setting-vpn.h" +#include "nm-setting-wired.h" +#include "nm-setting-wireless.h" +#include "nm-setting-ip4-config.h" +#include "nm-setting-bluetooth.h" +#include "nm-setting-8021x.h" +#include "nm-utils.h" #include "nm-glib-compat.h" -#include "nm-logging.h" -#include "writer.h" -#include "common.h" -#include "utils.h" +#include "nm-keyfile-writer.h" +#include "nm-keyfile-utils.h" + +typedef struct { + NMConnection *connection; + GKeyFile *keyfile; + GError *error; + NMKeyfileWriteHandler handler; + void *user_data; +} KeyfileWriterInfo; + /* Some setting properties also contain setting names, such as * NMSettingConnection's 'type' property (which specifies the base type of the @@ -55,9 +63,7 @@ * from the real setting name to the more-readable alias. */ static void -setting_alias_writer (GKeyFile *file, - const char *keyfile_dir, - const char *uuid, +setting_alias_writer (KeyfileWriterInfo *info, NMSetting *setting, const char *key, const GValue *value) @@ -66,13 +72,13 @@ setting_alias_writer (GKeyFile *file, str = g_value_get_string (value); alias = nm_keyfile_plugin_get_alias_for_setting_name (str); - nm_keyfile_plugin_kf_set_string (file, + nm_keyfile_plugin_kf_set_string (info->keyfile, nm_setting_get_name (setting), key, alias ? alias : str); } -static gboolean +static void write_array_of_uint (GKeyFile *file, NMSetting *setting, const char *key, @@ -84,7 +90,7 @@ write_array_of_uint (GKeyFile *file, array = (GArray *) g_value_get_boxed (value); if (!array || !array->len) - return TRUE; + return; tmp_array = g_new (gint, array->len); for (i = 0; i < array->len; i++) @@ -92,13 +98,10 @@ write_array_of_uint (GKeyFile *file, nm_keyfile_plugin_kf_set_integer_list (file, nm_setting_get_name (setting), key, tmp_array, array->len); g_free (tmp_array); - return TRUE; } static void -dns_writer (GKeyFile *file, - const char *keyfile_dir, - const char *uuid, +dns_writer (KeyfileWriterInfo *info, NMSetting *setting, const char *key, const GValue *value) @@ -107,7 +110,7 @@ dns_writer (GKeyFile *file, list = g_value_get_boxed (value); if (list && list[0]) { - nm_keyfile_plugin_kf_set_string_list (file, nm_setting_get_name (setting), key, + nm_keyfile_plugin_kf_set_string_list (info->keyfile, nm_setting_get_name (setting), key, (const char **) list, g_strv_length (list)); } } @@ -178,9 +181,7 @@ write_ip_values (GKeyFile *file, } static void -addr_writer (GKeyFile *file, - const char *keyfile_dir, - const char *uuid, +addr_writer (KeyfileWriterInfo *info, NMSetting *setting, const char *key, const GValue *value) @@ -191,13 +192,11 @@ addr_writer (GKeyFile *file, array = (GPtrArray *) g_value_get_boxed (value); if (array && array->len) - write_ip_values (file, setting_name, array, gateway, FALSE); + write_ip_values (info->keyfile, setting_name, array, gateway, FALSE); } static void -ip4_addr_label_writer (GKeyFile *file, - const char *keyfile_dir, - const char *uuid, +ip4_addr_label_writer (KeyfileWriterInfo *info, NMSetting *setting, const char *key, const GValue *value) @@ -206,9 +205,7 @@ ip4_addr_label_writer (GKeyFile *file, } static void -gateway_writer (GKeyFile *file, - const char *keyfile_dir, - const char *uuid, +gateway_writer (KeyfileWriterInfo *info, NMSetting *setting, const char *key, const GValue *value) @@ -217,9 +214,7 @@ gateway_writer (GKeyFile *file, } static void -route_writer (GKeyFile *file, - const char *keyfile_dir, - const char *uuid, +route_writer (KeyfileWriterInfo *info, NMSetting *setting, const char *key, const GValue *value) @@ -229,7 +224,7 @@ route_writer (GKeyFile *file, array = (GPtrArray *) g_value_get_boxed (value); if (array && array->len) - write_ip_values (file, setting_name, array, NULL, TRUE); + write_ip_values (info->keyfile, setting_name, array, NULL, TRUE); } static void @@ -271,9 +266,7 @@ write_hash_of_string (GKeyFile *file, } static void -ssid_writer (GKeyFile *file, - const char *keyfile_dir, - const char *uuid, +ssid_writer (KeyfileWriterInfo *info, NMSetting *setting, const char *key, const GValue *value) @@ -323,21 +316,19 @@ ssid_writer (GKeyFile *file, ssid[j++] = ssid_data[i]; } } - nm_keyfile_plugin_kf_set_string (file, setting_name, key, ssid); + nm_keyfile_plugin_kf_set_string (info->keyfile, setting_name, key, ssid); g_free (ssid); } else { tmp_array = g_new (gint, ssid_len); for (i = 0; i < ssid_len; i++) tmp_array[i] = (int) ssid_data[i]; - nm_keyfile_plugin_kf_set_integer_list (file, setting_name, key, tmp_array, ssid_len); + nm_keyfile_plugin_kf_set_integer_list (info->keyfile, setting_name, key, tmp_array, ssid_len); g_free (tmp_array); } } static void -password_raw_writer (GKeyFile *file, - const char *keyfile_dir, - const char *uuid, +password_raw_writer (KeyfileWriterInfo *info, NMSetting *setting, const char *key, const GValue *value) @@ -360,7 +351,7 @@ password_raw_writer (GKeyFile *file, tmp_array = g_new (gint, len); for (i = 0; i < len; i++) tmp_array[i] = (int) data[i]; - nm_keyfile_plugin_kf_set_integer_list (file, setting_name, key, tmp_array, len); + nm_keyfile_plugin_kf_set_integer_list (info->keyfile, setting_name, key, tmp_array, len); g_free (tmp_array); } @@ -419,83 +410,15 @@ static const ObjectType objtypes[10] = { { NULL }, }; -static gboolean -write_cert_key_file (const char *path, - const guint8 *data, - gsize data_len, - GError **error) -{ - char *tmppath; - int fd = -1, written; - gboolean success = FALSE; - - tmppath = g_malloc0 (strlen (path) + 10); - g_assert (tmppath); - memcpy (tmppath, path, strlen (path)); - strcat (tmppath, ".XXXXXX"); - - errno = 0; - fd = mkstemp (tmppath); - if (fd < 0) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Could not create temporary file for '%s': %d", - path, errno); - goto out; - } - - /* Only readable by root */ - errno = 0; - if (fchmod (fd, S_IRUSR | S_IWUSR) != 0) { - close (fd); - unlink (tmppath); - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Could not set permissions for temporary file '%s': %d", - path, errno); - goto out; - } - - errno = 0; - written = write (fd, data, data_len); - if (written != data_len) { - close (fd); - unlink (tmppath); - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Could not write temporary file for '%s': %d", - path, errno); - goto out; - } - close (fd); - - /* Try to rename */ - errno = 0; - if (rename (tmppath, path) == 0) - success = TRUE; - else { - unlink (tmppath); - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Could not rename temporary file to '%s': %d", - path, errno); - } - -out: - g_free (tmppath); - return success; -} - static void -cert_writer (GKeyFile *file, - const char *keyfile_dir, - const char *uuid, +cert_writer (KeyfileWriterInfo *info, NMSetting *setting, const char *key, const GValue *value) { - const char *setting_name = nm_setting_get_name (setting); - NMSetting8021xCKScheme scheme; - NMSetting8021xCKFormat format; - const char *path = NULL, *ext = "pem"; const ObjectType *objtype = NULL; - int i; + guint i; + NMKeyfileWriteTypeDataCert type_data = { 0 }; for (i = 0; i < G_N_ELEMENTS (objtypes) && objtypes[i].key; i++) { if (g_strcmp0 (objtypes[i].key, key) == 0) { @@ -503,82 +426,48 @@ cert_writer (GKeyFile *file, break; } } - if (!objtype) { - g_return_if_fail (objtype); - return; - } - - scheme = objtype->scheme_func (NM_SETTING_802_1X (setting)); - if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) { - path = objtype->path_func (NM_SETTING_802_1X (setting)); - g_assert (path); - - /* If the path is rooted in the keyfile directory, just use a - * relative path instead of an absolute one. - */ - if (g_str_has_prefix (path, keyfile_dir)) { - path += strlen (keyfile_dir); - while (*path == '/') - path++; - } - - nm_keyfile_plugin_kf_set_string (file, setting_name, key, path); - } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { - GBytes *blob; - const guint8 *blob_data; - gsize blob_len; - gboolean success; - GError *error = NULL; - char *new_path; - - blob = objtype->blob_func (NM_SETTING_802_1X (setting)); - g_assert (blob); - blob_data = g_bytes_get_data (blob, &blob_len); - - if (objtype->format_func) { - /* Get the extension for a private key */ - format = objtype->format_func (NM_SETTING_802_1X (setting)); - if (format == NM_SETTING_802_1X_CK_FORMAT_PKCS12) - ext = "p12"; - } else { - /* DER or PEM format certificate? */ - if (blob_len > 2 && blob_data[0] == 0x30 && blob_data[1] == 0x82) - ext = "der"; - } - - /* Write the raw data out to the standard file so that we can use paths - * from now on instead of pushing around the certificate data. - */ - new_path = g_strdup_printf ("%s/%s-%s.%s", keyfile_dir, uuid, objtype->suffix, ext); - g_assert (new_path); - - success = write_cert_key_file (new_path, blob_data, blob_len, &error); - if (success) { - /* Write the path value to the keyfile */ - nm_keyfile_plugin_kf_set_string (file, setting_name, key, new_path); - } else { - nm_log_warn (LOGD_SETTINGS, "Failed to write certificate/key %s: %s", - new_path, error->message); - g_error_free (error); - } - g_free (new_path); - } else { - /* scheme_func() returns UNKNOWN in all other cases. The only valid case - * where a scheme is allowed to be UNKNOWN, is unsetting the value. In this - * case, we don't expect the writer to be called, because the default value - * will not be serialized. - * The only other reason for the scheme to be UNKNOWN is an invalid cert. - * But our connection verifies, so that cannot happen either. */ + if (!objtype) g_return_if_reached (); + + if (!info->handler) + goto out_unhandled; + + type_data.setting = NM_SETTING_802_1X (setting); + type_data.property_name = key; + type_data.suffix = objtype->suffix; + type_data.scheme_func = objtype->scheme_func; + type_data.format_func = objtype->format_func; + type_data.path_func = objtype->path_func; + type_data.blob_func = objtype->blob_func; + + if (info->handler (info->connection, + info->keyfile, + NM_KEYFILE_WRITE_TYPE_CERT, + &type_data, + info->user_data, + &info->error)) + return; + +out_unhandled: + + /* scheme_func() would not return UNKNOWN, because UNKNOWN happens only + * if the cert is unset (1) or if the cert is invalid (2). + * (1) cannot happen, because we only reach cert_writer() for non-default + * properties. (2) cannot happen, because we verified the connection. + * + * Hence, at this point we do have a certifiacte, but no default implementation + * to write it. The handler *must* do something with these certifications. */ + if (!info->error) { + g_set_error (&info->error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + _("Failed to write unhandled certificate property %s.%s"), + nm_setting_get_name (setting), key); } } typedef struct { const char *setting_name; const char *key; - void (*writer) (GKeyFile *keyfile, - const char *keyfile_dir, - const char *uuid, + void (*writer) (KeyfileWriterInfo *info, NMSetting *setting, const char *key, const GValue *value); @@ -648,12 +537,6 @@ static KeyWriter key_writers[] = { { NULL, NULL, NULL } }; -typedef struct { - GKeyFile *keyfile; - const char *keyfile_dir; - const char *uuid; -} WriteInfo; - static void write_setting_value (NMSetting *setting, const char *key, @@ -661,12 +544,15 @@ write_setting_value (NMSetting *setting, GParamFlags flag, gpointer user_data) { - WriteInfo *info = user_data; + KeyfileWriterInfo *info = user_data; const char *setting_name; GType type = G_VALUE_TYPE (value); KeyWriter *writer = &key_writers[0]; GParamSpec *pspec; + if (info->error) + return; + /* Setting name gets picked up from the keyfile's section name instead */ if (!strcmp (key, NM_SETTING_NAME)) return; @@ -704,7 +590,7 @@ write_setting_value (NMSetting *setting, /* Look through the list of handlers for non-standard format key values */ while (writer->setting_name) { if (!strcmp (writer->setting_name, setting_name) && !strcmp (writer->key, key)) { - (*writer->writer) (info->keyfile, info->keyfile_dir, info->uuid, setting, key, value); + (*writer->writer) (info, setting, key, value); return; } writer++; @@ -763,184 +649,42 @@ write_setting_value (NMSetting *setting, } else if (type == G_TYPE_HASH_TABLE) { write_hash_of_string (info->keyfile, setting, key, value); } else if (type == G_TYPE_ARRAY) { - if (!write_array_of_uint (info->keyfile, setting, key, value)) { - nm_log_warn (LOGD_SETTINGS, "Unhandled setting property type (write) '%s/%s' : '%s'", - setting_name, key, g_type_name (type)); - } + write_array_of_uint (info->keyfile, setting, key, value); } else if (G_VALUE_HOLDS_FLAGS (value)) { /* Flags are guint but GKeyFile has no uint reader, just uint64 */ nm_keyfile_plugin_kf_set_uint64 (info->keyfile, setting_name, key, (guint64) g_value_get_flags (value)); } else if (G_VALUE_HOLDS_ENUM (value)) nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (gint) g_value_get_enum (value)); - else { - nm_log_warn (LOGD_SETTINGS, "Unhandled setting property type (write) '%s/%s' : '%s'", - setting_name, key, g_type_name (type)); - } + else + g_warn_if_reached (); } -static gboolean -_internal_write_connection (NMConnection *connection, - const char *keyfile_dir, - uid_t owner_uid, - pid_t owner_grp, - const char *existing_path, - char **out_path, - GError **error) +GKeyFile * +nm_keyfile_write (NMConnection *connection, + NMKeyfileWriteHandler handler, + void *user_data, + GError **error) { - GKeyFile *key_file; - char *data; - gsize len; - gboolean success = FALSE; - char *path; - const char *id; - WriteInfo info; - GError *local_err = NULL; + KeyfileWriterInfo info = { 0 }; - g_return_val_if_fail (!out_path || !*out_path, FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + g_return_val_if_fail (!error || !*error, NULL); if (!nm_connection_verify (connection, error)) - g_return_val_if_reached (FALSE); + return NULL; - id = nm_connection_get_id (connection); - g_assert (id && *id); - - info.keyfile = key_file = g_key_file_new (); - info.keyfile_dir = keyfile_dir; - info.uuid = nm_connection_get_uuid (connection); - g_assert (info.uuid); + info.connection = connection; + info.keyfile = g_key_file_new (); + info.error = NULL; + info.handler = handler; + info.user_data = user_data; nm_connection_for_each_setting_value (connection, write_setting_value, &info); - data = g_key_file_to_data (key_file, &len, error); - if (!data) - goto out; - /* If we have existing file path, use it. Else generate one from - * connection's ID. - */ - if (existing_path != NULL) { - path = g_strdup (existing_path); - } else { - char *filename_escaped = nm_keyfile_plugin_utils_escape_filename (id); - - path = g_build_filename (keyfile_dir, filename_escaped, NULL); - g_free (filename_escaped); + if (info.error) { + g_propagate_error (error, info.error); + g_key_file_unref (info.keyfile); + return NULL; } - - /* If a file with this path already exists (but isn't the existing path - * of the connection) then we need another name. Multiple connections - * can have the same ID (ie if two connections with the same ID are visible - * to different users) but of course can't have the same path. Yeah, - * there's a race here, but there's not a lot we can do about it, and - * we shouldn't get more than one connection with the same UUID either. - */ - if (g_strcmp0 (path, existing_path) != 0 && g_file_test (path, G_FILE_TEST_EXISTS)) { - guint i; - gboolean name_found = FALSE; - - /* A keyfile with this connection's ID already exists. Pick another name. */ - for (i = 0; i < 100; i++) { - char *filename, *filename_escaped; - - if (i == 0) - filename = g_strdup_printf ("%s-%s", id, nm_connection_get_uuid (connection)); - else - filename = g_strdup_printf ("%s-%s-%u", id, nm_connection_get_uuid (connection), i); - - filename_escaped = nm_keyfile_plugin_utils_escape_filename (filename); - - g_free (path); - path = g_strdup_printf ("%s/%s", keyfile_dir, filename_escaped); - g_free (filename); - g_free (filename_escaped); - if (g_strcmp0 (path, existing_path) == 0 || !g_file_test (path, G_FILE_TEST_EXISTS)) { - name_found = TRUE; - break; - } - } - if (!name_found) { - if (existing_path == NULL) { - /* this really should not happen, we tried hard to find an unused name... bail out. */ - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "could not find suitable keyfile file name (%s already used)", path); - g_free (path); - goto out; - } - /* Both our preferred path based on connection id and id-uuid are taken. - * Fallback to @existing_path */ - g_free (path); - path = g_strdup (existing_path); - } - } - - /* In case of updating the connection and changing the file path, - * we need to remove the old one, not to end up with two connections. - */ - if (existing_path != NULL && strcmp (path, existing_path) != 0) - unlink (existing_path); - - g_file_set_contents (path, data, len, &local_err); - if (local_err) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "%s.%d: error writing to file '%s': %s", __FILE__, __LINE__, - path, local_err->message); - g_error_free (local_err); - g_free (path); - goto out; - } - - if (chown (path, owner_uid, owner_grp) < 0) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "%s.%d: error chowning '%s': %d", __FILE__, __LINE__, - path, errno); - unlink (path); - } else { - if (chmod (path, S_IRUSR | S_IWUSR) < 0) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "%s.%d: error setting permissions on '%s': %d", __FILE__, - __LINE__, path, errno); - unlink (path); - } else { - if (out_path && g_strcmp0 (existing_path, path)) { - *out_path = path; /* pass path out to caller */ - path = NULL; - } - success = TRUE; - } - } - g_free (path); - -out: - g_free (data); - g_key_file_free (key_file); - return success; -} - -gboolean -nm_keyfile_plugin_write_connection (NMConnection *connection, - const char *existing_path, - char **out_path, - GError **error) -{ - return _internal_write_connection (connection, - KEYFILE_DIR, - 0, 0, - existing_path, - out_path, - error); -} - -gboolean -nm_keyfile_plugin_write_test_connection (NMConnection *connection, - const char *keyfile_dir, - uid_t owner_uid, - pid_t owner_grp, - char **out_path, - GError **error) -{ - return _internal_write_connection (connection, - keyfile_dir, - owner_uid, owner_grp, - NULL, - out_path, - error); + return info.keyfile; } diff --git a/libnm-core/nm-keyfile-writer.h b/libnm-core/nm-keyfile-writer.h index a602f2f4a..891778a35 100644 --- a/libnm-core/nm-keyfile-writer.h +++ b/libnm-core/nm-keyfile-writer.h @@ -19,23 +19,73 @@ * Copyright (C) 2008 - 2011 Red Hat, Inc. */ -#ifndef _KEYFILE_PLUGIN_WRITER_H -#define _KEYFILE_PLUGIN_WRITER_H +#ifndef __NM_KEYFILE_WRITER_H__ +#define __NM_KEYFILE_WRITER_H__ #include #include -#include -gboolean nm_keyfile_plugin_write_connection (NMConnection *connection, - const char *existing_path, - char **out_path, - GError **error); +#include "nm-connection.h" +#include "nm-setting-8021x.h" -gboolean nm_keyfile_plugin_write_test_connection (NMConnection *connection, - const char *keyfile_dir, - uid_t owner_uid, - pid_t owner_grp, - char **out_path, - GError **error); -#endif /* _KEYFILE_PLUGIN_WRITER_H */ +typedef enum { + NM_KEYFILE_WRITE_TYPE_CERT = 1, +} NMKeyfileWriteType; + +/** + * NMKeyfileWriteHandler: + * + * This is a hook to tweak the serialization. + * + * Handler for certain properties or events that are not entirely contained + * within the keyfile or that might be serialized differently. The @type and + * @type_data arguments tell which kind of argument we have at hand. + * + * Currently only the type %NM_KEYFILE_WRITE_TYPE_CERT is supported, which provides + * @type_data as %NMKeyfileWriteTypeDataCert. However, this handler should be generic enough + * to support other types as well. + * + * This don't have to be only "properties". For example, nm_keyfile_read() uses + * a similar handler to push warnings to the caller. + * + * If the handler raises an error, it should set the @error value. This causes + * the an overall failure. + * + * Returns: whether the issue was handled. If the type was unhandled, + * a default action will be performed. This might be raise an error, + * do some fallback parsing, or do nothing. + */ +typedef gboolean (*NMKeyfileWriteHandler) (NMConnection *connection, + GKeyFile *keyfile, + NMKeyfileWriteType type, + void *type_data, + void *user_data, + GError **error); + +/** + * NMKeyfileWriteTypeDataCert: + * + * this struct is passed as @type_data for the @NMKeyfileWriteHandler of + * type %NM_KEYFILE_WRITE_TYPE_CERT. + */ +typedef struct { + NMSetting8021x *setting; + const char *property_name; + + /* The following functions are helpers that simplify the implementation + * of the handler. */ + const char *suffix; + NMSetting8021xCKScheme (*scheme_func) (NMSetting8021x *setting); + NMSetting8021xCKFormat (*format_func) (NMSetting8021x *setting); + const char * (*path_func) (NMSetting8021x *setting); + GBytes * (*blob_func) (NMSetting8021x *setting); +} NMKeyfileWriteTypeDataCert; + + +GKeyFile *nm_keyfile_write (NMConnection *connection, + NMKeyfileWriteHandler handler, + void *user_data, + GError **error); + +#endif /* __NM_KEYFILE_WRITER_H__ */ diff --git a/po/POTFILES.in b/po/POTFILES.in index 926dd6c39..6c09b21ff 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -48,6 +48,8 @@ libnm-core/crypto.c libnm-core/crypto_gnutls.c libnm-core/crypto_nss.c libnm-core/nm-connection.c +libnm-core/nm-keyfile-reader.c +libnm-core/nm-keyfile-writer.c libnm-core/nm-setting-8021x.c libnm-core/nm-setting-adsl.c libnm-core/nm-setting-bluetooth.c