summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2015-03-12 18:17:21 +0100
committerThomas Haller <thaller@redhat.com>2015-03-12 18:22:12 +0100
commit59eb5312a5d64fd4dd91b2348ab035506bfb12a4 (patch)
tree8ed96d29452bece679082017eb4f0f07ecdb1950
parent0429ed85addddaf96e231183efe2a2b9f5f653b3 (diff)
parent997fc07ca5b3bcafcdaa69dc7c7f6d8671efa4b4 (diff)
downloadNetworkManager-59eb5312a5d64fd4dd91b2348ab035506bfb12a4.tar.gz
keyfile: merge branch 'th/libnm-keyfile-bgo744699'
Move basic keyfile functionality from settings plugin to libnm-core. This is a first step to have a semi-standard way to stringify connections back and forth, which is also available to libnm users. Still the new functions are internal API (nm-keyfile-internal.h). Let's decide later how the public API should really look like. https://bugzilla.gnome.org/show_bug.cgi?id=744699
-rw-r--r--.gitignore1
-rw-r--r--clients/cli/settings.c11
-rw-r--r--include/nm-test-utils.h63
-rw-r--r--libnm-core/Makefile.libnm-core5
-rw-r--r--libnm-core/nm-core-internal.h6
-rw-r--r--libnm-core/nm-keyfile-internal.h166
-rw-r--r--libnm-core/nm-keyfile-reader.c1656
-rw-r--r--libnm-core/nm-keyfile-utils.c207
-rw-r--r--libnm-core/nm-keyfile-utils.h79
-rw-r--r--libnm-core/nm-keyfile-writer.c751
-rw-r--r--libnm-core/nm-setting-8021x.c199
-rw-r--r--libnm-core/nm-setting-8021x.h5
-rw-r--r--libnm-core/nm-utils.c125
-rw-r--r--libnm-core/tests/Makefile.am35
-rw-r--r--libnm-core/tests/certs/test-ca-cert.pem27
-rw-r--r--libnm-core/tests/certs/test-key-and-cert.pem118
-rw-r--r--libnm-core/tests/test-general.c181
-rw-r--r--libnm-core/tests/test-keyfile.c512
-rw-r--r--libnm-core/tests/test-setting-8021x.c6
-rw-r--r--libnm-util/nm-setting-8021x.c86
-rw-r--r--libnm/libnm.ver1
-rw-r--r--po/POTFILES.in2
-rw-r--r--src/NetworkManagerUtils.c117
-rw-r--r--src/NetworkManagerUtils.h6
-rw-r--r--src/dhcp-manager/nm-dhcp-listener.c3
-rw-r--r--src/platform/nm-linux-platform.c7
-rw-r--r--src/platform/nm-platform.c3
-rw-r--r--src/settings/plugins/ibft/reader.c18
-rw-r--r--src/settings/plugins/ifcfg-rh/reader.c6
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.c6
-rw-r--r--src/settings/plugins/ifnet/connection_parser.c8
-rw-r--r--src/settings/plugins/keyfile/common.h2
-rw-r--r--src/settings/plugins/keyfile/reader.c1333
-rw-r--r--src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Blob4
-rw-r--r--src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Old6
-rw-r--r--src/settings/plugins/keyfile/tests/test-keyfile.c54
-rw-r--r--src/settings/plugins/keyfile/utils.c175
-rw-r--r--src/settings/plugins/keyfile/utils.h51
-rw-r--r--src/settings/plugins/keyfile/writer.c736
-rw-r--r--src/settings/plugins/keyfile/writer.h1
-rw-r--r--src/tests/test-general.c178
41 files changed, 4336 insertions, 2620 deletions
diff --git a/.gitignore b/.gitignore
index 40448f92bc..397524e3f0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -143,6 +143,7 @@ valgrind-*.log
/libnm-core/tests/test-crypto
/libnm-core/tests/test-settings-defaults
/libnm-core/tests/test-general
+/libnm-core/tests/test-keyfile
/libnm-core/tests/test-need-secrets
/libnm-core/tests/test-secrets
/libnm-core/tests/test-setting-8021x
diff --git a/clients/cli/settings.c b/clients/cli/settings.c
index 47c7321756..7da47d43a8 100644
--- a/clients/cli/settings.c
+++ b/clients/cli/settings.c
@@ -29,6 +29,7 @@
#include "common.h"
#include "settings.h"
#include "nm-glib-compat.h"
+#include "nm-utils-internal.h"
/* Forward declarations */
static char *wep_key_type_to_string (NMWepKeyType type);
@@ -2691,13 +2692,12 @@ nmc_property_connection_describe_secondaries (NMSetting *setting, const char *pr
static gboolean \
def_func (NMSetting *setting, const char *prop, const char *val, GError **error) \
{ \
- const char *SCHEME_PATH = "file://"; \
char *val_strip = g_strstrip (g_strdup (val)); \
char *p = val_strip; \
gboolean success; \
\
- if (strncmp (val_strip, SCHEME_PATH, strlen (SCHEME_PATH)) == 0) \
- p += strlen (SCHEME_PATH); \
+ if (strncmp (val_strip, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH, STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)) == 0) \
+ p += STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH); \
\
success = set_func (NM_SETTING_802_1X (setting), \
p, \
@@ -2713,14 +2713,13 @@ nmc_property_connection_describe_secondaries (NMSetting *setting, const char *pr
def_func (NMSetting *setting, const char *prop, const char *val, GError **error) \
{ \
char **strv = NULL; \
- const char *SCHEME_PATH = "file://"; \
char *val_strip = g_strstrip (g_strdup (val)); \
char *p = val_strip; \
const char *path, *password; \
gboolean success; \
\
- if (strncmp (val_strip, SCHEME_PATH, strlen (SCHEME_PATH)) == 0) \
- p += strlen (SCHEME_PATH); \
+ if (strncmp (val_strip, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH, STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)) == 0) \
+ p += STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH); \
\
strv = nmc_strsplit_set (p, " \t,", 2); \
path = strv[0]; \
diff --git a/include/nm-test-utils.h b/include/nm-test-utils.h
index 7961b03891..187a0f1931 100644
--- a/include/nm-test-utils.h
+++ b/include/nm-test-utils.h
@@ -576,6 +576,39 @@ __nmtst_spawn_sync (const char *working_directory, char **standard_out, char **s
/*******************************************************************************/
+inline static char *
+nmtst_file_resolve_relative_path (const char *rel, const char *cwd)
+{
+ gs_free char *cwd_free = NULL;
+
+ g_assert (rel && *rel);
+
+ if (g_path_is_absolute (rel))
+ return g_strdup (rel);
+
+ if (!cwd)
+ cwd = cwd_free = g_get_current_dir ();
+ return g_build_filename (cwd, rel, NULL);
+}
+
+inline static void
+_nmtst_assert_resolve_relative_path_equals (const char *f1, const char *f2, const char *file, int line)
+{
+ gs_free char *p1 = NULL, *p2 = NULL;
+
+ p1 = nmtst_file_resolve_relative_path (f1, NULL);
+ p2 = nmtst_file_resolve_relative_path (f2, NULL);
+ g_assert (p1 && *p1);
+
+ /* Fixme: later we might need to coalesce repeated '/', "./", and "../".
+ * For now, it's good enough. */
+ if (g_strcmp0 (p1, p2) != 0)
+ g_error ("%s:%d : filenames don't match \"%s\" vs. \"%s\" // \"%s\" - \"%s\"", file, line, f1, f2, p1, p2);
+}
+#define nmtst_assert_resolve_relative_path_equals(f1, f2) _nmtst_assert_resolve_relative_path_equals (f1, f2, __FILE__, __LINE__);
+
+/*******************************************************************************/
+
#ifdef __NETWORKMANAGER_PLATFORM_H__
inline static NMPlatformIP6Address *
@@ -1028,6 +1061,36 @@ nmtst_assert_hwaddr_equals (gconstpointer hwaddr1, gssize hwaddr1_len, const cha
nmtst_assert_hwaddr_equals (hwaddr1, hwaddr1_len, expected, G_STRLOC)
#endif
+#if defined(__NM_SIMPLE_CONNECTION_H__) && defined(__NM_SETTING_CONNECTION_H__) && defined(__NM_KEYFILE_INTERNAL_H__)
+
+inline static NMConnection *
+nmtst_create_connection_from_keyfile (const char *keyfile_str, const char *keyfile_name, const char *base_dir)
+{
+ GKeyFile *keyfile;
+ GError *error = NULL;
+ gboolean success;
+ NMConnection *con;
+
+ g_assert (keyfile_str);
+
+ keyfile = g_key_file_new ();
+ success = g_key_file_load_from_data (keyfile, keyfile_str, strlen (keyfile_str), G_KEY_FILE_NONE, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+
+ con = nm_keyfile_read (keyfile, keyfile_name, base_dir, NULL, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (NM_IS_CONNECTION (con));
+
+ g_key_file_unref (keyfile);
+
+ nmtst_connection_normalize (con);
+
+ return con;
+}
+
+#endif
+
#ifdef __NM_CONNECTION_H__
typedef enum {
diff --git a/libnm-core/Makefile.libnm-core b/libnm-core/Makefile.libnm-core
index fc6d779b5e..09a80c714b 100644
--- a/libnm-core/Makefile.libnm-core
+++ b/libnm-core/Makefile.libnm-core
@@ -48,6 +48,8 @@ libnm_core_private_headers = \
$(core)/crypto.h \
$(core)/nm-connection-private.h \
$(core)/nm-core-internal.h \
+ $(core)/nm-keyfile-internal.h \
+ $(core)/nm-keyfile-utils.h \
$(core)/nm-property-compare.h \
$(core)/nm-setting-private.h \
$(core)/nm-utils-private.h
@@ -57,6 +59,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-core-internal.h b/libnm-core/nm-core-internal.h
index 5af90267a5..86a301bd1b 100644
--- a/libnm-core/nm-core-internal.h
+++ b/libnm-core/nm-core-internal.h
@@ -122,6 +122,10 @@ char ** _nm_utils_strsplit_set (const char *str,
char *nm_utils_uuid_generate_from_string (const char *s, gssize slen, int uuid_type, gpointer type_args);
+#define NM_UTILS_UUID_NS "b425e9fb-7598-44b4-9e3b-5a2e3aaa4905"
+
+char *_nm_utils_uuid_generate_from_strings (const char *string1, ...) G_GNUC_NULL_TERMINATED;
+
void _nm_dbus_errors_init (void);
extern gboolean _nm_utils_is_manager_process;
@@ -132,4 +136,6 @@ GByteArray *nm_utils_rsa_key_encrypt (const guint8 *data,
char **out_password,
GError **error);
+gint64 _nm_utils_ascii_str_to_int64 (const char *str, guint base, gint64 min, gint64 max, gint64 fallback);
+
#endif
diff --git a/libnm-core/nm-keyfile-internal.h b/libnm-core/nm-keyfile-internal.h
new file mode 100644
index 0000000000..90af562cdf
--- /dev/null
+++ b/libnm-core/nm-keyfile-internal.h
@@ -0,0 +1,166 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service - keyfile plugin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ * Copyright (C) 2015 Red Hat, Inc.
+ */
+
+#ifndef __NM_KEYFILE_INTERNAL_H__
+#define __NM_KEYFILE_INTERNAL_H__
+
+#include <glib.h>
+#include <sys/types.h>
+
+#include "nm-connection.h"
+#include "nm-setting-8021x.h"
+
+
+/*********************************************************/
+
+#define NM_KEYFILE_CERT_SCHEME_PREFIX_BLOB "data:;base64,"
+#define NM_KEYFILE_CERT_SCHEME_PREFIX_PATH "file://"
+
+char *nm_keyfile_detect_unqualified_path_scheme (const char *base_dir,
+ gconstpointer pdata,
+ gsize data_len,
+ gboolean consider_exists,
+ gboolean *out_exists);
+
+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_INFO_MISSING_FILE = 2901,
+ 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);
+
+/*********************************************************/
+
+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);
+
+/*********************************************************/
+
+char *nm_keyfile_plugin_kf_get_string (GKeyFile *kf, const char *group, const char *key, GError **error);
+void nm_keyfile_plugin_kf_set_string (GKeyFile *kf, const char *group, const char *key, const char *value);
+
+
+#endif /* __NM_KEYFILE_INTERNAL_H__ */
diff --git a/libnm-core/nm-keyfile-reader.c b/libnm-core/nm-keyfile-reader.c
new file mode 100644
index 0000000000..87c1e14e8c
--- /dev/null
+++ b/libnm-core/nm-keyfile-reader.c
@@ -0,0 +1,1656 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service - keyfile plugin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2008 - 2015 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <glib/gi18n-lib.h>
+
+#include "nm-core-internal.h"
+#include "nm-utils-internal.h"
+#include "gsystem-local-alloc.h"
+#include "nm-glib-compat.h"
+#include "nm-keyfile-internal.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
+ * connection, e.g. ethernet or wifi) or 'slave-type' (specifies type of slave
+ * connection, e.g. bond or bridge). This function handles translating those
+ * properties' values to the real setting name if they are an alias.
+ */
+static void
+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 (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),
+ key, key_setting_name ? key_setting_name : s,
+ NULL);
+ g_free (s);
+ }
+}
+
+static void
+read_array_of_uint (GKeyFile *file,
+ NMSetting *setting,
+ const char *key)
+{
+ GArray *array = NULL;
+ gsize length;
+ int i;
+ gint *tmp;
+
+ 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);
+
+ for (i = 0; i < length; i++)
+ g_array_append_val (array, tmp[i]);
+
+ g_object_set (setting, key, array, NULL);
+ g_array_unref (array);
+}
+
+static gboolean
+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 (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 (property_name)
+ handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN,
+ _("ignoring invalid number '%s'"),
+ str);
+ return FALSE;
+ }
+
+ *out = (guint32) tmp;
+ return TRUE;
+}
+
+static gpointer
+build_address (KeyfileReaderInfo *info, int family, const char *address_str, guint32 plen, const char *property_name)
+{
+ NMIPAddress *addr;
+ GError *error = NULL;
+
+ g_return_val_if_fail (address_str, NULL);
+
+ addr = nm_ip_address_new (family, address_str, plen, &error);
+ if (!addr) {
+ 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);
+ }
+
+ return addr;
+}
+
+static gpointer
+build_route (KeyfileReaderInfo *info,
+ const char *property_name,
+ int family,
+ const char *dest_str, guint32 plen,
+ const char *gateway_str, const char *metric_str)
+{
+ NMIPRoute *route;
+ guint32 metric = 0;
+ GError *error = NULL;
+
+ g_return_val_if_fail (plen, NULL);
+ g_return_val_if_fail (dest_str, NULL);
+
+ /* Next hop */
+ if (gateway_str && gateway_str[0]) {
+ if (!nm_utils_ipaddr_valid (family, gateway_str)) {
+ /* Try workaround for routes written by broken keyfile writer.
+ * Due to bug bgo#719851, an older version of writer would have
+ * written "a:b:c:d::/plen,metric" if the gateway was ::, instead
+ * of "a:b:c:d::/plen,,metric" or "a:b:c:d::/plen,::,metric"
+ * Try workaround by interpreting gateway_str as metric to accept such
+ * invalid routes. This broken syntax should not be not officially
+ * supported.
+ **/
+ if ( family == AF_INET6
+ && !metric_str
+ && get_one_int (NULL, NULL, gateway_str, G_MAXUINT32, &metric))
+ gateway_str = NULL;
+ else {
+ 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;
+ }
+ }
+ } else
+ gateway_str = NULL;
+
+ /* parse metric, default to 0 */
+ if (metric_str) {
+ if (!get_one_int (info, property_name, metric_str, G_MAXUINT32, &metric))
+ return NULL;
+ }
+
+ route = nm_ip_route_new (family, dest_str, plen, gateway_str,
+ metric ? (gint64) metric : -1,
+ &error);
+ if (!route) {
+ 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);
+ }
+
+ return route;
+}
+
+/* On success, returns pointer to the zero-terminated field (original @current).
+ * The @current * pointer target is set to point to the rest of the input
+ * or %NULL if there is no more input. Sets error to %NULL for convenience.
+ *
+ * On failure, returns %NULL (unspecified). The @current pointer target is
+ * resets to its original value to allow skipping fields. The @error target
+ * is set to the character that breaks the parsing or %NULL if @current was %NULL.
+ *
+ * When @current target is %NULL, gracefully fail returning %NULL while
+ * leaving the @current target %NULL end setting @error to %NULL;
+ */
+static char *
+read_field (char **current, char **error, const char *characters, const char *delimiters)
+{
+ char *start;
+
+ g_return_val_if_fail (current, NULL);
+ g_return_val_if_fail (error, NULL);
+ g_return_val_if_fail (characters, NULL);
+ g_return_val_if_fail (delimiters, NULL);
+
+ *error = NULL;
+
+ if (!*current) {
+ /* graceful failure, leave '*current' NULL */
+ return NULL;
+ }
+
+ /* fail on empty input */
+ if (!**current)
+ return NULL;
+
+ /* remember beginning of input */
+ start = *current;
+
+ while (**current && strchr (characters, **current))
+ (*current)++;
+ if (**current)
+ if (strchr (delimiters, **current)) {
+ /* success, more data available */
+ *(*current)++ = '\0';
+ return start;
+ } else {
+ /* error, bad character */
+ *error = *current;
+ *current = start;
+ return NULL;
+ }
+ else {
+ /* success, end of input */
+ *current = NULL;
+ return start;
+ }
+}
+
+#define IP_ADDRESS_CHARS "0123456789abcdefABCDEF:.%"
+#define DIGITS "0123456789"
+#define DELIMITERS "/;,"
+
+
+/* The following IPv4 and IPv6 address formats are supported:
+ *
+ * address (DEPRECATED)
+ * address/plen
+ * address/gateway (DEPRECATED)
+ * address/plen,gateway
+ *
+ * The following IPv4 and IPv6 route formats are supported:
+ *
+ * address/plen (NETWORK dev DEVICE)
+ * address/plen,gateway (NETWORK via GATEWAY dev DEVICE)
+ * address/plen,,metric (NETWORK dev DEVICE metric METRIC)
+ * address/plen,gateway,metric (NETWORK via GATEWAY dev DEVICE metric METRIC)
+ *
+ * For backward, forward and sideward compatibility, slash (/),
+ * semicolon (;) and comma (,) are interchangable. The choice of
+ * separator in the above examples is therefore not significant.
+ *
+ * Leaving out the prefix length is discouraged and DEPRECATED. The
+ * default value of IPv6 prefix length was 64 and has not been
+ * changed. The default for IPv4 is now 24, which is the closest
+ * IPv4 equivalent. These defaults may just as well be changed to
+ * match the iproute2 defaults (32 for IPv4 and 128 for IPv6).
+ */
+static gpointer
+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,
+ NMSetting *setting)
+{
+ guint32 plen = G_MAXUINT32;
+ gpointer result;
+ char *address_str, *plen_str, *gateway_str, *metric_str, *current, *error;
+ gs_free char *value = NULL, *value_orig = 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 (&current, &error, IP_ADDRESS_CHARS, DELIMITERS);
+ if (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 (&current, &error, DIGITS, DELIMITERS);
+ /* get gateway field */
+ gateway_str = read_field (&current, &error, IP_ADDRESS_CHARS, DELIMITERS);
+ if (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 (&current, &error, DIGITS, DELIMITERS);
+ if (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;
+ if (current) {
+ /* there is still some data */
+ if (*current) {
+ /* another field follows */
+ 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 */
+ 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;
+ }
+ }
+
+#define DEFAULT_PREFIX(for_route, for_ipv6) ( (for_route) ? ( (for_ipv6) ? 128 : 24 ) : ( (for_ipv6) ? 64 : 24 ) )
+
+ /* parse plen, fallback to defaults */
+ if (plen_str) {
+ if (!get_one_int (info, property_name, plen_str, ipv6 ? 128 : 32, &plen)
+ || (route && plen == 0)) {
+ plen = DEFAULT_PREFIX (route, ipv6);
+ 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);
+ 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 (info, property_name,
+ ipv6 ? AF_INET6 : AF_INET,
+ address_str, plen, gateway_str, metric_str);
+ } else {
+ 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);
+ }
+
+#undef VALUE_ORIG
+
+ return result;
+}
+
+static void
+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");
+ gboolean routes = !strcmp (key, "routes");
+ static const char *key_names_routes[] = { "route", "routes", NULL };
+ static const char *key_names_addresses[] = { "address", "addresses", NULL };
+ const char **key_names = routes ? key_names_routes : key_names_addresses;
+ char *gateway = NULL;
+ GPtrArray *list;
+ GDestroyNotify free_func;
+ int i;
+
+ if (routes)
+ free_func = (GDestroyNotify) nm_ip_route_unref;
+ else
+ free_func = (GDestroyNotify) nm_ip_address_unref;
+ list = g_ptr_array_new_with_free_func (free_func);
+
+ for (i = -1; i < 1000; i++) {
+ const char **key_basename;
+
+ for (key_basename = key_names; *key_basename; key_basename++) {
+ char *key_name;
+ gpointer item;
+
+ /* -1 means no suffix */
+ if (i >= 0)
+ key_name = g_strdup_printf ("%s%d", *key_basename, i);
+ else
+ key_name = g_strdup (*key_basename);
+
+ 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);
+
+ }
+ }
+
+ if (list->len >= 1)
+ g_object_set (setting, key, list, NULL);
+
+ if (gateway) {
+ g_object_set (setting, "gateway", gateway, NULL);
+ g_free (gateway);
+ }
+
+ g_ptr_array_unref (list);
+}
+
+static void
+ip4_dns_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
+{
+ const char *setting_name = nm_setting_get_name (setting);
+ GPtrArray *array;
+ gsize length;
+ char **list, **iter;
+ int ret;
+
+ list = nm_keyfile_plugin_kf_get_string_list (info->keyfile, setting_name, key, &length, NULL);
+ if (!list || !g_strv_length (list))
+ return;
+
+ array = g_ptr_array_sized_new (length + 1);
+ for (iter = list; *iter; iter++) {
+ guint32 addr;
+
+ ret = inet_pton (AF_INET, *iter, &addr);
+ if (ret <= 0) {
+ 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;
+ }
+
+ g_ptr_array_add (array, *iter);
+ }
+ g_ptr_array_add (array, NULL);
+
+ g_object_set (setting, key, array->pdata, NULL);
+ g_ptr_array_unref (array);
+ g_strfreev (list);
+}
+
+static void
+ip6_dns_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
+{
+ const char *setting_name = nm_setting_get_name (setting);
+ GPtrArray *array = NULL;
+ gsize length;
+ char **list, **iter;
+ int ret;
+
+ list = nm_keyfile_plugin_kf_get_string_list (info->keyfile, setting_name, key, &length, NULL);
+ if (!list || !g_strv_length (list))
+ return;
+
+ array = g_ptr_array_sized_new (length + 1);
+
+ for (iter = list; *iter; iter++) {
+ struct in6_addr addr;
+
+ ret = inet_pton (AF_INET6, *iter, &addr);
+ if (ret <= 0) {
+ 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;
+ }
+
+ g_ptr_array_add (array, *iter);
+ }
+ g_ptr_array_add (array, NULL);
+
+ g_object_set (setting, key, array->pdata, NULL);
+ g_ptr_array_unref (array);
+ g_strfreev (list);
+}
+
+static void
+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;
+ gint *tmp_list;
+ GByteArray *array = NULL;
+ gsize length;
+
+ 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;
+
+ while (*p) {
+ if (*p == ':')
+ i++;
+ p++;
+ }
+
+ if (enforce_length == 0 || enforce_length == i+1) {
+ /* If we found enough it's probably a string-format MAC address */
+ array = g_byte_array_sized_new (i+1);
+ g_byte_array_set_size (array, i+1);
+ if (!nm_utils_hwaddr_aton (tmp_string, array->data, array->len)) {
+ g_byte_array_unref (array);
+ array = NULL;
+ }
+ }
+ }
+ g_free (tmp_string);
+
+ if (array == NULL) {
+ /* Old format; list of ints */
+ 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;
+
+ array = g_byte_array_sized_new (length);
+ for (i = 0; i < length; i++) {
+ int val = tmp_list[i];
+ const guint8 v = (guint8) (val & 0xFF);
+
+ if (val < 0 || val > 255) {
+ 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);
+ g_free (tmp_list);
+ return;
+ }
+ g_byte_array_append (array, &v, 1);
+ }
+ }
+ g_free (tmp_list);
+ }
+
+ if (!array) {
+ handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+ _("ignoring invalid MAC address"));
+ return;
+ }
+
+ mac_str = nm_utils_hwaddr_ntoa (array->data, array->len);
+ g_object_set (setting, key, mac_str, NULL);
+ g_free (mac_str);
+ g_byte_array_free (array, TRUE);
+}
+
+static void
+mac_address_parser_ETHER (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
+{
+ mac_address_parser (info, setting, key, ETH_ALEN);
+}
+
+static void
+mac_address_parser_INFINIBAND (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
+{
+ mac_address_parser (info, setting, key, INFINIBAND_ALEN);
+}
+
+static void
+read_hash_of_string (GKeyFile *file, NMSetting *setting, const char *key)
+{
+ char **keys, **iter;
+ char *value;
+ const char *setting_name = nm_setting_get_name (setting);
+
+ keys = nm_keyfile_plugin_kf_get_keys (file, setting_name, NULL, NULL);
+ if (!keys || !*keys)
+ return;
+
+ for (iter = keys; *iter; iter++) {
+ value = nm_keyfile_plugin_kf_get_string (file, setting_name, *iter, NULL);
+ if (!value)
+ continue;
+
+ if (NM_IS_SETTING_VPN (setting)) {
+ /* Add any item that's not a class property to the data hash */
+ if (!g_object_class_find_property (G_OBJECT_GET_CLASS (setting), *iter))
+ nm_setting_vpn_add_data_item (NM_SETTING_VPN (setting), *iter, value);
+ }
+ if (NM_IS_SETTING_BOND (setting)) {
+ if (strcmp (*iter, "interface-name"))
+ nm_setting_bond_add_option (NM_SETTING_BOND (setting), *iter, value);
+ }
+ g_free (value);
+ }
+ g_strfreev (keys);
+}
+
+static void
+unescape_semicolons (char *str)
+{
+ int i;
+ gsize len = strlen (str);
+
+ for (i = 0; i < len; i++) {
+ if (str[i] == '\\' && str[i+1] == ';') {
+ memmove(str + i, str + i + 1, len - (i + 1));
+ len--;
+ }
+ str[len] = '\0';
+ }
+}
+
+static GBytes *
+get_bytes (KeyfileReaderInfo *info,
+ const char *setting_name,
+ const char *key,
+ gboolean zero_terminate,
+ gboolean unescape_semicolon)
+{
+ GByteArray *array = NULL;
+ char *tmp_string;
+ gint *tmp_list;
+ gsize length;
+ int i;
+
+ 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 (info->keyfile, setting_name, key, NULL);
+ if (tmp_string) {
+ GRegex *regex;
+ GMatchInfo *match_info;
+ const char *pattern = "^[[:space:]]*[[:digit:]]{1,3}[[:space:]]*;([[:space:]]*[[:digit:]]{1,3}[[:space:]]*;)*([[:space:]]*)?$";
+
+ regex = g_regex_new (pattern, 0, 0, NULL);
+ g_regex_match (regex, tmp_string, 0, &match_info);
+ if (!g_match_info_matches (match_info)) {
+ /* Handle as a simple string (ie, new format) */
+ if (unescape_semicolon)
+ unescape_semicolons (tmp_string);
+ length = strlen (tmp_string);
+ if (zero_terminate)
+ length++;
+ array = g_byte_array_sized_new (length);
+ g_byte_array_append (array, (guint8 *) tmp_string, length);
+ }
+ g_match_info_free (match_info);
+ g_regex_unref (regex);
+ g_free (tmp_string);
+ }
+
+ if (!array) {
+ gboolean already_warned = FALSE;
+
+ /* Old format; list of ints */
+ tmp_list = nm_keyfile_plugin_kf_get_integer_list (info->keyfile, setting_name, key, &length, NULL);
+ if (!tmp_list) {
+ handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+ _("ignoring invalid binary property"));
+ return NULL;
+ }
+ array = g_byte_array_sized_new (length);
+ for (i = 0; i < length; i++) {
+ int val = tmp_list[i];
+ unsigned char v = (unsigned char) (val & 0xFF);
+
+ if (val < 0 || val > 255) {
+ 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));
+ }
+ g_free (tmp_list);
+ }
+
+ if (array->len == 0) {
+ g_byte_array_free (array, TRUE);
+ return NULL;
+ } else
+ return g_byte_array_free_to_bytes (array);
+}
+
+static void
+ssid_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
+{
+ const char *setting_name = nm_setting_get_name (setting);
+ GBytes *bytes;
+
+ bytes = get_bytes (info, setting_name, key, FALSE, TRUE);
+ if (bytes) {
+ g_object_set (setting, key, bytes, NULL);
+ g_bytes_unref (bytes);
+ } else if (!info->error) {
+ handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+ _("ignoring invalid SSID"));
+ }
+}
+
+static void
+password_raw_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
+{
+ const char *setting_name = nm_setting_get_name (setting);
+ GBytes *bytes;
+
+ bytes = get_bytes (info, setting_name, key, FALSE, TRUE);
+ if (bytes) {
+ g_object_set (setting, key, bytes, NULL);
+ g_bytes_unref (bytes);
+ } else if (!info->error) {
+ handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+ _("ignoring invalid raw password"));
+ }
+}
+
+static char *
+get_cert_path (const char *base_dir, const guint8 *cert_path, gsize cert_path_len)
+{
+ const char *base;
+ char *p = NULL, *path, *tmp;
+
+ 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);
+ memcpy (path, cert_path, cert_path_len);
+
+ if (path[0] == '/')
+ return path;
+
+ p = strrchr (path, '/');
+ if (p)
+ base = p + 1;
+
+ tmp = g_build_path ("/", base_dir, base, NULL);
+ g_free (path);
+ return tmp;
+}
+
+static const char *certext[] = { ".pem", ".cert", ".crt", ".cer", ".p12", ".der", ".key" };
+
+static gboolean
+has_cert_ext (const char *path)
+{
+ int i;
+
+ for (i = 0; i < G_N_ELEMENTS (certext); i++) {
+ if (g_str_has_suffix (path, certext[i]))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean
+handle_as_scheme (KeyfileReaderInfo *info, GBytes *bytes, NMSetting *setting, const char *key)
+{
+ const char *data;
+ gsize data_len, bin_len;
+
+ data = g_bytes_get_data (bytes, &data_len);
+
+ g_return_val_if_fail (data && data_len > 0, FALSE);
+
+ /* to be a scheme, @data must be a zero terminated string, which is counted by @data_len */
+ if (data[data_len - 1] != '\0')
+ return FALSE;
+ data_len--;
+
+ /* It's the PATH scheme, can just set plain data.
+ * In this case, @data_len includes */
+ if ( data_len >= STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH)
+ && g_str_has_prefix (data, NM_KEYFILE_CERT_SCHEME_PREFIX_PATH)) {
+ if (nm_setting_802_1x_check_cert_scheme (data, data_len + 1, NULL) == NM_SETTING_802_1X_CK_SCHEME_PATH) {
+ const char *path = &data[STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH)];
+ gs_free char *path_free = NULL;
+
+ if (path[0] != '/') {
+ /* we want to read absolute paths because we use keyfile as exchange
+ * between different processes which might not have the same cwd. */
+ path = path_free = get_cert_path (info->base_dir, (const guint8 *) path,
+ data_len - STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH));
+ }
+
+ g_object_set (setting, key, bytes, NULL);
+ if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
+ handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_INFO_MISSING_FILE,
+ _("certificate or key file '%s' does not exist"),
+ path);
+ }
+ } else {
+ handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+ _("invalid key/cert value path \"%s\""), data);
+ }
+ return TRUE;
+ }
+ if ( data_len > STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_BLOB)
+ && g_str_has_prefix (data, NM_KEYFILE_CERT_SCHEME_PREFIX_BLOB)) {
+ const char *cdata = data + STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_BLOB);
+ guchar *bin;
+ GBytes *bytes2;
+ gsize i;
+ gboolean valid_base64;
+
+ data_len -= STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_BLOB);
+
+ /* Let's be strict here. We expect valid base64, no funny stuff!!
+ * We didn't write such invalid data ourselfes and refuse to read it as blob. */
+ if ((valid_base64 = (data_len % 4 == 0))) {
+ for (i = 0; i < data_len; i++) {
+ char c = cdata[i];
+
+ if (!( (c >= 'a' && c <= 'z')
+ || (c >= 'A' && c <= 'Z')
+ || (c >= '0' && c <= '9')
+ || (c == '+' || c == '/'))) {
+ if (c != '=' || i < data_len - 2)
+ valid_base64 = FALSE;
+ else {
+ for (; i < data_len; i++) {
+ if (cdata[i] != '=')
+ valid_base64 = FALSE;
+ }
+ }
+ break;
+ }
+ }
+ }
+ if (!valid_base64) {
+ handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+ _("invalid key/cert value data:;base64, is not base64"));
+ return TRUE;
+ }
+
+ bin = g_base64_decode (cdata, &bin_len);
+
+ g_return_val_if_fail (bin_len > 0, FALSE);
+ if (nm_setting_802_1x_check_cert_scheme (bin, bin_len, NULL) != NM_SETTING_802_1X_CK_SCHEME_BLOB) {
+ /* The blob probably starts with "file://". Setting the cert data will confuse NMSetting8021x.
+ * In fact this is a limitation of NMSetting8021x which does not support setting blobs that start
+ * with file://. Just warn and return TRUE to signal that we ~handled~ the setting. */
+ g_free (bin);
+ handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+ _("invalid key/cert value data:;base64,file://"));
+ } else {
+ bytes2 = g_bytes_new_take (bin, bin_len);
+ g_object_set (setting, key, bytes2, NULL);
+ g_bytes_unref (bytes2);
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+char *
+nm_keyfile_detect_unqualified_path_scheme (const char *base_dir,
+ gconstpointer pdata,
+ gsize data_len,
+ gboolean consider_exists,
+ gboolean *out_exists)
+{
+ const char *data = pdata;
+ gboolean exists = FALSE;
+ gboolean success = FALSE;
+ gsize validate_len;
+ char *path;
+ GByteArray *tmp;
+
+ g_return_val_if_fail (base_dir && base_dir[0] == '/', NULL);
+
+ if (!pdata)
+ return NULL;
+ if (data_len == -1)
+ data_len = strlen (data);
+ if (data_len > 500 || data_len < 1)
+ return NULL;
+
+ /* If there's a trailing zero tell g_utf8_validate() to validate until the zero */
+ if (data[data_len - 1] == '\0') {
+ /* setting it to -1, would mean we accept data to contain NUL characters before the
+ * end. Don't accept any NUL in [0 .. data_len-1[ . */
+ validate_len = data_len - 1;
+ } else
+ validate_len = data_len;
+ if ( validate_len == 0
+ || g_utf8_validate ((const char *) data, validate_len, NULL) == FALSE)
+ return NULL;
+
+ /* Might be a bare path without the file:// prefix; in that case
+ * if it's an absolute path, use that, otherwise treat it as a
+ * relative path to the current directory.
+ */
+
+ path = get_cert_path (base_dir, (const guint8 *) data, data_len);
+ if ( !memchr (data, '/', data_len)
+ && !has_cert_ext (path)) {
+ if (!consider_exists)
+ goto out;
+ exists = g_file_test (path, G_FILE_TEST_EXISTS);
+ if (!exists)
+ goto out;
+ } else if (out_exists)
+ exists = g_file_test (path, G_FILE_TEST_EXISTS);
+
+ /* Construct the proper value as required for the PATH scheme */
+ tmp = g_byte_array_sized_new (strlen (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH) + strlen (path) + 1);
+ g_byte_array_append (tmp, (const guint8 *) NM_KEYFILE_CERT_SCHEME_PREFIX_PATH, strlen (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH));
+ g_byte_array_append (tmp, (const guint8 *) path, strlen (path) + 1);
+ if (nm_setting_802_1x_check_cert_scheme (tmp->data, tmp->len, NULL) == NM_SETTING_802_1X_CK_SCHEME_PATH) {
+ g_free (path);
+ path = (char *) g_byte_array_free (tmp, FALSE);
+ /* when returning TRUE, we must also be sure that @data_len does not look like
+ * the deprecated format of list of integers. With this implementation that is the
+ * case, as long as @consider_exists is FALSE. */
+ success = TRUE;
+ } else
+ g_byte_array_unref (tmp);
+
+out:
+ if (!success) {
+ g_free (path);
+ return NULL;
+ }
+ if (out_exists)
+ *out_exists = exists;
+ return path;
+}
+
+static gboolean
+handle_as_path (KeyfileReaderInfo *info,
+ GBytes *bytes,
+ NMSetting *setting,
+ const char *key)
+{
+ const guint8 *data;
+ gsize data_len;
+ char *path;
+ gboolean exists = FALSE;
+ GBytes *val;
+
+ data = g_bytes_get_data (bytes, &data_len);
+
+ path = nm_keyfile_detect_unqualified_path_scheme (info->base_dir, data, data_len, TRUE, &exists);
+ if (!path)
+ return FALSE;
+
+ /* Construct the proper value as required for the PATH scheme */
+ val = g_bytes_new_take (path, strlen (path) + 1);
+ g_object_set (setting, key, val, NULL);
+
+ /* Warn if the certificate didn't exist */
+ if (!exists) {
+ handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_INFO_MISSING_FILE,
+ _("certificate or key file '%s' does not exist"),
+ path);
+ }
+ g_bytes_unref (val);
+
+ return TRUE;
+}
+
+static void
+cert_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
+{
+ const char *setting_name = nm_setting_get_name (setting);
+ gs_unref_bytes GBytes *bytes = NULL;
+ gsize bin_len;
+ const char *bin;
+
+ bytes = get_bytes (info, setting_name, key, TRUE, FALSE);
+ if (bytes) {
+ /* Try as a path + scheme (ie, starts with "file://") */
+ if (handle_as_scheme (info, bytes, setting, key))
+ return;
+ if (info->error)
+ return;
+
+ /* If not, it might be a plain path */
+ if (handle_as_path (info, bytes, setting, key))
+ return;
+ if (info->error)
+ return;
+
+ bin = g_bytes_get_data (bytes, &bin_len);
+ if (nm_setting_802_1x_check_cert_scheme (bin, bin_len, NULL) != NM_SETTING_802_1X_CK_SCHEME_BLOB) {
+ /* The blob probably starts with "file://" but contains invalid characters for a path.
+ * Setting the cert data will confuse NMSetting8021x.
+ * In fact, NMSetting8021x does not support setting such binary data, so just warn and
+ * continue. */
+ handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+ _("invalid key/cert value is not a valid blob"));
+ } else
+ g_object_set (setting, key, bytes, NULL);
+ } else if (!info->error) {
+ handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+ _("invalid key/cert value"));
+ }
+}
+
+static void
+parity_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
+{
+ const char *setting_name = nm_setting_get_name (setting);
+ NMSettingSerialParity parity;
+ int int_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 (info->keyfile, setting_name, key, NULL);
+ if (!int_val) {
+ 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];
+ else {
+ /* This will hit the warning below */
+ int_val = 'X';
+ }
+ }
+ }
+
+ if (!int_val)
+ return;
+
+ switch (int_val) {
+ case 'E':
+ case 'e':
+ parity = NM_SETTING_SERIAL_PARITY_EVEN;
+ break;
+ case 'O':
+ case 'o':
+ parity = NM_SETTING_SERIAL_PARITY_ODD;
+ break;
+ case 'N':
+ case 'n':
+ parity = NM_SETTING_SERIAL_PARITY_NONE;
+ break;
+ default:
+ handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+ _("invalid parity value '%s'"),
+ str_val ? str_val : "");
+ return;
+ }
+
+ g_object_set (setting, key, parity, NULL);
+}
+
+typedef struct {
+ const char *setting_name;
+ const char *key;
+ gboolean check_for_key;
+ void (*parser) (KeyfileReaderInfo *info, NMSetting *setting, const char *key);
+} KeyParser;
+
+/* A table of keys that require further parsing/conversion because they are
+ * stored in a format that can't be automatically read using the key's type.
+ * i.e. IPv4 addresses, which are stored in NetworkManager as guint32, but are
+ * stored in keyfiles as strings, eg "10.1.1.2" or IPv6 addresses stored
+ * in struct in6_addr internally, but as string in keyfiles.
+ */
+static KeyParser key_parsers[] = {
+ { NM_SETTING_CONNECTION_SETTING_NAME,
+ NM_SETTING_CONNECTION_TYPE,
+ TRUE,
+ setting_alias_parser },
+ { NM_SETTING_BRIDGE_SETTING_NAME,
+ NM_SETTING_BRIDGE_MAC_ADDRESS,
+ TRUE,
+ mac_address_parser_ETHER },
+ { NM_SETTING_IP4_CONFIG_SETTING_NAME,
+ NM_SETTING_IP_CONFIG_ADDRESSES,
+ FALSE,
+ ip_address_or_route_parser },
+ { NM_SETTING_IP6_CONFIG_SETTING_NAME,
+ NM_SETTING_IP_CONFIG_ADDRESSES,
+ FALSE,
+ ip_address_or_route_parser },
+ { NM_SETTING_IP4_CONFIG_SETTING_NAME,
+ NM_SETTING_IP_CONFIG_ROUTES,
+ FALSE,
+ ip_address_or_route_parser },
+ { NM_SETTING_IP6_CONFIG_SETTING_NAME,
+ NM_SETTING_IP_CONFIG_ROUTES,
+ FALSE,
+ ip_address_or_route_parser },
+ { NM_SETTING_IP4_CONFIG_SETTING_NAME,
+ NM_SETTING_IP_CONFIG_DNS,
+ FALSE,
+ ip4_dns_parser },
+ { NM_SETTING_IP6_CONFIG_SETTING_NAME,
+ NM_SETTING_IP_CONFIG_DNS,
+ FALSE,
+ ip6_dns_parser },
+ { NM_SETTING_WIRED_SETTING_NAME,
+ NM_SETTING_WIRED_MAC_ADDRESS,
+ TRUE,
+ mac_address_parser_ETHER },
+ { NM_SETTING_WIRED_SETTING_NAME,
+ NM_SETTING_WIRED_CLONED_MAC_ADDRESS,
+ TRUE,
+ mac_address_parser_ETHER },
+ { NM_SETTING_WIRELESS_SETTING_NAME,
+ NM_SETTING_WIRELESS_MAC_ADDRESS,
+ TRUE,
+ mac_address_parser_ETHER },
+ { NM_SETTING_WIRELESS_SETTING_NAME,
+ NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS,
+ TRUE,
+ mac_address_parser_ETHER },
+ { NM_SETTING_WIRELESS_SETTING_NAME,
+ NM_SETTING_WIRELESS_BSSID,
+ TRUE,
+ mac_address_parser_ETHER },
+ { NM_SETTING_BLUETOOTH_SETTING_NAME,
+ NM_SETTING_BLUETOOTH_BDADDR,
+ TRUE,
+ mac_address_parser_ETHER },
+ { NM_SETTING_INFINIBAND_SETTING_NAME,
+ NM_SETTING_INFINIBAND_MAC_ADDRESS,
+ TRUE,
+ mac_address_parser_INFINIBAND },
+ { NM_SETTING_WIMAX_SETTING_NAME,
+ NM_SETTING_WIMAX_MAC_ADDRESS,
+ TRUE,
+ mac_address_parser_ETHER },
+ { NM_SETTING_WIRELESS_SETTING_NAME,
+ NM_SETTING_WIRELESS_SSID,
+ TRUE,
+ ssid_parser },
+ { NM_SETTING_802_1X_SETTING_NAME,
+ NM_SETTING_802_1X_PASSWORD_RAW,
+ TRUE,
+ password_raw_parser },
+ { NM_SETTING_802_1X_SETTING_NAME,
+ NM_SETTING_802_1X_CA_CERT,
+ TRUE,
+ cert_parser },
+ { NM_SETTING_802_1X_SETTING_NAME,
+ NM_SETTING_802_1X_CLIENT_CERT,
+ TRUE,
+ cert_parser },
+ { NM_SETTING_802_1X_SETTING_NAME,
+ NM_SETTING_802_1X_PRIVATE_KEY,
+ TRUE,
+ cert_parser },
+ { NM_SETTING_802_1X_SETTING_NAME,
+ NM_SETTING_802_1X_PHASE2_CA_CERT,
+ TRUE,
+ cert_parser },
+ { NM_SETTING_802_1X_SETTING_NAME,
+ NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
+ TRUE,
+ cert_parser },
+ { NM_SETTING_802_1X_SETTING_NAME,
+ NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
+ TRUE,
+ cert_parser },
+ { NM_SETTING_SERIAL_SETTING_NAME,
+ NM_SETTING_SERIAL_PARITY,
+ TRUE,
+ parity_parser },
+ { NULL, NULL, FALSE }
+};
+
+static void
+read_one_setting_value (NMSetting *setting,
+ const char *key,
+ const GValue *value,
+ GParamFlags flags,
+ gpointer user_data)
+{
+ KeyfileReaderInfo *info = user_data;
+ GKeyFile *keyfile = info->keyfile;
+ const char *setting_name;
+ int errsv;
+ GType type;
+ 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;
+
+ /* Setting name gets picked up from the keyfile's section name instead */
+ if (!strcmp (key, NM_SETTING_NAME))
+ return;
+
+ /* Don't read the NMSettingConnection object's 'read-only' property */
+ if ( NM_IS_SETTING_CONNECTION (setting)
+ && !strcmp (key, NM_SETTING_CONNECTION_READ_ONLY))
+ return;
+
+ setting_name = nm_setting_get_name (setting);
+
+ /* Look through the list of handlers for non-standard format key values */
+ while (parser->setting_name) {
+ if (!strcmp (parser->setting_name, setting_name) && !strcmp (parser->key, key)) {
+ check_for_key = parser->check_for_key;
+ break;
+ }
+ parser++;
+ }
+
+ /* VPN properties don't have the exact key name */
+ if (NM_IS_SETTING_VPN (setting))
+ check_for_key = FALSE;
+
+ /* Bonding 'options' don't have the exact key name. The options are right under [bond] group. */
+ if (NM_IS_SETTING_BOND (setting))
+ check_for_key = FALSE;
+
+ /* Check for the exact key in the GKeyFile if required. Most setting
+ * properties map 1:1 to a key in the GKeyFile, but for those properties
+ * 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 (keyfile, setting_name, key, &err)) {
+ /* Key doesn't exist or an error ocurred, thus nothing to do. */
+ if (err) {
+ if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+ _("error loading setting value: %s"),
+ err->message))
+ goto out_error;
+ }
+ return;
+ }
+
+ /* If there's a custom parser for this key, handle that before the generic
+ * parsers below.
+ */
+ if (parser->setting_name) {
+ (*parser->parser) (info, setting, key);
+ return;
+ }
+
+ type = G_VALUE_TYPE (value);
+
+ if (type == G_TYPE_STRING) {
+ char *str_val;
+
+ 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 (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 (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 (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 (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 (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) {
+ gs_free char *tmp_str = NULL;
+ gint64 int_val;
+
+ 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) {
+ 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);
+ } 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 (keyfile, setting_name, key, &length, NULL);
+
+ array = g_byte_array_sized_new (length);
+ for (i = 0; i < length; i++) {
+ int val = tmp[i];
+ unsigned char v = (unsigned char) (val & 0xFF);
+
+ if (val < 0 || val > 255) {
+ 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));
+ }
+
+ bytes = g_byte_array_free_to_bytes (array);
+ g_object_set (setting, key, bytes, NULL);
+ g_bytes_unref (bytes);
+ g_free (tmp);
+ } else if (type == G_TYPE_STRV) {
+ gchar **sa;
+ gsize length;
+
+ 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 (keyfile, setting, key);
+ } else if (type == G_TYPE_ARRAY) {
+ 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 (keyfile, setting_name, key, &err);
+ if (!err) {
+ if (uint_val <= G_MAXUINT)
+ g_object_set (setting, key, (guint) uint_val, NULL);
+ else {
+ 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;
+ }
+ }
+ } else if (G_VALUE_HOLDS_ENUM (value)) {
+ gint int_val;
+
+ int_val = nm_keyfile_plugin_kf_get_integer (keyfile, setting_name, key, &err);
+ if (!err)
+ g_object_set (setting, key, (gint) int_val, NULL);
+ } else {
+ 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 (KeyfileReaderInfo *info)
+{
+ const char *alias;
+ GType type;
+
+ alias = nm_keyfile_plugin_get_setting_name_for_alias (info->group);
+ if (!alias)
+ alias = info->group;
+
+ type = nm_setting_lookup_type (alias);
+ if (type) {
+ NMSetting *setting = g_object_new (type, NULL);
+
+ 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 (KeyfileReaderInfo *info, NMSettingVpn *s_vpn)
+{
+ char **keys, **iter;
+
+ 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 (info->keyfile, VPN_SECRETS_GROUP, *iter, NULL);
+ if (secret) {
+ nm_setting_vpn_add_secret (s_vpn, *iter, secret);
+ g_free (secret);
+ }
+ }
+ 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_read (GKeyFile *keyfile,
+ const char *keyfile_name,
+ const char *base_dir,
+ NMKeyfileReadHandler handler,
+ void *user_data,
+ GError **error)
+{
+ NMConnection *connection = NULL;
+ NMSettingConnection *s_con;
+ NMSetting *setting;
+ gchar **groups;
+ gsize length;
+ int i;
+ gboolean vpn_secrets = FALSE;
+ KeyfileReaderInfo info = { 0 };
+ gs_free char *base_dir_free = NULL;
+
+ g_return_val_if_fail (keyfile, NULL);
+ g_return_val_if_fail (!error || !*error, NULL);
+
+ 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 ();
+
+ 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)) {
+ vpn_secrets = TRUE;
+ continue;
+ }
+
+ 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) {
+ s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
+ nm_connection_add_setting (connection, NM_SETTING (s_con));
+ }
+
+ /* Make sure that we have 'id' even if not explictly specified in the keyfile */
+ if ( keyfile_name
+ && !nm_setting_connection_get_id (s_con)) {
+ char *base_name;
+
+ 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 ( keyfile_name
+ && !nm_setting_connection_get_uuid (s_con)) {
+ char *hashed_uuid;
+
+ 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);
+ }
+
+ /* Make sure that we have 'interface-name' even if it was specified in the
+ * "wrong" (ie, deprecated) group.
+ */
+ if ( !nm_setting_connection_get_interface_name (s_con)
+ && nm_setting_connection_get_connection_type (s_con)) {
+ char *interface_name;
+
+ interface_name = g_key_file_get_string (keyfile,
+ nm_setting_connection_get_connection_type (s_con),
+ "interface-name",
+ NULL);
+ if (interface_name) {
+ g_object_set (s_con, NM_SETTING_CONNECTION_INTERFACE_NAME, interface_name, NULL);
+ g_free (interface_name);
+ }
+ }
+
+ /* Handle vpn secrets after the 'vpn' setting was read */
+ if (vpn_secrets) {
+ NMSettingVpn *s_vpn;
+
+ s_vpn = nm_connection_get_setting_vpn (connection);
+ if (s_vpn) {
+ read_vpn_secrets (&info, s_vpn);
+ if (info.error)
+ goto out_error;
+ }
+ }
+
+ return connection;
+out_error:
+ g_propagate_error (error, info.error);
+ g_free (connection);
+ return NULL;
+}
diff --git a/libnm-core/nm-keyfile-utils.c b/libnm-core/nm-keyfile-utils.c
new file mode 100644
index 0000000000..61b30ab9c8
--- /dev/null
+++ b/libnm-core/nm-keyfile-utils.c
@@ -0,0 +1,207 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * 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.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "nm-keyfile-utils.h"
+#include "nm-keyfile-internal.h"
+#include "nm-setting-wired.h"
+#include "nm-setting-wireless.h"
+#include "nm-setting-wireless-security.h"
+
+
+typedef struct {
+ const char *setting;
+ const char *alias;
+} SettingAlias;
+
+static const SettingAlias alias_list[] = {
+ { NM_SETTING_WIRED_SETTING_NAME, "ethernet" },
+ { NM_SETTING_WIRELESS_SETTING_NAME, "wifi" },
+ { NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, "wifi-security" },
+};
+
+const char *
+nm_keyfile_plugin_get_alias_for_setting_name (const char *setting_name)
+{
+ guint i;
+
+ g_return_val_if_fail (setting_name != NULL, NULL);
+
+ for (i = 0; i < G_N_ELEMENTS (alias_list); i++) {
+ if (strcmp (setting_name, alias_list[i].setting) == 0)
+ return alias_list[i].alias;
+ }
+ return NULL;
+}
+
+const char *
+nm_keyfile_plugin_get_setting_name_for_alias (const char *alias)
+{
+ guint i;
+
+ g_return_val_if_fail (alias != NULL, NULL);
+
+ for (i = 0; i < G_N_ELEMENTS (alias_list); i++) {
+ if (strcmp (alias, alias_list[i].alias) == 0)
+ return alias_list[i].setting;
+ }
+ return NULL;
+}
+
+/**********************************************************************/
+
+/* List helpers */
+#define DEFINE_KF_LIST_WRAPPER(stype, get_ctype, set_ctype) \
+get_ctype \
+nm_keyfile_plugin_kf_get_##stype##_list (GKeyFile *kf, \
+ const char *group, \
+ const char *key, \
+ gsize *out_length, \
+ GError **error) \
+{ \
+ get_ctype list; \
+ const char *alias; \
+ GError *local = NULL; \
+ \
+ list = g_key_file_get_##stype##_list (kf, group, key, out_length, &local); \
+ if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) { \
+ alias = nm_keyfile_plugin_get_alias_for_setting_name (group); \
+ if (alias) { \
+ g_clear_error (&local); \
+ list = g_key_file_get_##stype##_list (kf, alias, key, out_length, &local); \
+ } \
+ } \
+ if (local) \
+ g_propagate_error (error, local); \
+ return list; \
+} \
+ \
+void \
+nm_keyfile_plugin_kf_set_##stype##_list (GKeyFile *kf, \
+ const char *group, \
+ const char *key, \
+ set_ctype list[], \
+ gsize length) \
+{ \
+ const char *alias; \
+ \
+ alias = nm_keyfile_plugin_get_alias_for_setting_name (group); \
+ g_key_file_set_##stype##_list (kf, alias ? alias : group, key, list, length); \
+}
+
+DEFINE_KF_LIST_WRAPPER(integer, gint*, gint);
+DEFINE_KF_LIST_WRAPPER(string, gchar **, const gchar* const);
+
+/* Single value helpers */
+#define DEFINE_KF_WRAPPER(stype, get_ctype, set_ctype) \
+get_ctype \
+nm_keyfile_plugin_kf_get_##stype (GKeyFile *kf, \
+ const char *group, \
+ const char *key, \
+ GError **error) \
+{ \
+ get_ctype val; \
+ const char *alias; \
+ GError *local = NULL; \
+ \
+ val = g_key_file_get_##stype (kf, group, key, &local); \
+ if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) { \
+ alias = nm_keyfile_plugin_get_alias_for_setting_name (group); \
+ if (alias) { \
+ g_clear_error (&local); \
+ val = g_key_file_get_##stype (kf, alias, key, &local); \
+ } \
+ } \
+ if (local) \
+ g_propagate_error (error, local); \
+ return val; \
+} \
+ \
+void \
+nm_keyfile_plugin_kf_set_##stype (GKeyFile *kf, \
+ const char *group, \
+ const char *key, \
+ set_ctype value) \
+{ \
+ const char *alias; \
+ \
+ alias = nm_keyfile_plugin_get_alias_for_setting_name (group); \
+ g_key_file_set_##stype (kf, alias ? alias : group, key, value); \
+}
+
+DEFINE_KF_WRAPPER(string, gchar*, const gchar*);
+DEFINE_KF_WRAPPER(integer, gint, gint);
+DEFINE_KF_WRAPPER(uint64, guint64, guint64);
+DEFINE_KF_WRAPPER(boolean, gboolean, gboolean);
+DEFINE_KF_WRAPPER(value, gchar*, const gchar*);
+
+
+gchar **
+nm_keyfile_plugin_kf_get_keys (GKeyFile *kf,
+ const char *group,
+ gsize *out_length,
+ GError **error)
+{
+ gchar **keys;
+ const char *alias;
+ GError *local = NULL;
+
+ keys = g_key_file_get_keys (kf, group, out_length, &local);
+ if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) {
+ alias = nm_keyfile_plugin_get_alias_for_setting_name (group);
+ if (alias) {
+ g_clear_error (&local);
+ keys = g_key_file_get_keys (kf, alias, out_length, &local);
+ }
+ }
+ if (local)
+ g_propagate_error (error, local);
+ return keys;
+}
+
+gboolean
+nm_keyfile_plugin_kf_has_key (GKeyFile *kf,
+ const char *group,
+ const char *key,
+ GError **error)
+{
+ gboolean has;
+ const char *alias;
+ GError *local = NULL;
+
+ has = g_key_file_has_key (kf, group, key, &local);
+ if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) {
+ alias = nm_keyfile_plugin_get_alias_for_setting_name (group);
+ if (alias) {
+ g_clear_error (&local);
+ has = g_key_file_has_key (kf, alias, key, &local);
+ }
+ }
+ if (local)
+ g_propagate_error (error, local);
+ return has;
+}
+
+
diff --git a/libnm-core/nm-keyfile-utils.h b/libnm-core/nm-keyfile-utils.h
new file mode 100644
index 0000000000..fd4334d112
--- /dev/null
+++ b/libnm-core/nm-keyfile-utils.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * 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-2015 Red Hat, Inc.
+ */
+
+#ifndef __NM_KEYFILE_UTILS_H__
+#define __NM_KEYFILE_UTILS_H__
+
+#include <glib.h>
+
+#define VPN_SECRETS_GROUP "vpn-secrets"
+
+const char *nm_keyfile_plugin_get_alias_for_setting_name (const char *setting_name);
+
+const char *nm_keyfile_plugin_get_setting_name_for_alias (const char *alias);
+
+/*********************************************************/
+
+/* List helpers */
+#define DEFINE_KF_LIST_WRAPPER_PROTO(stype, get_ctype, set_ctype) \
+get_ctype nm_keyfile_plugin_kf_get_##stype##_list (GKeyFile *kf, \
+ const char *group, \
+ const char *key, \
+ gsize *out_length, \
+ GError **error); \
+\
+void nm_keyfile_plugin_kf_set_##stype##_list (GKeyFile *kf, \
+ const char *group, \
+ const char *key, \
+ set_ctype list[], \
+ gsize length);
+DEFINE_KF_LIST_WRAPPER_PROTO(integer, gint*, gint)
+DEFINE_KF_LIST_WRAPPER_PROTO(string, gchar**, const gchar* const)
+
+/* Single-value helpers */
+#define DEFINE_KF_WRAPPER_PROTO(stype, get_ctype, set_ctype) \
+get_ctype nm_keyfile_plugin_kf_get_##stype (GKeyFile *kf, \
+ const char *group, \
+ const char *key, \
+ GError **error); \
+\
+void nm_keyfile_plugin_kf_set_##stype (GKeyFile *kf, \
+ const char *group, \
+ const char *key, \
+ set_ctype value);
+DEFINE_KF_WRAPPER_PROTO(string, gchar*, const gchar*)
+DEFINE_KF_WRAPPER_PROTO(integer, gint, gint)
+DEFINE_KF_WRAPPER_PROTO(uint64, guint64, guint64)
+DEFINE_KF_WRAPPER_PROTO(boolean, gboolean, gboolean)
+DEFINE_KF_WRAPPER_PROTO(value, gchar*, const gchar*)
+
+/* Misc */
+gchar ** nm_keyfile_plugin_kf_get_keys (GKeyFile *kf,
+ const char *group,
+ gsize *out_length,
+ GError **error);
+
+gboolean nm_keyfile_plugin_kf_has_key (GKeyFile *kf,
+ const char *group,
+ const char *key,
+ GError **error);
+
+#endif /* __NM_KEYFILE_UTILS_H__ */
+
diff --git a/libnm-core/nm-keyfile-writer.c b/libnm-core/nm-keyfile-writer.c
new file mode 100644
index 0000000000..ff433b264a
--- /dev/null
+++ b/libnm-core/nm-keyfile-writer.c
@@ -0,0 +1,751 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service - keyfile plugin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ * Copyright (C) 2008 - 2015 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <glib/gi18n-lib.h>
+
+#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 "gsystem-local-alloc.h"
+#include "nm-glib-compat.h"
+#include "nm-keyfile-internal.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
+ * connection, eg ethernet or wifi) or the 802-11-wireless setting's
+ * 'security' property which specifies whether or not the AP requires
+ * encrpytion. This function handles translating those properties' values
+ * from the real setting name to the more-readable alias.
+ */
+static void
+setting_alias_writer (KeyfileWriterInfo *info,
+ NMSetting *setting,
+ const char *key,
+ const GValue *value)
+{
+ const char *str, *alias;
+
+ str = g_value_get_string (value);
+ alias = nm_keyfile_plugin_get_alias_for_setting_name (str);
+ nm_keyfile_plugin_kf_set_string (info->keyfile,
+ nm_setting_get_name (setting),
+ key,
+ alias ? alias : str);
+}
+
+static void
+write_array_of_uint (GKeyFile *file,
+ NMSetting *setting,
+ const char *key,
+ const GValue *value)
+{
+ GArray *array;
+ int i;
+ int *tmp_array;
+
+ array = (GArray *) g_value_get_boxed (value);
+ if (!array || !array->len)
+ return;
+
+ tmp_array = g_new (gint, array->len);
+ for (i = 0; i < array->len; i++)
+ tmp_array[i] = g_array_index (array, int, i);
+
+ nm_keyfile_plugin_kf_set_integer_list (file, nm_setting_get_name (setting), key, tmp_array, array->len);
+ g_free (tmp_array);
+}
+
+static void
+dns_writer (KeyfileWriterInfo *info,
+ NMSetting *setting,
+ const char *key,
+ const GValue *value)
+{
+ char **list;
+
+ list = g_value_get_boxed (value);
+ if (list && list[0]) {
+ nm_keyfile_plugin_kf_set_string_list (info->keyfile, nm_setting_get_name (setting), key,
+ (const char **) list, g_strv_length (list));
+ }
+}
+
+static void
+write_ip_values (GKeyFile *file,
+ const char *setting_name,
+ GPtrArray *array,
+ const char *gateway,
+ gboolean is_route)
+{
+ GString *output;
+ int family, i;
+ const char *addr, *gw;
+ guint32 plen, metric;
+ char key_name[30], *key_name_idx;
+
+ if (!array->len)
+ return;
+
+ family = !strcmp (setting_name, NM_SETTING_IP4_CONFIG_SETTING_NAME) ? AF_INET : AF_INET6;
+
+ strcpy (key_name, is_route ? "route" : "address");
+ key_name_idx = key_name + strlen (key_name);
+
+ output = g_string_sized_new (2*INET_ADDRSTRLEN + 10);
+ for (i = 0; i < array->len; i++) {
+ if (is_route) {
+ NMIPRoute *route = array->pdata[i];
+
+ addr = nm_ip_route_get_dest (route);
+ plen = nm_ip_route_get_prefix (route);
+ gw = nm_ip_route_get_next_hop (route);
+ metric = MAX (0, nm_ip_route_get_metric (route));
+ } else {
+ NMIPAddress *address = array->pdata[i];
+
+ addr = nm_ip_address_get_address (address);
+ plen = nm_ip_address_get_prefix (address);
+ gw = i == 0 ? gateway : NULL;
+ metric = 0;
+ }
+
+ g_string_set_size (output, 0);
+ g_string_append_printf (output, "%s/%u", addr, plen);
+ if (metric || gw) {
+ /* Older versions of the plugin do not support the form
+ * "a.b.c.d/plen,,metric", so, we always have to write the
+ * gateway, even if there isn't one.
+ * The current version supports reading of the above form.
+ */
+ if (!gw) {
+ if (family == AF_INET)
+ gw = "0.0.0.0";
+ else
+ gw = "::";
+ }
+
+ g_string_append_printf (output, ",%s", gw);
+ if (metric)
+ g_string_append_printf (output, ",%lu", (unsigned long) metric);
+ }
+
+ sprintf (key_name_idx, "%d", i + 1);
+ nm_keyfile_plugin_kf_set_string (file, setting_name, key_name, output->str);
+ }
+ g_string_free (output, TRUE);
+}
+
+static void
+addr_writer (KeyfileWriterInfo *info,
+ NMSetting *setting,
+ const char *key,
+ const GValue *value)
+{
+ GPtrArray *array;
+ const char *setting_name = nm_setting_get_name (setting);
+ const char *gateway = nm_setting_ip_config_get_gateway (NM_SETTING_IP_CONFIG (setting));
+
+ array = (GPtrArray *) g_value_get_boxed (value);
+ if (array && array->len)
+ write_ip_values (info->keyfile, setting_name, array, gateway, FALSE);
+}
+
+static void
+ip4_addr_label_writer (KeyfileWriterInfo *info,
+ NMSetting *setting,
+ const char *key,
+ const GValue *value)
+{
+ /* skip */
+}
+
+static void
+gateway_writer (KeyfileWriterInfo *info,
+ NMSetting *setting,
+ const char *key,
+ const GValue *value)
+{
+ /* skip */
+}
+
+static void
+route_writer (KeyfileWriterInfo *info,
+ NMSetting *setting,
+ const char *key,
+ const GValue *value)
+{
+ GPtrArray *array;
+ const char *setting_name = nm_setting_get_name (setting);
+
+ array = (GPtrArray *) g_value_get_boxed (value);
+ if (array && array->len)
+ write_ip_values (info->keyfile, setting_name, array, NULL, TRUE);
+}
+
+static void
+write_hash_of_string (GKeyFile *file,
+ NMSetting *setting,
+ const char *key,
+ const GValue *value)
+{
+ GHashTableIter iter;
+ const char *property = NULL, *data = NULL;
+ const char *group_name = nm_setting_get_name (setting);
+ gboolean vpn_secrets = FALSE;
+
+ /* Write VPN secrets out to a different group to keep them separate */
+ if (NM_IS_SETTING_VPN (setting) && !strcmp (key, NM_SETTING_VPN_SECRETS)) {
+ group_name = VPN_SECRETS_GROUP;
+ vpn_secrets = TRUE;
+ }
+
+ g_hash_table_iter_init (&iter, (GHashTable *) g_value_get_boxed (value));
+ while (g_hash_table_iter_next (&iter, (gpointer *) &property, (gpointer *) &data)) {
+ gboolean write_item = TRUE;
+
+ /* Handle VPN secrets specially; they are nested in the property's hash;
+ * we don't want to write them if the secret is not saved, not required,
+ * or owned by a user's secret agent.
+ */
+ if (vpn_secrets) {
+ NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
+
+ nm_setting_get_secret_flags (setting, property, &secret_flags, NULL);
+ if (secret_flags != NM_SETTING_SECRET_FLAG_NONE)
+ write_item = FALSE;
+ }
+
+ if (write_item)
+ nm_keyfile_plugin_kf_set_string (file, group_name, property, data);
+ }
+}
+
+static void
+ssid_writer (KeyfileWriterInfo *info,
+ NMSetting *setting,
+ const char *key,
+ const GValue *value)
+{
+ GBytes *bytes;
+ const guint8 *ssid_data;
+ gsize ssid_len;
+ const char *setting_name = nm_setting_get_name (setting);
+ gboolean new_format = TRUE;
+ unsigned int semicolons = 0;
+ int i, *tmp_array;
+ char *ssid;
+
+ g_return_if_fail (G_VALUE_HOLDS (value, G_TYPE_BYTES));
+
+ bytes = g_value_get_boxed (value);
+ if (!bytes)
+ return;
+ ssid_data = g_bytes_get_data (bytes, &ssid_len);
+ if (ssid_len == 0)
+ return;
+
+ /* Check whether each byte is printable. If not, we have to use an
+ * integer list, otherwise we can just use a string.
+ */
+ for (i = 0; i < ssid_len; i++) {
+ char c = ssid_data[i] & 0xFF;
+ if (!g_ascii_isprint (c)) {
+ new_format = FALSE;
+ break;
+ }
+ if (c == ';')
+ semicolons++;
+ }
+
+ if (new_format) {
+ ssid = g_malloc0 (ssid_len + semicolons + 1);
+ if (semicolons == 0)
+ memcpy (ssid, ssid_data, ssid_len);
+ else {
+ /* Escape semicolons with backslashes to make strings
+ * containing ';', such as '16;17;' unambiguous */
+ int j = 0;
+ for (i = 0; i < ssid_len; i++) {
+ if (ssid_data[i] == ';')
+ ssid[j++] = '\\';
+ ssid[j++] = ssid_data[i];
+ }
+ }
+ 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 (info->keyfile, setting_name, key, tmp_array, ssid_len);
+ g_free (tmp_array);
+ }
+}
+
+static void
+password_raw_writer (KeyfileWriterInfo *info,
+ NMSetting *setting,
+ const char *key,
+ const GValue *value)
+{
+ const char *setting_name = nm_setting_get_name (setting);
+ GBytes *array;
+ int *tmp_array;
+ gsize i, len;
+ const char *data;
+
+ g_return_if_fail (G_VALUE_HOLDS (value, G_TYPE_BYTES));
+
+ array = (GBytes *) g_value_get_boxed (value);
+ if (!array)
+ return;
+ data = g_bytes_get_data (array, &len);
+ if (!data || !len)
+ return;
+
+ 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 (info->keyfile, setting_name, key, tmp_array, len);
+ g_free (tmp_array);
+}
+
+typedef struct ObjectType {
+ const char *key;
+ const char *suffix;
+ NMSetting8021xCKScheme (*scheme_func) (NMSetting8021x *setting);
+ NMSetting8021xCKFormat (*format_func) (NMSetting8021x *setting);
+ const char * (*path_func) (NMSetting8021x *setting);
+ GBytes * (*blob_func) (NMSetting8021x *setting);
+} ObjectType;
+
+static const ObjectType objtypes[10] = {
+ { NM_SETTING_802_1X_CA_CERT,
+ "ca-cert",
+ nm_setting_802_1x_get_ca_cert_scheme,
+ NULL,
+ nm_setting_802_1x_get_ca_cert_path,
+ nm_setting_802_1x_get_ca_cert_blob },
+
+ { NM_SETTING_802_1X_PHASE2_CA_CERT,
+ "inner-ca-cert",
+ nm_setting_802_1x_get_phase2_ca_cert_scheme,
+ NULL,
+ nm_setting_802_1x_get_phase2_ca_cert_path,
+ nm_setting_802_1x_get_phase2_ca_cert_blob },
+
+ { NM_SETTING_802_1X_CLIENT_CERT,
+ "client-cert",
+ nm_setting_802_1x_get_client_cert_scheme,
+ NULL,
+ nm_setting_802_1x_get_client_cert_path,
+ nm_setting_802_1x_get_client_cert_blob },
+
+ { NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
+ "inner-client-cert",
+ nm_setting_802_1x_get_phase2_client_cert_scheme,
+ NULL,
+ nm_setting_802_1x_get_phase2_client_cert_path,
+ nm_setting_802_1x_get_phase2_client_cert_blob },
+
+ { NM_SETTING_802_1X_PRIVATE_KEY,
+ "private-key",
+ nm_setting_802_1x_get_private_key_scheme,
+ nm_setting_802_1x_get_private_key_format,
+ nm_setting_802_1x_get_private_key_path,
+ nm_setting_802_1x_get_private_key_blob },
+
+ { NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
+ "inner-private-key",
+ nm_setting_802_1x_get_phase2_private_key_scheme,
+ nm_setting_802_1x_get_phase2_private_key_format,
+ nm_setting_802_1x_get_phase2_private_key_path,
+ nm_setting_802_1x_get_phase2_private_key_blob },
+
+ { NULL },
+};
+
+/**************************************************************************/
+
+static void
+cert_writer_default (NMConnection *connection,
+ GKeyFile *file,
+ NMKeyfileWriteTypeDataCert *cert_data)
+{
+ const char *setting_name = nm_setting_get_name (NM_SETTING (cert_data->setting));
+ NMSetting8021xCKScheme scheme;
+
+ scheme = cert_data->scheme_func (cert_data->setting);
+ if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) {
+ const char *path;
+ char *path_free = NULL, *tmp;
+ gs_free char *base_dir = NULL;
+
+ path = cert_data->path_func (cert_data->setting);
+ g_assert (path);
+
+ /* If the path is relative, make it an absolute path.
+ * Relative paths make a keyfile not easily usable in another
+ * context. */
+ if (path[0] && path[0] != '/') {
+ base_dir = g_get_current_dir ();
+ path = path_free = g_strconcat (base_dir, "/", path, NULL);
+ } else
+ base_dir = g_path_get_dirname (path);
+
+ /* path cannot start with "file://" or "data:;base64,", because it is an absolute path.
+ * Still, make sure that a prefix-less path will be recognized. This can happen
+ * for example if the path is longer then 500 chars. */
+ tmp = nm_keyfile_detect_unqualified_path_scheme (base_dir, path, -1, FALSE, NULL);
+ if (tmp)
+ g_clear_pointer (&tmp, g_free);
+ else
+ path = tmp = g_strconcat (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH, path, NULL);
+
+ /* Path contains at least a '/', hence it cannot be recognized as the old
+ * binary format consisting of a list of integers. */
+
+ nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->property_name, path);
+ g_free (tmp);
+ g_free (path_free);
+ } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
+ GBytes *blob;
+ const guint8 *blob_data;
+ gsize blob_len;
+ char *blob_base64, *val;
+
+ blob = cert_data->blob_func (cert_data->setting);
+ g_assert (blob);
+ blob_data = g_bytes_get_data (blob, &blob_len);
+
+ blob_base64 = g_base64_encode (blob_data, blob_len);
+ val = g_strconcat (NM_KEYFILE_CERT_SCHEME_PREFIX_BLOB, blob_base64, NULL);
+
+ nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->property_name, val);
+ g_free (val);
+ g_free (blob_base64);
+ } 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. */
+ g_return_if_reached ();
+ }
+}
+
+static void
+cert_writer (KeyfileWriterInfo *info,
+ NMSetting *setting,
+ const char *key,
+ const GValue *value)
+{
+ const ObjectType *objtype = NULL;
+ 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) {
+ objtype = &objtypes[i];
+ break;
+ }
+ }
+ if (!objtype)
+ g_return_if_reached ();
+
+ 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) {
+ if (info->handler (info->connection,
+ info->keyfile,
+ NM_KEYFILE_WRITE_TYPE_CERT,
+ &type_data,
+ info->user_data,
+ &info->error))
+ return;
+ if (info->error)
+ return;
+ }
+
+ cert_writer_default (info->connection, info->keyfile, &type_data);
+}
+
+/**************************************************************************/
+
+typedef struct {
+ const char *setting_name;
+ const char *key;
+ void (*writer) (KeyfileWriterInfo *info,
+ NMSetting *setting,
+ const char *key,
+ const GValue *value);
+} KeyWriter;
+
+/* A table of keys that require further parsing/conversion because they are
+ * stored in a format that can't be automatically read using the key's type.
+ * i.e. IPv4 addresses, which are stored in NetworkManager as guint32, but are
+ * stored in keyfiles as strings, eg "10.1.1.2" or IPv6 addresses stored
+ * in struct in6_addr internally, but as string in keyfiles.
+ */
+static KeyWriter key_writers[] = {
+ { NM_SETTING_CONNECTION_SETTING_NAME,
+ NM_SETTING_CONNECTION_TYPE,
+ setting_alias_writer },
+ { NM_SETTING_IP4_CONFIG_SETTING_NAME,
+ NM_SETTING_IP_CONFIG_ADDRESSES,
+ addr_writer },
+ { NM_SETTING_IP4_CONFIG_SETTING_NAME,
+ "address-labels",
+ ip4_addr_label_writer },
+ { NM_SETTING_IP6_CONFIG_SETTING_NAME,
+ NM_SETTING_IP_CONFIG_ADDRESSES,
+ addr_writer },
+ { NM_SETTING_IP4_CONFIG_SETTING_NAME,
+ NM_SETTING_IP_CONFIG_GATEWAY,
+ gateway_writer },
+ { NM_SETTING_IP6_CONFIG_SETTING_NAME,
+ NM_SETTING_IP_CONFIG_GATEWAY,
+ gateway_writer },
+ { NM_SETTING_IP4_CONFIG_SETTING_NAME,
+ NM_SETTING_IP_CONFIG_ROUTES,
+ route_writer },
+ { NM_SETTING_IP6_CONFIG_SETTING_NAME,
+ NM_SETTING_IP_CONFIG_ROUTES,
+ route_writer },
+ { NM_SETTING_IP4_CONFIG_SETTING_NAME,
+ NM_SETTING_IP_CONFIG_DNS,
+ dns_writer },
+ { NM_SETTING_IP6_CONFIG_SETTING_NAME,
+ NM_SETTING_IP_CONFIG_DNS,
+ dns_writer },
+ { NM_SETTING_WIRELESS_SETTING_NAME,
+ NM_SETTING_WIRELESS_SSID,
+ ssid_writer },
+ { NM_SETTING_802_1X_SETTING_NAME,
+ NM_SETTING_802_1X_PASSWORD_RAW,
+ password_raw_writer },
+ { NM_SETTING_802_1X_SETTING_NAME,
+ NM_SETTING_802_1X_CA_CERT,
+ cert_writer },
+ { NM_SETTING_802_1X_SETTING_NAME,
+ NM_SETTING_802_1X_CLIENT_CERT,
+ cert_writer },
+ { NM_SETTING_802_1X_SETTING_NAME,
+ NM_SETTING_802_1X_PRIVATE_KEY,
+ cert_writer },
+ { NM_SETTING_802_1X_SETTING_NAME,
+ NM_SETTING_802_1X_PHASE2_CA_CERT,
+ cert_writer },
+ { NM_SETTING_802_1X_SETTING_NAME,
+ NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
+ cert_writer },
+ { NM_SETTING_802_1X_SETTING_NAME,
+ NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
+ cert_writer },
+ { NULL, NULL, NULL }
+};
+
+static void
+write_setting_value (NMSetting *setting,
+ const char *key,
+ const GValue *value,
+ GParamFlags flag,
+ gpointer 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;
+
+ /* Don't write the NMSettingConnection object's 'read-only' property */
+ if ( NM_IS_SETTING_CONNECTION (setting)
+ && !strcmp (key, NM_SETTING_CONNECTION_READ_ONLY))
+ return;
+
+ setting_name = nm_setting_get_name (setting);
+
+ /* If the value is the default value, remove the item from the keyfile */
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), key);
+ if (pspec) {
+ if (g_param_value_defaults (pspec, (GValue *) value)) {
+ g_key_file_remove_key (info->keyfile, setting_name, key, NULL);
+ return;
+ }
+ }
+
+ /* Don't write secrets that are owned by user secret agents or aren't
+ * supposed to be saved. VPN secrets are handled specially though since
+ * the secret flags there are in a third-level hash in the 'secrets'
+ * property.
+ */
+ if (pspec && (pspec->flags & NM_SETTING_PARAM_SECRET) && !NM_IS_SETTING_VPN (setting)) {
+ NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
+
+ if (!nm_setting_get_secret_flags (setting, key, &secret_flags, NULL))
+ g_assert_not_reached ();
+ if (secret_flags != NM_SETTING_SECRET_FLAG_NONE)
+ return;
+ }
+
+ /* 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, setting, key, value);
+ return;
+ }
+ writer++;
+ }
+
+ if (type == G_TYPE_STRING) {
+ const char *str;
+
+ str = g_value_get_string (value);
+ if (str)
+ nm_keyfile_plugin_kf_set_string (info->keyfile, setting_name, key, str);
+ } else if (type == G_TYPE_UINT)
+ nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (int) g_value_get_uint (value));
+ else if (type == G_TYPE_INT)
+ nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, g_value_get_int (value));
+ else if (type == G_TYPE_UINT64) {
+ char *numstr;
+
+ numstr = g_strdup_printf ("%" G_GUINT64_FORMAT, g_value_get_uint64 (value));
+ nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr);
+ g_free (numstr);
+ } else if (type == G_TYPE_INT64) {
+ char *numstr;
+
+ numstr = g_strdup_printf ("%" G_GINT64_FORMAT, g_value_get_int64 (value));
+ nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr);
+ g_free (numstr);
+ } else if (type == G_TYPE_BOOLEAN) {
+ nm_keyfile_plugin_kf_set_boolean (info->keyfile, setting_name, key, g_value_get_boolean (value));
+ } else if (type == G_TYPE_CHAR) {
+ nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (int) g_value_get_schar (value));
+ } else if (type == G_TYPE_BYTES) {
+ GBytes *bytes;
+ const guint8 *data;
+ gsize len = 0;
+
+ bytes = g_value_get_boxed (value);
+ data = bytes ? g_bytes_get_data (bytes, &len) : NULL;
+
+ if (data != NULL && len > 0) {
+ int *tmp_array;
+ int i;
+
+ 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 (info->keyfile, setting_name, key, tmp_array, len);
+ g_free (tmp_array);
+ }
+ } else if (type == G_TYPE_STRV) {
+ char **array;
+
+ array = (char **) g_value_get_boxed (value);
+ nm_keyfile_plugin_kf_set_string_list (info->keyfile, setting_name, key, (const gchar **const) array, g_strv_length (array));
+ } else if (type == G_TYPE_HASH_TABLE) {
+ write_hash_of_string (info->keyfile, setting, key, value);
+ } else if (type == G_TYPE_ARRAY) {
+ 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
+ g_warn_if_reached ();
+}
+
+GKeyFile *
+nm_keyfile_write (NMConnection *connection,
+ NMKeyfileWriteHandler handler,
+ void *user_data,
+ GError **error)
+{
+ KeyfileWriterInfo info = { 0 };
+
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+ g_return_val_if_fail (!error || !*error, NULL);
+
+ if (!nm_connection_verify (connection, error))
+ return NULL;
+
+ 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);
+
+ if (info.error) {
+ g_propagate_error (error, info.error);
+ g_key_file_unref (info.keyfile);
+ return NULL;
+ }
+ return info.keyfile;
+}
+
diff --git a/libnm-core/nm-setting-8021x.c b/libnm-core/nm-setting-8021x.c
index 41559e0c8a..f355c18e70 100644
--- a/libnm-core/nm-setting-8021x.c
+++ b/libnm-core/nm-setting-8021x.c
@@ -31,6 +31,8 @@
#include "nm-utils-private.h"
#include "nm-setting-private.h"
#include "nm-core-enum-types.h"
+#include "nm-utils-internal.h"
+#include "gsystem-local-alloc.h"
/**
* SECTION:nm-setting-8021x
@@ -60,8 +62,6 @@
* ISBN: 978-1587051548
**/
-#define SCHEME_PATH "file://"
-
G_DEFINE_TYPE_WITH_CODE (NMSetting8021x, nm_setting_802_1x, NM_TYPE_SETTING,
_nm_register_setting (802_1X, 2))
NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_802_1X)
@@ -400,25 +400,122 @@ nm_setting_802_1x_get_system_ca_certs (NMSetting8021x *setting)
}
static NMSetting8021xCKScheme
-get_cert_scheme (GBytes *bytes)
+get_cert_scheme (GBytes *bytes, GError **error)
{
- gconstpointer data;
+ const char *data;
gsize length;
- if (!bytes)
+ if (!bytes) {
+ g_set_error_literal (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("data missing"));
return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN;
+ }
data = g_bytes_get_data (bytes, &length);
- if (!length)
+ return nm_setting_802_1x_check_cert_scheme (data, length, error);
+}
+
+/**
+ * nm_setting_802_1x_check_cert_scheme:
+ * @pdata: (allow-none): the data pointer
+ * @length: the length of the data
+ * @error: (allow-none): (out): validation reason
+ *
+ * Determines and verifies the blob type.
+ * When setting certificate properties of NMSetting8021x
+ * the blob must be not UNKNOWN (or NULL).
+ *
+ * Returns: the scheme of the blob or %NM_SETTING_802_1X_CK_SCHEME_UNKNOWN.
+ * For NULL it also returns NM_SETTING_802_1X_CK_SCHEME_UNKNOWN.
+ *
+ * Since: 1.2
+ **/
+NMSetting8021xCKScheme
+nm_setting_802_1x_check_cert_scheme (gconstpointer pdata, gsize length, GError **error)
+{
+ const char *data = pdata;
+
+ g_return_val_if_fail (!length || data, NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);
+
+ if (!length || !data) {
+ g_set_error_literal (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("binary data missing"));
return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN;
+ }
+
+ /* interpret the blob as PATH if it starts with "file://". */
+ if ( length >= STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)
+ && !memcmp (data, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH, STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH))) {
+ /* But it must also be NUL terminated, contain at least
+ * one non-NUL character, and contain only one trailing NUL
+ * chracter.
+ * And ensure it's UTF-8 valid too so we can pass it through
+ * D-Bus and stuff like that. */
+
+ if (data[length - 1] != '\0') {
+ g_set_error_literal (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("file:// URI not NUL terminated"));
+ return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN;
+ }
+ length--;
+
+ if (length <= STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)) {
+ g_set_error_literal (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("file:// URI is empty"));
+ return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN;
+ }
+
+ if (!g_utf8_validate (data + STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH), length - STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH), NULL)) {
+ g_set_error_literal (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("file:// URI is not valid UTF-8"));
+ return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN;
+ }
- if ( (length > strlen (SCHEME_PATH))
- && !memcmp (data, SCHEME_PATH, strlen (SCHEME_PATH)))
return NM_SETTING_802_1X_CK_SCHEME_PATH;
+ }
return NM_SETTING_802_1X_CK_SCHEME_BLOB;
}
+static GByteArray *
+load_and_verify_certificate (const char *cert_path,
+ NMSetting8021xCKScheme scheme,
+ NMCryptoFileFormat *out_file_format,
+ GError **error)
+{
+ NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+ GByteArray *array;
+
+ array = crypto_load_and_verify_certificate (cert_path, &format, error);
+
+ if (!array || !array->len || format == NM_CRYPTO_FILE_FORMAT_UNKNOWN) {
+ /* the array is empty or the format is already unknown. */
+ format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+ } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
+ /* If we load the file as blob, we must ensure that the binary data does not
+ * start with file://. NMSetting8021x cannot represent blobs that start with
+ * file://.
+ * If that's the case, coerce the format to UNKNOWN. The callers will take care
+ * of that and not set the blob. */
+ if (nm_setting_802_1x_check_cert_scheme (array->data, array->len, NULL) != NM_SETTING_802_1X_CK_SCHEME_BLOB)
+ format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+ }
+
+ if (out_file_format)
+ *out_file_format = format;
+ return array;
+}
+
/**
* nm_setting_802_1x_get_ca_cert_scheme:
* @setting: the #NMSetting8021x
@@ -434,7 +531,7 @@ nm_setting_802_1x_get_ca_cert_scheme (NMSetting8021x *setting)
{
g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);
- return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_cert);
+ return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_cert, NULL);
}
/**
@@ -488,20 +585,23 @@ nm_setting_802_1x_get_ca_cert_path (NMSetting8021x *setting)
g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL);
data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_cert, NULL);
- return (const char *)data + strlen (SCHEME_PATH);
+ return (const char *)data + strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH);
}
static GBytes *
path_to_scheme_value (const char *path)
{
GByteArray *array;
+ gsize len;
- g_return_val_if_fail (path != NULL, NULL);
+ g_return_val_if_fail (path != NULL && path[0], NULL);
- /* Add the path scheme tag to the front, then the fielname */
- array = g_byte_array_sized_new (strlen (path) + strlen (SCHEME_PATH) + 1);
- g_byte_array_append (array, (const guint8 *) SCHEME_PATH, strlen (SCHEME_PATH));
- g_byte_array_append (array, (const guint8 *) path, strlen (path));
+ len = strlen (path);
+
+ /* Add the path scheme tag to the front, then the filename */
+ array = g_byte_array_sized_new (len + strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH) + 1);
+ g_byte_array_append (array, (const guint8 *) NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH, strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH));
+ g_byte_array_append (array, (const guint8 *) path, len);
g_byte_array_append (array, (const guint8 *) "\0", 1);
return g_byte_array_free_to_bytes (array);
@@ -558,7 +658,7 @@ nm_setting_802_1x_set_ca_cert (NMSetting8021x *setting,
return TRUE;
}
- data = crypto_load_and_verify_certificate (cert_path, &format, error);
+ data = load_and_verify_certificate (cert_path, scheme, &format, error);
if (data) {
/* wpa_supplicant can only use raw x509 CA certs */
if (format == NM_CRYPTO_FILE_FORMAT_X509) {
@@ -766,7 +866,7 @@ nm_setting_802_1x_get_client_cert_scheme (NMSetting8021x *setting)
{
g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);
- return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->client_cert);
+ return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->client_cert, NULL);
}
/**
@@ -814,7 +914,7 @@ nm_setting_802_1x_get_client_cert_path (NMSetting8021x *setting)
g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL);
data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->client_cert, NULL);
- return (const char *)data + strlen (SCHEME_PATH);
+ return (const char *)data + strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH);
}
/**
@@ -872,7 +972,7 @@ nm_setting_802_1x_set_client_cert (NMSetting8021x *setting,
return TRUE;
}
- data = crypto_load_and_verify_certificate (cert_path, &format, error);
+ data = load_and_verify_certificate (cert_path, scheme, &format, error);
if (data) {
gboolean valid = FALSE;
@@ -1029,7 +1129,7 @@ nm_setting_802_1x_get_phase2_ca_cert_scheme (NMSetting8021x *setting)
{
g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);
- return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_ca_cert);
+ return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_ca_cert, NULL);
}
/**
@@ -1083,7 +1183,7 @@ nm_setting_802_1x_get_phase2_ca_cert_path (NMSetting8021x *setting)
g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL);
data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_ca_cert, NULL);
- return (const char *)data + strlen (SCHEME_PATH);
+ return (const char *)data + strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH);
}
/**
@@ -1137,7 +1237,7 @@ nm_setting_802_1x_set_phase2_ca_cert (NMSetting8021x *setting,
return TRUE;
}
- data = crypto_load_and_verify_certificate (cert_path, &format, error);
+ data = load_and_verify_certificate (cert_path, scheme, &format, error);
if (data) {
/* wpa_supplicant can only use raw x509 CA certs */
if (format == NM_CRYPTO_FILE_FORMAT_X509) {
@@ -1349,7 +1449,7 @@ nm_setting_802_1x_get_phase2_client_cert_scheme (NMSetting8021x *setting)
{
g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);
- return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_client_cert);
+ return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_client_cert, NULL);
}
/**
@@ -1397,7 +1497,7 @@ nm_setting_802_1x_get_phase2_client_cert_path (NMSetting8021x *setting)
g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL);
data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_client_cert, NULL);
- return (const char *)data + strlen (SCHEME_PATH);
+ return (const char *)data + strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH);
}
/**
@@ -1455,7 +1555,7 @@ nm_setting_802_1x_set_phase2_client_cert (NMSetting8021x *setting,
return TRUE;
}
- data = crypto_load_and_verify_certificate (cert_path, &format, error);
+ data = load_and_verify_certificate (cert_path, scheme, &format, error);
if (data) {
gboolean valid = FALSE;
@@ -1604,7 +1704,7 @@ nm_setting_802_1x_get_private_key_scheme (NMSetting8021x *setting)
{
g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);
- return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key);
+ return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key, NULL);
}
/**
@@ -1656,7 +1756,7 @@ nm_setting_802_1x_get_private_key_path (NMSetting8021x *setting)
g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL);
data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key, NULL);
- return (const char *)data + strlen (SCHEME_PATH);
+ return (const char *)data + strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH);
}
static void
@@ -1678,7 +1778,7 @@ file_to_secure_bytes (const char *filename)
if (g_file_get_contents (filename, &contents, &length, NULL)) {
array = g_byte_array_sized_new (length);
g_byte_array_append (array, (guint8 *) contents, length);
- g_assert (array->len == length);
+ memset (contents, 0, length);
g_free (contents);
return g_bytes_new_with_free_func (array->data, array->len, free_secure_bytes, array);
}
@@ -1790,7 +1890,8 @@ nm_setting_802_1x_set_private_key (NMSetting8021x *setting,
priv->private_key_password = g_strdup (password);
if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
- /* Shouldn't fail this since we just verified the private key above */
+ /* FIXME: potential race after verifying the private key above */
+ /* FIXME: ensure blob doesn't start with file:// */
priv->private_key = file_to_secure_bytes (key_path);
g_assert (priv->private_key);
} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH)
@@ -1941,7 +2042,7 @@ nm_setting_802_1x_get_phase2_private_key_scheme (NMSetting8021x *setting)
{
g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);
- return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key);
+ return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key, NULL);
}
/**
@@ -1993,7 +2094,7 @@ nm_setting_802_1x_get_phase2_private_key_path (NMSetting8021x *setting)
g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL);
data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key, NULL);
- return (const char *)data + strlen (SCHEME_PATH);
+ return (const char *)data + strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH);
}
/**
@@ -2101,7 +2202,8 @@ nm_setting_802_1x_set_phase2_private_key (NMSetting8021x *setting,
priv->phase2_private_key_password = g_strdup (password);
if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
- /* Shouldn't fail this since we just verified the private key above */
+ /* FIXME: potential race after verifying the private key above */
+ /* FIXME: ensure blob doesn't start with file:// */
priv->phase2_private_key = file_to_secure_bytes (key_path);
g_assert (priv->phase2_private_key);
} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH)
@@ -2575,35 +2677,18 @@ need_secrets (NMSetting *setting)
static gboolean
verify_cert (GBytes *bytes, const char *prop_name, GError **error)
{
- gconstpointer data;
- gsize length;
-
- if (!bytes)
- return TRUE;
+ GError *local = NULL;
- switch (get_cert_scheme (bytes)) {
- case NM_SETTING_802_1X_CK_SCHEME_BLOB:
+ if ( !bytes
+ || get_cert_scheme (bytes, &local) != NM_SETTING_802_1X_CK_SCHEME_UNKNOWN)
return TRUE;
- case NM_SETTING_802_1X_CK_SCHEME_PATH:
- /* For path-based schemes, verify that the path is zero-terminated */
- data = g_bytes_get_data (bytes, &length);
- if (((const guchar *)data)[length - 1] == '\0') {
- /* And ensure it's UTF-8 valid too so we can pass it through
- * D-Bus and stuff like that.
- */
- if (g_utf8_validate ((const char *)data + strlen (SCHEME_PATH), -1, NULL))
- return TRUE;
- }
- break;
- default:
- break;
- }
- g_set_error_literal (error,
- NM_CONNECTION_ERROR,
- NM_CONNECTION_ERROR_INVALID_PROPERTY,
- _("property is invalid"));
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("certificate is invalid: %s"), local->message);
g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, prop_name);
+ g_error_free (local);
return FALSE;
}
diff --git a/libnm-core/nm-setting-8021x.h b/libnm-core/nm-setting-8021x.h
index 43885daaa3..da86071a63 100644
--- a/libnm-core/nm-setting-8021x.h
+++ b/libnm-core/nm-setting-8021x.h
@@ -31,6 +31,8 @@
G_BEGIN_DECLS
+#define NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH "file://"
+
/**
* NMSetting8021xCKFormat:
* @NM_SETTING_802_1X_CK_FORMAT_UNKNOWN: unknown file format
@@ -147,6 +149,9 @@ GType nm_setting_802_1x_get_type (void);
NMSetting *nm_setting_802_1x_new (void);
+NM_AVAILABLE_IN_1_2
+NMSetting8021xCKScheme nm_setting_802_1x_check_cert_scheme (gconstpointer pdata, gsize length, GError **error);
+
guint32 nm_setting_802_1x_get_num_eap_methods (NMSetting8021x *setting);
const char * nm_setting_802_1x_get_eap_method (NMSetting8021x *setting, guint32 i);
gboolean nm_setting_802_1x_add_eap_method (NMSetting8021x *setting, const char *eap);
diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c
index 321c03f18c..7e0c6d9217 100644
--- a/libnm-core/nm-utils.c
+++ b/libnm-core/nm-utils.c
@@ -22,6 +22,7 @@
#include "config.h"
#include <string.h>
+#include <errno.h>
#include <stdlib.h>
#include <netinet/ether.h>
#include <arpa/inet.h>
@@ -1939,6 +1940,8 @@ nm_utils_ip_routes_from_variant (GVariant *value,
return routes;
}
+/**********************************************************************************************/
+
/**
* nm_utils_uuid_generate:
*
@@ -2012,6 +2015,50 @@ nm_utils_uuid_generate_from_string (const char *s, gssize slen, int uuid_type, g
}
/**
+ * _nm_utils_uuid_generate_from_strings:
+ * @string1: a variadic list of strings. Must be NULL terminated.
+ *
+ * Returns a variant3 UUID based on the concatenated C strings.
+ * It does not simply concatenate them, but also includes the
+ * terminating '\0' character. For example "a", "b", gives
+ * "a\0b\0".
+ *
+ * This has the advantage, that the following invocations
+ * all give different UUIDs: (NULL), (""), ("",""), ("","a"), ("a",""),
+ * ("aa"), ("aa", ""), ("", "aa"), ...
+ */
+char *
+_nm_utils_uuid_generate_from_strings (const char *string1, ...)
+{
+ GString *str;
+ va_list args;
+ const char *s;
+ char *uuid;
+
+ if (!string1)
+ return nm_utils_uuid_generate_from_string (NULL, 0, NM_UTILS_UUID_TYPE_VARIANT3, NM_UTILS_UUID_NS);
+
+ str = g_string_sized_new (120); /* effectively allocates power of 2 (128)*/
+
+ g_string_append_len (str, string1, strlen (string1) + 1);
+
+ va_start (args, string1);
+ s = va_arg (args, const char *);
+ while (s) {
+ g_string_append_len (str, s, strlen (s) + 1);
+ s = va_arg (args, const char *);
+ }
+ va_end (args);
+
+ uuid = nm_utils_uuid_generate_from_string (str->str, str->len, NM_UTILS_UUID_TYPE_VARIANT3, NM_UTILS_UUID_NS);
+
+ g_string_free (str, TRUE);
+ return uuid;
+}
+
+/**********************************************************************************************/
+
+/**
* nm_utils_rsa_key_encrypt:
* @data: (array length=len): RSA private key data to be encrypted
* @len: length of @data
@@ -3278,3 +3325,81 @@ nm_utils_bond_mode_string_to_int (const char *mode)
}
return -1;
}
+
+/**********************************************************************************************/
+
+/* _nm_utils_ascii_str_to_int64:
+ *
+ * A wrapper for g_ascii_strtoll, that checks whether the whole string
+ * can be successfully converted to a number and is within a given
+ * range. On any error, @fallback will be returned and %errno will be set
+ * to a non-zero value. On success, %errno will be set to zero, check %errno
+ * for errors. Any trailing or leading (ascii) white space is ignored and the
+ * functions is locale independent.
+ *
+ * The function is guaranteed to return a value between @min and @max
+ * (inclusive) or @fallback. Also, the parsing is rather strict, it does
+ * not allow for any unrecognized characters, except leading and trailing
+ * white space.
+ **/
+gint64
+_nm_utils_ascii_str_to_int64 (const char *str, guint base, gint64 min, gint64 max, gint64 fallback)
+{
+ gint64 v;
+ size_t len;
+ char buf[64], *s, *str_free = NULL;
+
+ if (str) {
+ while (g_ascii_isspace (str[0]))
+ str++;
+ }
+ if (!str || !str[0]) {
+ errno = EINVAL;
+ return fallback;
+ }
+
+ len = strlen (str);
+ if (g_ascii_isspace (str[--len])) {
+ /* backward search the first non-ws character.
+ * We already know that str[0] is non-ws. */
+ while (g_ascii_isspace (str[--len]))
+ ;
+
+ /* str[len] is now the last non-ws character... */
+ len++;
+
+ if (len >= sizeof (buf))
+ s = str_free = g_malloc (len + 1);
+ else
+ s = buf;
+
+ memcpy (s, str, len);
+ s[len] = 0;
+
+ /*
+ g_assert (len > 0 && len < strlen (str) && len == strlen (s));
+ g_assert (!g_ascii_isspace (str[len-1]) && g_ascii_isspace (str[len]));
+ g_assert (strncmp (str, s, len) == 0);
+ */
+
+ str = s;
+ }
+
+ errno = 0;
+ v = g_ascii_strtoll (str, &s, base);
+
+ if (errno != 0)
+ v = fallback;
+ else if (s[0] != 0) {
+ errno = EINVAL;
+ v = fallback;
+ } else if (v > max || v < min) {
+ errno = ERANGE;
+ v = fallback;
+ }
+
+ if (G_UNLIKELY (str_free))
+ g_free (str_free);
+ return v;
+}
+
diff --git a/libnm-core/tests/Makefile.am b/libnm-core/tests/Makefile.am
index 79aa73dbf9..daa5825b50 100644
--- a/libnm-core/tests/Makefile.am
+++ b/libnm-core/tests/Makefile.am
@@ -15,6 +15,7 @@ noinst_PROGRAMS = \
test-compare \
test-crypto \
test-general \
+ test-keyfile \
test-secrets \
test-setting-8021x \
test-setting-dcb \
@@ -38,20 +39,22 @@ endif
# -name "test-pkcs12" \
# -out test-cert.p12
-EXTRA_DIST = \
- certs/test_ca_cert.pem \
- certs/test_ca_cert.der \
- certs/test_key_and_cert.pem \
- certs/test-cert.p12 \
- certs/test2_ca_cert.pem \
- certs/test2_key_and_cert.pem \
- certs/test2-cert.p12 \
- certs/ca-no-ending-newline.pem \
- certs/test-key-only.pem \
- certs/test-key-only-decrypted.der \
- certs/test-key-only-decrypted.pem \
- certs/pkcs8-enc-key.pem \
- certs/pkcs8-noenc-key.pem \
- certs/pkcs8-decrypted.der \
- certs/test-aes-key.pem
+EXTRA_DIST = \
+ certs/ca-no-ending-newline.pem \
+ certs/pkcs8-decrypted.der \
+ certs/pkcs8-enc-key.pem \
+ certs/pkcs8-noenc-key.pem \
+ certs/test2_ca_cert.pem \
+ certs/test2-cert.p12 \
+ certs/test2_key_and_cert.pem \
+ certs/test-aes-key.pem \
+ certs/test_ca_cert.der \
+ certs/test_ca_cert.pem \
+ certs/test-ca-cert.pem \
+ certs/test-cert.p12 \
+ certs/test_key_and_cert.pem \
+ certs/test-key-and-cert.pem \
+ certs/test-key-only-decrypted.der \
+ certs/test-key-only-decrypted.pem \
+ certs/test-key-only.pem
diff --git a/libnm-core/tests/certs/test-ca-cert.pem b/libnm-core/tests/certs/test-ca-cert.pem
new file mode 100644
index 0000000000..ef1be20d2b
--- /dev/null
+++ b/libnm-core/tests/certs/test-ca-cert.pem
@@ -0,0 +1,27 @@
+-----BEGIN CERTIFICATE-----
+MIIEjzCCA3egAwIBAgIJAOvnZPt59yIZMA0GCSqGSIb3DQEBBQUAMIGLMQswCQYD
+VQQGEwJVUzESMBAGA1UECBMJQmVya3NoaXJlMRAwDgYDVQQHEwdOZXdidXJ5MRcw
+FQYDVQQKEw5NeSBDb21wYW55IEx0ZDEQMA4GA1UECxMHVGVzdGluZzENMAsGA1UE
+AxMEdGVzdDEcMBoGCSqGSIb3DQEJARYNdGVzdEB0ZXN0LmNvbTAeFw0wOTAzMTAx
+NTEyMTRaFw0xOTAzMDgxNTEyMTRaMIGLMQswCQYDVQQGEwJVUzESMBAGA1UECBMJ
+QmVya3NoaXJlMRAwDgYDVQQHEwdOZXdidXJ5MRcwFQYDVQQKEw5NeSBDb21wYW55
+IEx0ZDEQMA4GA1UECxMHVGVzdGluZzENMAsGA1UEAxMEdGVzdDEcMBoGCSqGSIb3
+DQEJARYNdGVzdEB0ZXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAKot9j+/+CX1/gZLgJHIXCRgCItKLGnf7qGbgqB9T2ACBqR0jllKWwDKrcWU
+xjXNIc+GF9Wnv+lX6G0Okn4Zt3/uRNobL+2b/yOF7M3Td3/9W873zdkQQX930YZc
+Rr8uxdRPP5bxiCgtcw632y21sSEbG9mjccAUnV/0jdvfmMNj0i8gN6E0fMBiJ9S3
+FkxX/KFvt9JWE9CtoyL7ki7UIDq+6vj7Gd5N0B3dOa1y+rRHZzKlJPcSXQSEYUS4
+HmKDwiKSVahft8c4tDn7KPi0vex91hlgZVd3usL2E/Vq7o5D9FAZ5kZY0AdFXwdm
+J4lO4Mj7ac7GE4vNERNcXVIX59sCAwEAAaOB8zCB8DAdBgNVHQ4EFgQUuDU3Mr7P
+T3n1e3Sy8hBauoDFahAwgcAGA1UdIwSBuDCBtYAUuDU3Mr7PT3n1e3Sy8hBauoDF
+ahChgZGkgY4wgYsxCzAJBgNVBAYTAlVTMRIwEAYDVQQIEwlCZXJrc2hpcmUxEDAO
+BgNVBAcTB05ld2J1cnkxFzAVBgNVBAoTDk15IENvbXBhbnkgTHRkMRAwDgYDVQQL
+EwdUZXN0aW5nMQ0wCwYDVQQDEwR0ZXN0MRwwGgYJKoZIhvcNAQkBFg10ZXN0QHRl
+c3QuY29tggkA6+dk+3n3IhkwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOC
+AQEAVRG4aALIvCXCiKfe7K+iJxjBVRDFPEf7JWA9LGgbFOn6pNvbxonrR+0BETdc
+JV1ET4ct2xsE7QNFIkp9GKRC+6J32zCo8qtLCD5+v436r8TUG2/t2JRMkb9I2XVT
+p7RJoot6M0Ltf8KNQUPYh756xmKZ4USfQUwc58MOSDGY8VWEXJOYij9Pf0e0c52t
+qiCEjXH7uXiS8Pgq9TYm7AkWSOrglYhSa83x0f8mtT8Q15nBESIHZ6o8FAS2bBgn
+B0BkrKRjtBUkuJG3vTox+bYINh2Gxi1JZHWSV1tN5z3hd4VFcKqanW5OgQwToBqp
+3nniskIjbH0xjgZf/nVMyLnjxg==
+-----END CERTIFICATE-----
diff --git a/libnm-core/tests/certs/test-key-and-cert.pem b/libnm-core/tests/certs/test-key-and-cert.pem
new file mode 100644
index 0000000000..dec9aa1b8f
--- /dev/null
+++ b/libnm-core/tests/certs/test-key-and-cert.pem
@@ -0,0 +1,118 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,4DE0615F23D82107
+
+QPNCO5Dobvz9dDhN32KkZRoEifW+HDm2PCbRQhKDiscGwB6LgypvVjHNsZiFKwzz
+L4R51UqgQeJx7GSGJqE626e9z9J+UNBhop02aOO2X0eSPdvBzr/uJ6Umiyr1xqD7
+zWf7u9l5kXElDJRhK+87GMBewp4Ie9NeXDjhF8hzC5Kiulen4AH3AYnfH3S7DimU
+h8GFMg8inrudrTbcjBhCdPeHG2jCygOxw3InRFz7uaN6LIhOaPQvmvpP4Cc1WRnW
+ZPq9o+eU3fPWPD5t+Op/VzYLvKwgBy/yK1rQXUm6ZMO7MhhRJ94ZCsJv+nVWpJlv
+QyBlxDKxwfkfYbDELdnnDQdHdMbKatLqa0KhSkgpp8LywBtanPz731tyT0r7b3na
+eLdra59lRU7ZQLPEdS3lPZd2O/KQvWf8wbg7MjXS9LxQ7R5HOPu6DNJlwXVZBmmo
+cAfu2q8ubU2IePvWLD1GOrBi6hE9TiGvFJkw+wBK+t72sz3njv9Xm/zlxruaEk5m
+RW/kybU3FP4PtjriBbskz3/VZaaxuRN7OoOYTkmyHmG1ADgcRUV6fea19qqsBlN8
+xb+SRtoH28oT/JVWU5neE2dbNzk5LeVO+w70NNdR5s5xqkBhbGGaJxvXwNP4ltFr
+T06SMh8znOLKwWB00aRtwfU7jOwR3mOleQO4ugIHmau3zp1TqzAHW8XtpuV7qVeI
+ESZOZuf0vW43BtNzgLXt1+r+bmsMsRwhnyomL9M0TUyyBdVYY9GkzTG9pOESheRo
+RSvAZ8qKGUliTpgBcbt2v1+NqkszcHa6FxuvS8YU4uo5/GqsgTxHTNIB232hIrrZ
+EIm6QL9TC5oFXMjy6UNqoCm5Nb8DBJ6aErt7pt7aoktqUW3O3QIzQT3IbZ4nAcTt
+lVF4d7j29I9t7bcC8GOVU1neilguZUss4ghJg9x4zI5UZdR7hZ8fbFT47TyxB+j5
+r0YdmjbjVTaSyaN2JGh1wvb4TzawGNVx/U2EJE16HigOtPfsfQRJ3x+FROKBdVa4
+aIFYXkRBeIPxX6n9pcw0lBCsnXo6/5iTjQSk2VqO3rHO/wyWiEjNczhL33dY2A8W
+GG5ECMO5SqXZHQQzpABqK94dxe3UC8aEESO5NhEqDuV7qQGol0qPKrUA3wb0jb2e
+DrejJ9HS2m1SUDmjpvvmEGy6GN7CRibbKt5rNZdJNNvWArOF5d0F6wkixQLl73oE
+lq5gLQQk9n7ClleKLhlQpBCorxilBbzmSUekkJLi0eaZiBBFWBX9udqnUZloXTgO
+8qwuO8K/GPR9Jy1/UH2Vh1H+wivaqKTVgEb0NotzgzECgTEFKJafl7rUNs1OZRZ3
+VBjevi6+iDpxVFgF71kXfdUC4ph0E1XDl0ja2rrKQGivMkUhWJ57+4EV5+hBkAnt
+G0RV45NwHXLrK2bd8F9PlRk2XHW6mIcFRXsW1DjeBhk/sQjvlO9R01GRSgcXtekJ
+tmX17FWrMrzXHpvy1IC3fk4RVnSjpzQ8O+17YE8/la9wVaeZZzHyYFmMT7VXjIhW
+QozJQ0vJ2jxJRh5GYn3tpJzdaeRfvTBik0pChNdUTnWP+BJ35xoCTs8iwJbmgVZ1
+-----END RSA PRIVATE KEY-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1 (0x1)
+ Signature Algorithm: md5WithRSAEncryption
+ Issuer: C=US, ST=Berkshire, L=Newbury, O=My Company Ltd, OU=Testing, CN=test/emailAddress=test@test.com
+ Validity
+ Not Before: Mar 10 15:13:16 2009 GMT
+ Not After : Mar 8 15:13:16 2019 GMT
+ Subject: C=US, ST=Berkshire, O=My Company Ltd, OU=Testing, CN=test1/emailAddress=test@test.com
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (2048 bit)
+ Modulus (2048 bit):
+ 00:cd:34:b1:2e:b0:04:c6:f4:2b:a2:c0:a0:39:7a:
+ 82:ed:96:c4:f7:19:83:91:5c:b4:e7:9c:de:ec:48:
+ ec:2d:e4:51:08:26:42:ac:d3:98:26:7a:72:f7:49:
+ c2:9e:66:05:c6:47:29:fe:3b:ac:6b:af:6f:5e:a8:
+ 03:5a:73:33:ba:19:03:00:35:f5:00:bc:a8:be:14:
+ ce:46:69:e3:6d:ed:34:37:85:55:87:62:b3:b7:c9:
+ c0:cc:9a:aa:61:05:5b:cd:a2:17:42:d3:e5:6f:1c:
+ 60:8d:c2:15:41:46:f8:12:54:d0:38:57:e1:fd:8d:
+ 44:c8:fb:56:b3:b9:6c:e9:f8:9e:21:11:57:1b:8b:
+ f9:cf:e3:17:e7:d8:fd:ac:d1:01:c6:92:30:f3:2d:
+ c9:d6:c1:f0:3d:fd:ca:30:dd:75:74:e7:d1:6b:75:
+ d8:c5:4d:43:61:fe:f6:ad:7e:4c:63:7c:03:17:a2:
+ 06:8f:d0:8b:69:d3:7a:07:0f:0b:a2:cf:0c:70:38:
+ ba:cc:55:35:60:84:58:d8:d2:be:1f:ef:76:a9:ba:
+ ae:6a:dc:08:97:80:de:42:00:b7:d4:ce:9a:b0:36:
+ 2a:c7:6f:45:04:7c:ea:41:19:d8:b9:19:04:1f:11:
+ a9:22:80:bd:69:08:15:0d:3c:de:cd:7e:88:6c:0f:
+ a3:43
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Comment:
+ OpenSSL Generated Certificate
+ X509v3 Subject Key Identifier:
+ CE:03:7E:EF:E7:DE:C9:87:BF:DE:56:F4:C8:A3:40:F6:C8:6F:05:8C
+ X509v3 Authority Key Identifier:
+ keyid:B8:35:37:32:BE:CF:4F:79:F5:7B:74:B2:F2:10:5A:BA:80:C5:6A:10
+ DirName:/C=US/ST=Berkshire/L=Newbury/O=My Company Ltd/OU=Testing/CN=test/emailAddress=test@test.com
+ serial:EB:E7:64:FB:79:F7:22:19
+
+ Signature Algorithm: md5WithRSAEncryption
+ 7a:20:93:63:40:73:7d:33:01:2e:c0:13:52:a4:a7:e1:4d:82:
+ f4:fb:b2:7b:d0:2b:5a:3f:0e:3c:28:61:71:ab:01:4d:fe:89:
+ b5:cd:2f:97:59:93:53:9d:51:86:48:dd:b9:e4:73:5e:22:0b:
+ 12:0d:25:39:76:16:44:06:0c:40:45:21:6b:a6:b1:e0:bf:76:
+ 1b:36:f3:1e:41:82:57:d9:59:b7:60:40:43:1c:1d:79:f6:48:
+ 32:5c:4e:e2:06:89:96:41:d2:54:1f:4a:6f:f6:78:a5:3c:02:
+ 85:21:e2:65:e1:8a:6d:24:19:95:f8:c0:35:ab:bd:ff:3d:f1:
+ fb:50:2d:30:1e:67:a6:7c:50:f9:d5:77:66:77:5a:14:0f:5c:
+ cd:21:09:9b:a3:92:57:19:dd:01:a4:18:c5:f9:70:e4:17:43:
+ 8d:b1:e6:61:e9:50:89:83:4f:ce:a4:57:68:58:40:70:ae:71:
+ 1c:47:66:d2:30:54:50:ea:3a:87:32:64:3b:18:42:fe:5a:19:
+ 07:64:f7:f1:b1:10:07:fd:a7:d2:a7:a8:05:79:5b:25:ba:69:
+ 7b:1a:3e:b1:3e:e4:17:17:01:ba:eb:54:ae:83:00:ed:66:62:
+ 8d:c0:3e:8a:b4:27:5f:e9:01:ce:20:c3:34:a9:28:c0:6f:c7:
+ 3b:65:fe:f9
+-----BEGIN CERTIFICATE-----
+MIIEojCCA4qgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBizELMAkGA1UEBhMCVVMx
+EjAQBgNVBAgTCUJlcmtzaGlyZTEQMA4GA1UEBxMHTmV3YnVyeTEXMBUGA1UEChMO
+TXkgQ29tcGFueSBMdGQxEDAOBgNVBAsTB1Rlc3RpbmcxDTALBgNVBAMTBHRlc3Qx
+HDAaBgkqhkiG9w0BCQEWDXRlc3RAdGVzdC5jb20wHhcNMDkwMzEwMTUxMzE2WhcN
+MTkwMzA4MTUxMzE2WjB6MQswCQYDVQQGEwJVUzESMBAGA1UECBMJQmVya3NoaXJl
+MRcwFQYDVQQKEw5NeSBDb21wYW55IEx0ZDEQMA4GA1UECxMHVGVzdGluZzEOMAwG
+A1UEAxMFdGVzdDExHDAaBgkqhkiG9w0BCQEWDXRlc3RAdGVzdC5jb20wggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDNNLEusATG9CuiwKA5eoLtlsT3GYOR
+XLTnnN7sSOwt5FEIJkKs05gmenL3ScKeZgXGRyn+O6xrr29eqANaczO6GQMANfUA
+vKi+FM5GaeNt7TQ3hVWHYrO3ycDMmqphBVvNohdC0+VvHGCNwhVBRvgSVNA4V+H9
+jUTI+1azuWzp+J4hEVcbi/nP4xfn2P2s0QHGkjDzLcnWwfA9/cow3XV059FrddjF
+TUNh/vatfkxjfAMXogaP0Itp03oHDwuizwxwOLrMVTVghFjY0r4f73apuq5q3AiX
+gN5CALfUzpqwNirHb0UEfOpBGdi5GQQfEakigL1pCBUNPN7NfohsD6NDAgMBAAGj
+ggEfMIIBGzAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVy
+YXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUzgN+7+feyYe/3lb0yKNA9shvBYww
+gcAGA1UdIwSBuDCBtYAUuDU3Mr7PT3n1e3Sy8hBauoDFahChgZGkgY4wgYsxCzAJ
+BgNVBAYTAlVTMRIwEAYDVQQIEwlCZXJrc2hpcmUxEDAOBgNVBAcTB05ld2J1cnkx
+FzAVBgNVBAoTDk15IENvbXBhbnkgTHRkMRAwDgYDVQQLEwdUZXN0aW5nMQ0wCwYD
+VQQDEwR0ZXN0MRwwGgYJKoZIhvcNAQkBFg10ZXN0QHRlc3QuY29tggkA6+dk+3n3
+IhkwDQYJKoZIhvcNAQEEBQADggEBAHogk2NAc30zAS7AE1Kkp+FNgvT7snvQK1o/
+DjwoYXGrAU3+ibXNL5dZk1OdUYZI3bnkc14iCxINJTl2FkQGDEBFIWumseC/dhs2
+8x5BglfZWbdgQEMcHXn2SDJcTuIGiZZB0lQfSm/2eKU8AoUh4mXhim0kGZX4wDWr
+vf898ftQLTAeZ6Z8UPnVd2Z3WhQPXM0hCZujklcZ3QGkGMX5cOQXQ42x5mHpUImD
+T86kV2hYQHCucRxHZtIwVFDqOocyZDsYQv5aGQdk9/GxEAf9p9KnqAV5WyW6aXsa
+PrE+5BcXAbrrVK6DAO1mYo3APoq0J1/pAc4gwzSpKMBvxztl/vk=
+-----END CERTIFICATE-----
diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c
index 7df4e57aa5..a417d3fe31 100644
--- a/libnm-core/tests/test-general.c
+++ b/libnm-core/tests/test-general.c
@@ -4038,6 +4038,184 @@ test_nm_utils_uuid_generate_from_string (void)
_test_uuid (NM_UTILS_UUID_TYPE_VARIANT3, "002a0ada-f547-375a-bab5-896a11d1927e", "a\0b", 3, UUID_NS_DNS);
}
+/*******************************************/
+
+static void
+__test_uuid (const char *expected_uuid, const char *str, gssize slen, char *uuid_test)
+{
+ g_assert (uuid_test);
+ g_assert (nm_utils_is_uuid (uuid_test));
+
+ if (strcmp (uuid_test, expected_uuid)) {
+ g_error ("UUID test failed (1): text=%s, len=%lld, expected=%s, uuid_test=%s",
+ str, (long long) slen, expected_uuid, uuid_test);
+ }
+ g_free (uuid_test);
+
+ uuid_test = nm_utils_uuid_generate_from_string (str, slen, NM_UTILS_UUID_TYPE_VARIANT3, NM_UTILS_UUID_NS);
+
+ g_assert (uuid_test);
+ g_assert (nm_utils_is_uuid (uuid_test));
+
+ if (strcmp (uuid_test, expected_uuid)) {
+ g_error ("UUID test failed (2): text=%s; len=%lld, expected=%s, uuid2=%s",
+ str, (long long) slen, expected_uuid, uuid_test);
+ }
+ g_free (uuid_test);
+}
+
+#define _test_uuid(expected_uuid, str, strlen, ...) __test_uuid (expected_uuid, str, strlen, _nm_utils_uuid_generate_from_strings(__VA_ARGS__, NULL))
+
+static void
+test_nm_utils_uuid_generate_from_strings (void)
+{
+ _test_uuid ("b07c334a-399b-32de-8d50-58e4e08f98e3", "", 0, NULL);
+ _test_uuid ("b8a426cb-bcb5-30a3-bd8f-6786fea72df9", "\0", 1, "");
+ _test_uuid ("12a4a982-7aae-39e1-951e-41aeb1250959", "a\0", 2, "a");
+ _test_uuid ("69e22c7e-f89f-3a43-b239-1cb52ed8db69", "aa\0", 3, "aa");
+ _test_uuid ("59829fd3-5ad5-3d90-a7b0-4911747e4088", "\0\0", 2, "", "");
+ _test_uuid ("01ad0e06-6c50-3384-8d86-ddab81421425", "a\0\0", 3, "a", "");
+ _test_uuid ("e1ed8647-9ed3-3ec8-8c6d-e8204524d71d", "aa\0\0", 4, "aa", "");
+ _test_uuid ("fb1c7cd6-275c-3489-9382-83b900da8af0", "\0a\0", 3, "", "a");
+ _test_uuid ("5d79494e-c4ba-31a6-80a2-d6016ccd7e17", "a\0a\0", 4, "a", "a");
+ _test_uuid ("fd698d86-1b60-3ebe-855f-7aada9950a8d", "aa\0a\0", 5, "aa", "a");
+ _test_uuid ("8c573b48-0f01-30ba-bb94-c5f59f4fe517", "\0aa\0", 4, "", "aa");
+ _test_uuid ("2bdd3d46-eb83-3c53-a41b-a724d04b5544", "a\0aa\0", 5, "a", "aa");
+ _test_uuid ("13d4b780-07c1-3ba7-b449-81c4844ef039", "aa\0aa\0", 6, "aa", "aa");
+ _test_uuid ("dd265bf7-c05a-3037-9939-b9629858a477", "a\0b\0", 4, "a", "b");
+}
+
+/******************************************************************************/
+
+static void
+test_nm_utils_ascii_str_to_int64_check (const char *str, guint base, gint64 min,
+ gint64 max, gint64 fallback, int exp_errno,
+ gint64 exp_val)
+{
+ gint64 v;
+
+ errno = 1;
+ v = _nm_utils_ascii_str_to_int64 (str, base, min, max, fallback);
+ g_assert_cmpint (errno, ==, exp_errno);
+ g_assert_cmpint (v, ==, exp_val);
+}
+
+static void
+test_nm_utils_ascii_str_to_int64_do (const char *str, guint base, gint64 min,
+ gint64 max, gint64 fallback, int exp_errno,
+ gint64 exp_val)
+{
+ const char *sign = "";
+ const char *val;
+ static const char *whitespaces[] = {
+ "",
+ " ",
+ "\r\n\t",
+ " \r\n\t ",
+ " \r\n\t \t\r\n\t",
+ NULL,
+ };
+ static const char *nulls[] = {
+ "",
+ "0",
+ "00",
+ "0000",
+ "0000000000000000",
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ NULL,
+ };
+ const char **ws_pre, **ws_post, **null;
+ guint i;
+
+ if (str == NULL || exp_errno != 0) {
+ test_nm_utils_ascii_str_to_int64_check (str, base, min, max, fallback, exp_errno, exp_val);
+ return;
+ }
+
+ if (strncmp (str, "-", 1) == 0)
+ sign = "-";
+
+ val = str + strlen (sign);
+
+ for (ws_pre = whitespaces; *ws_pre; ws_pre++) {
+ for (ws_post = whitespaces; *ws_post; ws_post++) {
+ for (null = nulls; *null; null++) {
+ for (i = 0; ; i++) {
+ char *s;
+ const char *str_base = "";
+
+ if (base == 16) {
+ if (i == 1)
+ str_base = "0x";
+ else if (i > 1)
+ break;
+ } else if (base == 8) {
+ if (i == 1)
+ str_base = "0";
+ else if (i > 1)
+ break;
+ } else if (base == 0) {
+ if (i > 0)
+ break;
+ /* with base==0, a leading zero would be interpreted as octal. Only test without *null */
+ if ((*null)[0])
+ break;
+ } else {
+ if (i > 0)
+ break;
+ }
+
+ s = g_strdup_printf ("%s%s%s%s%s%s", *ws_pre, sign, str_base, *null, val, *ws_post);
+
+ test_nm_utils_ascii_str_to_int64_check (s, base, min, max, fallback, exp_errno, exp_val);
+ g_free (s);
+ }
+ }
+ }
+ }
+}
+
+static void
+test_nm_utils_ascii_str_to_int64 (void)
+{
+ test_nm_utils_ascii_str_to_int64_do (NULL, 10, 0, 10000, -1, EINVAL, -1);
+ test_nm_utils_ascii_str_to_int64_do ("", 10, 0, 10000, -1, EINVAL, -1);
+ test_nm_utils_ascii_str_to_int64_do ("1x", 10, 0, 10000, -1, EINVAL, -1);
+ test_nm_utils_ascii_str_to_int64_do ("4711", 10, 0, 10000, -1, 0, 4711);
+ test_nm_utils_ascii_str_to_int64_do ("10000", 10, 0, 10000, -1, 0, 10000);
+ test_nm_utils_ascii_str_to_int64_do ("10001", 10, 0, 10000, -1, ERANGE, -1);
+ test_nm_utils_ascii_str_to_int64_do ("FF", 16, 0, 10000, -1, 0, 255);
+ test_nm_utils_ascii_str_to_int64_do ("FF", 10, 0, 10000, -2, EINVAL, -2);
+ test_nm_utils_ascii_str_to_int64_do ("9223372036854775807", 10, 0, G_MAXINT64, -2, 0, G_MAXINT64);
+ test_nm_utils_ascii_str_to_int64_do ("7FFFFFFFFFFFFFFF", 16, 0, G_MAXINT64, -2, 0, G_MAXINT64);
+ test_nm_utils_ascii_str_to_int64_do ("9223372036854775808", 10, 0, G_MAXINT64, -2, ERANGE, -2);
+ test_nm_utils_ascii_str_to_int64_do ("-9223372036854775808", 10, G_MININT64, 0, -2, 0, G_MININT64);
+ test_nm_utils_ascii_str_to_int64_do ("-9223372036854775808", 10, G_MININT64+1, 0, -2, ERANGE, -2);
+ test_nm_utils_ascii_str_to_int64_do ("-9223372036854775809", 10, G_MININT64, 0, -2, ERANGE, -2);
+ test_nm_utils_ascii_str_to_int64_do ("1.0", 10, 1, 1, -1, EINVAL, -1);
+ test_nm_utils_ascii_str_to_int64_do ("1x0", 16, -10, 10, -100, EINVAL, -100);
+ test_nm_utils_ascii_str_to_int64_do ("0", 16, -10, 10, -100, 0, 0);
+ test_nm_utils_ascii_str_to_int64_do ("10001111", 2, -1000, 1000, -100000, 0, 0x8F);
+ test_nm_utils_ascii_str_to_int64_do ("-10001111", 2, -1000, 1000, -100000, 0, -0x8F);
+ test_nm_utils_ascii_str_to_int64_do ("1111111", 2, G_MININT64, G_MAXINT64, -1, 0, 0x7F);
+ test_nm_utils_ascii_str_to_int64_do ("111111111111111", 2, G_MININT64, G_MAXINT64, -1, 0, 0x7FFF);
+ test_nm_utils_ascii_str_to_int64_do ("11111111111111111111111111111111111111111111111", 2, G_MININT64, G_MAXINT64, -1, 0, 0x7FFFFFFFFFFF);
+ test_nm_utils_ascii_str_to_int64_do ("111111111111111111111111111111111111111111111111111111111111111", 2, G_MININT64, G_MAXINT64, -1, 0, 0x7FFFFFFFFFFFFFFF);
+ test_nm_utils_ascii_str_to_int64_do ("100000000000000000000000000000000000000000000000000000000000000", 2, G_MININT64, G_MAXINT64, -1, 0, 0x4000000000000000);
+ test_nm_utils_ascii_str_to_int64_do ("1000000000000000000000000000000000000000000000000000000000000000", 2, G_MININT64, G_MAXINT64, -1, ERANGE, -1);
+ test_nm_utils_ascii_str_to_int64_do ("-100000000000000000000000000000000000000000000000000000000000000", 2, G_MININT64, G_MAXINT64, -1, 0, -0x4000000000000000);
+ test_nm_utils_ascii_str_to_int64_do ("111111111111111111111111111111111111111111111111111111111111111", 2, G_MININT64, G_MAXINT64, -1, 0, 0x7FFFFFFFFFFFFFFF);
+ test_nm_utils_ascii_str_to_int64_do ("-100000000000000000000000000000000000000000000000000000000000000", 2, G_MININT64, G_MAXINT64, -1, 0, -0x4000000000000000);
+ test_nm_utils_ascii_str_to_int64_do ("0x70", 10, G_MININT64, G_MAXINT64, -1, EINVAL, -1);
+ test_nm_utils_ascii_str_to_int64_do ("4711", 0, G_MININT64, G_MAXINT64, -1, 0, 4711);
+ test_nm_utils_ascii_str_to_int64_do ("04711", 0, G_MININT64, G_MAXINT64, -1, 0, 04711);
+ test_nm_utils_ascii_str_to_int64_do ("0x4711", 0, G_MININT64, G_MAXINT64, -1, 0, 0x4711);
+ test_nm_utils_ascii_str_to_int64_do ("080", 0, G_MININT64, G_MAXINT64, -1, EINVAL, -1);
+ test_nm_utils_ascii_str_to_int64_do ("070", 0, G_MININT64, G_MAXINT64, -1, 0, 7*8);
+ test_nm_utils_ascii_str_to_int64_do ("0x70", 0, G_MININT64, G_MAXINT64, -1, 0, 0x70);
+}
+
/******************************************************************************/
NMTST_DEFINE ();
@@ -4137,6 +4315,9 @@ int main (int argc, char **argv)
g_test_add_func ("/core/general/hexstr2bin", test_hexstr2bin);
g_test_add_func ("/core/general/test_nm_utils_uuid_generate_from_string", test_nm_utils_uuid_generate_from_string);
+ g_test_add_func ("/core/general/_nm_utils_uuid_generate_from_strings", test_nm_utils_uuid_generate_from_strings);
+
+ g_test_add_func ("/core/general/_nm_utils_ascii_str_to_int64", test_nm_utils_ascii_str_to_int64);
return g_test_run ();
}
diff --git a/libnm-core/tests/test-keyfile.c b/libnm-core/tests/test-keyfile.c
new file mode 100644
index 0000000000..43958cb6bb
--- /dev/null
+++ b/libnm-core/tests/test-keyfile.c
@@ -0,0 +1,512 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2015 Red Hat, Inc.
+ *
+ */
+
+#include "config.h"
+
+#include "nm-utils-internal.h"
+#include "nm-keyfile-utils.h"
+#include "nm-keyfile-internal.h"
+
+#include "nm-simple-connection.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-wired.h"
+#include "nm-setting-8021x.h"
+
+#include "nm-test-utils.h"
+
+
+#define TEST_WIRED_TLS_CA_CERT TEST_CERT_DIR"/test-ca-cert.pem"
+#define TEST_WIRED_TLS_PRIVKEY TEST_CERT_DIR"/test-key-and-cert.pem"
+
+
+/******************************************************************************/
+
+#define CLEAR(con, keyfile) \
+ G_STMT_START { \
+ NMConnection **_con = (con); \
+ GKeyFile **_keyfile = (keyfile); \
+ \
+ g_clear_object (_con); \
+ g_clear_pointer (_keyfile, g_key_file_unref); \
+ } G_STMT_END
+
+static void
+_assert_gbytes (GBytes *bytes, gconstpointer data, gssize len)
+{
+ g_assert ((data && len > 0) || !len || (data && len == -1));
+
+ if (len == -1)
+ len = strlen (data);
+
+ if (!len)
+ g_assert (!bytes);
+ else {
+ g_assert_cmpint (g_bytes_get_size (bytes), ==, len);
+ g_assert (memcmp (g_bytes_get_data (bytes, NULL), data, len) == 0);
+ }
+}
+
+static GKeyFile *
+_keyfile_load_from_data (const char *str)
+{
+ GError *error = NULL;
+ gboolean success;
+ GKeyFile *keyfile;
+
+ g_assert (str);
+
+ keyfile = g_key_file_new ();
+ success = g_key_file_load_from_data (keyfile, str, strlen (str), G_KEY_FILE_NONE, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+
+ return keyfile;
+}
+
+static gboolean
+_keyfile_a_contains_all_in_b (GKeyFile *kf_a, GKeyFile *kf_b)
+{
+ gs_strfreev char **groups = NULL;
+ guint i, j;
+
+ if (kf_a == kf_b)
+ return TRUE;
+
+ groups = g_key_file_get_groups (kf_a, NULL);
+ for (i = 0; groups && groups[i]; i++) {
+ gs_strfreev char **keys = NULL;
+
+ keys = g_key_file_get_keys (kf_a, groups[i], NULL, NULL);
+ if (keys) {
+ for (j = 0; keys[j]; j++) {
+ gs_free char *key_a = g_key_file_get_value (kf_a, groups[i], keys[j], NULL);
+ gs_free char *key_b = g_key_file_get_value (kf_b, groups[i], keys[j], NULL);
+
+ if (g_strcmp0 (key_a, key_b) != 0)
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+}
+
+static gboolean
+_keyfile_equals (GKeyFile *kf_a, GKeyFile *kf_b)
+{
+ return _keyfile_a_contains_all_in_b (kf_a, kf_b) && _keyfile_a_contains_all_in_b (kf_b, kf_a);
+}
+
+static void
+_keyfile_convert (NMConnection **con,
+ GKeyFile **keyfile,
+ const char *keyfile_name,
+ const char *base_dir,
+ NMKeyfileReadHandler read_handler,
+ void *read_data,
+ NMKeyfileWriteHandler write_handler,
+ void *write_data,
+ gboolean needs_normalization)
+{
+ NMConnection *c, *c2;
+ GKeyFile *k, *k2;
+ GError *error = NULL;
+ NMSetting8021x *s1, *s2;
+
+ /* convert from @con to @keyfile and check that we can make
+ * full round trips and obtaining the same result. */
+
+ g_assert (con);
+ g_assert (keyfile);
+ g_assert (*con || *keyfile);
+
+ if (!*keyfile) {
+ k = nm_keyfile_write (*con, write_handler, read_data, &error);
+ g_assert_no_error (error);
+ g_assert (k);
+ *keyfile = k;
+ } else
+ k = *keyfile;
+ if (!*con) {
+ c = nm_keyfile_read (*keyfile, keyfile_name, base_dir, read_handler, read_data, &error);
+ g_assert_no_error (error);
+ g_assert (c);
+ if (needs_normalization)
+ nmtst_assert_connection_verifies_after_normalization (c, 0, 0);
+ else
+ nmtst_assert_connection_verifies_without_normalization (c);
+ *con = c;
+ } else
+ c = *con;
+
+ k2 = nm_keyfile_write (c, write_handler, read_data, &error);
+ g_assert_no_error (error);
+ g_assert (k2);
+
+ c2 = nm_keyfile_read (k, keyfile_name, base_dir, read_handler, read_data, &error);
+ g_assert_no_error (error);
+ g_assert (c2);
+ if (needs_normalization)
+ nmtst_assert_connection_verifies_after_normalization (c2, 0, 0);
+ else
+ nmtst_assert_connection_verifies_without_normalization (c2);
+
+ s1 = nm_connection_get_setting_802_1x (*con);
+ s2 = nm_connection_get_setting_802_1x (c2);
+ if (s1 || s2) {
+ g_assert_cmpint (nm_setting_802_1x_get_ca_cert_scheme (s1), ==, nm_setting_802_1x_get_ca_cert_scheme (s2));
+ switch (nm_setting_802_1x_get_ca_cert_scheme (s1)) {
+ case NM_SETTING_802_1X_CK_SCHEME_PATH:
+ nmtst_assert_resolve_relative_path_equals (nm_setting_802_1x_get_ca_cert_path (s1), nm_setting_802_1x_get_ca_cert_path (s2));
+ break;
+ case NM_SETTING_802_1X_CK_SCHEME_BLOB: {
+ GBytes *b1, *b2;
+
+ b1 = nm_setting_802_1x_get_ca_cert_blob (s1);
+ b2 = nm_setting_802_1x_get_ca_cert_blob (s2);
+ g_assert_cmpint (g_bytes_get_size (b1), ==, g_bytes_get_size (b2));
+ g_assert (memcmp (g_bytes_get_data (b1, NULL), g_bytes_get_data (b2, NULL), g_bytes_get_size (b1)) == 0);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ nmtst_assert_connection_equals (c2, FALSE, *con, FALSE);
+ _keyfile_equals (k2, *keyfile);
+
+ g_object_unref (c2);
+ g_key_file_unref (k2);
+}
+
+/******************************************************************************/
+
+static void
+_test_8021x_cert_check (NMConnection *con,
+ NMSetting8021xCKScheme expected_scheme,
+ const void *value,
+ gssize val_len)
+{
+ GKeyFile *keyfile = NULL;
+ NMSetting8021x *s_8021x;
+ gs_free char *kval = NULL;
+
+ _keyfile_convert (&con, &keyfile, NULL, NULL, NULL, NULL, NULL, NULL, FALSE);
+
+ s_8021x = nm_connection_get_setting_802_1x (con);
+
+ g_assert (nm_setting_802_1x_get_ca_cert_scheme (s_8021x) == expected_scheme);
+
+ if (expected_scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) {
+ const char *path = nm_setting_802_1x_get_ca_cert_path (s_8021x);
+
+ g_assert_cmpstr (path, ==, value);
+ g_assert (val_len == -1 || strlen (path) == val_len);
+
+ kval = g_key_file_get_string (keyfile, "802-1x", "ca-cert", NULL);
+ g_assert (kval);
+ g_assert_cmpstr (kval, ==, value);
+ } else if (expected_scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
+ GBytes *blob = nm_setting_802_1x_get_ca_cert_blob (s_8021x);
+ gs_free char *file_blob = NULL;
+
+ if (val_len == -1) {
+ gsize l;
+ gboolean success;
+
+ success = g_file_get_contents (value, &file_blob, &l, NULL);
+ g_assert (success);
+
+ value = file_blob;
+ val_len = l;
+ }
+
+ g_assert (blob);
+ g_assert_cmpint (g_bytes_get_size (blob), ==, val_len);
+ g_assert (!memcmp (g_bytes_get_data (blob, NULL), value, val_len));
+
+ kval = g_key_file_get_string (keyfile, "802-1x", "ca-cert", NULL);
+ g_assert (kval);
+ g_assert (g_str_has_prefix (kval, NM_KEYFILE_CERT_SCHEME_PREFIX_BLOB));
+ }
+
+ g_key_file_unref (keyfile);
+}
+
+static void
+_test_8021x_cert_check_blob_full (NMConnection *con, const void *data, gsize len)
+{
+ GBytes *bytes;
+ NMSetting8021x *s_8021x = nm_connection_get_setting_802_1x (con);
+
+ bytes = g_bytes_new (data, len);
+ g_object_set (s_8021x,
+ NM_SETTING_802_1X_CA_CERT,
+ bytes,
+ NULL);
+ _test_8021x_cert_check (con, NM_SETTING_802_1X_CK_SCHEME_BLOB, g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes));
+ g_bytes_unref (bytes);
+}
+#define _test_8021x_cert_check_blob(con, data) _test_8021x_cert_check_blob_full(con, data, STRLEN (data))
+
+static void
+test_8021x_cert (void)
+{
+ NMSetting8021x *s_8021x;
+ gs_unref_object NMConnection *con = nmtst_create_minimal_connection ("test-cert", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL);
+ GError *error = NULL;
+ gboolean success;
+ NMSetting8021xCKScheme scheme = NM_SETTING_802_1X_CK_SCHEME_PATH;
+ gs_free char *full_TEST_WIRED_TLS_CA_CERT = nmtst_file_resolve_relative_path (TEST_WIRED_TLS_CA_CERT, NULL);
+ gs_free char *full_TEST_WIRED_TLS_PRIVKEY = nmtst_file_resolve_relative_path (TEST_WIRED_TLS_PRIVKEY, NULL);
+
+ /* test writing/reading of certificates of NMSetting8021x */
+
+ /* create a valid connection with NMSetting8021x */
+ s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
+ nm_setting_802_1x_add_eap_method (s_8021x, "tls");
+ g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, "Bill Smith", NULL);
+ success = nm_setting_802_1x_set_ca_cert (s_8021x,
+ full_TEST_WIRED_TLS_CA_CERT,
+ scheme,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (success);
+ success = nm_setting_802_1x_set_client_cert (s_8021x,
+ full_TEST_WIRED_TLS_CA_CERT,
+ scheme,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (success);
+ success = nm_setting_802_1x_set_private_key (s_8021x,
+ full_TEST_WIRED_TLS_PRIVKEY,
+ "test1",
+ scheme,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (success);
+
+
+ /* test reseting ca-cert to different values and see whether we can write/read. */
+
+ nm_connection_add_setting (con, NM_SETTING (s_8021x));
+ nmtst_assert_connection_verifies_and_normalizable (con);
+
+
+ _test_8021x_cert_check (con, scheme, full_TEST_WIRED_TLS_CA_CERT, -1);
+
+ scheme = NM_SETTING_802_1X_CK_SCHEME_BLOB;
+ success = nm_setting_802_1x_set_ca_cert (s_8021x,
+ full_TEST_WIRED_TLS_CA_CERT,
+ scheme,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (success);
+ _test_8021x_cert_check (con, scheme, full_TEST_WIRED_TLS_CA_CERT, -1);
+
+ _test_8021x_cert_check_blob (con, "a");
+ _test_8021x_cert_check_blob (con, "\0");
+ _test_8021x_cert_check_blob (con, "10");
+ _test_8021x_cert_check_blob (con, "data:;base64,a");
+ _test_8021x_cert_check_blob_full (con, "data:;base64,a", STRLEN ("data:;base64,a") + 1);
+ _test_8021x_cert_check_blob (con, "data:;base64,file://a");
+ _test_8021x_cert_check_blob (con, "123");
+
+}
+
+/******************************************************************************/
+
+static void
+test_8021x_cert_read (void)
+{
+ GKeyFile *keyfile = NULL;
+ gs_unref_object NMConnection *con = NULL;
+ NMSetting8021x *s_8021x;
+
+ con = nmtst_create_connection_from_keyfile (
+ "[connection]\n"
+ "type=ethernet",
+ "/test_8021x_cert_read/test0", NULL);
+ CLEAR (&con, &keyfile);
+
+
+ keyfile = _keyfile_load_from_data (
+ "[connection]\n"
+ "type=ethernet"
+ );
+ _keyfile_convert (&con, &keyfile, "/test_8021x_cert_read/test1", NULL, NULL, NULL, NULL, NULL, TRUE);
+ CLEAR (&con, &keyfile);
+
+ keyfile = _keyfile_load_from_data (
+ "[connection]\n"
+ "type=802-3-ethernet\n"
+
+ "[802-1x]\n"
+ "eap=tls;\n"
+ "identity=Bill Smith\n"
+ "ca-cert=48;130;2;52;48;130;1;161;2;16;2;173;102;126;78;69;254;94;87;111;60;152;25;94;221;192;48;13;6;9;42;134;72;134;247;13;1;1;2;5;0;48;95;49;11;48;9;6;3;85;4;6;19;2;85;83;49;32;48;30;6;3;85;4;10;19;23;82;83;65;32;68;97;116;97;32;83;101;99;117;114;105;116;121;44;32;73;110;99;46;49;46;48;44;6;3;85;4;11;19;37;83;101;99;117;114;101;32;83;101;114;118;101;114;32;67;101;114;116;105;102;105;99;97;116;105;111;110;32;65;117;116;104;111;114;105;116;121;48;30;23;13;57;52;49;49;48;57;48;48;48;48;48;48;90;23;13;49;48;48;49;48;55;50;51;53;57;53;57;90;48;95;49;11;48;9;6;3;85;4;6;19;2;85;83;49;32;48;30;6;3;85;4;10;19;23;82;83;65;32;68;97;116;97;32;83;101;99;117;114;105;116;121;44;32;73;110;99;46;49;46;48;44;6;3;85;4;11;19;37;83;101;99;117;114;101;32;83;101;114;118;101;114;32;67;101;114;116;105;102;105;99;97;116;105;111;110;32;65;117;116;104;111;114;105;116;121;48;129;155;48;13;6;9;42;134;72;134;247;13;1;1;1;5;0;3;129;137;0;48;129;133;2;126;0;146;206;122;193;174;131;62;90;170;137;131;87;172;37;1;118;12;173;174;142;44;55;206;235;53;120;100;84;3;229;132;64;81;201;191;143;8;226;138;130;8;210;22;134;55;85;233;177;33;2;173;118;104;129;154;5;162;75;201;75;37;102;34;86;108;136;7;143;247;129;89;109;132;7;101;112;19;113;118;62;155;119;76;227;80;137;86;152;72;185;29;167;41;26;19;46;74;17;89;156;30;21;213;73;84;44;115;58;105;130;177;151;57;156;109;112;103;72;229;221;45;214;200;30;123;2;3;1;0;1;48;13;6;9;42;134;72;134;247;13;1;1;2;5;0;3;126;0;101;221;126;225;178;236;176;226;58;224;236;113;70;154;25;17;184;211;199;160;180;3;64;38;2;62;9;156;225;18;179;209;90;246;55;165;183;97;3;182;91;22;105;59;198;68;8;12;136;83;12;107;151;73;199;62;53;220;108;185;187;170;223;92;187;58;47;147;96;182;169;75;77;242;32;247;205;95;127;100;123;142;220;0;92;215;250;119;202;57;22;89;111;14;234;211;181;131;127;77;77;66;86;118;180;201;95;4;248;56;248;235;210;95;117;95;205;123;252;229;142;128;124;252;80;\n"
+ "client-cert=102;105;108;101;58;47;47;47;104;111;109;101;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;99;108;105;101;110;116;46;112;101;109;0;\n"
+ "private-key=102;105;108;101;58;47;47;47;104;111;109;101;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;99;108;105;101;110;116;46;112;101;109;0;\n"
+ "private-key-password=12345testing\n"
+ );
+ _keyfile_convert (&con, &keyfile, "/test_8021x_cert_read/test2", NULL, NULL, NULL, NULL, NULL, TRUE);
+ CLEAR (&con, &keyfile);
+
+
+ keyfile = _keyfile_load_from_data (
+ "[connection]\n"
+ "type=802-3-ethernet\n"
+
+ "[802-1x]\n"
+ "eap=tls;\n"
+ "identity=Bill Smith\n"
+ /* unqualified strings are only recognized as path up to 500 chars*/
+ "ca-cert=" "/111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"
+ "/111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"
+ "/111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"
+ "/111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"
+ "/11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111\n"
+ "client-cert=/222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222221"
+ "/222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222221"
+ "/222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222221"
+ "/222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222221"
+ "/222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222\n"
+ "private-key=file://"
+ "/333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333331"
+ "/333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333331"
+ "/333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333331"
+ "/333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333331"
+ "/33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333111111\n"
+ "private-key-password=12345testing\n"
+ );
+ _keyfile_convert (&con, &keyfile, "/test_8021x_cert_read/test2", NULL, NULL, NULL, NULL, NULL, TRUE);
+ s_8021x = nm_connection_get_setting_802_1x (con);
+
+ g_assert (nm_setting_802_1x_get_ca_cert_scheme (s_8021x) == NM_SETTING_802_1X_CK_SCHEME_PATH);
+ g_assert (g_str_has_prefix (nm_setting_802_1x_get_ca_cert_path (s_8021x), "/111111111111"));
+ g_assert_cmpint (strlen (nm_setting_802_1x_get_ca_cert_path (s_8021x)), ==, 499);
+
+ g_assert (nm_setting_802_1x_get_client_cert_scheme (s_8021x) == NM_SETTING_802_1X_CK_SCHEME_BLOB);
+ g_assert (g_str_has_prefix (g_bytes_get_data (nm_setting_802_1x_get_client_cert_blob (s_8021x), NULL), "/2222222222"));
+ g_assert_cmpint (g_bytes_get_size (nm_setting_802_1x_get_client_cert_blob (s_8021x)), ==, 500 + 1 /* keyfile reader adds a trailing NUL */);
+
+ g_assert (nm_setting_802_1x_get_private_key_scheme (s_8021x) == NM_SETTING_802_1X_CK_SCHEME_PATH);
+ g_assert (g_str_has_prefix (nm_setting_802_1x_get_private_key_path (s_8021x), "/333333333"));
+ g_assert_cmpint (strlen (nm_setting_802_1x_get_private_key_path (s_8021x)), ==, 505);
+ CLEAR (&con, &keyfile);
+
+
+ keyfile = _keyfile_load_from_data (
+ "[connection]\n"
+ "type=802-3-ethernet\n"
+
+ "[802-1x]\n"
+ "eap=tls;\n"
+ "identity=Bill Smith\n"
+ "ca-cert=/\n"
+ "client-cert=a.pem\n"
+ "private-key=data:;base64,aGFsbG8=\n" // hallo
+ "private-key-password=12345testing\n"
+ );
+ _keyfile_convert (&con, &keyfile, "/test_8021x_cert_read/test2", NULL, NULL, NULL, NULL, NULL, TRUE);
+ s_8021x = nm_connection_get_setting_802_1x (con);
+
+ g_assert (nm_setting_802_1x_get_ca_cert_scheme (s_8021x) == NM_SETTING_802_1X_CK_SCHEME_PATH);
+ g_assert_cmpstr (nm_setting_802_1x_get_ca_cert_path (s_8021x), ==, "/");
+
+ g_assert (nm_setting_802_1x_get_client_cert_scheme (s_8021x) == NM_SETTING_802_1X_CK_SCHEME_PATH);
+ g_assert_cmpstr (nm_setting_802_1x_get_client_cert_path (s_8021x), ==, "/test_8021x_cert_read/a.pem");
+
+ g_assert (nm_setting_802_1x_get_private_key_scheme (s_8021x) == NM_SETTING_802_1X_CK_SCHEME_BLOB);
+ _assert_gbytes (nm_setting_802_1x_get_private_key_blob (s_8021x), "hallo", -1);
+ CLEAR (&con, &keyfile);
+
+
+ keyfile = _keyfile_load_from_data (
+ "[connection]\n"
+ "type=802-3-ethernet\n"
+
+ "[802-1x]\n"
+ "eap=tls;\n"
+ "identity=Bill Smith\n"
+ "ca-cert=file://data:;base64,x\n"
+ "client-cert=abc.der\n"
+ "private-key=abc.deR\n"
+ "private-key-password=12345testing\n"
+ );
+ _keyfile_convert (&con, &keyfile, "/test_8021x_cert_read/test2", NULL, NULL, NULL, NULL, NULL, TRUE);
+ s_8021x = nm_connection_get_setting_802_1x (con);
+
+ g_assert (nm_setting_802_1x_get_ca_cert_scheme (s_8021x) == NM_SETTING_802_1X_CK_SCHEME_PATH);
+ g_assert_cmpstr (nm_setting_802_1x_get_ca_cert_path (s_8021x), ==, "data:;base64,x");
+
+ g_assert (nm_setting_802_1x_get_client_cert_scheme (s_8021x) == NM_SETTING_802_1X_CK_SCHEME_PATH);
+ g_assert_cmpstr (nm_setting_802_1x_get_client_cert_path (s_8021x), ==, "/test_8021x_cert_read/abc.der");
+
+ g_assert (nm_setting_802_1x_get_private_key_scheme (s_8021x) == NM_SETTING_802_1X_CK_SCHEME_BLOB);
+ _assert_gbytes (nm_setting_802_1x_get_private_key_blob (s_8021x), "abc.deR\0", 8);
+ CLEAR (&con, &keyfile);
+
+
+ keyfile = _keyfile_load_from_data (
+ "[connection]\n"
+ "type=802-3-ethernet\n"
+
+ "[802-1x]\n"
+ "eap=tls;\n"
+ "identity=Bill Smith\n"
+ "ca-cert=104;97;108;108;111;\n" /* "hallo" without trailing NUL */
+ "client-cert=104;097;108;108;111;0;\n"
+ "private-key=hallo\n"
+ "private-key-password=12345testing\n"
+ );
+ _keyfile_convert (&con, &keyfile, "/test_8021x_cert_read/test2", NULL, NULL, NULL, NULL, NULL, TRUE);
+ s_8021x = nm_connection_get_setting_802_1x (con);
+
+ g_assert (nm_setting_802_1x_get_ca_cert_scheme (s_8021x) == NM_SETTING_802_1X_CK_SCHEME_BLOB);
+ _assert_gbytes (nm_setting_802_1x_get_ca_cert_blob (s_8021x), "hallo", 5);
+
+ g_assert (nm_setting_802_1x_get_client_cert_scheme (s_8021x) == NM_SETTING_802_1X_CK_SCHEME_BLOB);
+ _assert_gbytes (nm_setting_802_1x_get_client_cert_blob (s_8021x), "hallo\0", 6);
+
+ g_assert (nm_setting_802_1x_get_private_key_scheme (s_8021x) == NM_SETTING_802_1X_CK_SCHEME_BLOB);
+ _assert_gbytes (nm_setting_802_1x_get_private_key_blob (s_8021x), "hallo\0", 6);
+ CLEAR (&con, &keyfile);
+}
+
+/******************************************************************************/
+
+NMTST_DEFINE ();
+
+int main (int argc, char **argv)
+{
+ nmtst_init (&argc, &argv, TRUE);
+
+ g_test_add_func ("/core/keyfile/test_8021x_cert", test_8021x_cert);
+ g_test_add_func ("/core/keyfile/test_8021x_cert_read", test_8021x_cert_read);
+
+ return g_test_run ();
+}
+
diff --git a/libnm-core/tests/test-setting-8021x.c b/libnm-core/tests/test-setting-8021x.c
index 992379c7a2..16f6016e23 100644
--- a/libnm-core/tests/test-setting-8021x.c
+++ b/libnm-core/tests/test-setting-8021x.c
@@ -57,15 +57,13 @@ compare_blob_data (const char *test,
g_free (contents);
}
-#define SCHEME_PATH "file://"
-
static void
check_scheme_path (GBytes *value, const char *path)
{
const guint8 *p = g_bytes_get_data (value, NULL);
- g_assert (memcmp (p, SCHEME_PATH, strlen (SCHEME_PATH)) == 0);
- p += strlen (SCHEME_PATH);
+ g_assert (memcmp (p, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH, strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)) == 0);
+ p += strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH);
g_assert (memcmp (p, path, strlen (path)) == 0);
p += strlen (path);
g_assert (*p == '\0');
diff --git a/libnm-util/nm-setting-8021x.c b/libnm-util/nm-setting-8021x.c
index 6d5268a17b..1dc768c210 100644
--- a/libnm-util/nm-setting-8021x.c
+++ b/libnm-util/nm-setting-8021x.c
@@ -33,6 +33,7 @@
#include "crypto.h"
#include "nm-utils-private.h"
#include "nm-setting-private.h"
+#include "nm-utils-internal.h"
/**
* SECTION:nm-setting-8021x
@@ -430,13 +431,52 @@ get_cert_scheme (GByteArray *array)
if (!array || !array->len)
return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN;
- if ( (array->len > strlen (SCHEME_PATH))
- && !memcmp (array->data, SCHEME_PATH, strlen (SCHEME_PATH)))
- return NM_SETTING_802_1X_CK_SCHEME_PATH;
+ /* interpret the blob as PATH if it starts with "file://". */
+ if ( array->len >= STRLEN (SCHEME_PATH)
+ && !memcmp (array->data, SCHEME_PATH, STRLEN (SCHEME_PATH))) {
+ /* But it must also be NUL terminated, contain at least
+ * one non-NUL character, and contain only one trailing NUL
+ * chracter.
+ * And ensure it's UTF-8 valid too so we can pass it through
+ * D-Bus and stuff like that. */
+ if ( array->len > STRLEN (SCHEME_PATH) + 1
+ && array->data[array->len - 1] == '\0'
+ && g_utf8_validate ((const char *) &array->data[STRLEN (SCHEME_PATH)], array->len - (STRLEN (SCHEME_PATH) + 1), NULL))
+ return NM_SETTING_802_1X_CK_SCHEME_PATH;
+ return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN;
+ }
return NM_SETTING_802_1X_CK_SCHEME_BLOB;
}
+static GByteArray *
+load_and_verify_certificate (const char *cert_path,
+ NMSetting8021xCKScheme scheme,
+ NMCryptoFileFormat *out_file_format,
+ GError **error)
+{
+ NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+ GByteArray *array;
+
+ array = crypto_load_and_verify_certificate (cert_path, &format, error);
+
+ if (!array || !array->len || format == NM_CRYPTO_FILE_FORMAT_UNKNOWN)
+ format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+ else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
+ /* If we load the file as blob, we must ensure that the binary data does not
+ * start with file://. NMSetting8021x cannot represent blobs that start with
+ * file://.
+ * If that's the case, coerce the format to UNKNOWN. The callers will take care
+ * of that and not set the blob. */
+ if (get_cert_scheme (array) != NM_SETTING_802_1X_CK_SCHEME_BLOB)
+ format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+ }
+
+ if (out_file_format)
+ *out_file_format = format;
+ return array;
+}
+
/**
* nm_setting_802_1x_get_ca_cert_scheme:
* @setting: the #NMSetting8021x
@@ -511,14 +551,16 @@ static GByteArray *
path_to_scheme_value (const char *path)
{
GByteArray *array;
+ gsize len;
+
+ g_return_val_if_fail (path != NULL && path[0], NULL);
- g_return_val_if_fail (path != NULL, NULL);
+ len = strlen (path);
- /* Add the path scheme tag to the front, then the fielname */
- array = g_byte_array_sized_new (strlen (path) + strlen (SCHEME_PATH) + 1);
- g_assert (array);
+ /* Add the path scheme tag to the front, then the filename */
+ array = g_byte_array_sized_new (len + strlen (SCHEME_PATH) + 1);
g_byte_array_append (array, (const guint8 *) SCHEME_PATH, strlen (SCHEME_PATH));
- g_byte_array_append (array, (const guint8 *) path, strlen (path));
+ g_byte_array_append (array, (const guint8 *) path, len);
g_byte_array_append (array, (const guint8 *) "\0", 1);
return array;
}
@@ -578,7 +620,7 @@ nm_setting_802_1x_set_ca_cert (NMSetting8021x *setting,
return TRUE;
}
- data = crypto_load_and_verify_certificate (cert_path, &format, error);
+ data = load_and_verify_certificate (cert_path, scheme, &format, error);
if (data) {
/* wpa_supplicant can only use raw x509 CA certs */
if (format == NM_CRYPTO_FILE_FORMAT_X509) {
@@ -894,7 +936,7 @@ nm_setting_802_1x_set_client_cert (NMSetting8021x *setting,
return TRUE;
}
- data = crypto_load_and_verify_certificate (cert_path, &format, error);
+ data = load_and_verify_certificate (cert_path, scheme, &format, error);
if (data) {
gboolean valid = FALSE;
@@ -1159,7 +1201,7 @@ nm_setting_802_1x_set_phase2_ca_cert (NMSetting8021x *setting,
return TRUE;
}
- data = crypto_load_and_verify_certificate (cert_path, &format, error);
+ data = load_and_verify_certificate (cert_path, scheme, &format, error);
if (data) {
/* wpa_supplicant can only use raw x509 CA certs */
if (format == NM_CRYPTO_FILE_FORMAT_X509) {
@@ -1479,7 +1521,7 @@ nm_setting_802_1x_set_phase2_client_cert (NMSetting8021x *setting,
return TRUE;
}
- data = crypto_load_and_verify_certificate (cert_path, &format, error);
+ data = load_and_verify_certificate (cert_path, scheme, &format, error);
if (data) {
gboolean valid = FALSE;
@@ -2604,25 +2646,9 @@ need_secrets (NMSetting *setting)
static gboolean
verify_cert (GByteArray *array, const char *prop_name, GError **error)
{
- if (!array)
- return TRUE;
-
- switch (get_cert_scheme (array)) {
- case NM_SETTING_802_1X_CK_SCHEME_BLOB:
+ if ( !array
+ || get_cert_scheme (array) != NM_SETTING_802_1X_CK_SCHEME_UNKNOWN)
return TRUE;
- case NM_SETTING_802_1X_CK_SCHEME_PATH:
- /* For path-based schemes, verify that the path is zero-terminated */
- if (array->data[array->len - 1] == '\0') {
- /* And ensure it's UTF-8 valid too so we can pass it through
- * D-Bus and stuff like that.
- */
- if (g_utf8_validate ((const char *) (array->data + strlen (SCHEME_PATH)), -1, NULL))
- return TRUE;
- }
- break;
- default:
- break;
- }
g_set_error_literal (error,
NM_SETTING_802_1X_ERROR,
diff --git a/libnm/libnm.ver b/libnm/libnm.ver
index 258336ae3d..44b83f6ecb 100644
--- a/libnm/libnm.ver
+++ b/libnm/libnm.ver
@@ -847,6 +847,7 @@ local:
libnm_1_2_0 {
global:
+ nm_setting_802_1x_check_cert_scheme;
nm_setting_bridge_get_multicast_snooping;
nm_setting_wireless_get_powersave;
nm_utils_bond_mode_int_to_string;
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 926dd6c39e..6c09b21ff0 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
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c
index 67633bccef..57f817649e 100644
--- a/src/NetworkManagerUtils.c
+++ b/src/NetworkManagerUtils.c
@@ -1798,123 +1798,6 @@ nm_utils_cmp_connection_by_autoconnect_priority (NMConnection **a, NMConnection
return 0;
}
-/* nm_utils_ascii_str_to_int64:
- *
- * A wrapper for g_ascii_strtoll, that checks whether the whole string
- * can be successfully converted to a number and is within a given
- * range. On any error, @fallback will be returned and %errno will be set
- * to a non-zero value. On success, %errno will be set to zero, check %errno
- * for errors. Any trailing or leading (ascii) white space is ignored and the
- * functions is locale independent.
- *
- * The function is guaranteed to return a value between @min and @max
- * (inclusive) or @fallback. Also, the parsing is rather strict, it does
- * not allow for any unrecognized characters, except leading and trailing
- * white space.
- **/
-gint64
-nm_utils_ascii_str_to_int64 (const char *str, guint base, gint64 min, gint64 max, gint64 fallback)
-{
- gint64 v;
- size_t len;
- char buf[64], *s, *str_free = NULL;
-
- if (str) {
- while (g_ascii_isspace (str[0]))
- str++;
- }
- if (!str || !str[0]) {
- errno = EINVAL;
- return fallback;
- }
-
- len = strlen (str);
- if (g_ascii_isspace (str[--len])) {
- /* backward search the first non-ws character.
- * We already know that str[0] is non-ws. */
- while (g_ascii_isspace (str[--len]))
- ;
-
- /* str[len] is now the last non-ws character... */
- len++;
-
- if (len >= sizeof (buf))
- s = str_free = g_malloc (len + 1);
- else
- s = buf;
-
- memcpy (s, str, len);
- s[len] = 0;
-
- /*
- g_assert (len > 0 && len < strlen (str) && len == strlen (s));
- g_assert (!g_ascii_isspace (str[len-1]) && g_ascii_isspace (str[len]));
- g_assert (strncmp (str, s, len) == 0);
- */
-
- str = s;
- }
-
- errno = 0;
- v = g_ascii_strtoll (str, &s, base);
-
- if (errno != 0)
- v = fallback;
- else if (s[0] != 0) {
- errno = EINVAL;
- v = fallback;
- } else if (v > max || v < min) {
- errno = ERANGE;
- v = fallback;
- }
-
- if (G_UNLIKELY (str_free))
- g_free (str_free);
- return v;
-}
-
-/**
- * nm_utils_uuid_generate_from_strings:
- * @string1: a variadic list of strings. Must be NULL terminated.
- *
- * Returns a variant3 UUID based on the concatenated C strings.
- * It does not simply concatenate them, but also includes the
- * terminating '\0' character. For example "a", "b", gives
- * "a\0b\0".
- *
- * This has the advantage, that the following invocations
- * all give different UUIDs: (NULL), (""), ("",""), ("","a"), ("a",""),
- * ("aa"), ("aa", ""), ("", "aa"), ...
- */
-char *
-nm_utils_uuid_generate_from_strings (const char *string1, ...)
-{
- GString *str;
- va_list args;
- const char *s;
- char *uuid;
-
- if (!string1)
- return nm_utils_uuid_generate_from_string (NULL, 0, NM_UTILS_UUID_TYPE_VARIANT3, NM_UTILS_UUID_NS);
-
- str = g_string_sized_new (120); /* effectively allocates power of 2 (128)*/
-
- g_string_append_len (str, string1, strlen (string1) + 1);
-
- va_start (args, string1);
- s = va_arg (args, const char *);
- while (s) {
- g_string_append_len (str, s, strlen (s) + 1);
- s = va_arg (args, const char *);
- }
- va_end (args);
-
- uuid = nm_utils_uuid_generate_from_string (str->str, str->len, NM_UTILS_UUID_TYPE_VARIANT3, NM_UTILS_UUID_NS);
-
- g_string_free (str, TRUE);
- return uuid;
-}
-
/**************************************************************************/
static gint64 monotonic_timestamp_offset_sec;
diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h
index 25b3e27a0d..23d8330a34 100644
--- a/src/NetworkManagerUtils.h
+++ b/src/NetworkManagerUtils.h
@@ -163,12 +163,6 @@ int nm_utils_cmp_connection_by_autoconnect_priority (NMConnection **a, NMConnect
void nm_utils_log_connection_diff (NMConnection *connection, NMConnection *diff_base, guint32 level, guint64 domain, const char *name, const char *prefix);
-gint64 nm_utils_ascii_str_to_int64 (const char *str, guint base, gint64 min, gint64 max, gint64 fallback);
-
-#define NM_UTILS_UUID_NS "b425e9fb-7598-44b4-9e3b-5a2e3aaa4905"
-
-char *nm_utils_uuid_generate_from_strings (const char *string1, ...) G_GNUC_NULL_TERMINATED;
-
#define NM_UTILS_NS_PER_SECOND ((gint64) 1000000000)
gint64 nm_utils_get_monotonic_timestamp_ns (void);
gint64 nm_utils_get_monotonic_timestamp_us (void);
diff --git a/src/dhcp-manager/nm-dhcp-listener.c b/src/dhcp-manager/nm-dhcp-listener.c
index bb29717a97..ca57ae595a 100644
--- a/src/dhcp-manager/nm-dhcp-listener.c
+++ b/src/dhcp-manager/nm-dhcp-listener.c
@@ -31,6 +31,7 @@
#include <unistd.h>
#include "nm-dhcp-listener.h"
+#include "nm-core-internal.h"
#include "nm-logging.h"
#include "nm-dbus-manager.h"
#include "nm-dbus-glib-types.h"
@@ -132,7 +133,7 @@ handle_event (DBusGProxy *proxy,
}
pid_str = get_option (options, "pid");
- pid = nm_utils_ascii_str_to_int64 (pid_str, 10, 0, G_MAXINT32, -1);
+ pid = _nm_utils_ascii_str_to_int64 (pid_str, 10, 0, G_MAXINT32, -1);
if (pid == -1) {
nm_log_warn (LOGD_DHCP, "DHCP event: couldn't convert PID '%s' to an integer", pid_str ? pid_str : "(null)");
goto out;
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
index e1887e5a4b..47075f226f 100644
--- a/src/platform/nm-linux-platform.c
+++ b/src/platform/nm-linux-platform.c
@@ -55,6 +55,7 @@
#endif
#include "gsystem-local-alloc.h"
+#include "nm-core-internal.h"
#include "NetworkManagerUtils.h"
#include "nm-linux-platform.h"
#include "NetworkManagerUtils.h"
@@ -3025,7 +3026,7 @@ tun_get_properties (NMPlatform *platform, int ifindex, NMPlatformTunProperties *
val = nm_platform_sysctl_get (path);
g_free (path);
if (val) {
- props->owner = nm_utils_ascii_str_to_int64 (val, 10, -1, G_MAXINT64, -1);
+ props->owner = _nm_utils_ascii_str_to_int64 (val, 10, -1, G_MAXINT64, -1);
if (errno)
success = FALSE;
g_free (val);
@@ -3036,7 +3037,7 @@ tun_get_properties (NMPlatform *platform, int ifindex, NMPlatformTunProperties *
val = nm_platform_sysctl_get (path);
g_free (path);
if (val) {
- props->group = nm_utils_ascii_str_to_int64 (val, 10, -1, G_MAXINT64, -1);
+ props->group = _nm_utils_ascii_str_to_int64 (val, 10, -1, G_MAXINT64, -1);
if (errno)
success = FALSE;
g_free (val);
@@ -3049,7 +3050,7 @@ tun_get_properties (NMPlatform *platform, int ifindex, NMPlatformTunProperties *
if (val) {
gint64 flags;
- flags = nm_utils_ascii_str_to_int64 (val, 16, 0, G_MAXINT64, 0);
+ flags = _nm_utils_ascii_str_to_int64 (val, 16, 0, G_MAXINT64, 0);
if (!errno) {
#ifndef IFF_MULTI_QUEUE
const int IFF_MULTI_QUEUE = 0x0100;
diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c
index cbf2c8cd61..9500a75424 100644
--- a/src/platform/nm-platform.c
+++ b/src/platform/nm-platform.c
@@ -35,6 +35,7 @@
#include "NetworkManagerUtils.h"
#include "nm-logging.h"
#include "nm-enum-types.h"
+#include "nm-core-internal.h"
#define debug(...) nm_log_dbg (LOGD_PLATFORM, __VA_ARGS__)
@@ -322,7 +323,7 @@ nm_platform_sysctl_get_int_checked (const char *path, guint base, gint64 min, gi
return fallback;
}
- ret = nm_utils_ascii_str_to_int64 (value, base, min, max, fallback);
+ ret = _nm_utils_ascii_str_to_int64 (value, base, min, max, fallback);
g_free (value);
return ret;
}
diff --git a/src/settings/plugins/ibft/reader.c b/src/settings/plugins/ibft/reader.c
index e3a0168a6c..9bc6346278 100644
--- a/src/settings/plugins/ibft/reader.c
+++ b/src/settings/plugins/ibft/reader.c
@@ -397,13 +397,13 @@ connection_setting_add (const GPtrArray *block,
prefix ? prefix : "",
iface);
- uuid = nm_utils_uuid_generate_from_strings ("ibft",
- s_hwaddr,
- s_vlanid ? "V" : "v",
- s_vlanid ? s_vlanid : "",
- s_ip4addr ? "A" : "DHCP",
- s_ip4addr ? s_ip4addr : "",
- NULL);
+ uuid = _nm_utils_uuid_generate_from_strings ("ibft",
+ s_hwaddr,
+ s_vlanid ? "V" : "v",
+ s_vlanid ? s_vlanid : "",
+ s_ip4addr ? "A" : "DHCP",
+ s_ip4addr ? s_ip4addr : "",
+ NULL);
s_con = nm_setting_connection_new ();
g_object_set (s_con,
@@ -431,7 +431,7 @@ is_ibft_vlan_device (const GPtrArray *block)
/* VLAN 0 is normally a valid VLAN ID, but in the iBFT case it
* means "no VLAN".
*/
- if (nm_utils_ascii_str_to_int64 (s_vlan_id, 10, 1, 4095, -1) != -1)
+ if (_nm_utils_ascii_str_to_int64 (s_vlan_id, 10, 1, 4095, -1) != -1)
return TRUE;
}
return FALSE;
@@ -458,7 +458,7 @@ vlan_setting_add_from_block (const GPtrArray *block,
g_assert (vlan_id_str);
/* VLAN 0 is normally a valid VLAN ID, but in the iBFT case it means "no VLAN" */
- vlan_id = nm_utils_ascii_str_to_int64 (vlan_id_str, 10, 1, 4095, -1);
+ vlan_id = _nm_utils_ascii_str_to_int64 (vlan_id_str, 10, 1, 4095, -1);
if (vlan_id == -1) {
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
"Invalid VLAN_ID '%s'", vlan_id_str);
diff --git a/src/settings/plugins/ifcfg-rh/reader.c b/src/settings/plugins/ifcfg-rh/reader.c
index c40fecf50b..febbd90ac6 100644
--- a/src/settings/plugins/ifcfg-rh/reader.c
+++ b/src/settings/plugins/ifcfg-rh/reader.c
@@ -503,7 +503,7 @@ read_one_ip4_route (shvarFile *ifcfg,
/* Metric */
value = svGetValue (ifcfg, metric_tag, FALSE);
if (value) {
- metric = nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT32, -1);
+ metric = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT32, -1);
if (metric < 0) {
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
"Invalid IP4 route metric '%s'", value);
@@ -3344,7 +3344,7 @@ make_wireless_setting (shvarFile *ifcfg,
value = svGetValue (ifcfg, "CHANNEL", FALSE);
if (value) {
errno = 0;
- chan = nm_utils_ascii_str_to_int64 (value, 10, 1, 196, 0);
+ chan = _nm_utils_ascii_str_to_int64 (value, 10, 1, 196, 0);
if (errno || (chan == 0)) {
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
"Invalid wireless channel '%s'", value);
@@ -4889,7 +4889,7 @@ devtimeout_from_file (const char *filename)
devtimeout_str = svGetValue (ifcfg, "DEVTIMEOUT", FALSE);
if (devtimeout_str) {
- devtimeout = nm_utils_ascii_str_to_int64 (devtimeout_str, 10, 0, G_MAXUINT, 0);
+ devtimeout = _nm_utils_ascii_str_to_int64 (devtimeout_str, 10, 0, G_MAXUINT, 0);
g_free (devtimeout_str);
} else
devtimeout = 0;
diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c
index 5f3d173b2a..ed12683072 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.c
+++ b/src/settings/plugins/ifcfg-rh/shvar.c
@@ -36,7 +36,7 @@
#include "shvar.h"
-#include "NetworkManagerUtils.h"
+#include "nm-core-internal.h"
#include "nm-logging.h"
#define PARSE_WARNING(msg...) nm_log_warn (LOGD_SETTINGS, " " msg)
@@ -321,7 +321,7 @@ svTrueValue (shvarFile *s, const char *key, gboolean def)
* @max: the maximum for range-check
* @fallback: the fallback value in any error case
*
- * Reads a value @key and converts it to an integer using nm_utils_ascii_str_to_int64().
+ * Reads a value @key and converts it to an integer using _nm_utils_ascii_str_to_int64().
* In case of error, @errno will be set and @fallback returned. */
gint64
svGetValueInt64 (shvarFile *s, const char *key, guint base, gint64 min, gint64 max, gint64 fallback)
@@ -336,7 +336,7 @@ svGetValueInt64 (shvarFile *s, const char *key, guint base, gint64 min, gint64 m
return fallback;
}
- result = nm_utils_ascii_str_to_int64 (tmp, base, min, max, fallback);
+ result = _nm_utils_ascii_str_to_int64 (tmp, base, min, max, fallback);
errsv = errno;
if (errsv != 0)
PARSE_WARNING ("Error reading '%s' value '%s' as integer (%d)", key, tmp, errsv);
diff --git a/src/settings/plugins/ifnet/connection_parser.c b/src/settings/plugins/ifnet/connection_parser.c
index 41baf31507..888ee589ad 100644
--- a/src/settings/plugins/ifnet/connection_parser.c
+++ b/src/settings/plugins/ifnet/connection_parser.c
@@ -706,13 +706,13 @@ make_ip4_setting (NMConnection *connection,
GError *local = NULL;
if ((metric_str = ifnet_get_data (conn_name, "metric")) != NULL) {
- metric = nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1);
+ metric = _nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1);
} else {
metric_str = ifnet_get_global_data ("metric");
if (metric_str) {
stripped = g_strdup (metric_str);
strip_string (stripped, '"');
- metric = nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1);
+ metric = _nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1);
g_free (stripped);
} else
metric = -1;
@@ -846,13 +846,13 @@ make_ip6_setting (NMConnection *connection,
/* metric is not per routes configuration right now
* global metric is also supported (metric="x") */
if ((metric_str = ifnet_get_data (conn_name, "metric")) != NULL)
- metric = nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1);
+ metric = _nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1);
else {
metric_str = ifnet_get_global_data ("metric");
if (metric_str) {
stripped = g_strdup (metric_str);
strip_string (stripped, '"');
- metric = nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1);
+ metric = _nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1);
g_free (stripped);
} else
metric = 1;
diff --git a/src/settings/plugins/keyfile/common.h b/src/settings/plugins/keyfile/common.h
index 7bde4bf3a9..86fe002855 100644
--- a/src/settings/plugins/keyfile/common.h
+++ b/src/settings/plugins/keyfile/common.h
@@ -28,7 +28,5 @@
#define KEYFILE_DIR NMCONFDIR "/system-connections"
-#define VPN_SECRETS_GROUP "vpn-secrets"
-
#endif /* __COMMON_H__ */
diff --git a/src/settings/plugins/keyfile/reader.c b/src/settings/plugins/keyfile/reader.c
index 1a1d9663d3..a149e06d93 100644
--- a/src/settings/plugins/keyfile/reader.c
+++ b/src/settings/plugins/keyfile/reader.c
@@ -15,1239 +15,76 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 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) 2015 Red Hat, Inc.
*/
#include "config.h"
-#include <errno.h>
-#include <stdlib.h>
#include <sys/stat.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <arpa/inet.h>
#include <string.h>
-#include "nm-core-internal.h"
-#include "nm-dbus-glib-types.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"
-
-/* Some setting properties also contain setting names, such as
- * NMSettingConnection's 'type' property (which specifies the base type of the
- * connection, e.g. ethernet or wifi) or 'slave-type' (specifies type of slave
- * connection, e.g. bond or bridge). This function handles translating those
- * 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)
-{
- 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);
- if (s) {
- key_setting_name = nm_keyfile_plugin_get_setting_name_for_alias (s);
- g_object_set (G_OBJECT (setting),
- key, key_setting_name ? key_setting_name : s,
- NULL);
- g_free (s);
- }
-}
-
-static gboolean
-read_array_of_uint (GKeyFile *file,
- NMSetting *setting,
- const char *key)
-{
- GArray *array = NULL;
- gsize length;
- int i;
- gint *tmp;
-
- 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)
-{
- long tmp;
- char *endptr;
-
- if (!str || !str[0]) {
- if (key_name)
- nm_log_warn (LOGD_SETTINGS, "%s: ignoring missing number %s", __func__, key_name);
- 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);
- return FALSE;
- }
-
- *out = (guint32) tmp;
- return TRUE;
-}
-
-static gpointer
-build_address (int family, const char *address_str, guint32 plen)
-{
- NMIPAddress *addr;
- GError *error = NULL;
-
- g_return_val_if_fail (address_str, NULL);
-
- 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);
- g_error_free (error);
- }
-
- return addr;
-}
-
-static gpointer
-build_route (int family,
- const char *dest_str, guint32 plen,
- const char *gateway_str, const char *metric_str,
- const char *key_name)
-{
- NMIPRoute *route;
- guint32 metric = 0;
- GError *error = NULL;
-
- g_return_val_if_fail (plen, NULL);
- g_return_val_if_fail (dest_str, NULL);
-
- /* Next hop */
- if (gateway_str && gateway_str[0]) {
- if (!nm_utils_ipaddr_valid (family, gateway_str)) {
- /* Try workaround for routes written by broken keyfile writer.
- * Due to bug bgo#719851, an older version of writer would have
- * written "a:b:c:d::/plen,metric" if the gateway was ::, instead
- * of "a:b:c:d::/plen,,metric" or "a:b:c:d::/plen,::,metric"
- * Try workaround by interpreting gateway_str as metric to accept such
- * invalid routes. This broken syntax should not be not officially
- * supported.
- **/
- if ( family == AF_INET6
- && !metric_str
- && get_one_int (gateway_str, G_MAXUINT32, NULL, &metric))
- gateway_str = NULL;
- else {
- nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid gateway '%s'", __func__, gateway_str);
- return NULL;
- }
- }
- } else
- gateway_str = NULL;
-
- /* parse metric, default to 0 */
- if (metric_str) {
- if (!get_one_int (metric_str, G_MAXUINT32, key_name, &metric))
- return NULL;
- }
-
- route = nm_ip_route_new (family, dest_str, plen, gateway_str,
- metric ? (gint64) metric : -1,
- &error);
- if (!route) {
- nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid %s route: %s", __func__,
- family == AF_INET ? "IPv4" : "IPv6",
- error->message);
- g_error_free (error);
- }
-
- return route;
-}
-
-/* On success, returns pointer to the zero-terminated field (original @current).
- * The @current * pointer target is set to point to the rest of the input
- * or %NULL if there is no more input. Sets error to %NULL for convenience.
- *
- * On failure, returns %NULL (unspecified). The @current pointer target is
- * resets to its original value to allow skipping fields. The @error target
- * is set to the character that breaks the parsing or %NULL if @current was %NULL.
- *
- * When @current target is %NULL, gracefully fail returning %NULL while
- * leaving the @current target %NULL end setting @error to %NULL;
- */
-static char *
-read_field (char **current, char **error, const char *characters, const char *delimiters)
-{
- char *start;
-
- g_return_val_if_fail (current, NULL);
- g_return_val_if_fail (error, NULL);
- g_return_val_if_fail (characters, NULL);
- g_return_val_if_fail (delimiters, NULL);
-
- *error = NULL;
-
- if (!*current) {
- /* graceful failure, leave '*current' NULL */
- return NULL;
- }
-
- /* fail on empty input */
- if (!**current)
- return NULL;
-
- /* remember beginning of input */
- start = *current;
-
- while (**current && strchr (characters, **current))
- (*current)++;
- if (**current)
- if (strchr (delimiters, **current)) {
- /* success, more data available */
- *(*current)++ = '\0';
- return start;
- } else {
- /* error, bad character */
- *error = *current;
- *current = start;
- return NULL;
- }
- else {
- /* success, end of input */
- *current = NULL;
- return start;
- }
-}
-
-#define IP_ADDRESS_CHARS "0123456789abcdefABCDEF:.%"
-#define DIGITS "0123456789"
-#define DELIMITERS "/;,"
-
-
-/* The following IPv4 and IPv6 address formats are supported:
- *
- * address (DEPRECATED)
- * address/plen
- * address/gateway (DEPRECATED)
- * address/plen,gateway
- *
- * The following IPv4 and IPv6 route formats are supported:
- *
- * address/plen (NETWORK dev DEVICE)
- * address/plen,gateway (NETWORK via GATEWAY dev DEVICE)
- * address/plen,,metric (NETWORK dev DEVICE metric METRIC)
- * address/plen,gateway,metric (NETWORK via GATEWAY dev DEVICE metric METRIC)
- *
- * For backward, forward and sideward compatibility, slash (/),
- * semicolon (;) and comma (,) are interchangable. The choice of
- * separator in the above examples is therefore not significant.
- *
- * Leaving out the prefix length is discouraged and DEPRECATED. The
- * default value of IPv6 prefix length was 64 and has not been
- * changed. The default for IPv4 is now 24, which is the closest
- * IPv4 equivalent. These defaults may just as well be changed to
- * match the iproute2 defaults (32 for IPv4 and 128 for IPv6).
- */
-static gpointer
-read_one_ip_address_or_route (GKeyFile *file,
- const char *setting_name,
- const char *key_name,
- gboolean ipv6,
- gboolean route,
- char **out_gateway)
-{
- guint32 plen = G_MAXUINT32;
- gpointer result;
- char *address_str, *plen_str, *gateway_str, *metric_str, *value, *current, *error;
-
- current = value = nm_keyfile_plugin_kf_get_string (file, setting_name, key_name, NULL);
- if (!value)
- return NULL;
-
- /* get address field */
- address_str = read_field (&current, &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;
- }
- /* get prefix length field (skippable) */
- plen_str = read_field (&current, &error, DIGITS, DELIMITERS);
- /* get gateway field */
- gateway_str = read_field (&current, &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;
- }
- /* for routes, get metric */
- if (route) {
- metric_str = read_field (&current, &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;
- }
- } else
- metric_str = NULL;
- if (current) {
- /* 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;
- } 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);
- }
- }
-
-#define DEFAULT_PREFIX(for_route, for_ipv6) ( (for_route) ? ( (for_ipv6) ? 128 : 24 ) : ( (for_ipv6) ? 64 : 24 ) )
-
- /* parse plen, fallback to defaults */
- if (plen_str) {
- if (!get_one_int (plen_str, ipv6 ? 128 : 32, key_name, &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);
- }
- } 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);
- }
-
- /* 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);
- } else {
- result = build_address (ipv6 ? AF_INET6 : AF_INET,
- address_str, plen);
- if (out_gateway && gateway_str)
- *out_gateway = g_strdup (gateway_str);
- }
- g_free (value);
- return result;
-error:
- g_free (value);
- return NULL;
-}
+#include "nm-logging.h"
+#include "nm-keyfile-internal.h"
-static void
-ip_address_or_route_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
+static const char *
+_fmt_warn (const char *group, NMSetting *setting, const char *property_name, const char *message, char **out_message)
{
- const char *setting_name = nm_setting_get_name (setting);
- gboolean ipv6 = !strcmp (setting_name, "ipv6");
- gboolean routes = !strcmp (key, "routes");
- static const char *key_names_routes[] = { "route", "routes", NULL };
- static const char *key_names_addresses[] = { "address", "addresses", NULL };
- const char **key_names = routes ? key_names_routes : key_names_addresses;
- char *gateway = NULL;
- GPtrArray *list;
- GDestroyNotify free_func;
- int i;
-
- if (routes)
- free_func = (GDestroyNotify) nm_ip_route_unref;
- else
- free_func = (GDestroyNotify) nm_ip_address_unref;
- list = g_ptr_array_new_with_free_func (free_func);
+ const char *setting_name = setting ? nm_setting_get_name (setting) : NULL;
- for (i = -1; i < 1000; i++) {
- const char **key_basename;
-
- for (key_basename = key_names; *key_basename; key_basename++) {
- char *key_name;
- gpointer item;
+ if (group) {
+ char *res;
- /* -1 means no suffix */
- if (i >= 0)
- key_name = g_strdup_printf ("%s%d", *key_basename, i);
+ if (setting_name) {
+ if (property_name && !strcmp (group, setting_name))
+ res = g_strdup_printf ("%s.%s: %s", group, property_name, message);
+ else if (property_name)
+ res = g_strdup_printf ("%s/%s.%s: %s", group, setting_name, property_name, message);
+ else if (!strcmp (group, setting_name))
+ res = g_strdup_printf ("%s: %s", group, message);
else
- key_name = g_strdup (*key_basename);
-
- item = read_one_ip_address_or_route (keyfile, setting_name, key_name, ipv6, routes,
- gateway ? NULL : &gateway);
- if (item)
- g_ptr_array_add (list, item);
-
- g_free (key_name);
- }
- }
-
- if (list->len >= 1)
- g_object_set (setting, key, list, NULL);
-
- if (gateway) {
- g_object_set (setting, "gateway", gateway, NULL);
- g_free (gateway);
- }
-
- g_ptr_array_unref (list);
-}
-
-static void
-ip4_dns_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
-{
- const char *setting_name = nm_setting_get_name (setting);
- GPtrArray *array;
- gsize length;
- char **list, **iter;
- int ret;
-
- list = nm_keyfile_plugin_kf_get_string_list (keyfile, setting_name, key, &length, NULL);
- if (!list || !g_strv_length (list))
- return;
-
- array = g_ptr_array_sized_new (length + 1);
- for (iter = list; *iter; iter++) {
- guint32 addr;
-
- ret = inet_pton (AF_INET, *iter, &addr);
- if (ret <= 0) {
- nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid DNS server address '%s'", __func__, *iter);
- continue;
- }
-
- g_ptr_array_add (array, *iter);
- }
- g_ptr_array_add (array, NULL);
-
- g_object_set (setting, key, array->pdata, NULL);
- g_ptr_array_unref (array);
- g_strfreev (list);
-}
-
-static void
-ip6_dns_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
-{
- const char *setting_name = nm_setting_get_name (setting);
- GPtrArray *array = NULL;
- gsize length;
- char **list, **iter;
- int ret;
-
- list = nm_keyfile_plugin_kf_get_string_list (keyfile, setting_name, key, &length, NULL);
- if (!list || !g_strv_length (list))
- return;
-
- array = g_ptr_array_sized_new (length + 1);
-
- for (iter = list; *iter; iter++) {
- struct in6_addr addr;
-
- 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);
- continue;
- }
-
- g_ptr_array_add (array, *iter);
- }
- g_ptr_array_add (array, NULL);
-
- g_object_set (setting, key, array->pdata, NULL);
- g_ptr_array_unref (array);
- g_strfreev (list);
-}
-
-static void
-mac_address_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path, gsize enforce_length)
-{
- const char *setting_name = nm_setting_get_name (setting);
- char *tmp_string = NULL, *p, *mac_str;
- gint *tmp_list;
- GByteArray *array = NULL;
- gsize length;
-
- p = tmp_string = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, NULL);
- if (tmp_string && tmp_string[0]) {
- /* Look for enough ':' characters to signify a MAC address */
- guint i = 0;
-
- while (*p) {
- if (*p == ':')
- i++;
- p++;
- }
-
- if (enforce_length == 0 || enforce_length == i+1) {
- /* If we found enough it's probably a string-format MAC address */
- array = g_byte_array_sized_new (i+1);
- g_byte_array_set_size (array, i+1);
- if (!nm_utils_hwaddr_aton (tmp_string, array->data, array->len)) {
- g_byte_array_unref (array);
- array = NULL;
- }
- }
- }
- g_free (tmp_string);
-
- if (array == NULL) {
- /* Old format; list of ints */
- tmp_list = nm_keyfile_plugin_kf_get_integer_list (keyfile, setting_name, key, &length, NULL);
- if (length > 0 && (enforce_length == 0 || enforce_length == length)) {
- gsize i;
-
- array = g_byte_array_sized_new (length);
- for (i = 0; i < length; i++) {
- int val = tmp_list[i];
- 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);
- g_byte_array_free (array, TRUE);
- array = NULL;
- break;
- }
- g_byte_array_append (array, &v, 1);
- }
- }
- g_free (tmp_list);
- }
-
- if (!array) {
- nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid MAC address for %s / %s",
- __func__, setting_name, key);
- return;
- }
-
- mac_str = nm_utils_hwaddr_ntoa (array->data, array->len);
- g_object_set (setting, key, mac_str, NULL);
- g_free (mac_str);
- g_byte_array_free (array, TRUE);
-}
-
-static void
-mac_address_parser_ETHER (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
-{
- mac_address_parser (setting, key, keyfile, keyfile_path, ETH_ALEN);
-}
-
-static void
-mac_address_parser_INFINIBAND (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
-{
- mac_address_parser (setting, key, keyfile, keyfile_path, INFINIBAND_ALEN);
-}
-
-static void
-read_hash_of_string (GKeyFile *file, NMSetting *setting, const char *key)
-{
- char **keys, **iter;
- char *value;
- const char *setting_name = nm_setting_get_name (setting);
-
- keys = nm_keyfile_plugin_kf_get_keys (file, setting_name, NULL, NULL);
- if (!keys || !*keys)
- return;
-
- for (iter = keys; *iter; iter++) {
- value = nm_keyfile_plugin_kf_get_string (file, setting_name, *iter, NULL);
- if (!value)
- continue;
-
- if (NM_IS_SETTING_VPN (setting)) {
- /* Add any item that's not a class property to the data hash */
- if (!g_object_class_find_property (G_OBJECT_GET_CLASS (setting), *iter))
- nm_setting_vpn_add_data_item (NM_SETTING_VPN (setting), *iter, value);
- }
- if (NM_IS_SETTING_BOND (setting)) {
- if (strcmp (*iter, "interface-name"))
- nm_setting_bond_add_option (NM_SETTING_BOND (setting), *iter, value);
- }
- g_free (value);
- }
- g_strfreev (keys);
-}
-
-static void
-unescape_semicolons (char *str)
-{
- int i;
- gsize len = strlen (str);
-
- for (i = 0; i < len; i++) {
- if (str[i] == '\\' && str[i+1] == ';') {
- memmove(str + i, str + i + 1, len - (i + 1));
- len--;
- }
- str[len] = '\0';
- }
-}
-
-static GBytes *
-get_bytes (GKeyFile *keyfile,
- const char *setting_name,
- const char *key,
- gboolean zero_terminate,
- gboolean unescape_semicolon)
-{
- GByteArray *array = NULL;
- char *tmp_string;
- gint *tmp_list;
- gsize length;
- int i;
-
- /* 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);
- if (tmp_string) {
- GRegex *regex;
- GMatchInfo *match_info;
- const char *pattern = "^[[:space:]]*[[:digit:]]{1,3}[[:space:]]*;([[:space:]]*[[:digit:]]{1,3}[[:space:]]*;)*([[:space:]]*)?$";
-
- regex = g_regex_new (pattern, 0, 0, NULL);
- g_regex_match (regex, tmp_string, 0, &match_info);
- if (!g_match_info_matches (match_info)) {
- /* Handle as a simple string (ie, new format) */
- if (unescape_semicolon)
- unescape_semicolons (tmp_string);
- length = strlen (tmp_string);
- if (zero_terminate)
- length++;
- array = g_byte_array_sized_new (length);
- g_byte_array_append (array, (guint8 *) tmp_string, length);
- }
- g_match_info_free (match_info);
- g_regex_unref (regex);
- g_free (tmp_string);
- }
-
- if (!array) {
- /* Old format; list of ints */
- tmp_list = 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++) {
- int val = tmp_list[i];
- 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);
- } else
- g_byte_array_append (array, (const unsigned char *) &v, sizeof (v));
- }
- g_free (tmp_list);
- }
-
- if (array->len == 0) {
- g_byte_array_free (array, TRUE);
- return NULL;
+ res = g_strdup_printf ("%s/%s: %s", group, setting_name, message);
+ } else
+ res = g_strdup_printf ("%s: %s", group, message);
+ *out_message = res;
+ return res;
} else
- return g_byte_array_free_to_bytes (array);
-}
-
-static void
-ssid_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
-{
- const char *setting_name = nm_setting_get_name (setting);
- GBytes *bytes;
-
- bytes = get_bytes (keyfile, 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);
- }
-}
-
-static void
-password_raw_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
-{
- const char *setting_name = nm_setting_get_name (setting);
- GBytes *bytes;
-
- bytes = get_bytes (keyfile, 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);
- }
-}
-
-static char *
-get_cert_path (const char *keyfile_path, const guint8 *cert_path, gsize cert_path_len)
-{
- const char *base;
- char *p = NULL, *path, *dirname, *tmp;
-
- g_return_val_if_fail (keyfile_path != NULL, NULL);
- g_return_val_if_fail (cert_path != NULL, NULL);
-
- base = path = g_malloc0 (cert_path_len + 1);
- memcpy (path, cert_path, cert_path_len);
-
- if (path[0] == '/')
- return path;
-
- p = strrchr (path, '/');
- if (p)
- base = p + 1;
-
- dirname = g_path_get_dirname (keyfile_path);
- tmp = g_build_path ("/", dirname, base, NULL);
- g_free (dirname);
- g_free (path);
- return tmp;
+ return message;
}
-#define SCHEME_PATH "file://"
-
-static const char *certext[] = { ".pem", ".cert", ".crt", ".cer", ".p12", ".der", ".key" };
-
static gboolean
-has_cert_ext (const char *path)
-{
- int i;
-
- for (i = 0; i < G_N_ELEMENTS (certext); i++) {
- if (g_str_has_suffix (path, certext[i]))
- return TRUE;
- }
- return FALSE;
-}
-
-static gboolean
-handle_as_scheme (GBytes *bytes, NMSetting *setting, const char *key)
-{
- const guint8 *data;
- gsize data_len;
-
- data = g_bytes_get_data (bytes, &data_len);
+_handler_read (GKeyFile *keyfile,
+ NMConnection *connection,
+ NMKeyfileReadType type,
+ void *type_data,
+ void *user_data,
+ GError **error)
+{
+ if (type == NM_KEYFILE_READ_TYPE_WARN) {
+ NMKeyfileReadTypeDataWarn *warn_data = type_data;
+ NMLogLevel level;
+ char *message_free = NULL;
+
+ if (warn_data->severity > NM_KEYFILE_WARN_SEVERITY_WARN)
+ level = LOGL_ERR;
+ else if (warn_data->severity >= NM_KEYFILE_WARN_SEVERITY_WARN)
+ level = LOGL_WARN;
+ else if (warn_data->severity == NM_KEYFILE_WARN_SEVERITY_INFO_MISSING_FILE)
+ level = LOGL_WARN;
+ else
+ level = LOGL_INFO;
- /* It's the PATH scheme, can just set plain data */
- if ( (data_len > strlen (SCHEME_PATH))
- && g_str_has_prefix ((const char *) data, SCHEME_PATH)
- && (data[data_len - 1] == '\0')) {
- g_object_set (setting, key, bytes, NULL);
+ nm_log (level, LOGD_SETTINGS, "keyfile: %s",
+ _fmt_warn (warn_data->group, warn_data->setting,
+ warn_data->property_name, warn_data->message,
+ &message_free));
+ g_free (message_free);
return TRUE;
}
return FALSE;
}
-static gboolean
-handle_as_path (GBytes *bytes,
- NMSetting *setting,
- const char *key,
- const char *keyfile_path)
-{
- const guint8 *data;
- gsize data_len;
- gsize validate_len;
- char *path;
- gboolean exists, success = FALSE;
-
- data = g_bytes_get_data (bytes, &data_len);
- if (data_len > 500 || data_len < 1)
- return FALSE;
-
- /* If there's a trailing NULL tell g_utf8_validate() to to until the NULL */
- if (data[data_len - 1] == '\0')
- validate_len = -1;
- else
- validate_len = data_len;
-
- if (g_utf8_validate ((const char *) data, validate_len, NULL) == FALSE)
- return FALSE;
-
- /* Might be a bare path without the file:// prefix; in that case
- * if it's an absolute path, use that, otherwise treat it as a
- * relative path to the current directory.
- */
-
- path = get_cert_path (keyfile_path, data, data_len);
- exists = g_file_test (path, G_FILE_TEST_EXISTS);
- if ( exists
- || memchr (data, '/', data_len)
- || has_cert_ext (path)) {
- GByteArray *tmp;
- GBytes *val;
-
- /* Construct the proper value as required for the PATH scheme */
- tmp = g_byte_array_sized_new (strlen (SCHEME_PATH) + strlen (path) + 1);
- g_byte_array_append (tmp, (const guint8 *) SCHEME_PATH, strlen (SCHEME_PATH));
- g_byte_array_append (tmp, (const guint8 *) path, strlen (path));
- g_byte_array_append (tmp, (const guint8 *) "\0", 1);
- val = g_byte_array_free_to_bytes (tmp);
- g_object_set (setting, key, val, NULL);
- g_bytes_unref (val);
- success = TRUE;
-
- /* Warn if the certificate didn't exist */
- if (exists == FALSE)
- nm_log_warn (LOGD_SETTINGS, "certificate or key %s does not exist", path);
- }
- g_free (path);
-
- return success;
-}
-
-static void
-cert_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
-{
- const char *setting_name = nm_setting_get_name (setting);
- GBytes *bytes;
- gboolean success = FALSE;
-
- bytes = get_bytes (keyfile, 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);
-
- /* 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);
- }
-
- if (bytes)
- g_bytes_unref (bytes);
-}
-
-static void
-parity_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
-{
- const char *setting_name = nm_setting_get_name (setting);
- NMSettingSerialParity parity;
- int int_val;
- char *str_val;
-
- /* 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);
- if (!int_val) {
- str_val = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, NULL);
- if (str_val) {
- if (str_val[0] && !str_val[1])
- int_val = str_val[0];
- else {
- /* This will hit the warning below */
- int_val = 'X';
- }
- }
- g_free (str_val);
- }
-
- if (!int_val)
- return;
-
- switch (int_val) {
- case 'E':
- case 'e':
- parity = NM_SETTING_SERIAL_PARITY_EVEN;
- break;
- case 'O':
- case 'o':
- parity = NM_SETTING_SERIAL_PARITY_ODD;
- break;
- case 'N':
- case 'n':
- parity = NM_SETTING_SERIAL_PARITY_NONE;
- break;
- default:
- nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid value for %s / %s",
- __func__, setting_name, key);
- return;
- }
-
- g_object_set (setting, key, parity, NULL);
-}
-
-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);
-} KeyParser;
-
-/* A table of keys that require further parsing/conversion because they are
- * stored in a format that can't be automatically read using the key's type.
- * i.e. IPv4 addresses, which are stored in NetworkManager as guint32, but are
- * stored in keyfiles as strings, eg "10.1.1.2" or IPv6 addresses stored
- * in struct in6_addr internally, but as string in keyfiles.
- */
-static KeyParser key_parsers[] = {
- { NM_SETTING_CONNECTION_SETTING_NAME,
- NM_SETTING_CONNECTION_TYPE,
- TRUE,
- setting_alias_parser },
- { NM_SETTING_BRIDGE_SETTING_NAME,
- NM_SETTING_BRIDGE_MAC_ADDRESS,
- TRUE,
- mac_address_parser_ETHER },
- { NM_SETTING_IP4_CONFIG_SETTING_NAME,
- NM_SETTING_IP_CONFIG_ADDRESSES,
- FALSE,
- ip_address_or_route_parser },
- { NM_SETTING_IP6_CONFIG_SETTING_NAME,
- NM_SETTING_IP_CONFIG_ADDRESSES,
- FALSE,
- ip_address_or_route_parser },
- { NM_SETTING_IP4_CONFIG_SETTING_NAME,
- NM_SETTING_IP_CONFIG_ROUTES,
- FALSE,
- ip_address_or_route_parser },
- { NM_SETTING_IP6_CONFIG_SETTING_NAME,
- NM_SETTING_IP_CONFIG_ROUTES,
- FALSE,
- ip_address_or_route_parser },
- { NM_SETTING_IP4_CONFIG_SETTING_NAME,
- NM_SETTING_IP_CONFIG_DNS,
- FALSE,
- ip4_dns_parser },
- { NM_SETTING_IP6_CONFIG_SETTING_NAME,
- NM_SETTING_IP_CONFIG_DNS,
- FALSE,
- ip6_dns_parser },
- { NM_SETTING_WIRED_SETTING_NAME,
- NM_SETTING_WIRED_MAC_ADDRESS,
- TRUE,
- mac_address_parser_ETHER },
- { NM_SETTING_WIRED_SETTING_NAME,
- NM_SETTING_WIRED_CLONED_MAC_ADDRESS,
- TRUE,
- mac_address_parser_ETHER },
- { NM_SETTING_WIRELESS_SETTING_NAME,
- NM_SETTING_WIRELESS_MAC_ADDRESS,
- TRUE,
- mac_address_parser_ETHER },
- { NM_SETTING_WIRELESS_SETTING_NAME,
- NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS,
- TRUE,
- mac_address_parser_ETHER },
- { NM_SETTING_WIRELESS_SETTING_NAME,
- NM_SETTING_WIRELESS_BSSID,
- TRUE,
- mac_address_parser_ETHER },
- { NM_SETTING_BLUETOOTH_SETTING_NAME,
- NM_SETTING_BLUETOOTH_BDADDR,
- TRUE,
- mac_address_parser_ETHER },
- { NM_SETTING_INFINIBAND_SETTING_NAME,
- NM_SETTING_INFINIBAND_MAC_ADDRESS,
- TRUE,
- mac_address_parser_INFINIBAND },
- { NM_SETTING_WIMAX_SETTING_NAME,
- NM_SETTING_WIMAX_MAC_ADDRESS,
- TRUE,
- mac_address_parser_ETHER },
- { NM_SETTING_WIRELESS_SETTING_NAME,
- NM_SETTING_WIRELESS_SSID,
- TRUE,
- ssid_parser },
- { NM_SETTING_802_1X_SETTING_NAME,
- NM_SETTING_802_1X_PASSWORD_RAW,
- TRUE,
- password_raw_parser },
- { NM_SETTING_802_1X_SETTING_NAME,
- NM_SETTING_802_1X_CA_CERT,
- TRUE,
- cert_parser },
- { NM_SETTING_802_1X_SETTING_NAME,
- NM_SETTING_802_1X_CLIENT_CERT,
- TRUE,
- cert_parser },
- { NM_SETTING_802_1X_SETTING_NAME,
- NM_SETTING_802_1X_PRIVATE_KEY,
- TRUE,
- cert_parser },
- { NM_SETTING_802_1X_SETTING_NAME,
- NM_SETTING_802_1X_PHASE2_CA_CERT,
- TRUE,
- cert_parser },
- { NM_SETTING_802_1X_SETTING_NAME,
- NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
- TRUE,
- cert_parser },
- { NM_SETTING_802_1X_SETTING_NAME,
- NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
- TRUE,
- cert_parser },
- { NM_SETTING_SERIAL_SETTING_NAME,
- NM_SETTING_SERIAL_PARITY,
- TRUE,
- parity_parser },
- { NULL, NULL, FALSE }
-};
-
-typedef struct {
- GKeyFile *keyfile;
- const char *keyfile_path;
-} ReadInfo;
-
-static void
-read_one_setting_value (NMSetting *setting,
- const char *key,
- const GValue *value,
- GParamFlags flags,
- gpointer user_data)
-{
- ReadInfo *info = user_data;
- const char *setting_name;
- int errsv;
- GType type;
- GError *err = NULL;
- gboolean check_for_key = TRUE;
- KeyParser *parser = &key_parsers[0];
-
- /* Property is not writable */
- if (!(flags & G_PARAM_WRITABLE))
- return;
-
- /* Setting name gets picked up from the keyfile's section name instead */
- if (!strcmp (key, NM_SETTING_NAME))
- return;
-
- /* Don't read the NMSettingConnection object's 'read-only' property */
- if ( NM_IS_SETTING_CONNECTION (setting)
- && !strcmp (key, NM_SETTING_CONNECTION_READ_ONLY))
- return;
-
- setting_name = nm_setting_get_name (setting);
-
- /* Look through the list of handlers for non-standard format key values */
- while (parser->setting_name) {
- if (!strcmp (parser->setting_name, setting_name) && !strcmp (parser->key, key)) {
- check_for_key = parser->check_for_key;
- break;
- }
- parser++;
- }
-
- /* VPN properties don't have the exact key name */
- if (NM_IS_SETTING_VPN (setting))
- check_for_key = FALSE;
-
- /* Bonding 'options' don't have the exact key name. The options are right under [bond] group. */
- if (NM_IS_SETTING_BOND (setting))
- check_for_key = FALSE;
-
- /* Check for the exact key in the GKeyFile if required. Most setting
- * properties map 1:1 to a key in the GKeyFile, but for those properties
- * 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)) {
- /* 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);
- }
- return;
- }
-
- /* If there's a custom parser for this key, handle that before the generic
- * parsers below.
- */
- if (parser->setting_name) {
- (*parser->parser) (setting, key, info->keyfile, info->keyfile_path);
- return;
- }
-
- type = G_VALUE_TYPE (value);
-
- if (type == G_TYPE_STRING) {
- char *str_val;
-
- str_val = nm_keyfile_plugin_kf_get_string (info->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);
- 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);
- 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);
- 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);
-
- 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);
- 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;
- gint64 int_val;
-
- tmp_str = nm_keyfile_plugin_kf_get_value (info->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
- g_object_set (setting, key, int_val, NULL);
- g_free (tmp_str);
- } else if (type == G_TYPE_BYTES) {
- gint *tmp;
- GByteArray *array;
- GBytes *bytes;
- gsize length;
- int i;
-
- tmp = nm_keyfile_plugin_kf_get_integer_list (info->keyfile, setting_name, key, &length, NULL);
-
- array = g_byte_array_sized_new (length);
- for (i = 0; i < length; i++) {
- int val = tmp[i];
- 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);
- } else
- g_byte_array_append (array, (const unsigned char *) &v, sizeof (v));
- }
-
- bytes = g_byte_array_free_to_bytes (array);
- g_object_set (setting, key, bytes, NULL);
- g_bytes_unref (bytes);
- g_free (tmp);
- } else if (type == G_TYPE_STRV) {
- gchar **sa;
- gsize length;
-
- sa = nm_keyfile_plugin_kf_get_string_list (info->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);
- } 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));
- }
- } 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);
- 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));
- }
- }
- 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);
- 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));
- }
-}
-
-static NMSetting *
-read_setting (GKeyFile *file, const char *keyfile_path, const char *group)
-{
- 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;
-
- type = nm_setting_lookup_type (group);
- 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);
-
- return setting;
-}
-
-static void
-read_vpn_secrets (GKeyFile *file, NMSettingVpn *s_vpn)
-{
- char **keys, **iter;
-
- keys = nm_keyfile_plugin_kf_get_keys (file, 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);
- if (secret) {
- nm_setting_vpn_add_secret (s_vpn, *iter, secret);
- g_free (secret);
- }
- }
- g_strfreev (keys);
-}
-
NMConnection *
nm_keyfile_plugin_connection_from_file (const char *filename, GError **error)
{
@@ -1255,12 +92,6 @@ nm_keyfile_plugin_connection_from_file (const char *filename, GError **error)
struct stat statbuf;
gboolean bad_permissions;
NMConnection *connection = NULL;
- NMSettingConnection *s_con;
- NMSetting *setting;
- gchar **groups;
- gsize length;
- int i;
- gboolean vpn_secrets = FALSE;
GError *verify_error = NULL;
if (stat (filename, &statbuf) != 0 || !S_ISREG (statbuf.st_mode)) {
@@ -1282,77 +113,14 @@ nm_keyfile_plugin_connection_from_file (const char *filename, GError **error)
if (!g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, error))
goto out;
- connection = nm_simple_connection_new ();
-
- groups = g_key_file_get_groups (key_file, &length);
- for (i = 0; i < length; i++) {
- /* Only read out secrets when needed */
- if (!strcmp (groups[i], VPN_SECRETS_GROUP)) {
- vpn_secrets = TRUE;
- continue;
- }
-
- setting = read_setting (key_file, filename, groups[i]);
- if (setting)
- nm_connection_add_setting (connection, setting);
- }
-
- s_con = nm_connection_get_setting_connection (connection);
- if (!s_con) {
- s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
- nm_connection_add_setting (connection, NM_SETTING (s_con));
- }
-
- /* Make sure that we have 'id' even if not explictly specified in the keyfile */
- if (!nm_setting_connection_get_id (s_con)) {
- char *base_name;
-
- base_name = g_path_get_basename (filename);
- 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)) {
- char *hashed_uuid;
-
- hashed_uuid = nm_utils_uuid_generate_from_strings ("keyfile", filename, NULL);
- g_object_set (s_con, NM_SETTING_CONNECTION_UUID, hashed_uuid, NULL);
- g_free (hashed_uuid);
- }
-
- /* Make sure that we have 'interface-name' even if it was specified in the
- * "wrong" (ie, deprecated) group.
- */
- if ( !nm_setting_connection_get_interface_name (s_con)
- && nm_setting_connection_get_connection_type (s_con)) {
- char *interface_name;
-
- interface_name = g_key_file_get_string (key_file,
- nm_setting_connection_get_connection_type (s_con),
- "interface-name",
- NULL);
- if (interface_name) {
- g_object_set (s_con, NM_SETTING_CONNECTION_INTERFACE_NAME, interface_name, NULL);
- g_free (interface_name);
- }
- }
-
- /* Handle vpn secrets after the 'vpn' setting was read */
- if (vpn_secrets) {
- NMSettingVpn *s_vpn;
-
- s_vpn = nm_connection_get_setting_vpn (connection);
- if (s_vpn)
- read_vpn_secrets (key_file, s_vpn);
- }
-
- g_strfreev (groups);
+ connection = nm_keyfile_read (key_file, filename, NULL, _handler_read, NULL, error);
+ if (!connection)
+ goto out;
/* 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",
+ "invalid connection: %s",
verify_error->message);
g_clear_error (&verify_error);
g_object_unref (connection);
@@ -1363,3 +131,4 @@ out:
g_key_file_free (key_file);
return connection;
}
+
diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Blob b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Blob
index 9f4ef62fd4..62e6ae31fd 100644
--- a/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Blob
+++ b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Blob
@@ -8,8 +8,8 @@ type=802-3-ethernet
eap=tls;
identity=Bill Smith
ca-cert=48;130;2;52;48;130;1;161;2;16;2;173;102;126;78;69;254;94;87;111;60;152;25;94;221;192;48;13;6;9;42;134;72;134;247;13;1;1;2;5;0;48;95;49;11;48;9;6;3;85;4;6;19;2;85;83;49;32;48;30;6;3;85;4;10;19;23;82;83;65;32;68;97;116;97;32;83;101;99;117;114;105;116;121;44;32;73;110;99;46;49;46;48;44;6;3;85;4;11;19;37;83;101;99;117;114;101;32;83;101;114;118;101;114;32;67;101;114;116;105;102;105;99;97;116;105;111;110;32;65;117;116;104;111;114;105;116;121;48;30;23;13;57;52;49;49;48;57;48;48;48;48;48;48;90;23;13;49;48;48;49;48;55;50;51;53;57;53;57;90;48;95;49;11;48;9;6;3;85;4;6;19;2;85;83;49;32;48;30;6;3;85;4;10;19;23;82;83;65;32;68;97;116;97;32;83;101;99;117;114;105;116;121;44;32;73;110;99;46;49;46;48;44;6;3;85;4;11;19;37;83;101;99;117;114;101;32;83;101;114;118;101;114;32;67;101;114;116;105;102;105;99;97;116;105;111;110;32;65;117;116;104;111;114;105;116;121;48;129;155;48;13;6;9;42;134;72;134;247;13;1;1;1;5;0;3;129;137;0;48;129;133;2;126;0;146;206;122;193;174;131;62;90;170;137;131;87;172;37;1;118;12;173;174;142;44;55;206;235;53;120;100;84;3;229;132;64;81;201;191;143;8;226;138;130;8;210;22;134;55;85;233;177;33;2;173;118;104;129;154;5;162;75;201;75;37;102;34;86;108;136;7;143;247;129;89;109;132;7;101;112;19;113;118;62;155;119;76;227;80;137;86;152;72;185;29;167;41;26;19;46;74;17;89;156;30;21;213;73;84;44;115;58;105;130;177;151;57;156;109;112;103;72;229;221;45;214;200;30;123;2;3;1;0;1;48;13;6;9;42;134;72;134;247;13;1;1;2;5;0;3;126;0;101;221;126;225;178;236;176;226;58;224;236;113;70;154;25;17;184;211;199;160;180;3;64;38;2;62;9;156;225;18;179;209;90;246;55;165;183;97;3;182;91;22;105;59;198;68;8;12;136;83;12;107;151;73;199;62;53;220;108;185;187;170;223;92;187;58;47;147;96;182;169;75;77;242;32;247;205;95;127;100;123;142;220;0;92;215;250;119;202;57;22;89;111;14;234;211;181;131;127;77;77;66;86;118;180;201;95;4;248;56;248;235;210;95;117;95;205;123;252;229;142;128;124;252;80;
-client-cert=102;105;108;101;58;47;47;47;104;111;109;101;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;99;108;105;101;110;116;46;112;101;109;0;
-private-key=102;105;108;101;58;47;47;47;104;111;109;101;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;99;108;105;101;110;116;46;112;101;109;0;
+client-cert=102;105;108;101;58;47;47;47;67;65;83;65;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;99;108;105;101;110;116;46;112;101;109;0;
+private-key=102;105;108;101;58;47;47;47;67;65;83;65;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;99;108;105;101;110;116;46;112;101;109;0;
private-key-password=12345testing
[ipv4]
diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Old b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Old
index 61afdd91c8..d3da598c81 100644
--- a/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Old
+++ b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Old
@@ -7,9 +7,9 @@ type=802-3-ethernet
[802-1x]
eap=tls;
identity=Bill Smith
-ca-cert=102;105;108;101;58;47;47;47;104;111;109;101;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;67;65;47;101;97;112;116;101;115;116;95;99;97;95;99;101;114;116;46;112;101;109;0;
-client-cert=102;105;108;101;58;47;47;47;104;111;109;101;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;99;108;105;101;110;116;46;112;101;109;0;
-private-key=102;105;108;101;58;47;47;47;104;111;109;101;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;99;108;105;101;110;116;46;112;101;109;0;
+ca-cert=102;105;108;101;58;47;47;47;67;65;83;65;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;67;65;47;101;97;112;116;101;115;116;95;99;97;95;99;101;114;116;46;112;101;109;0;
+client-cert=102;105;108;101;58;47;47;47;67;65;83;65;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;99;108;105;101;110;116;46;112;101;109;0;
+private-key=102;105;108;101;58;47;47;47;67;65;83;65;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;99;108;105;101;110;116;46;112;101;109;0;
private-key-password=12345testing
[ipv4]
diff --git a/src/settings/plugins/keyfile/tests/test-keyfile.c b/src/settings/plugins/keyfile/tests/test-keyfile.c
index b1de5d3ca1..a86bfed6c2 100644
--- a/src/settings/plugins/keyfile/tests/test-keyfile.c
+++ b/src/settings/plugins/keyfile/tests/test-keyfile.c
@@ -100,33 +100,33 @@ test_read_valid_wired_connection (void)
const char *expected6_dnssearch3 = "gnu.org";
g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
- "*ipv4.addresses1*semicolon at the end*");
+ "*ipv4.addresses:*semicolon at the end*addresses1*");
g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
- "*ipv4.addresses2*semicolon at the end*");
+ "*ipv4.addresses:*semicolon at the end*addresses2*");
g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
- "*Missing prefix length*ipv4.address4*");
+ "*missing prefix length*address4*");
g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
- "*Missing prefix length*ipv4.address5*");
+ "*missing prefix length*address5*");
g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
- "*ipv4.routes2*semicolon at the end*");
+ "*ipv4.routes*semicolon at the end*routes2*");
g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
- "*ipv4.routes3*semicolon at the end*");
+ "*ipv4.routes*semicolon at the end*routes3*");
g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
- "*ipv4.routes5*semicolon at the end*");
+ "*ipv4.routes*semicolon at the end*routes5*");
g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
- "*ipv4.routes8*semicolon at the end*");
+ "*ipv4.routes*semicolon at the end*routes8*");
g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
- "*Missing prefix length*ipv6.address4*");
+ "*missing prefix length*address4*");
g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
- "*ipv6.address5*semicolon at the end*");
+ "*ipv6.address*semicolon at the end*address5*");
g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
- "*Missing prefix length*ipv6.address5*");
+ "*missing prefix length*address5*");
g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
- "*ipv6.address7*semicolon at the end*");
+ "*ipv6.address*semicolon at the end*address7*");
g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
- "*ipv6.routes1*semicolon at the end*");
+ "*ipv6.routes*semicolon at the end*routes1*");
g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
- "*ipv6.route6*semicolon at the end*");
+ "*ipv6.route*semicolon at the end*route6*");
connection = nm_keyfile_plugin_connection_from_file (TEST_WIRED_FILE, NULL);
g_test_assert_expected_messages ();
ASSERT (connection != NULL,
@@ -773,11 +773,11 @@ test_read_wired_mac_case (void)
const char *expected_uuid = "4e80a56d-c99f-4aad-a6dd-b449bc398c57";
g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
- "*ipv4.addresses1*semicolon at the end*");
+ "*ipv4.addresses*semicolon at the end*addresses1*");
g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
- "*ipv4.addresses2*semicolon at the end*");
+ "*ipv4.addresses*semicolon at the end*addresses2*");
g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
- "*ipv6.routes1*semicolon at the end*");
+ "*ipv6.routes*semicolon at the end*routes1*");
connection = nm_keyfile_plugin_connection_from_file (TEST_WIRED_MAC_CASE_FILE, NULL);
g_test_assert_expected_messages ();
ASSERT (connection != NULL,
@@ -2127,6 +2127,10 @@ test_read_wired_8021x_tls_blob_connection (void)
gboolean success;
GBytes *blob;
+ g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+ "*<warn> keyfile: 802-1x.client-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*");
+ g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+ "*<warn> keyfile: 802-1x.private-key: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*");
connection = nm_keyfile_plugin_connection_from_file (TEST_WIRED_TLS_BLOB_FILE, &error);
if (connection == NULL) {
g_assert (error);
@@ -2174,10 +2178,10 @@ test_read_wired_8021x_tls_blob_connection (void)
g_assert_cmpint (g_bytes_get_size (blob), ==, 568);
tmp = nm_setting_802_1x_get_client_cert_path (s_8021x);
- g_assert_cmpstr (tmp, ==, "/home/dcbw/Desktop/certinfra/client.pem");
+ g_assert_cmpstr (tmp, ==, "/CASA/dcbw/Desktop/certinfra/client.pem");
tmp = nm_setting_802_1x_get_private_key_path (s_8021x);
- g_assert_cmpstr (tmp, ==, "/home/dcbw/Desktop/certinfra/client.pem");
+ g_assert_cmpstr (tmp, ==, "/CASA/dcbw/Desktop/certinfra/client.pem");
g_object_unref (connection);
}
@@ -2259,6 +2263,12 @@ test_read_wired_8021x_tls_old_connection (void)
const char *tmp;
gboolean success;
+ g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+ "*<warn> keyfile: 802-1x.ca-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/CA/eaptest_ca_cert.pem' does not exist*");
+ g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+ "*<warn> keyfile: 802-1x.client-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*");
+ g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+ "*<warn> keyfile: 802-1x.private-key: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*");
connection = nm_keyfile_plugin_connection_from_file (TEST_WIRED_TLS_OLD_FILE, &error);
if (connection == NULL) {
g_assert (error);
@@ -2292,13 +2302,13 @@ test_read_wired_8021x_tls_old_connection (void)
g_assert (g_strcmp0 (tmp, "12345testing") == 0);
tmp = nm_setting_802_1x_get_ca_cert_path (s_8021x);
- g_assert (g_strcmp0 (tmp, "/home/dcbw/Desktop/certinfra/CA/eaptest_ca_cert.pem") == 0);
+ g_assert (g_strcmp0 (tmp, "/CASA/dcbw/Desktop/certinfra/CA/eaptest_ca_cert.pem") == 0);
tmp = nm_setting_802_1x_get_client_cert_path (s_8021x);
- g_assert (g_strcmp0 (tmp, "/home/dcbw/Desktop/certinfra/client.pem") == 0);
+ g_assert (g_strcmp0 (tmp, "/CASA/dcbw/Desktop/certinfra/client.pem") == 0);
tmp = nm_setting_802_1x_get_private_key_path (s_8021x);
- g_assert (g_strcmp0 (tmp, "/home/dcbw/Desktop/certinfra/client.pem") == 0);
+ g_assert (g_strcmp0 (tmp, "/CASA/dcbw/Desktop/certinfra/client.pem") == 0);
g_object_unref (connection);
}
diff --git a/src/settings/plugins/keyfile/utils.c b/src/settings/plugins/keyfile/utils.c
index 30400b1bc4..4809c642c0 100644
--- a/src/settings/plugins/keyfile/utils.c
+++ b/src/settings/plugins/keyfile/utils.c
@@ -146,178 +146,3 @@ nm_keyfile_plugin_utils_escape_filename (const char *filename)
return g_string_free (str, FALSE);;
}
-
-typedef struct {
- const char *setting;
- const char *alias;
-} SettingAlias;
-
-static const SettingAlias alias_list[] = {
- { NM_SETTING_WIRED_SETTING_NAME, "ethernet" },
- { NM_SETTING_WIRELESS_SETTING_NAME, "wifi" },
- { NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, "wifi-security" },
-};
-
-const char *
-nm_keyfile_plugin_get_alias_for_setting_name (const char *setting_name)
-{
- guint i;
-
- g_return_val_if_fail (setting_name != NULL, NULL);
-
- for (i = 0; i < G_N_ELEMENTS (alias_list); i++) {
- if (strcmp (setting_name, alias_list[i].setting) == 0)
- return alias_list[i].alias;
- }
- return NULL;
-}
-
-const char *
-nm_keyfile_plugin_get_setting_name_for_alias (const char *alias)
-{
- guint i;
-
- g_return_val_if_fail (alias != NULL, NULL);
-
- for (i = 0; i < G_N_ELEMENTS (alias_list); i++) {
- if (strcmp (alias, alias_list[i].alias) == 0)
- return alias_list[i].setting;
- }
- return NULL;
-}
-
-/**********************************************************************/
-
-/* List helpers */
-#define DEFINE_KF_LIST_WRAPPER(stype, get_ctype, set_ctype) \
-get_ctype \
-nm_keyfile_plugin_kf_get_##stype##_list (GKeyFile *kf, \
- const char *group, \
- const char *key, \
- gsize *out_length, \
- GError **error) \
-{ \
- get_ctype list; \
- const char *alias; \
- GError *local = NULL; \
- \
- list = g_key_file_get_##stype##_list (kf, group, key, out_length, &local); \
- if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) { \
- alias = nm_keyfile_plugin_get_alias_for_setting_name (group); \
- if (alias) { \
- g_clear_error (&local); \
- list = g_key_file_get_##stype##_list (kf, alias, key, out_length, &local); \
- } \
- } \
- if (local) \
- g_propagate_error (error, local); \
- return list; \
-} \
- \
-void \
-nm_keyfile_plugin_kf_set_##stype##_list (GKeyFile *kf, \
- const char *group, \
- const char *key, \
- set_ctype list[], \
- gsize length) \
-{ \
- const char *alias; \
- \
- alias = nm_keyfile_plugin_get_alias_for_setting_name (group); \
- g_key_file_set_##stype##_list (kf, alias ? alias : group, key, list, length); \
-}
-
-DEFINE_KF_LIST_WRAPPER(integer, gint*, gint);
-DEFINE_KF_LIST_WRAPPER(string, gchar **, const gchar* const);
-
-/* Single value helpers */
-#define DEFINE_KF_WRAPPER(stype, get_ctype, set_ctype) \
-get_ctype \
-nm_keyfile_plugin_kf_get_##stype (GKeyFile *kf, \
- const char *group, \
- const char *key, \
- GError **error) \
-{ \
- get_ctype val; \
- const char *alias; \
- GError *local = NULL; \
- \
- val = g_key_file_get_##stype (kf, group, key, &local); \
- if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) { \
- alias = nm_keyfile_plugin_get_alias_for_setting_name (group); \
- if (alias) { \
- g_clear_error (&local); \
- val = g_key_file_get_##stype (kf, alias, key, &local); \
- } \
- } \
- if (local) \
- g_propagate_error (error, local); \
- return val; \
-} \
- \
-void \
-nm_keyfile_plugin_kf_set_##stype (GKeyFile *kf, \
- const char *group, \
- const char *key, \
- set_ctype value) \
-{ \
- const char *alias; \
- \
- alias = nm_keyfile_plugin_get_alias_for_setting_name (group); \
- g_key_file_set_##stype (kf, alias ? alias : group, key, value); \
-}
-
-DEFINE_KF_WRAPPER(string, gchar*, const gchar*);
-DEFINE_KF_WRAPPER(integer, gint, gint);
-DEFINE_KF_WRAPPER(uint64, guint64, guint64);
-DEFINE_KF_WRAPPER(boolean, gboolean, gboolean);
-DEFINE_KF_WRAPPER(value, gchar*, const gchar*);
-
-
-gchar **
-nm_keyfile_plugin_kf_get_keys (GKeyFile *kf,
- const char *group,
- gsize *out_length,
- GError **error)
-{
- gchar **keys;
- const char *alias;
- GError *local = NULL;
-
- keys = g_key_file_get_keys (kf, group, out_length, &local);
- if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) {
- alias = nm_keyfile_plugin_get_alias_for_setting_name (group);
- if (alias) {
- g_clear_error (&local);
- keys = g_key_file_get_keys (kf, alias, out_length, &local);
- }
- }
- if (local)
- g_propagate_error (error, local);
- return keys;
-}
-
-gboolean
-nm_keyfile_plugin_kf_has_key (GKeyFile *kf,
- const char *group,
- const char *key,
- GError **error)
-{
- gboolean has;
- const char *alias;
- GError *local = NULL;
-
- has = g_key_file_has_key (kf, group, key, &local);
- if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) {
- alias = nm_keyfile_plugin_get_alias_for_setting_name (group);
- if (alias) {
- g_clear_error (&local);
- has = g_key_file_has_key (kf, alias, key, &local);
- }
- }
- if (local)
- g_propagate_error (error, local);
- return has;
-}
-
-
diff --git a/src/settings/plugins/keyfile/utils.h b/src/settings/plugins/keyfile/utils.h
index 456cd1a88f..d0862284cd 100644
--- a/src/settings/plugins/keyfile/utils.h
+++ b/src/settings/plugins/keyfile/utils.h
@@ -22,7 +22,6 @@
#define _UTILS_H_
#include <glib.h>
-#include "common.h"
#include "NetworkManagerUtils.h"
#define NM_KEYFILE_CONNECTION_LOG_PATH(path) str_if_set (path,"in-memory")
@@ -35,55 +34,5 @@ gboolean nm_keyfile_plugin_utils_should_ignore_file (const char *filename);
char *nm_keyfile_plugin_utils_escape_filename (const char *filename);
-const char *nm_keyfile_plugin_get_alias_for_setting_name (const char *setting_name);
-
-const char *nm_keyfile_plugin_get_setting_name_for_alias (const char *alias);
-
-/*********************************************************/
-
-/* List helpers */
-#define DEFINE_KF_LIST_WRAPPER_PROTO(stype, get_ctype, set_ctype) \
-get_ctype nm_keyfile_plugin_kf_get_##stype##_list (GKeyFile *kf, \
- const char *group, \
- const char *key, \
- gsize *out_length, \
- GError **error); \
-\
-void nm_keyfile_plugin_kf_set_##stype##_list (GKeyFile *kf, \
- const char *group, \
- const char *key, \
- set_ctype list[], \
- gsize length);
-DEFINE_KF_LIST_WRAPPER_PROTO(integer, gint*, gint)
-DEFINE_KF_LIST_WRAPPER_PROTO(string, gchar**, const gchar* const)
-
-/* Single-value helpers */
-#define DEFINE_KF_WRAPPER_PROTO(stype, get_ctype, set_ctype) \
-get_ctype nm_keyfile_plugin_kf_get_##stype (GKeyFile *kf, \
- const char *group, \
- const char *key, \
- GError **error); \
-\
-void nm_keyfile_plugin_kf_set_##stype (GKeyFile *kf, \
- const char *group, \
- const char *key, \
- set_ctype value);
-DEFINE_KF_WRAPPER_PROTO(string, gchar*, const gchar*)
-DEFINE_KF_WRAPPER_PROTO(integer, gint, gint)
-DEFINE_KF_WRAPPER_PROTO(uint64, guint64, guint64)
-DEFINE_KF_WRAPPER_PROTO(boolean, gboolean, gboolean)
-DEFINE_KF_WRAPPER_PROTO(value, gchar*, const gchar*)
-
-/* Misc */
-gchar ** nm_keyfile_plugin_kf_get_keys (GKeyFile *kf,
- const char *group,
- gsize *out_length,
- GError **error);
-
-gboolean nm_keyfile_plugin_kf_has_key (GKeyFile *kf,
- const char *group,
- const char *key,
- GError **error);
-
#endif /* _UTILS_H_ */
diff --git a/src/settings/plugins/keyfile/writer.c b/src/settings/plugins/keyfile/writer.c
index 314ff11608..9cf119c0c8 100644
--- a/src/settings/plugins/keyfile/writer.c
+++ b/src/settings/plugins/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"
@@ -24,407 +24,20 @@
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
-#include <stdio.h>
#include <errno.h>
-
-#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 <string.h>
-#include <arpa/inet.h>
-#include "nm-glib-compat.h"
#include "nm-logging.h"
#include "writer.h"
#include "common.h"
#include "utils.h"
+#include "nm-keyfile-internal.h"
-/* Some setting properties also contain setting names, such as
- * NMSettingConnection's 'type' property (which specifies the base type of the
- * connection, eg ethernet or wifi) or the 802-11-wireless setting's
- * 'security' property which specifies whether or not the AP requires
- * encrpytion. This function handles translating those properties' values
- * from the real setting name to the more-readable alias.
- */
-static void
-setting_alias_writer (GKeyFile *file,
- const char *keyfile_dir,
- const char *uuid,
- NMSetting *setting,
- const char *key,
- const GValue *value)
-{
- const char *str, *alias;
-
- str = g_value_get_string (value);
- alias = nm_keyfile_plugin_get_alias_for_setting_name (str);
- nm_keyfile_plugin_kf_set_string (file,
- nm_setting_get_name (setting),
- key,
- alias ? alias : str);
-}
-
-static gboolean
-write_array_of_uint (GKeyFile *file,
- NMSetting *setting,
- const char *key,
- const GValue *value)
-{
- GArray *array;
- int i;
- int *tmp_array;
-
- array = (GArray *) g_value_get_boxed (value);
- if (!array || !array->len)
- return TRUE;
-
- tmp_array = g_new (gint, array->len);
- for (i = 0; i < array->len; i++)
- tmp_array[i] = g_array_index (array, int, i);
-
- 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,
- NMSetting *setting,
- const char *key,
- const GValue *value)
-{
- char **list;
-
- list = g_value_get_boxed (value);
- if (list && list[0]) {
- nm_keyfile_plugin_kf_set_string_list (file, nm_setting_get_name (setting), key,
- (const char **) list, g_strv_length (list));
- }
-}
-
-static void
-write_ip_values (GKeyFile *file,
- const char *setting_name,
- GPtrArray *array,
- const char *gateway,
- gboolean is_route)
-{
- GString *output;
- int family, i;
- const char *addr, *gw;
- guint32 plen, metric;
- char key_name[30], *key_name_idx;
-
- if (!array->len)
- return;
-
- family = !strcmp (setting_name, NM_SETTING_IP4_CONFIG_SETTING_NAME) ? AF_INET : AF_INET6;
-
- strcpy (key_name, is_route ? "route" : "address");
- key_name_idx = key_name + strlen (key_name);
-
- output = g_string_sized_new (2*INET_ADDRSTRLEN + 10);
- for (i = 0; i < array->len; i++) {
- if (is_route) {
- NMIPRoute *route = array->pdata[i];
-
- addr = nm_ip_route_get_dest (route);
- plen = nm_ip_route_get_prefix (route);
- gw = nm_ip_route_get_next_hop (route);
- metric = MAX (0, nm_ip_route_get_metric (route));
- } else {
- NMIPAddress *address = array->pdata[i];
-
- addr = nm_ip_address_get_address (address);
- plen = nm_ip_address_get_prefix (address);
- gw = i == 0 ? gateway : NULL;
- metric = 0;
- }
-
- g_string_set_size (output, 0);
- g_string_append_printf (output, "%s/%u", addr, plen);
- if (metric || gw) {
- /* Older versions of the plugin do not support the form
- * "a.b.c.d/plen,,metric", so, we always have to write the
- * gateway, even if there isn't one.
- * The current version supports reading of the above form.
- */
- if (!gw) {
- if (family == AF_INET)
- gw = "0.0.0.0";
- else
- gw = "::";
- }
-
- g_string_append_printf (output, ",%s", gw);
- if (metric)
- g_string_append_printf (output, ",%lu", (unsigned long) metric);
- }
-
- sprintf (key_name_idx, "%d", i + 1);
- nm_keyfile_plugin_kf_set_string (file, setting_name, key_name, output->str);
- }
- g_string_free (output, TRUE);
-}
-
-static void
-addr_writer (GKeyFile *file,
- const char *keyfile_dir,
- const char *uuid,
- NMSetting *setting,
- const char *key,
- const GValue *value)
-{
- GPtrArray *array;
- const char *setting_name = nm_setting_get_name (setting);
- const char *gateway = nm_setting_ip_config_get_gateway (NM_SETTING_IP_CONFIG (setting));
-
- array = (GPtrArray *) g_value_get_boxed (value);
- if (array && array->len)
- write_ip_values (file, setting_name, array, gateway, FALSE);
-}
-
-static void
-ip4_addr_label_writer (GKeyFile *file,
- const char *keyfile_dir,
- const char *uuid,
- NMSetting *setting,
- const char *key,
- const GValue *value)
-{
- /* skip */
-}
-
-static void
-gateway_writer (GKeyFile *file,
- const char *keyfile_dir,
- const char *uuid,
- NMSetting *setting,
- const char *key,
- const GValue *value)
-{
- /* skip */
-}
-
-static void
-route_writer (GKeyFile *file,
- const char *keyfile_dir,
- const char *uuid,
- NMSetting *setting,
- const char *key,
- const GValue *value)
-{
- GPtrArray *array;
- const char *setting_name = nm_setting_get_name (setting);
-
- array = (GPtrArray *) g_value_get_boxed (value);
- if (array && array->len)
- write_ip_values (file, setting_name, array, NULL, TRUE);
-}
-
-static void
-write_hash_of_string (GKeyFile *file,
- NMSetting *setting,
- const char *key,
- const GValue *value)
-{
- GHashTableIter iter;
- const char *property = NULL, *data = NULL;
- const char *group_name = nm_setting_get_name (setting);
- gboolean vpn_secrets = FALSE;
-
- /* Write VPN secrets out to a different group to keep them separate */
- if (NM_IS_SETTING_VPN (setting) && !strcmp (key, NM_SETTING_VPN_SECRETS)) {
- group_name = VPN_SECRETS_GROUP;
- vpn_secrets = TRUE;
- }
-
- g_hash_table_iter_init (&iter, (GHashTable *) g_value_get_boxed (value));
- while (g_hash_table_iter_next (&iter, (gpointer *) &property, (gpointer *) &data)) {
- gboolean write_item = TRUE;
-
- /* Handle VPN secrets specially; they are nested in the property's hash;
- * we don't want to write them if the secret is not saved, not required,
- * or owned by a user's secret agent.
- */
- if (vpn_secrets) {
- NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
-
- nm_setting_get_secret_flags (setting, property, &secret_flags, NULL);
- if (secret_flags != NM_SETTING_SECRET_FLAG_NONE)
- write_item = FALSE;
- }
-
- if (write_item)
- nm_keyfile_plugin_kf_set_string (file, group_name, property, data);
- }
-}
-
-static void
-ssid_writer (GKeyFile *file,
- const char *keyfile_dir,
- const char *uuid,
- NMSetting *setting,
- const char *key,
- const GValue *value)
-{
- GBytes *bytes;
- const guint8 *ssid_data;
- gsize ssid_len;
- const char *setting_name = nm_setting_get_name (setting);
- gboolean new_format = TRUE;
- unsigned int semicolons = 0;
- int i, *tmp_array;
- char *ssid;
-
- g_return_if_fail (G_VALUE_HOLDS (value, G_TYPE_BYTES));
-
- bytes = g_value_get_boxed (value);
- if (!bytes)
- return;
- ssid_data = g_bytes_get_data (bytes, &ssid_len);
- if (ssid_len == 0)
- return;
-
- /* Check whether each byte is printable. If not, we have to use an
- * integer list, otherwise we can just use a string.
- */
- for (i = 0; i < ssid_len; i++) {
- char c = ssid_data[i] & 0xFF;
- if (!g_ascii_isprint (c)) {
- new_format = FALSE;
- break;
- }
- if (c == ';')
- semicolons++;
- }
- if (new_format) {
- ssid = g_malloc0 (ssid_len + semicolons + 1);
- if (semicolons == 0)
- memcpy (ssid, ssid_data, ssid_len);
- else {
- /* Escape semicolons with backslashes to make strings
- * containing ';', such as '16;17;' unambiguous */
- int j = 0;
- for (i = 0; i < ssid_len; i++) {
- if (ssid_data[i] == ';')
- ssid[j++] = '\\';
- ssid[j++] = ssid_data[i];
- }
- }
- nm_keyfile_plugin_kf_set_string (file, 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);
- g_free (tmp_array);
- }
-}
-
-static void
-password_raw_writer (GKeyFile *file,
- const char *keyfile_dir,
- const char *uuid,
- NMSetting *setting,
- const char *key,
- const GValue *value)
-{
- const char *setting_name = nm_setting_get_name (setting);
- GBytes *array;
- int *tmp_array;
- gsize i, len;
- const char *data;
-
- g_return_if_fail (G_VALUE_HOLDS (value, G_TYPE_BYTES));
-
- array = (GBytes *) g_value_get_boxed (value);
- if (!array)
- return;
- data = g_bytes_get_data (array, &len);
- if (!data || !len)
- return;
-
- 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);
- g_free (tmp_array);
-}
+typedef struct {
+ const char *keyfile_dir;
+} WriteInfo;
-typedef struct ObjectType {
- const char *key;
- const char *suffix;
- const char *privkey_pw_prop;
- NMSetting8021xCKScheme (*scheme_func) (NMSetting8021x *setting);
- NMSetting8021xCKFormat (*format_func) (NMSetting8021x *setting);
- const char * (*path_func) (NMSetting8021x *setting);
- GBytes * (*blob_func) (NMSetting8021x *setting);
-} ObjectType;
-
-static const ObjectType objtypes[10] = {
- { NM_SETTING_802_1X_CA_CERT,
- "ca-cert",
- NULL,
- nm_setting_802_1x_get_ca_cert_scheme,
- NULL,
- nm_setting_802_1x_get_ca_cert_path,
- nm_setting_802_1x_get_ca_cert_blob },
-
- { NM_SETTING_802_1X_PHASE2_CA_CERT,
- "inner-ca-cert",
- NULL,
- nm_setting_802_1x_get_phase2_ca_cert_scheme,
- NULL,
- nm_setting_802_1x_get_phase2_ca_cert_path,
- nm_setting_802_1x_get_phase2_ca_cert_blob },
-
- { NM_SETTING_802_1X_CLIENT_CERT,
- "client-cert",
- NULL,
- nm_setting_802_1x_get_client_cert_scheme,
- NULL,
- nm_setting_802_1x_get_client_cert_path,
- nm_setting_802_1x_get_client_cert_blob },
-
- { NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
- "inner-client-cert",
- NULL,
- nm_setting_802_1x_get_phase2_client_cert_scheme,
- NULL,
- nm_setting_802_1x_get_phase2_client_cert_path,
- nm_setting_802_1x_get_phase2_client_cert_blob },
-
- { NM_SETTING_802_1X_PRIVATE_KEY,
- "private-key",
- NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD,
- nm_setting_802_1x_get_private_key_scheme,
- nm_setting_802_1x_get_private_key_format,
- nm_setting_802_1x_get_private_key_path,
- nm_setting_802_1x_get_private_key_blob },
-
- { NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
- "inner-private-key",
- NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD,
- nm_setting_802_1x_get_phase2_private_key_scheme,
- nm_setting_802_1x_get_phase2_private_key_format,
- nm_setting_802_1x_get_phase2_private_key_path,
- nm_setting_802_1x_get_phase2_private_key_blob },
-
- { NULL },
-};
static gboolean
write_cert_key_file (const char *path,
@@ -490,61 +103,75 @@ out:
}
static void
-cert_writer (GKeyFile *file,
- const char *keyfile_dir,
- const char *uuid,
- NMSetting *setting,
- const char *key,
- const GValue *value)
+cert_writer (NMConnection *connection,
+ GKeyFile *file,
+ NMKeyfileWriteTypeDataCert *cert_data,
+ WriteInfo *info,
+ GError **error)
{
- const char *setting_name = nm_setting_get_name (setting);
+ const char *setting_name = nm_setting_get_name (NM_SETTING (cert_data->setting));
NMSetting8021xCKScheme scheme;
NMSetting8021xCKFormat format;
const char *path = NULL, *ext = "pem";
- const ObjectType *objtype = NULL;
- int i;
- for (i = 0; i < G_N_ELEMENTS (objtypes) && objtypes[i].key; i++) {
- if (g_strcmp0 (objtypes[i].key, key) == 0) {
- objtype = &objtypes[i];
- break;
- }
- }
- if (!objtype) {
- g_return_if_fail (objtype);
- return;
- }
-
- scheme = objtype->scheme_func (NM_SETTING_802_1X (setting));
+ scheme = cert_data->scheme_func (cert_data->setting);
if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) {
- path = objtype->path_func (NM_SETTING_802_1X (setting));
+ char *tmp = NULL;
+ const char *accepted_path = NULL;
+
+ path = cert_data->path_func (cert_data->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++;
+ if (g_str_has_prefix (path, info->keyfile_dir)) {
+ const char *p = path + strlen (info->keyfile_dir);
+
+ /* If the path is rooted in the keyfile directory, just use a
+ * relative path instead of an absolute one.
+ */
+ if (*p == '/') {
+ while (*p == '/')
+ p++;
+ if (p[0]) {
+ /* If @p looks like an integer list, the following detection will fail too and
+ * we will file:// qualify the path below. We thus avoid writing a path string
+ * that would be interpreted as legacy binary format by reader. */
+ tmp = nm_keyfile_detect_unqualified_path_scheme (info->keyfile_dir, p, -1, FALSE, NULL);
+ if (tmp) {
+ g_clear_pointer (&tmp, g_free);
+ accepted_path = p;
+ }
+ }
+ }
+ }
+ if (!accepted_path) {
+ /* What we are about to write, must also be understood by the reader.
+ * Otherwise, add a file:// prefix */
+ tmp = nm_keyfile_detect_unqualified_path_scheme (info->keyfile_dir, path, -1, FALSE, NULL);
+ if (tmp) {
+ g_clear_pointer (&tmp, g_free);
+ accepted_path = path;
+ }
}
- nm_keyfile_plugin_kf_set_string (file, setting_name, key, path);
+ if (!accepted_path)
+ accepted_path = tmp = g_strconcat (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH, path, NULL);
+ nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->property_name, accepted_path);
+ g_free (tmp);
} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
GBytes *blob;
const guint8 *blob_data;
gsize blob_len;
gboolean success;
- GError *error = NULL;
+ GError *local = NULL;
char *new_path;
- blob = objtype->blob_func (NM_SETTING_802_1X (setting));
+ blob = cert_data->blob_func (cert_data->setting);
g_assert (blob);
blob_data = g_bytes_get_data (blob, &blob_len);
- if (objtype->format_func) {
+ if (cert_data->format_func) {
/* Get the extension for a private key */
- format = objtype->format_func (NM_SETTING_802_1X (setting));
+ format = cert_data->format_func (cert_data->setting);
if (format == NM_SETTING_802_1X_CK_FORMAT_PKCS12)
ext = "p12";
} else {
@@ -556,226 +183,46 @@ cert_writer (GKeyFile *file,
/* 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);
+ new_path = g_strdup_printf ("%s/%s-%s.%s", info->keyfile_dir, nm_connection_get_uuid (connection),
+ cert_data->suffix, ext);
- success = write_cert_key_file (new_path, blob_data, blob_len, &error);
+ success = write_cert_key_file (new_path, blob_data, blob_len, &local);
if (success) {
- /* Write the path value to the keyfile */
- nm_keyfile_plugin_kf_set_string (file, setting_name, key, new_path);
+ /* Write the path value to the keyfile.
+ * We know, that basename(new_path) starts with a UUID, hence no conflict with "data:;base64," */
+ nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->property_name, strrchr (new_path, '/') + 1);
} else {
- nm_log_warn (LOGD_SETTINGS, "Failed to write certificate/key %s: %s",
- new_path, error->message);
- g_error_free (error);
+ nm_log_warn (LOGD_SETTINGS, "keyfile: %s.%s: failed to write certificate to file %s: %s",
+ setting_name, cert_data->property_name, new_path, local->message);
+ g_error_free (local);
}
g_free (new_path);
- } else
- g_assert_not_reached ();
+ } 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. */
+ g_return_if_reached ();
+ }
}
-typedef struct {
- const char *setting_name;
- const char *key;
- void (*writer) (GKeyFile *keyfile,
- const char *keyfile_dir,
- const char *uuid,
- NMSetting *setting,
- const char *key,
- const GValue *value);
-} KeyWriter;
-
-/* A table of keys that require further parsing/conversion because they are
- * stored in a format that can't be automatically read using the key's type.
- * i.e. IPv4 addresses, which are stored in NetworkManager as guint32, but are
- * stored in keyfiles as strings, eg "10.1.1.2" or IPv6 addresses stored
- * in struct in6_addr internally, but as string in keyfiles.
- */
-static KeyWriter key_writers[] = {
- { NM_SETTING_CONNECTION_SETTING_NAME,
- NM_SETTING_CONNECTION_TYPE,
- setting_alias_writer },
- { NM_SETTING_IP4_CONFIG_SETTING_NAME,
- NM_SETTING_IP_CONFIG_ADDRESSES,
- addr_writer },
- { NM_SETTING_IP4_CONFIG_SETTING_NAME,
- "address-labels",
- ip4_addr_label_writer },
- { NM_SETTING_IP6_CONFIG_SETTING_NAME,
- NM_SETTING_IP_CONFIG_ADDRESSES,
- addr_writer },
- { NM_SETTING_IP4_CONFIG_SETTING_NAME,
- NM_SETTING_IP_CONFIG_GATEWAY,
- gateway_writer },
- { NM_SETTING_IP6_CONFIG_SETTING_NAME,
- NM_SETTING_IP_CONFIG_GATEWAY,
- gateway_writer },
- { NM_SETTING_IP4_CONFIG_SETTING_NAME,
- NM_SETTING_IP_CONFIG_ROUTES,
- route_writer },
- { NM_SETTING_IP6_CONFIG_SETTING_NAME,
- NM_SETTING_IP_CONFIG_ROUTES,
- route_writer },
- { NM_SETTING_IP4_CONFIG_SETTING_NAME,
- NM_SETTING_IP_CONFIG_DNS,
- dns_writer },
- { NM_SETTING_IP6_CONFIG_SETTING_NAME,
- NM_SETTING_IP_CONFIG_DNS,
- dns_writer },
- { NM_SETTING_WIRELESS_SETTING_NAME,
- NM_SETTING_WIRELESS_SSID,
- ssid_writer },
- { NM_SETTING_802_1X_SETTING_NAME,
- NM_SETTING_802_1X_PASSWORD_RAW,
- password_raw_writer },
- { NM_SETTING_802_1X_SETTING_NAME,
- NM_SETTING_802_1X_CA_CERT,
- cert_writer },
- { NM_SETTING_802_1X_SETTING_NAME,
- NM_SETTING_802_1X_CLIENT_CERT,
- cert_writer },
- { NM_SETTING_802_1X_SETTING_NAME,
- NM_SETTING_802_1X_PRIVATE_KEY,
- cert_writer },
- { NM_SETTING_802_1X_SETTING_NAME,
- NM_SETTING_802_1X_PHASE2_CA_CERT,
- cert_writer },
- { NM_SETTING_802_1X_SETTING_NAME,
- NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
- cert_writer },
- { NM_SETTING_802_1X_SETTING_NAME,
- NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
- cert_writer },
- { 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,
- const GValue *value,
- GParamFlags flag,
- gpointer user_data)
+static gboolean
+_handler_write (NMConnection *connection,
+ GKeyFile *keyfile,
+ NMKeyfileWriteType type,
+ void *type_data,
+ void *user_data,
+ GError **error)
{
- WriteInfo *info = user_data;
- const char *setting_name;
- GType type = G_VALUE_TYPE (value);
- KeyWriter *writer = &key_writers[0];
- GParamSpec *pspec;
-
- /* Setting name gets picked up from the keyfile's section name instead */
- if (!strcmp (key, NM_SETTING_NAME))
- return;
-
- /* Don't write the NMSettingConnection object's 'read-only' property */
- if ( NM_IS_SETTING_CONNECTION (setting)
- && !strcmp (key, NM_SETTING_CONNECTION_READ_ONLY))
- return;
-
- setting_name = nm_setting_get_name (setting);
-
- /* If the value is the default value, remove the item from the keyfile */
- pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), key);
- if (pspec) {
- if (g_param_value_defaults (pspec, (GValue *) value)) {
- g_key_file_remove_key (info->keyfile, setting_name, key, NULL);
- return;
- }
- }
-
- /* Don't write secrets that are owned by user secret agents or aren't
- * supposed to be saved. VPN secrets are handled specially though since
- * the secret flags there are in a third-level hash in the 'secrets'
- * property.
- */
- if (pspec && (pspec->flags & NM_SETTING_PARAM_SECRET) && !NM_IS_SETTING_VPN (setting)) {
- NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
-
- if (!nm_setting_get_secret_flags (setting, key, &secret_flags, NULL))
- g_assert_not_reached ();
- if (secret_flags != NM_SETTING_SECRET_FLAG_NONE)
- return;
- }
-
- /* 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);
- return;
- }
- writer++;
- }
-
- if (type == G_TYPE_STRING) {
- const char *str;
-
- str = g_value_get_string (value);
- if (str)
- nm_keyfile_plugin_kf_set_string (info->keyfile, setting_name, key, str);
- } else if (type == G_TYPE_UINT)
- nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (int) g_value_get_uint (value));
- else if (type == G_TYPE_INT)
- nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, g_value_get_int (value));
- else if (type == G_TYPE_UINT64) {
- char *numstr;
-
- numstr = g_strdup_printf ("%" G_GUINT64_FORMAT, g_value_get_uint64 (value));
- nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr);
- g_free (numstr);
- } else if (type == G_TYPE_INT64) {
- char *numstr;
-
- numstr = g_strdup_printf ("%" G_GINT64_FORMAT, g_value_get_int64 (value));
- nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr);
- g_free (numstr);
- } else if (type == G_TYPE_BOOLEAN) {
- nm_keyfile_plugin_kf_set_boolean (info->keyfile, setting_name, key, g_value_get_boolean (value));
- } else if (type == G_TYPE_CHAR) {
- nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (int) g_value_get_schar (value));
- } else if (type == G_TYPE_BYTES) {
- GBytes *bytes;
- const guint8 *data;
- gsize len = 0;
-
- bytes = g_value_get_boxed (value);
- data = bytes ? g_bytes_get_data (bytes, &len) : NULL;
-
- if (data != NULL && len > 0) {
- int *tmp_array;
- int i;
-
- 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 (info->keyfile, setting_name, key, tmp_array, len);
- g_free (tmp_array);
- }
- } else if (type == G_TYPE_STRV) {
- char **array;
-
- array = (char **) g_value_get_boxed (value);
- nm_keyfile_plugin_kf_set_string_list (info->keyfile, setting_name, key, (const gchar **const) array, g_strv_length (array));
- } 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));
- }
- } 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));
+ if (type == NM_KEYFILE_WRITE_TYPE_CERT) {
+ cert_writer (connection, keyfile,
+ (NMKeyfileWriteTypeDataCert *) type_data,
+ (WriteInfo *) user_data, error);
+ return TRUE;
}
+ return FALSE;
}
static gboolean
@@ -793,10 +240,11 @@ _internal_write_connection (NMConnection *connection,
gboolean success = FALSE;
char *path;
const char *id;
- WriteInfo info;
+ WriteInfo info = { 0 };
GError *local_err = NULL;
g_return_val_if_fail (!out_path || !*out_path, FALSE);
+ g_return_val_if_fail (keyfile_dir && keyfile_dir[0] == '/', FALSE);
if (!nm_connection_verify (connection, error))
g_return_val_if_reached (FALSE);
@@ -804,14 +252,15 @@ _internal_write_connection (NMConnection *connection,
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);
- nm_connection_for_each_setting_value (connection, write_setting_value, &info);
+
+ key_file = nm_keyfile_write (connection, _handler_write, &info, error);
+ if (!key_file)
+ return FALSE;
data = g_key_file_to_data (key_file, &len, error);
+ g_key_file_unref (key_file);
if (!data)
- goto out;
+ return FALSE;
/* If we have existing file path, use it. Else generate one from
* connection's ID.
@@ -910,7 +359,6 @@ _internal_write_connection (NMConnection *connection,
out:
g_free (data);
- g_key_file_free (key_file);
return success;
}
diff --git a/src/settings/plugins/keyfile/writer.h b/src/settings/plugins/keyfile/writer.h
index a602f2f4a3..8b08812d86 100644
--- a/src/settings/plugins/keyfile/writer.h
+++ b/src/settings/plugins/keyfile/writer.h
@@ -22,7 +22,6 @@
#ifndef _KEYFILE_PLUGIN_WRITER_H
#define _KEYFILE_PLUGIN_WRITER_H
-#include <sys/types.h>
#include <glib.h>
#include <nm-connection.h>
diff --git a/src/tests/test-general.c b/src/tests/test-general.c
index 21533cc743..dae872c86d 100644
--- a/src/tests/test-general.c
+++ b/src/tests/test-general.c
@@ -30,135 +30,6 @@
#include "nm-test-utils.h"
-static void
-test_nm_utils_ascii_str_to_int64_check (const char *str, guint base, gint64 min,
- gint64 max, gint64 fallback, int exp_errno,
- gint64 exp_val)
-{
- gint64 v;
-
- errno = 1;
- v = nm_utils_ascii_str_to_int64 (str, base, min, max, fallback);
- g_assert_cmpint (errno, ==, exp_errno);
- g_assert_cmpint (v, ==, exp_val);
-}
-
-static void
-test_nm_utils_ascii_str_to_int64_do (const char *str, guint base, gint64 min,
- gint64 max, gint64 fallback, int exp_errno,
- gint64 exp_val)
-{
- const char *sign = "";
- const char *val;
- static const char *whitespaces[] = {
- "",
- " ",
- "\r\n\t",
- " \r\n\t ",
- " \r\n\t \t\r\n\t",
- NULL,
- };
- static const char *nulls[] = {
- "",
- "0",
- "00",
- "0000",
- "0000000000000000",
- "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
- "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
- NULL,
- };
- const char **ws_pre, **ws_post, **null;
- guint i;
-
- if (str == NULL || exp_errno != 0) {
- test_nm_utils_ascii_str_to_int64_check (str, base, min, max, fallback, exp_errno, exp_val);
- return;
- }
-
- if (strncmp (str, "-", 1) == 0)
- sign = "-";
-
- val = str + strlen (sign);
-
- for (ws_pre = whitespaces; *ws_pre; ws_pre++) {
- for (ws_post = whitespaces; *ws_post; ws_post++) {
- for (null = nulls; *null; null++) {
- for (i = 0; ; i++) {
- char *s;
- const char *str_base = "";
-
- if (base == 16) {
- if (i == 1)
- str_base = "0x";
- else if (i > 1)
- break;
- } else if (base == 8) {
- if (i == 1)
- str_base = "0";
- else if (i > 1)
- break;
- } else if (base == 0) {
- if (i > 0)
- break;
- /* with base==0, a leading zero would be interpreted as octal. Only test without *null */
- if ((*null)[0])
- break;
- } else {
- if (i > 0)
- break;
- }
-
- s = g_strdup_printf ("%s%s%s%s%s%s", *ws_pre, sign, str_base, *null, val, *ws_post);
-
- test_nm_utils_ascii_str_to_int64_check (s, base, min, max, fallback, exp_errno, exp_val);
- g_free (s);
- }
- }
- }
- }
-}
-
-static void
-test_nm_utils_ascii_str_to_int64 (void)
-{
- test_nm_utils_ascii_str_to_int64_do (NULL, 10, 0, 10000, -1, EINVAL, -1);
- test_nm_utils_ascii_str_to_int64_do ("", 10, 0, 10000, -1, EINVAL, -1);
- test_nm_utils_ascii_str_to_int64_do ("1x", 10, 0, 10000, -1, EINVAL, -1);
- test_nm_utils_ascii_str_to_int64_do ("4711", 10, 0, 10000, -1, 0, 4711);
- test_nm_utils_ascii_str_to_int64_do ("10000", 10, 0, 10000, -1, 0, 10000);
- test_nm_utils_ascii_str_to_int64_do ("10001", 10, 0, 10000, -1, ERANGE, -1);
- test_nm_utils_ascii_str_to_int64_do ("FF", 16, 0, 10000, -1, 0, 255);
- test_nm_utils_ascii_str_to_int64_do ("FF", 10, 0, 10000, -2, EINVAL, -2);
- test_nm_utils_ascii_str_to_int64_do ("9223372036854775807", 10, 0, G_MAXINT64, -2, 0, G_MAXINT64);
- test_nm_utils_ascii_str_to_int64_do ("7FFFFFFFFFFFFFFF", 16, 0, G_MAXINT64, -2, 0, G_MAXINT64);
- test_nm_utils_ascii_str_to_int64_do ("9223372036854775808", 10, 0, G_MAXINT64, -2, ERANGE, -2);
- test_nm_utils_ascii_str_to_int64_do ("-9223372036854775808", 10, G_MININT64, 0, -2, 0, G_MININT64);
- test_nm_utils_ascii_str_to_int64_do ("-9223372036854775808", 10, G_MININT64+1, 0, -2, ERANGE, -2);
- test_nm_utils_ascii_str_to_int64_do ("-9223372036854775809", 10, G_MININT64, 0, -2, ERANGE, -2);
- test_nm_utils_ascii_str_to_int64_do ("1.0", 10, 1, 1, -1, EINVAL, -1);
- test_nm_utils_ascii_str_to_int64_do ("1x0", 16, -10, 10, -100, EINVAL, -100);
- test_nm_utils_ascii_str_to_int64_do ("0", 16, -10, 10, -100, 0, 0);
- test_nm_utils_ascii_str_to_int64_do ("10001111", 2, -1000, 1000, -100000, 0, 0x8F);
- test_nm_utils_ascii_str_to_int64_do ("-10001111", 2, -1000, 1000, -100000, 0, -0x8F);
- test_nm_utils_ascii_str_to_int64_do ("1111111", 2, G_MININT64, G_MAXINT64, -1, 0, 0x7F);
- test_nm_utils_ascii_str_to_int64_do ("111111111111111", 2, G_MININT64, G_MAXINT64, -1, 0, 0x7FFF);
- test_nm_utils_ascii_str_to_int64_do ("11111111111111111111111111111111111111111111111", 2, G_MININT64, G_MAXINT64, -1, 0, 0x7FFFFFFFFFFF);
- test_nm_utils_ascii_str_to_int64_do ("111111111111111111111111111111111111111111111111111111111111111", 2, G_MININT64, G_MAXINT64, -1, 0, 0x7FFFFFFFFFFFFFFF);
- test_nm_utils_ascii_str_to_int64_do ("100000000000000000000000000000000000000000000000000000000000000", 2, G_MININT64, G_MAXINT64, -1, 0, 0x4000000000000000);
- test_nm_utils_ascii_str_to_int64_do ("1000000000000000000000000000000000000000000000000000000000000000", 2, G_MININT64, G_MAXINT64, -1, ERANGE, -1);
- test_nm_utils_ascii_str_to_int64_do ("-100000000000000000000000000000000000000000000000000000000000000", 2, G_MININT64, G_MAXINT64, -1, 0, -0x4000000000000000);
- test_nm_utils_ascii_str_to_int64_do ("111111111111111111111111111111111111111111111111111111111111111", 2, G_MININT64, G_MAXINT64, -1, 0, 0x7FFFFFFFFFFFFFFF);
- test_nm_utils_ascii_str_to_int64_do ("-100000000000000000000000000000000000000000000000000000000000000", 2, G_MININT64, G_MAXINT64, -1, 0, -0x4000000000000000);
- test_nm_utils_ascii_str_to_int64_do ("0x70", 10, G_MININT64, G_MAXINT64, -1, EINVAL, -1);
- test_nm_utils_ascii_str_to_int64_do ("4711", 0, G_MININT64, G_MAXINT64, -1, 0, 4711);
- test_nm_utils_ascii_str_to_int64_do ("04711", 0, G_MININT64, G_MAXINT64, -1, 0, 04711);
- test_nm_utils_ascii_str_to_int64_do ("0x4711", 0, G_MININT64, G_MAXINT64, -1, 0, 0x4711);
- test_nm_utils_ascii_str_to_int64_do ("080", 0, G_MININT64, G_MAXINT64, -1, EINVAL, -1);
- test_nm_utils_ascii_str_to_int64_do ("070", 0, G_MININT64, G_MAXINT64, -1, 0, 7*8);
- test_nm_utils_ascii_str_to_int64_do ("0x70", 0, G_MININT64, G_MAXINT64, -1, 0, 0x70);
-}
-
/* Reference implementation for nm_utils_ip6_address_clear_host_address.
* Taken originally from set_address_masked(), src/rdisc/nm-lndp-rdisc.c
**/
@@ -727,53 +598,6 @@ test_connection_sort_autoconnect_priority (void)
/*******************************************/
-static void
-__test_uuid (const char *expected_uuid, const char *str, gssize slen, char *uuid_test)
-{
- g_assert (uuid_test);
- g_assert (nm_utils_is_uuid (uuid_test));
-
- if (strcmp (uuid_test, expected_uuid)) {
- g_error ("UUID test failed (1): text=%s, len=%lld, expected=%s, uuid_test=%s",
- str, (long long) slen, expected_uuid, uuid_test);
- }
- g_free (uuid_test);
-
- uuid_test = nm_utils_uuid_generate_from_string (str, slen, NM_UTILS_UUID_TYPE_VARIANT3, NM_UTILS_UUID_NS);
-
- g_assert (uuid_test);
- g_assert (nm_utils_is_uuid (uuid_test));
-
- if (strcmp (uuid_test, expected_uuid)) {
- g_error ("UUID test failed (2): text=%s; len=%lld, expected=%s, uuid2=%s",
- str, (long long) slen, expected_uuid, uuid_test);
- }
- g_free (uuid_test);
-}
-
-#define _test_uuid(expected_uuid, str, strlen, ...) __test_uuid (expected_uuid, str, strlen, nm_utils_uuid_generate_from_strings(__VA_ARGS__, NULL))
-
-static void
-test_nm_utils_uuid_generate_from_strings (void)
-{
- _test_uuid ("b07c334a-399b-32de-8d50-58e4e08f98e3", "", 0, NULL);
- _test_uuid ("b8a426cb-bcb5-30a3-bd8f-6786fea72df9", "\0", 1, "");
- _test_uuid ("12a4a982-7aae-39e1-951e-41aeb1250959", "a\0", 2, "a");
- _test_uuid ("69e22c7e-f89f-3a43-b239-1cb52ed8db69", "aa\0", 3, "aa");
- _test_uuid ("59829fd3-5ad5-3d90-a7b0-4911747e4088", "\0\0", 2, "", "");
- _test_uuid ("01ad0e06-6c50-3384-8d86-ddab81421425", "a\0\0", 3, "a", "");
- _test_uuid ("e1ed8647-9ed3-3ec8-8c6d-e8204524d71d", "aa\0\0", 4, "aa", "");
- _test_uuid ("fb1c7cd6-275c-3489-9382-83b900da8af0", "\0a\0", 3, "", "a");
- _test_uuid ("5d79494e-c4ba-31a6-80a2-d6016ccd7e17", "a\0a\0", 4, "a", "a");
- _test_uuid ("fd698d86-1b60-3ebe-855f-7aada9950a8d", "aa\0a\0", 5, "aa", "a");
- _test_uuid ("8c573b48-0f01-30ba-bb94-c5f59f4fe517", "\0aa\0", 4, "", "aa");
- _test_uuid ("2bdd3d46-eb83-3c53-a41b-a724d04b5544", "a\0aa\0", 5, "a", "aa");
- _test_uuid ("13d4b780-07c1-3ba7-b449-81c4844ef039", "aa\0aa\0", 6, "aa", "aa");
- _test_uuid ("dd265bf7-c05a-3037-9939-b9629858a477", "a\0b\0", 4, "a", "b");
-}
-
-/*******************************************/
-
static const char *_test_match_spec_all[] = {
"e",
"em",
@@ -893,7 +717,6 @@ main (int argc, char **argv)
{
nmtst_init_with_logging (&argc, &argv, NULL, "ALL");
- g_test_add_func ("/general/nm_utils_ascii_str_to_int64", test_nm_utils_ascii_str_to_int64);
g_test_add_func ("/general/nm_utils_ip6_address_clear_host_address", test_nm_utils_ip6_address_clear_host_address);
g_test_add_func ("/general/nm_utils_log_connection_diff", test_nm_utils_log_connection_diff);
@@ -908,7 +731,6 @@ main (int argc, char **argv)
g_test_add_func ("/general/connection-sort/autoconnect-priority", test_connection_sort_autoconnect_priority);
- g_test_add_func ("/general/nm_utils_uuid_generate_from_strings", test_nm_utils_uuid_generate_from_strings);
g_test_add_func ("/general/nm_match_spec_interface_name", test_nm_match_spec_interface_name);
return g_test_run ();