keyfile: support writing certificates as blob inside the keyfile
authorThomas Haller <thaller@redhat.com>
Tue, 24 Feb 2015 21:22:14 +0000 (22:22 +0100)
committerThomas Haller <thaller@redhat.com>
Thu, 12 Mar 2015 17:16:58 +0000 (18:16 +0100)
keyfile should become our main import/export format. It is desirable,
that a keyfile can contain every aspect of a connection.

For blob certificates, the writer in core daemon would always write
them to a file and convert the scheme to path.
This behavior is not great for a (hyptetical) `nmcli connection export`
command because it would have to export them somehow outside of keyfile,
e.g. by writing them to temporary files.

Instead, if the write handler does not handle a certificate, use a
default implementation in nm_keyfile_write() which adds the blob inside
the keyfile.

Interestingly, keyfile reader already supported reading certificate
blobs. But this legacy format accepts the blob as arbitrary
binary without marking the format and without scheme prefix.
Instead of writing the binary data directly, write it with a new
uri scheme "data:;base64," and encode it in base64.

Also go through some lengths to make sure that whatever path
keyfile plugin writes, can be read back again. That is, because
keyfile writer preferably writes relative paths without prefix.
Add nm_keyfile_detect_unqualified_path_scheme() to encapsulate
the detection of pathnames without file:// prefix and use it to
check whether the path name must be fully qualified.

13 files changed:
.gitignore
libnm-core/nm-keyfile-internal.h
libnm-core/nm-keyfile-reader.c
libnm-core/nm-keyfile-writer.c
libnm-core/tests/Makefile.am
libnm-core/tests/certs/test-ca-cert.pem [new file with mode: 0644]
libnm-core/tests/certs/test-key-and-cert.pem [new file with mode: 0644]
libnm-core/tests/test-keyfile.c [new file with mode: 0644]
src/settings/plugins/keyfile/reader.c
src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Blob
src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Old
src/settings/plugins/keyfile/tests/test-keyfile.c
src/settings/plugins/keyfile/writer.c

index 40448f9..397524e 100644 (file)
@@ -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
index 9953917..90af562 100644 (file)
 #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;
@@ -58,6 +64,7 @@ typedef gboolean (*NMKeyfileReadHandler) (GKeyFile *keyfile,
 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;
 
index aa54b7b..87c1e14 100644 (file)
@@ -31,6 +31,7 @@
 #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"
@@ -838,38 +839,127 @@ has_cert_ext (const char *path)
 }
 
 static gboolean
-handle_as_scheme (GBytes *bytes, NMSetting *setting, const char *key)
+handle_as_scheme (KeyfileReaderInfo *info, GBytes *bytes, NMSetting *setting, const char *key)
 {
-       const guint8 *data;
-       gsize data_len;
+       const char *data;
+       gsize data_len, bin_len;
 
        data = g_bytes_get_data (bytes, &data_len);
 
-       /* It's the PATH scheme, can just set plain data */
-       if (   (data_len > strlen (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH))
-           && g_str_has_prefix ((const char *) data, NM_KEYFILE_CERT_SCHEME_PREFIX_PATH)
-           && (data[data_len - 1] == '\0')) {
-               g_object_set (setting, key, bytes, NULL);
+       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;
 }
 
-static gboolean
-handle_as_path (KeyfileReaderInfo *info,
-                GBytes *bytes,
-                NMSetting *setting,
-                const char *key)
+char *
+nm_keyfile_detect_unqualified_path_scheme (const char *base_dir,
+                                           gconstpointer pdata,
+                                           gsize data_len,
+                                           gboolean consider_exists,
+                                           gboolean *out_exists)
 {
-       const guint8 *data;
-       gsize data_len;
+       const char *data = pdata;
+       gboolean exists = FALSE;
+       gboolean success = FALSE;
        gsize validate_len;
        char *path;
-       gboolean exists, success = FALSE;
+       GByteArray *tmp;
 
-       data = g_bytes_get_data (bytes, &data_len);
+       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 FALSE;
+               return NULL;
 
        /* If there's a trailing zero tell g_utf8_validate() to validate until the zero */
        if (data[data_len - 1] == '\0') {
@@ -878,74 +968,119 @@ handle_as_path (KeyfileReaderInfo *info,
                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 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 (info->base_dir, 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 (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));
-               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);
+       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);
 
-               /* Warn if the certificate didn't exist */
-               if (exists == FALSE)
-                       handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
-                                    _("certificate or key '%s' does not exist"),
-                                    path);
+out:
+       if (!success) {
+               g_free (path);
+               return NULL;
        }
-       g_free (path);
+       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;
 
-       return success;
+       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);
-       GBytes *bytes;
-       gboolean success = FALSE;
+       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://") */
-               success = handle_as_scheme (bytes, setting, key);
+               if (handle_as_scheme (info, bytes, setting, key))
+                       return;
+               if (info->error)
+                       return;
 
                /* If not, it might be a plain path */
-               if (success == FALSE)
-                       success = handle_as_path (info, bytes, setting, key);
+               if (handle_as_path (info, bytes, setting, key))
+                       return;
                if (info->error)
-                       goto out_error;
-
-               /* If neither of those two, assume blob with certificate data */
-               if (success == FALSE)
+                       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"));
        }
-
-out_error:
-       if (bytes)
-               g_bytes_unref (bytes);
 }
 
 static void
index f5b4ef2..ff433b2 100644 (file)
@@ -42,6 +42,7 @@
 #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"
@@ -410,6 +411,76 @@ static const ObjectType objtypes[10] = {
        { 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,
@@ -429,9 +500,6 @@ cert_writer (KeyfileWriterInfo *info,
        if (!objtype)
                g_return_if_reached ();
 
-       if (!info->handler)
-               goto out_unhandled;
-
        type_data.setting = NM_SETTING_802_1X (setting);
        type_data.property_name = key;
        type_data.suffix = objtype->suffix;
@@ -440,30 +508,23 @@ cert_writer (KeyfileWriterInfo *info,
        type_data.path_func = objtype->path_func;
        type_data.blob_func = objtype->blob_func;
 
-       if (info->handler (info->connection,
-                          info->keyfile,
-                          NM_KEYFILE_WRITE_TYPE_CERT,
-                          &type_data,
-                          info->user_data,
-                          &info->error))
-               return;
-
-out_unhandled:
-
-       /* scheme_func() would not return UNKNOWN, because UNKNOWN happens only
-        * if the cert is unset (1) or if the cert is invalid (2).
-        * (1) cannot happen, because we only reach cert_writer() for non-default
-        * properties. (2) cannot happen, because we verified the connection.
-        *
-        * Hence, at this point we do have a certifiacte, but no default implementation
-        * to write it. The handler *must* do something with these certifications. */
-       if (!info->error) {
-               g_set_error (&info->error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
-                            _("Failed to write unhandled certificate property %s.%s"),
-                            nm_setting_get_name (setting), key);
+       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;
index 457864c..daa5825 100644 (file)
@@ -15,6 +15,7 @@ noinst_PROGRAMS =             \
        test-compare            \
        test-crypto             \
        test-general            \
+       test-keyfile            \
        test-secrets            \
        test-setting-8021x      \
        test-setting-dcb        \
@@ -49,8 +50,10 @@ EXTRA_DIST =                            \
        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 (file)
index 0000000..ef1be20
--- /dev/null
@@ -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 (file)
index 0000000..dec9aa1
--- /dev/null
@@ -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-keyfile.c b/libnm-core/tests/test-keyfile.c
new file mode 100644 (file)
index 0000000..a0f590e
--- /dev/null
@@ -0,0 +1,505 @@
+/* -*- 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;
+
+       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 ();
+}
+
index 3e4cfed..a149e06 100644 (file)
@@ -70,6 +70,8 @@ _handler_read (GKeyFile *keyfile,
                        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;
 
index 9f4ef62..62e6ae3 100644 (file)
@@ -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]
index 61afdd9..d3da598 100644 (file)
@@ -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]
index 1e90824..a86bfed 100644 (file)
@@ -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);
 }
index 6b4a35b..9cf119c 100644 (file)
@@ -116,24 +116,47 @@ cert_writer (NMConnection *connection,
 
        scheme = cert_data->scheme_func (cert_data->setting);
        if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) {
+               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, 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])
-                                       path = 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, cert_data->property_name, 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;
@@ -165,8 +188,9 @@ cert_writer (NMConnection *connection,
 
                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, cert_data->property_name, 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, "keyfile: %s.%s: failed to write certificate to file %s: %s",
                                     setting_name, cert_data->property_name, new_path, local->message);