keyfile: support writing certificates as blob inside the keyfile

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.
This commit is contained in:
Thomas Haller 2015-02-24 22:22:14 +01:00
parent a49680dacd
commit c9a8764ad2
13 changed files with 999 additions and 106 deletions

1
.gitignore vendored
View 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

View file

@ -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;

View 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,23 +839,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 +1020,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

View 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;

View 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

View file

@ -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-----

View file

@ -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-----

View file

@ -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 ();
}

View 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;

View 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]

View 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]

View 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);
}

View 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);