diff --git a/.gitignore b/.gitignore index 2e1bf5febc..9868ec3eaf 100644 --- a/.gitignore +++ b/.gitignore @@ -150,6 +150,7 @@ test-*.trs /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/libnm-core/nm-keyfile-internal.h b/libnm-core/nm-keyfile-internal.h index 995391747c..90af562cdf 100644 --- a/libnm-core/nm-keyfile-internal.h +++ b/libnm-core/nm-keyfile-internal.h @@ -34,6 +34,12 @@ #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; diff --git a/libnm-core/nm-keyfile-reader.c b/libnm-core/nm-keyfile-reader.c index aa54b7bd77..c1d5ce7559 100644 --- a/libnm-core/nm-keyfile-reader.c +++ b/libnm-core/nm-keyfile-reader.c @@ -31,10 +31,12 @@ #include #include "nm-core-internal.h" +#include "nm-macros-internal.h" #include "gsystem-local-alloc.h" #include "nm-glib-compat.h" #include "nm-keyfile-internal.h" #include "nm-keyfile-utils.h" +#include "nm-setting-private.h" typedef struct { @@ -838,23 +840,179 @@ 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; } +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, @@ -863,89 +1021,67 @@ handle_as_path (KeyfileReaderInfo *info, { const guint8 *data; gsize data_len; - gsize validate_len; char *path; - gboolean exists, success = FALSE; + gboolean exists = FALSE; + GBytes *val; data = g_bytes_get_data (bytes, &data_len); - if (data_len > 500 || data_len < 1) + + path = nm_keyfile_detect_unqualified_path_scheme (info->base_dir, data, data_len, TRUE, &exists); + if (!path) return FALSE; - /* 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; + /* 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); - if ( validate_len == 0 - || 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 (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); - success = TRUE; - - /* 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); + /* 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_free (path); + g_bytes_unref (val); - return success; + 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; + return; - /* If neither of those two, assume blob with certificate data */ - if (success == FALSE) + 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 diff --git a/libnm-core/nm-keyfile-writer.c b/libnm-core/nm-keyfile-writer.c index f5b4ef234f..ff433b264a 100644 --- a/libnm-core/nm-keyfile-writer.c +++ b/libnm-core/nm-keyfile-writer.c @@ -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; diff --git a/libnm-core/tests/Makefile.am b/libnm-core/tests/Makefile.am index 457864c440..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 \ @@ -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 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-keyfile.c b/libnm-core/tests/test-keyfile.c new file mode 100644 index 0000000000..2de114da77 --- /dev/null +++ b/libnm-core/tests/test-keyfile.c @@ -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-macros-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 (); +} + diff --git a/src/settings/plugins/keyfile/reader.c b/src/settings/plugins/keyfile/reader.c index 3e4cfed8ed..a149e06d93 100644 --- a/src/settings/plugins/keyfile/reader.c +++ b/src/settings/plugins/keyfile/reader.c @@ -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; 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 f491dbd561..fcb1da60f6 100644 --- a/src/settings/plugins/keyfile/tests/test-keyfile.c +++ b/src/settings/plugins/keyfile/tests/test-keyfile.c @@ -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, + "* 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, + "* 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, + "* 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, + "* 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, + "* 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/writer.c b/src/settings/plugins/keyfile/writer.c index 6b4a35b1b0..9cf119c0c8 100644 --- a/src/settings/plugins/keyfile/writer.c +++ b/src/settings/plugins/keyfile/writer.c @@ -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);