From 98ca7022e35a7dd735f02a1c7bcd2ed8113562cf Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 30 Aug 2018 17:12:20 +0200 Subject: [PATCH 01/61] libnm: fix leaking private-key in nm_setting_802_1x_set_phase2_private_key() --- libnm-core/nm-setting-8021x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libnm-core/nm-setting-8021x.c b/libnm-core/nm-setting-8021x.c index 0d61f91c5d..f8e0d270e6 100644 --- a/libnm-core/nm-setting-8021x.c +++ b/libnm-core/nm-setting-8021x.c @@ -2637,6 +2637,7 @@ nm_setting_802_1x_set_phase2_private_key (NMSetting8021x *setting, password_changed = TRUE; } + g_bytes_unref (priv->phase2_private_key); if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { /* FIXME: potential race after verifying the private key above */ /* FIXME: ensure blob doesn't start with file:// */ From fcf254c03a7c2a37ae1138240f756760f217ca6f Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sun, 2 Sep 2018 14:33:34 +0200 Subject: [PATCH 02/61] libnm/keyfile: fix double free in keyfile's get_bytes() Fixes: 5e7b14af03fef98566367c211c0e7f75ad9a9525 --- libnm-core/nm-keyfile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libnm-core/nm-keyfile.c b/libnm-core/nm-keyfile.c index 3f9d60bbb7..924777f503 100644 --- a/libnm-core/nm-keyfile.c +++ b/libnm-core/nm-keyfile.c @@ -969,7 +969,7 @@ get_bytes (KeyfileReaderInfo *info, * byte-array. The reason is that zero_terminate is there to terminate * *valid* strings. It's not there to terminated invalid (empty) strings. */ - return g_bytes_new_take (tmp_string, 0); + return g_bytes_new_static ("", 0); } for (length = 0; tmp_string[length]; length++) { From f15d82bc91bf35ca908df08a779c95eef86ddd6b Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 28 Aug 2018 16:47:13 +0200 Subject: [PATCH 03/61] shared: add nm_auto_unref_bytearray macro Internally, GByteArray is actually a GArray, so it would be safe to use "gs_unref_array" macro. However, that is rather ugly, and means to rely on an internal implementation detail of GByteArray. Instead, add a cleanup macro for GByteArray. --- shared/nm-utils/nm-macros-internal.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/shared/nm-utils/nm-macros-internal.h b/shared/nm-utils/nm-macros-internal.h index 3d2520d131..7742749d4b 100644 --- a/shared/nm-utils/nm-macros-internal.h +++ b/shared/nm-utils/nm-macros-internal.h @@ -260,6 +260,9 @@ NM_AUTO_DEFINE_FCN_STRUCT (GValue, _nm_auto_unset_gvalue, g_value_unset) NM_AUTO_DEFINE_FCN_VOID0 (void *, _nm_auto_unref_gtypeclass, g_type_class_unref) #define nm_auto_unref_gtypeclass nm_auto(_nm_auto_unref_gtypeclass) +NM_AUTO_DEFINE_FCN0 (GByteArray *, _nm_auto_unref_bytearray, g_byte_array_unref) +#define nm_auto_unref_bytearray nm_auto(_nm_auto_unref_bytearray) + static inline void _nm_auto_free_gstring (GString **str) { From 74815fd8e023c2a3713129fe906b83c856248adf Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 29 Aug 2018 10:21:52 +0200 Subject: [PATCH 04/61] shared: drop unnecessary NM_AUTO_DEFINE_FCN_STRUCT() macro It serves no purpose, as it just directly calls the function. We don't need to define this intermediary. --- shared/nm-utils/nm-macros-internal.h | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/shared/nm-utils/nm-macros-internal.h b/shared/nm-utils/nm-macros-internal.h index 7742749d4b..c8734a4b4a 100644 --- a/shared/nm-utils/nm-macros-internal.h +++ b/shared/nm-utils/nm-macros-internal.h @@ -91,12 +91,6 @@ static inline void name (Type *v) \ func (*v); \ } -#define NM_AUTO_DEFINE_FCN_STRUCT(Type, name, func) \ -static inline void name (Type *v) \ -{ \ - func (v); \ -} - /*****************************************************************************/ /** @@ -254,8 +248,7 @@ NM_AUTO_DEFINE_FCN (char *, _nm_auto_free_secret, nm_free_secret) */ #define nm_auto_free_secret nm_auto(_nm_auto_free_secret) -NM_AUTO_DEFINE_FCN_STRUCT (GValue, _nm_auto_unset_gvalue, g_value_unset) -#define nm_auto_unset_gvalue nm_auto(_nm_auto_unset_gvalue) +#define nm_auto_unset_gvalue nm_auto(g_value_unset) NM_AUTO_DEFINE_FCN_VOID0 (void *, _nm_auto_unref_gtypeclass, g_type_class_unref) #define nm_auto_unref_gtypeclass nm_auto(_nm_auto_unref_gtypeclass) From b232508707e7bca57edd926e13cd2d211afe3df5 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 28 Aug 2018 21:20:36 +0200 Subject: [PATCH 05/61] shared: add nm-secret-utils.h helper We already had nm_free_secret() to clear the secret out of a NUL terminated string. That works well for secrets which are strings, it can be used with a cleanup attribute (nm_auto_free_secret) and as a cleanup function for a GBytes. However, it does not work for secrets which are binary. For those, we must also track the length of the allocated data and clear it. Add two new structs NMSecretPtr and NMSecretBuf to help with that. --- Makefile.am | 4 + libnm-core/nm-setting-vpn.c | 2 + libnm/nm-vpn-service-plugin.c | 1 + shared/meson.build | 1 + shared/nm-utils/nm-macros-internal.h | 20 ---- shared/nm-utils/nm-secret-utils.c | 134 ++++++++++++++++++++++++ shared/nm-utils/nm-secret-utils.h | 151 +++++++++++++++++++++++++++ 7 files changed, 293 insertions(+), 20 deletions(-) create mode 100644 shared/nm-utils/nm-secret-utils.c create mode 100644 shared/nm-utils/nm-secret-utils.h diff --git a/Makefile.am b/Makefile.am index 8176019594..c2c7af44f0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -501,6 +501,7 @@ libnm_core_lib_h_priv = \ shared/nm-utils/nm-dedup-multi.h \ shared/nm-utils/nm-enum-utils.h \ shared/nm-utils/nm-hash-utils.h \ + shared/nm-utils/nm-secret-utils.h \ shared/nm-utils/nm-shared-utils.h \ shared/nm-utils/nm-random-utils.h \ shared/nm-utils/nm-udev-utils.h \ @@ -567,6 +568,7 @@ libnm_core_lib_c_real = \ shared/nm-utils/nm-dedup-multi.c \ shared/nm-utils/nm-enum-utils.c \ shared/nm-utils/nm-hash-utils.c \ + shared/nm-utils/nm-secret-utils.c \ shared/nm-utils/nm-shared-utils.c \ shared/nm-utils/nm-random-utils.c \ shared/nm-utils/nm-udev-utils.c \ @@ -4736,6 +4738,8 @@ EXTRA_DIST += \ shared/nm-utils/nm-jansson.h \ shared/nm-utils/nm-obj.h \ shared/nm-utils/nm-macros-internal.h \ + shared/nm-utils/nm-secret-utils.c \ + shared/nm-utils/nm-secret-utils.h \ shared/nm-utils/nm-shared-utils.c \ shared/nm-utils/nm-shared-utils.h \ shared/nm-utils/nm-test-utils.h \ diff --git a/libnm-core/nm-setting-vpn.c b/libnm-core/nm-setting-vpn.c index 3b61736128..fcbeec6854 100644 --- a/libnm-core/nm-setting-vpn.c +++ b/libnm-core/nm-setting-vpn.c @@ -25,6 +25,8 @@ #include #include +#include "nm-utils/nm-secret-utils.h" + #include "nm-setting-vpn.h" #include "nm-utils.h" #include "nm-utils-private.h" diff --git a/libnm/nm-vpn-service-plugin.c b/libnm/nm-vpn-service-plugin.c index 29c666c796..2213824edb 100644 --- a/libnm/nm-vpn-service-plugin.c +++ b/libnm/nm-vpn-service-plugin.c @@ -27,6 +27,7 @@ #include #include +#include "nm-utils/nm-secret-utils.h" #include "nm-enum-types.h" #include "nm-utils.h" #include "nm-connection.h" diff --git a/shared/meson.build b/shared/meson.build index 8faec8765b..c14b9d723b 100644 --- a/shared/meson.build +++ b/shared/meson.build @@ -51,6 +51,7 @@ shared_files_libnm_core = files(''' nm-utils/nm-enum-utils.c nm-utils/nm-hash-utils.c nm-utils/nm-random-utils.c + nm-utils/nm-secret-utils.c nm-utils/nm-shared-utils.c nm-utils/nm-udev-utils.c '''.split()) diff --git a/shared/nm-utils/nm-macros-internal.h b/shared/nm-utils/nm-macros-internal.h index c8734a4b4a..6937994c38 100644 --- a/shared/nm-utils/nm-macros-internal.h +++ b/shared/nm-utils/nm-macros-internal.h @@ -207,7 +207,6 @@ NM_AUTO_DEFINE_FCN0 (GKeyFile *, gs_local_keyfile_unref, g_key_file_unref) /*****************************************************************************/ static inline int nm_close (int fd); -static inline void nm_free_secret (char *secret); /** * nm_auto_free: @@ -238,16 +237,6 @@ NM_AUTO_DEFINE_FCN (GList *, _nm_auto_free_list, g_list_free) NM_AUTO_DEFINE_FCN0 (GChecksum *, _nm_auto_checksum_free, g_checksum_free) #define nm_auto_free_checksum nm_auto(_nm_auto_checksum_free) -NM_AUTO_DEFINE_FCN (char *, _nm_auto_free_secret, nm_free_secret) -/** - * nm_auto_free_secret: - * - * Call g_free() on a variable location when it goes out of scope. - * Also, previously, calls memset(loc, 0, strlen(loc)) to clear out - * the secret. - */ -#define nm_auto_free_secret nm_auto(_nm_auto_free_secret) - #define nm_auto_unset_gvalue nm_auto(g_value_unset) NM_AUTO_DEFINE_FCN_VOID0 (void *, _nm_auto_unref_gtypeclass, g_type_class_unref) @@ -830,15 +819,6 @@ fcn (void) \ /*****************************************************************************/ -static inline void -nm_free_secret (char *secret) -{ - if (secret) { - memset (secret, 0, strlen (secret)); - g_free (secret); - } -} - static inline GString * nm_gstring_prepare (GString **l) { diff --git a/shared/nm-utils/nm-secret-utils.c b/shared/nm-utils/nm-secret-utils.c new file mode 100644 index 0000000000..65f99c65d9 --- /dev/null +++ b/shared/nm-utils/nm-secret-utils.c @@ -0,0 +1,134 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-secret-utils.h" + +/*****************************************************************************/ + +void +nm_explicit_bzero (void *s, gsize n) +{ + /* gracefully handle n == 0. This is important, callers rely on it. */ + if (n > 0) { + nm_assert (s); +#if defined (HAVE_DECL_EXPLICIT_BZERO) && HAVE_DECL_EXPLICIT_BZERO + explicit_bzero (s, n); +#else + /* don't bother with a workaround. Use a reasonable glibc. */ + memset (s, 0, n); +#endif + } +} + +/*****************************************************************************/ + +char * +nm_secret_strchomp (char *secret) +{ + gsize len; + + g_return_val_if_fail (secret, NULL); + + /* it's actually identical to g_strchomp(). However, + * the glib function does not document, that it clears the + * memory. For @secret, we don't only want to truncate trailing + * spaces, we want to overwrite them with NUL. */ + + len = strlen (secret); + while (len--) { + if (g_ascii_isspace ((guchar) secret[len])) + secret[len] = '\0'; + else + break; + } + + return secret; +} + +/*****************************************************************************/ + +GBytes * +nm_secret_copy_to_gbytes (gconstpointer mem, gsize mem_len) +{ + NMSecretBuf *b; + + if (mem_len == 0) + return g_bytes_new_static ("", 0); + + nm_assert (mem); + + /* NUL terminate the buffer. + * + * The entire buffer is already malloc'ed and likely has some room for padding. + * Thus, in many situations, this additional byte will cause no overhead in + * practice. + * + * Even if it causes an overhead, do it just for safety. Yes, the returned + * bytes is not a NUL terminated string and no user must rely on this. Do + * not treat binary data as NUL terminated strings, unless you know what + * you are doing. Anyway, defensive FTW. + */ + + b = nm_secret_buf_new (mem_len + 1); + memcpy (b->bin, mem, mem_len); + b->bin[mem_len] = 0; + return nm_secret_buf_to_gbytes_take (b, mem_len); +} + +/*****************************************************************************/ + +NMSecretBuf * +nm_secret_buf_new (gsize len) +{ + NMSecretBuf *secret; + + nm_assert (len > 0); + + secret = g_malloc (sizeof (NMSecretBuf) + len); + *((gsize *) &(secret->len)) = len; + return secret; +} + +static void +_secret_buf_free (gpointer user_data) +{ + NMSecretBuf *secret = user_data; + + nm_assert (secret); + nm_assert (secret->len > 0); + + nm_explicit_bzero (secret->bin, secret->len); + g_free (user_data); +} + +GBytes * +nm_secret_buf_to_gbytes_take (NMSecretBuf *secret, gssize actual_len) +{ + nm_assert (secret); + nm_assert (secret->len > 0); + nm_assert (actual_len == -1 || (actual_len >= 0 && actual_len <= secret->len)); + return g_bytes_new_with_free_func (secret->bin, + actual_len >= 0 ? (gsize) actual_len : secret->len, + _secret_buf_free, + secret); +} diff --git a/shared/nm-utils/nm-secret-utils.h b/shared/nm-utils/nm-secret-utils.h new file mode 100644 index 0000000000..21a3c1ba11 --- /dev/null +++ b/shared/nm-utils/nm-secret-utils.h @@ -0,0 +1,151 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2018 Red Hat, Inc. + */ + +#ifndef __NM_SECRET_UTILS_H__ +#define __NM_SECRET_UTILS_H__ + +#include "nm-macros-internal.h" + +/*****************************************************************************/ + +void nm_explicit_bzero (void *s, gsize n); + +/*****************************************************************************/ + +char *nm_secret_strchomp (char *secret); + +/*****************************************************************************/ + +static inline void +nm_free_secret (char *secret) +{ + if (secret) { + nm_explicit_bzero (secret, strlen (secret)); + g_free (secret); + } +} + +NM_AUTO_DEFINE_FCN (char *, _nm_auto_free_secret, nm_free_secret) +/** + * nm_auto_free_secret: + * + * Call g_free() on a variable location when it goes out of scope. + * Also, previously, calls memset(loc, 0, strlen(loc)) to clear out + * the secret. + */ +#define nm_auto_free_secret nm_auto(_nm_auto_free_secret) + +/*****************************************************************************/ + +GBytes *nm_secret_copy_to_gbytes (gconstpointer mem, gsize mem_len); + +/*****************************************************************************/ + +/* NMSecretPtr is a pair of malloc'ed data pointer and the length of the + * data. The purpose is to use it in combination with nm_auto_clear_secret_ptr + * which ensures that the data pointer (with all len bytes) is cleared upon + * cleanup. */ +typedef struct { + gsize len; + + /* the data pointer. This pointer must be allocated with malloc (at least + * when used with nm_secret_ptr_clear()). */ + union { + char *str; + void *ptr; + guint8 *bin; + }; +} NMSecretPtr; + +static inline void +nm_secret_ptr_clear (NMSecretPtr *secret) +{ + if (secret) { + if (secret->len > 0) { + if (secret->ptr) + nm_explicit_bzero (secret->ptr, secret->len); + secret->len = 0; + } + nm_clear_g_free (&secret->ptr); + } +} + +#define nm_auto_clear_secret_ptr nm_auto(nm_secret_ptr_clear) + +#define NM_SECRET_PTR_STATIC(_len) \ + ((const NMSecretPtr) { \ + .len = _len, \ + .ptr = ((guint8 [_len]) { }), \ + }) + +static inline void +nm_secret_ptr_clear_static (const NMSecretPtr *secret) +{ + if (secret) { + if (secret->len > 0) { + nm_assert (secret->ptr); + nm_explicit_bzero (secret->ptr, secret->len); + } + } +} + +#define nm_auto_clear_static_secret_ptr nm_auto(nm_secret_ptr_clear_static) + +static inline void +nm_secret_ptr_move (NMSecretPtr *dst, NMSecretPtr *src) +{ + if (dst && dst != src) { + *dst = *src; + src->len = 0; + src->ptr = NULL; + } +} + +/*****************************************************************************/ + +typedef struct { + const gsize len; + union { + char str[0]; + guint8 bin[0]; + }; +} NMSecretBuf; + +static inline void +_nm_auto_free_secret_buf (NMSecretBuf **ptr) +{ + NMSecretBuf *b = *ptr; + + if (b) { + nm_assert (b->len > 0); + nm_explicit_bzero (b->bin, b->len); + g_free (b); + } +} +#define nm_auto_free_secret_buf nm_auto(_nm_auto_free_secret_buf) + +NMSecretBuf *nm_secret_buf_new (gsize len); + +GBytes *nm_secret_buf_to_gbytes_take (NMSecretBuf *secret, gssize actual_len); + +/*****************************************************************************/ + +#endif /* __NM_SECRET_UTILS_H__ */ From 5bb8e2fa4d96c747d94c0d532a242c865c9f7944 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 29 Aug 2018 13:44:52 +0200 Subject: [PATCH 06/61] shared: add nm_utils_hexchar_to_int() --- shared/nm-utils/nm-shared-utils.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/shared/nm-utils/nm-shared-utils.h b/shared/nm-utils/nm-shared-utils.h index 26875534d9..50bce0eb69 100644 --- a/shared/nm-utils/nm-shared-utils.h +++ b/shared/nm-utils/nm-shared-utils.h @@ -210,6 +210,27 @@ GVariant *nm_utils_gbytes_to_variant_ay (GBytes *bytes); /*****************************************************************************/ +static inline int +nm_utils_hexchar_to_int (char ch) +{ + G_STATIC_ASSERT_EXPR ('0' < 'A'); + G_STATIC_ASSERT_EXPR ('A' < 'a'); + + if (ch >= '0') { + if (ch <= '9') + return ch - '0'; + if (ch >= 'A') { + if (ch <= 'F') + return ((int) ch) + (10 - (int) 'A'); + if (ch >= 'a' && ch <= 'f') + return ((int) ch) + (10 - (int) 'a'); + } + } + return -1; +} + +/*****************************************************************************/ + const char *nm_utils_dbus_path_get_last_component (const char *dbus_path); int nm_utils_dbus_path_cmp (const char *dbus_path_a, const char *dbus_path_b); From 4c4c85eab06b710dc3e0b5c7ed4cf82e44fc8698 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 31 Aug 2018 08:45:03 +0200 Subject: [PATCH 07/61] shared: add _NM_INT_NOT_NEGATIVE() helper --- shared/nm-utils/nm-shared-utils.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/shared/nm-utils/nm-shared-utils.h b/shared/nm-utils/nm-shared-utils.h index 50bce0eb69..b081db9123 100644 --- a/shared/nm-utils/nm-shared-utils.h +++ b/shared/nm-utils/nm-shared-utils.h @@ -26,6 +26,25 @@ /*****************************************************************************/ +static inline gboolean +_NM_INT_NOT_NEGATIVE (gssize val) +{ + /* whether an enum (without negative values) is a signed int, depends on compiler options + * and compiler implementation. + * + * When using such an enum for accessing an array, one naturally wants to check + * that the enum is not negative. However, the compiler doesn't like a plain + * comparisong "enum_val >= 0", because (if the enum is unsigned), it will warn + * that the expression is always true *duh*. Not even a cast to a signed + * type helps to avoid the compiler warning in any case. + * + * The sole purpose of this function is to avoid a compiler warning, when checking + * that an enum is not negative. */ + return val >= 0; +} + +/*****************************************************************************/ + static inline char nm_utils_addr_family_to_char (int addr_family) { From c5c0ffdfd0b5fd757ef42b505d1f4cc6f1476a54 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sat, 1 Sep 2018 18:07:57 +0200 Subject: [PATCH 08/61] shared: add nm_gbytes_equal0() helper Like g_bytes_equal(), except that it accepts %NULL arguments. --- shared/nm-utils/nm-shared-utils.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/shared/nm-utils/nm-shared-utils.h b/shared/nm-utils/nm-shared-utils.h index b081db9123..171544feab 100644 --- a/shared/nm-utils/nm-shared-utils.h +++ b/shared/nm-utils/nm-shared-utils.h @@ -221,6 +221,12 @@ nm_utils_is_separator (const char c) /*****************************************************************************/ +static inline gboolean +nm_gbytes_equal0 (GBytes *a, GBytes *b) +{ + return a == b || (a && b && g_bytes_equal (a, b)); +} + gboolean nm_utils_gbytes_equal_mem (GBytes *bytes, gconstpointer mem_data, gsize mem_len); From 6b813b904face9519b0d441d7c58d227863e386a Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 30 Aug 2018 13:56:05 +0200 Subject: [PATCH 09/61] core: extend nm_utils_*_get_contents() to zero temporary memory When reading a file, we may allocate intermediate buffers (realloc()). Also, reading might fail halfway through the process. Add a new flag that makes sure that this memory is cleared. The point is when reading secrets, that we don't accidentally leave private sensitive material in memory. --- src/nm-core-utils.c | 77 ++++++++++++++++++++------- src/nm-core-utils.h | 14 +++++ src/platform/nm-linux-platform.c | 12 +++-- src/platform/tests/test-link.c | 14 +++-- src/settings/plugins/ifcfg-rh/shvar.c | 1 + 5 files changed, 94 insertions(+), 24 deletions(-) diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index 9aab919ebf..94940c1cef 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -38,6 +38,7 @@ #include #include "nm-utils/nm-random-utils.h" +#include "nm-utils/nm-secret-utils.h" #include "nm-utils.h" #include "nm-core-internal.h" #include "nm-setting-connection.h" @@ -2586,6 +2587,32 @@ _get_contents_error (GError **error, int errsv, const char *format, ...) return -errsv; } +static char * +_mem_realloc (char *old, gboolean do_bzero_mem, gsize cur_len, gsize new_len) +{ + char *new; + + /* re-allocating to zero bytes is an odd case. We don't need it + * and it's not supported. */ + nm_assert (new_len > 0); + + /* regardless of success/failure, @old will always be freed/consumed. */ + + if (do_bzero_mem && cur_len > 0) { + new = g_try_malloc (new_len); + if (new) + memcpy (new, old, NM_MIN (cur_len, new_len)); + nm_explicit_bzero (old, cur_len); + g_free (old); + } else { + new = g_try_realloc (old, new_len); + if (!new) + g_free (old); + } + + return new; +} + /** * nm_utils_fd_get_contents: * @fd: open file descriptor to read. The fd will not be closed, @@ -2604,6 +2631,7 @@ _get_contents_error (GError **error, int errsv, const char *format, ...) * If you set it to 1K, read will fail because fstat() claims the * file is larger. * + * @flags: %NMUtilsFileGetContentsFlags for reading the file. * @contents: the output buffer with the file read. It is always * NUL terminated. The buffer is at most @max_length long, including * the NUL byte. That is, it reads only files up to a length of @@ -2621,6 +2649,7 @@ int nm_utils_fd_get_contents (int fd, gboolean close_fd, gsize max_length, + NMUtilsFileGetContentsFlags flags, char **contents, gsize *length, GError **error) @@ -2628,6 +2657,7 @@ nm_utils_fd_get_contents (int fd, nm_auto_close int fd_keeper = close_fd ? fd : -1; struct stat stat_buf; gs_free char *str = NULL; + const bool do_bzero_mem = NM_FLAGS_HAS (flags, NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET); g_return_val_if_fail (fd >= 0, -EINVAL); g_return_val_if_fail (contents, -EINVAL); @@ -2654,17 +2684,16 @@ nm_utils_fd_get_contents (int fd, return _get_contents_error (error, ENOMEM, "failure to allocate buffer of %zu+1 bytes", n_stat); n_read = nm_utils_fd_read_loop (fd, str, n_stat, TRUE); - if (n_read < 0) + if (n_read < 0) { + if (do_bzero_mem) + nm_explicit_bzero (str, n_stat); return _get_contents_error (error, n_read, "error reading %zu bytes from file descriptor", n_stat); + } str[n_read] = '\0'; if (n_read < n_stat) { - char *tmp; - - tmp = g_try_realloc (str, n_read + 1); - if (!tmp) + if (!(str = _mem_realloc (str, do_bzero_mem, n_stat + 1, n_read + 1))) return _get_contents_error (error, ENOMEM, "failure to reallocate buffer with %zu bytes", n_read + 1); - str = tmp; } NM_SET_OUT (length, n_read); } else { @@ -2695,48 +2724,56 @@ nm_utils_fd_get_contents (int fd, n_read = fread (buf, 1, sizeof (buf), f); errsv = errno; - if (ferror (f)) + if (ferror (f)) { + if (do_bzero_mem) + nm_explicit_bzero (buf, sizeof (buf)); return _get_contents_error (error, errsv, "error during fread"); + } if ( n_have > G_MAXSIZE - 1 - n_read || n_have + n_read + 1 > max_length) { + if (do_bzero_mem) + nm_explicit_bzero (buf, sizeof (buf)); return _get_contents_error (error, EMSGSIZE, "file stream too large (%zu+1 bytes with maximum %zu bytes)", (n_have > G_MAXSIZE - 1 - n_read) ? G_MAXSIZE : n_have + n_read, max_length); } if (n_have + n_read + 1 >= n_alloc) { - char *tmp; + gsize old_n_alloc = n_alloc; - if (str) { + if (n_alloc != 0) { + nm_assert (str); if (n_alloc >= max_length / 2) n_alloc = max_length; else n_alloc *= 2; - } else + } else { + nm_assert (!str); n_alloc = NM_MIN (n_read + 1, sizeof (buf)); + } - tmp = g_try_realloc (str, n_alloc); - if (!tmp) + if (!(str = _mem_realloc (str, do_bzero_mem, old_n_alloc, n_alloc))) { + if (do_bzero_mem) + nm_explicit_bzero (buf, sizeof (buf)); return _get_contents_error (error, ENOMEM, "failure to allocate buffer of %zu bytes", n_alloc); - str = tmp; + } } memcpy (str + n_have, buf, n_read); n_have += n_read; } + if (do_bzero_mem) + nm_explicit_bzero (buf, sizeof (buf)); + if (n_alloc == 0) str = g_new0 (char, 1); else { str[n_have] = '\0'; if (n_have + 1 < n_alloc) { - char *tmp; - - tmp = g_try_realloc (str, n_have + 1); - if (!tmp) + if (!(str = _mem_realloc (str, do_bzero_mem, n_alloc, n_have + 1))) return _get_contents_error (error, ENOMEM, "failure to truncate buffer to %zu bytes", n_have + 1); - str = tmp; } } @@ -2753,6 +2790,7 @@ nm_utils_fd_get_contents (int fd, * @filename: the filename to open. Possibly relative to @dirfd. * @max_length: allocate at most @max_length bytes. * WARNING: see nm_utils_fd_get_contents() hint about @max_length. + * @flags: %NMUtilsFileGetContentsFlags for reading the file. * @contents: the output buffer with the file read. It is always * NUL terminated. The buffer is at most @max_length long, including * the NUL byte. That is, it reads only files up to a length of @@ -2770,6 +2808,7 @@ int nm_utils_file_get_contents (int dirfd, const char *filename, gsize max_length, + NMUtilsFileGetContentsFlags flags, char **contents, gsize *length, GError **error) @@ -2809,6 +2848,7 @@ nm_utils_file_get_contents (int dirfd, return nm_utils_fd_get_contents (fd, TRUE, max_length, + flags, contents, length, error); @@ -2942,6 +2982,7 @@ nm_utils_get_boot_id (void) gs_free char *contents = NULL; nm_utils_file_get_contents (-1, "/proc/sys/kernel/random/boot_id", 0, + NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, &contents, NULL, NULL); if (contents) { g_strstrip (contents); diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h index 53c72e16f9..af72b823b9 100644 --- a/src/nm-core-utils.h +++ b/src/nm-core-utils.h @@ -264,9 +264,22 @@ gboolean nm_utils_sysctl_ip_conf_is_path (int addr_family, const char *path, con gboolean nm_utils_is_specific_hostname (const char *name); +/** + * NMUtilsFileGetContentsFlags: + * @NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE: no flag + * @NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET: if present, ensure that no + * data is left in memory. Essentially, it means to call explicity_bzero() + * to not leave key material on the heap (when reading secrets). + */ +typedef enum { + NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE = 0, + NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET = (1 << 0), +} NMUtilsFileGetContentsFlags; + int nm_utils_fd_get_contents (int fd, gboolean close_fd, gsize max_length, + NMUtilsFileGetContentsFlags flags, char **contents, gsize *length, GError **error); @@ -274,6 +287,7 @@ int nm_utils_fd_get_contents (int fd, int nm_utils_file_get_contents (int dirfd, const char *filename, gsize max_length, + NMUtilsFileGetContentsFlags flags, char **contents, gsize *length, GError **error); diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 9ff976a259..6288b2c1de 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -853,7 +853,9 @@ _linktype_read_devtype (int dirfd) nm_assert (dirfd >= 0); - if (nm_utils_file_get_contents (dirfd, "uevent", 1*1024*1024, &contents, NULL, NULL) < 0) + if (nm_utils_file_get_contents (dirfd, "uevent", 1*1024*1024, + NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, + &contents, NULL, NULL) < 0) return NULL; for (cont = contents; cont; cont = end) { end = strpbrk (cont, "\r\n"); @@ -3515,7 +3517,9 @@ _log_dbg_sysctl_set_impl (NMPlatform *platform, const char *pathid, int dirfd, c char *contents; gs_free char *value_escaped = g_strescape (value, NULL); - if (nm_utils_file_get_contents (dirfd, path, 1*1024*1024, &contents, NULL, &error) < 0) { + if (nm_utils_file_get_contents (dirfd, path, 1*1024*1024, + NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, + &contents, NULL, &error) < 0) { _LOGD ("sysctl: setting '%s' to '%s' (current value cannot be read: %s)", pathid, value_escaped, error->message); g_clear_error (&error); return; @@ -3732,7 +3736,9 @@ sysctl_get (NMPlatform *platform, const char *pathid, int dirfd, const char *pat pathid = path; } - if (nm_utils_file_get_contents (dirfd, path, 1*1024*1024, &contents, NULL, &error) < 0) { + if (nm_utils_file_get_contents (dirfd, path, 1*1024*1024, + NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, + &contents, NULL, &error) < 0) { /* We assume FAILED means EOPNOTSUP */ if ( g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT) || g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NODEV) diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c index 20b3374b28..5e6d02698a 100644 --- a/src/platform/tests/test-link.c +++ b/src/platform/tests/test-link.c @@ -2622,7 +2622,9 @@ test_sysctl_rename (void) case 0: { gs_free char *c = NULL; - if (nm_utils_file_get_contents (dirfd, "ifindex", 1*1024*1024, &c, NULL, NULL) < 0) + if (nm_utils_file_get_contents (dirfd, "ifindex", 1*1024*1024, + NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, + &c, NULL, NULL) < 0) g_assert_not_reached(); g_assert_cmpint (ifindex[0], ==, (int) _nm_utils_ascii_str_to_int64 (c, 10, 0, G_MAXINT, -1)); break; @@ -2686,7 +2688,9 @@ test_sysctl_netns_switch (void) { gs_free char *c = NULL; - if (nm_utils_file_get_contents (dirfd, "ifindex", 0, &c, NULL, NULL) < 0) + if (nm_utils_file_get_contents (dirfd, "ifindex", 0, + NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, + &c, NULL, NULL) < 0) g_assert_not_reached(); g_assert_cmpint (ifindex, ==, (int) _nm_utils_ascii_str_to_int64 (c, 10, 0, G_MAXINT, -1)); } @@ -2698,7 +2702,11 @@ test_sysctl_netns_switch (void) { gs_free char *c = NULL; - if (nm_utils_file_get_contents (-1, nm_sprintf_bufa (100, "/sys/class/net/%s/ifindex", IFNAME), 0, &c, NULL, NULL) < 0) + if (nm_utils_file_get_contents (-1, + nm_sprintf_bufa (100, "/sys/class/net/%s/ifindex", IFNAME), + 0, + NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, + &c, NULL, NULL) < 0) ifindex_tmp = -1; else ifindex_tmp = _nm_utils_ascii_str_to_int64 (c, 10, 0, G_MAXINT, -2); diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c index f09bc2472f..5c439d5292 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.c +++ b/src/settings/plugins/ifcfg-rh/shvar.c @@ -821,6 +821,7 @@ svOpenFileInternal (const char *name, gboolean create, GError **error) if (nm_utils_fd_get_contents (closefd ? nm_steal_fd (&fd) : fd, closefd, 10 * 1024 * 1024, + NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, &arena, NULL, &local) < 0) { From 6ee7453bc1623e38f83f5198d39eea7d18d26df5 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 30 Aug 2018 15:00:39 +0200 Subject: [PATCH 10/61] shared: add "nm-io-utils.h" --- Makefile.am | 4 ++++ shared/meson.build | 1 + shared/nm-utils/nm-io-utils.c | 26 ++++++++++++++++++++++++++ shared/nm-utils/nm-io-utils.h | 29 +++++++++++++++++++++++++++++ 4 files changed, 60 insertions(+) create mode 100644 shared/nm-utils/nm-io-utils.c create mode 100644 shared/nm-utils/nm-io-utils.h diff --git a/Makefile.am b/Makefile.am index c2c7af44f0..1183d311b5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -501,6 +501,7 @@ libnm_core_lib_h_priv = \ shared/nm-utils/nm-dedup-multi.h \ shared/nm-utils/nm-enum-utils.h \ shared/nm-utils/nm-hash-utils.h \ + shared/nm-utils/nm-io-utils.h \ shared/nm-utils/nm-secret-utils.h \ shared/nm-utils/nm-shared-utils.h \ shared/nm-utils/nm-random-utils.h \ @@ -568,6 +569,7 @@ libnm_core_lib_c_real = \ shared/nm-utils/nm-dedup-multi.c \ shared/nm-utils/nm-enum-utils.c \ shared/nm-utils/nm-hash-utils.c \ + shared/nm-utils/nm-io-utils.c \ shared/nm-utils/nm-secret-utils.c \ shared/nm-utils/nm-shared-utils.c \ shared/nm-utils/nm-random-utils.c \ @@ -4735,6 +4737,8 @@ EXTRA_DIST += \ shared/nm-utils/nm-compat.c \ shared/nm-utils/nm-compat.h \ shared/nm-utils/nm-glib.h \ + shared/nm-utils/nm-io-utils.c \ + shared/nm-utils/nm-io-utils.h \ shared/nm-utils/nm-jansson.h \ shared/nm-utils/nm-obj.h \ shared/nm-utils/nm-macros-internal.h \ diff --git a/shared/meson.build b/shared/meson.build index c14b9d723b..e1cf620bfd 100644 --- a/shared/meson.build +++ b/shared/meson.build @@ -50,6 +50,7 @@ shared_files_libnm_core = files(''' nm-utils/nm-dedup-multi.c nm-utils/nm-enum-utils.c nm-utils/nm-hash-utils.c + nm-utils/nm-io-utils.c nm-utils/nm-random-utils.c nm-utils/nm-secret-utils.c nm-utils/nm-shared-utils.c diff --git a/shared/nm-utils/nm-io-utils.c b/shared/nm-utils/nm-io-utils.c new file mode 100644 index 0000000000..69403c27f2 --- /dev/null +++ b/shared/nm-utils/nm-io-utils.c @@ -0,0 +1,26 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-io-utils.h" + +/*****************************************************************************/ diff --git a/shared/nm-utils/nm-io-utils.h b/shared/nm-utils/nm-io-utils.h new file mode 100644 index 0000000000..e632deb89d --- /dev/null +++ b/shared/nm-utils/nm-io-utils.h @@ -0,0 +1,29 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2018 Red Hat, Inc. + */ + +#ifndef __NM_IO_UTILS_H__ +#define __NM_IO_UTILS_H__ + +#include "nm-macros-internal.h" + +/*****************************************************************************/ + +#endif /* __NM_IO_UTILS_H__ */ From ff163d9d0dddc2a12dec8092f54b31e984136a44 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 30 Aug 2018 15:13:52 +0200 Subject: [PATCH 11/61] shared: move file-get-contents and file-set-contents helper to shared/ These functions are not specific to "src/". Also, they will be needed by outside of "src/" soon. --- shared/nm-utils/nm-io-utils.c | 404 ++++++++++++++++++ shared/nm-utils/nm-io-utils.h | 34 ++ src/nm-core-utils.c | 401 +---------------- src/nm-core-utils.h | 34 -- src/platform/nm-linux-platform.c | 1 + src/platform/tests/test-link.c | 1 + .../plugins/ifcfg-rh/nms-ifcfg-rh-writer.c | 1 + src/settings/plugins/ifcfg-rh/shvar.c | 1 + .../plugins/keyfile/nms-keyfile-writer.c | 2 + 9 files changed, 445 insertions(+), 434 deletions(-) diff --git a/shared/nm-utils/nm-io-utils.c b/shared/nm-utils/nm-io-utils.c index 69403c27f2..88cb13ff18 100644 --- a/shared/nm-utils/nm-io-utils.c +++ b/shared/nm-utils/nm-io-utils.c @@ -23,4 +23,408 @@ #include "nm-io-utils.h" +#include +#include +#include + +#include "nm-shared-utils.h" +#include "nm-secret-utils.h" + /*****************************************************************************/ + +_nm_printf (3, 4) +static int +_get_contents_error (GError **error, int errsv, const char *format, ...) +{ + if (errsv < 0) + errsv = -errsv; + else if (!errsv) + errsv = errno; + + if (error) { + char *msg; + va_list args; + + va_start (args, format); + msg = g_strdup_vprintf (format, args); + va_end (args); + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errsv), + "%s: %s", + msg, g_strerror (errsv)); + g_free (msg); + } + return -errsv; +} + +static char * +_mem_realloc (char *old, gboolean do_bzero_mem, gsize cur_len, gsize new_len) +{ + char *new; + + /* re-allocating to zero bytes is an odd case. We don't need it + * and it's not supported. */ + nm_assert (new_len > 0); + + /* regardless of success/failure, @old will always be freed/consumed. */ + + if (do_bzero_mem && cur_len > 0) { + new = g_try_malloc (new_len); + if (new) + memcpy (new, old, NM_MIN (cur_len, new_len)); + nm_explicit_bzero (old, cur_len); + g_free (old); + } else { + new = g_try_realloc (old, new_len); + if (!new) + g_free (old); + } + + return new; +} + +/** + * nm_utils_fd_get_contents: + * @fd: open file descriptor to read. The fd will not be closed, + * but don't rely on its state afterwards. + * @close_fd: if %TRUE, @fd will be closed by the function. + * Passing %TRUE here might safe a syscall for dup(). + * @max_length: allocate at most @max_length bytes. If the + * file is larger, reading will fail. Set to zero to use + * a very large default. + * WARNING: @max_length is here to avoid a crash for huge/unlimited files. + * For example, stat(/sys/class/net/enp0s25/ifindex) gives a filesize of + * 4K, although the actual real is small. @max_length is the memory + * allocated in the process of reading the file, thus it must be at least + * the size reported by fstat. + * If you set it to 1K, read will fail because fstat() claims the + * file is larger. + * @flags: %NMUtilsFileGetContentsFlags for reading the file. + * @contents: the output buffer with the file read. It is always + * NUL terminated. The buffer is at most @max_length long, including + * the NUL byte. That is, it reads only files up to a length of + * @max_length - 1 bytes. + * @length: optional output argument of the read file size. + * + * A reimplementation of g_file_get_contents() with a few differences: + * - accepts an open fd, instead of a path name. This allows you to + * use openat(). + * - limits the maxium filesize to max_length. + * + * Returns: a negative error code on failure. + */ +int +nm_utils_fd_get_contents (int fd, + gboolean close_fd, + gsize max_length, + NMUtilsFileGetContentsFlags flags, + char **contents, + gsize *length, + GError **error) +{ + nm_auto_close int fd_keeper = close_fd ? fd : -1; + struct stat stat_buf; + gs_free char *str = NULL; + const bool do_bzero_mem = NM_FLAGS_HAS (flags, NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET); + + g_return_val_if_fail (fd >= 0, -EINVAL); + g_return_val_if_fail (contents, -EINVAL); + g_return_val_if_fail (!error || !*error, -EINVAL); + + if (fstat (fd, &stat_buf) < 0) + return _get_contents_error (error, 0, "failure during fstat"); + + if (!max_length) { + /* default to a very large size, but not extreme */ + max_length = 2 * 1024 * 1024; + } + + if ( stat_buf.st_size > 0 + && S_ISREG (stat_buf.st_mode)) { + const gsize n_stat = stat_buf.st_size; + ssize_t n_read; + + if (n_stat > max_length - 1) + return _get_contents_error (error, EMSGSIZE, "file too large (%zu+1 bytes with maximum %zu bytes)", n_stat, max_length); + + str = g_try_malloc (n_stat + 1); + if (!str) + return _get_contents_error (error, ENOMEM, "failure to allocate buffer of %zu+1 bytes", n_stat); + + n_read = nm_utils_fd_read_loop (fd, str, n_stat, TRUE); + if (n_read < 0) { + if (do_bzero_mem) + nm_explicit_bzero (str, n_stat); + return _get_contents_error (error, n_read, "error reading %zu bytes from file descriptor", n_stat); + } + str[n_read] = '\0'; + + if (n_read < n_stat) { + if (!(str = _mem_realloc (str, do_bzero_mem, n_stat + 1, n_read + 1))) + return _get_contents_error (error, ENOMEM, "failure to reallocate buffer with %zu bytes", n_read + 1); + } + NM_SET_OUT (length, n_read); + } else { + nm_auto_fclose FILE *f = NULL; + char buf[4096]; + gsize n_have, n_alloc; + int fd2; + + if (fd_keeper >= 0) + fd2 = nm_steal_fd (&fd_keeper); + else { + fd2 = fcntl (fd, F_DUPFD_CLOEXEC, 0); + if (fd2 < 0) + return _get_contents_error (error, 0, "error during dup"); + } + + if (!(f = fdopen (fd2, "r"))) { + nm_close (fd2); + return _get_contents_error (error, 0, "failure during fdopen"); + } + + n_have = 0; + n_alloc = 0; + + while (!feof (f)) { + int errsv; + gsize n_read; + + n_read = fread (buf, 1, sizeof (buf), f); + errsv = errno; + if (ferror (f)) { + if (do_bzero_mem) + nm_explicit_bzero (buf, sizeof (buf)); + return _get_contents_error (error, errsv, "error during fread"); + } + + if ( n_have > G_MAXSIZE - 1 - n_read + || n_have + n_read + 1 > max_length) { + if (do_bzero_mem) + nm_explicit_bzero (buf, sizeof (buf)); + return _get_contents_error (error, EMSGSIZE, "file stream too large (%zu+1 bytes with maximum %zu bytes)", + (n_have > G_MAXSIZE - 1 - n_read) ? G_MAXSIZE : n_have + n_read, + max_length); + } + + if (n_have + n_read + 1 >= n_alloc) { + gsize old_n_alloc = n_alloc; + + if (n_alloc != 0) { + nm_assert (str); + if (n_alloc >= max_length / 2) + n_alloc = max_length; + else + n_alloc *= 2; + } else { + nm_assert (!str); + n_alloc = NM_MIN (n_read + 1, sizeof (buf)); + } + + if (!(str = _mem_realloc (str, do_bzero_mem, old_n_alloc, n_alloc))) { + if (do_bzero_mem) + nm_explicit_bzero (buf, sizeof (buf)); + return _get_contents_error (error, ENOMEM, "failure to allocate buffer of %zu bytes", n_alloc); + } + } + + memcpy (str + n_have, buf, n_read); + n_have += n_read; + } + + if (do_bzero_mem) + nm_explicit_bzero (buf, sizeof (buf)); + + if (n_alloc == 0) + str = g_new0 (char, 1); + else { + str[n_have] = '\0'; + if (n_have + 1 < n_alloc) { + if (!(str = _mem_realloc (str, do_bzero_mem, n_alloc, n_have + 1))) + return _get_contents_error (error, ENOMEM, "failure to truncate buffer to %zu bytes", n_have + 1); + } + } + + NM_SET_OUT (length, n_have); + } + + *contents = g_steal_pointer (&str); + return 0; +} + +/** + * nm_utils_file_get_contents: + * @dirfd: optional file descriptor to use openat(). If negative, use plain open(). + * @filename: the filename to open. Possibly relative to @dirfd. + * @max_length: allocate at most @max_length bytes. + * WARNING: see nm_utils_fd_get_contents() hint about @max_length. + * @flags: %NMUtilsFileGetContentsFlags for reading the file. + * @contents: the output buffer with the file read. It is always + * NUL terminated. The buffer is at most @max_length long, including + * the NUL byte. That is, it reads only files up to a length of + * @max_length - 1 bytes. + * @length: optional output argument of the read file size. + * + * A reimplementation of g_file_get_contents() with a few differences: + * - accepts an @dirfd to open @filename relative to that path via openat(). + * - limits the maxium filesize to max_length. + * - uses O_CLOEXEC on internal file descriptor + * + * Returns: a negative error code on failure. + */ +int +nm_utils_file_get_contents (int dirfd, + const char *filename, + gsize max_length, + NMUtilsFileGetContentsFlags flags, + char **contents, + gsize *length, + GError **error) +{ + int fd; + int errsv; + + g_return_val_if_fail (filename && filename[0], -EINVAL); + + if (dirfd >= 0) { + fd = openat (dirfd, filename, O_RDONLY | O_CLOEXEC); + if (fd < 0) { + errsv = errno; + + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errsv), + "Failed to open file \"%s\" with openat: %s", + filename, + g_strerror (errsv)); + return -errsv; + } + } else { + fd = open (filename, O_RDONLY | O_CLOEXEC); + if (fd < 0) { + errsv = errno; + + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errsv), + "Failed to open file \"%s\": %s", + filename, + g_strerror (errsv)); + return -errsv; + } + } + return nm_utils_fd_get_contents (fd, + TRUE, + max_length, + flags, + contents, + length, + error); +} + +/*****************************************************************************/ + +/* + * Copied from GLib's g_file_set_contents() et al., but allows + * specifying a mode for the new file. + */ +gboolean +nm_utils_file_set_contents (const char *filename, + const char *contents, + gssize length, + mode_t mode, + GError **error) +{ + gs_free char *tmp_name = NULL; + struct stat statbuf; + int errsv; + gssize s; + int fd; + + g_return_val_if_fail (filename, FALSE); + g_return_val_if_fail (contents || !length, FALSE); + g_return_val_if_fail (!error || !*error, FALSE); + g_return_val_if_fail (length >= -1, FALSE); + + if (length == -1) + length = strlen (contents); + + tmp_name = g_strdup_printf ("%s.XXXXXX", filename); + fd = g_mkstemp_full (tmp_name, O_RDWR, mode); + if (fd < 0) { + errsv = errno; + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errsv), + "failed to create file %s: %s", + tmp_name, + g_strerror (errsv)); + return FALSE; + } + + while (length > 0) { + s = write (fd, contents, length); + if (s < 0) { + errsv = errno; + if (errsv == EINTR) + continue; + + nm_close (fd); + unlink (tmp_name); + + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errsv), + "failed to write to file %s: %s", + tmp_name, + g_strerror (errsv)); + return FALSE; + } + + g_assert (s <= length); + + contents += s; + length -= s; + } + + /* If the final destination exists and is > 0 bytes, we want to sync the + * newly written file to ensure the data is on disk when we rename over + * the destination. Otherwise if we get a system crash we can lose both + * the new and the old file on some filesystems. (I.E. those that don't + * guarantee the data is written to the disk before the metadata.) + */ + if ( lstat (filename, &statbuf) == 0 + && statbuf.st_size > 0 + && fsync (fd) != 0) { + errsv = errno; + + nm_close (fd); + unlink (tmp_name); + + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errsv), + "failed to fsync %s: %s", + tmp_name, + g_strerror (errsv)); + return FALSE; + } + + nm_close (fd); + + if (rename (tmp_name, filename)) { + errsv = errno; + unlink (tmp_name); + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errsv), + "failed to rename %s to %s: %s", + tmp_name, + filename, + g_strerror (errsv)); + return FALSE; + } + + return TRUE; +} diff --git a/shared/nm-utils/nm-io-utils.h b/shared/nm-utils/nm-io-utils.h index e632deb89d..dc72a2a6c5 100644 --- a/shared/nm-utils/nm-io-utils.h +++ b/shared/nm-utils/nm-io-utils.h @@ -26,4 +26,38 @@ /*****************************************************************************/ +/** + * NMUtilsFileGetContentsFlags: + * @NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE: no flag + * @NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET: if present, ensure that no + * data is left in memory. Essentially, it means to call explicity_bzero() + * to not leave key material on the heap (when reading secrets). + */ +typedef enum { + NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE = 0, + NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET = (1 << 0), +} NMUtilsFileGetContentsFlags; + +int nm_utils_fd_get_contents (int fd, + gboolean close_fd, + gsize max_length, + NMUtilsFileGetContentsFlags flags, + char **contents, + gsize *length, + GError **error); + +int nm_utils_file_get_contents (int dirfd, + const char *filename, + gsize max_length, + NMUtilsFileGetContentsFlags flags, + char **contents, + gsize *length, + GError **error); + +gboolean nm_utils_file_set_contents (const char *filename, + const char *contents, + gssize length, + mode_t mode, + GError **error); + #endif /* __NM_IO_UTILS_H__ */ diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index 94940c1cef..ca8c952671 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -38,7 +38,7 @@ #include #include "nm-utils/nm-random-utils.h" -#include "nm-utils/nm-secret-utils.h" +#include "nm-utils/nm-io-utils.h" #include "nm-utils.h" #include "nm-core-internal.h" #include "nm-setting-connection.h" @@ -2561,301 +2561,6 @@ nm_utils_machine_id_read (void) /*****************************************************************************/ -_nm_printf (3, 4) -static int -_get_contents_error (GError **error, int errsv, const char *format, ...) -{ - if (errsv < 0) - errsv = -errsv; - else if (!errsv) - errsv = errno; - - if (error) { - char *msg; - va_list args; - - va_start (args, format); - msg = g_strdup_vprintf (format, args); - va_end (args); - g_set_error (error, - G_FILE_ERROR, - g_file_error_from_errno (errsv), - "%s: %s", - msg, g_strerror (errsv)); - g_free (msg); - } - return -errsv; -} - -static char * -_mem_realloc (char *old, gboolean do_bzero_mem, gsize cur_len, gsize new_len) -{ - char *new; - - /* re-allocating to zero bytes is an odd case. We don't need it - * and it's not supported. */ - nm_assert (new_len > 0); - - /* regardless of success/failure, @old will always be freed/consumed. */ - - if (do_bzero_mem && cur_len > 0) { - new = g_try_malloc (new_len); - if (new) - memcpy (new, old, NM_MIN (cur_len, new_len)); - nm_explicit_bzero (old, cur_len); - g_free (old); - } else { - new = g_try_realloc (old, new_len); - if (!new) - g_free (old); - } - - return new; -} - -/** - * nm_utils_fd_get_contents: - * @fd: open file descriptor to read. The fd will not be closed, - * but don't rely on its state afterwards. - * @close_fd: if %TRUE, @fd will be closed by the function. - * Passing %TRUE here might safe a syscall for dup(). - * @max_length: allocate at most @max_length bytes. If the - * file is larger, reading will fail. Set to zero to use - * a very large default. - * - * WARNING: @max_length is here to avoid a crash for huge/unlimited files. - * For example, stat(/sys/class/net/enp0s25/ifindex) gives a filesize of - * 4K, although the actual real is small. @max_length is the memory - * allocated in the process of reading the file, thus it must be at least - * the size reported by fstat. - * If you set it to 1K, read will fail because fstat() claims the - * file is larger. - * - * @flags: %NMUtilsFileGetContentsFlags for reading the file. - * @contents: the output buffer with the file read. It is always - * NUL terminated. The buffer is at most @max_length long, including - * the NUL byte. That is, it reads only files up to a length of - * @max_length - 1 bytes. - * @length: optional output argument of the read file size. - * - * A reimplementation of g_file_get_contents() with a few differences: - * - accepts an open fd, instead of a path name. This allows you to - * use openat(). - * - limits the maxium filesize to max_length. - * - * Returns: a negative error code on failure. - */ -int -nm_utils_fd_get_contents (int fd, - gboolean close_fd, - gsize max_length, - NMUtilsFileGetContentsFlags flags, - char **contents, - gsize *length, - GError **error) -{ - nm_auto_close int fd_keeper = close_fd ? fd : -1; - struct stat stat_buf; - gs_free char *str = NULL; - const bool do_bzero_mem = NM_FLAGS_HAS (flags, NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET); - - g_return_val_if_fail (fd >= 0, -EINVAL); - g_return_val_if_fail (contents, -EINVAL); - g_return_val_if_fail (!error || !*error, -EINVAL); - - if (fstat (fd, &stat_buf) < 0) - return _get_contents_error (error, 0, "failure during fstat"); - - if (!max_length) { - /* default to a very large size, but not extreme */ - max_length = 2 * 1024 * 1024; - } - - if ( stat_buf.st_size > 0 - && S_ISREG (stat_buf.st_mode)) { - const gsize n_stat = stat_buf.st_size; - ssize_t n_read; - - if (n_stat > max_length - 1) - return _get_contents_error (error, EMSGSIZE, "file too large (%zu+1 bytes with maximum %zu bytes)", n_stat, max_length); - - str = g_try_malloc (n_stat + 1); - if (!str) - return _get_contents_error (error, ENOMEM, "failure to allocate buffer of %zu+1 bytes", n_stat); - - n_read = nm_utils_fd_read_loop (fd, str, n_stat, TRUE); - if (n_read < 0) { - if (do_bzero_mem) - nm_explicit_bzero (str, n_stat); - return _get_contents_error (error, n_read, "error reading %zu bytes from file descriptor", n_stat); - } - str[n_read] = '\0'; - - if (n_read < n_stat) { - if (!(str = _mem_realloc (str, do_bzero_mem, n_stat + 1, n_read + 1))) - return _get_contents_error (error, ENOMEM, "failure to reallocate buffer with %zu bytes", n_read + 1); - } - NM_SET_OUT (length, n_read); - } else { - nm_auto_fclose FILE *f = NULL; - char buf[4096]; - gsize n_have, n_alloc; - int fd2; - - if (fd_keeper >= 0) - fd2 = nm_steal_fd (&fd_keeper); - else { - fd2 = fcntl (fd, F_DUPFD_CLOEXEC, 0); - if (fd2 < 0) - return _get_contents_error (error, 0, "error during dup"); - } - - if (!(f = fdopen (fd2, "r"))) { - nm_close (fd2); - return _get_contents_error (error, 0, "failure during fdopen"); - } - - n_have = 0; - n_alloc = 0; - - while (!feof (f)) { - int errsv; - gsize n_read; - - n_read = fread (buf, 1, sizeof (buf), f); - errsv = errno; - if (ferror (f)) { - if (do_bzero_mem) - nm_explicit_bzero (buf, sizeof (buf)); - return _get_contents_error (error, errsv, "error during fread"); - } - - if ( n_have > G_MAXSIZE - 1 - n_read - || n_have + n_read + 1 > max_length) { - if (do_bzero_mem) - nm_explicit_bzero (buf, sizeof (buf)); - return _get_contents_error (error, EMSGSIZE, "file stream too large (%zu+1 bytes with maximum %zu bytes)", - (n_have > G_MAXSIZE - 1 - n_read) ? G_MAXSIZE : n_have + n_read, - max_length); - } - - if (n_have + n_read + 1 >= n_alloc) { - gsize old_n_alloc = n_alloc; - - if (n_alloc != 0) { - nm_assert (str); - if (n_alloc >= max_length / 2) - n_alloc = max_length; - else - n_alloc *= 2; - } else { - nm_assert (!str); - n_alloc = NM_MIN (n_read + 1, sizeof (buf)); - } - - if (!(str = _mem_realloc (str, do_bzero_mem, old_n_alloc, n_alloc))) { - if (do_bzero_mem) - nm_explicit_bzero (buf, sizeof (buf)); - return _get_contents_error (error, ENOMEM, "failure to allocate buffer of %zu bytes", n_alloc); - } - } - - memcpy (str + n_have, buf, n_read); - n_have += n_read; - } - - if (do_bzero_mem) - nm_explicit_bzero (buf, sizeof (buf)); - - if (n_alloc == 0) - str = g_new0 (char, 1); - else { - str[n_have] = '\0'; - if (n_have + 1 < n_alloc) { - if (!(str = _mem_realloc (str, do_bzero_mem, n_alloc, n_have + 1))) - return _get_contents_error (error, ENOMEM, "failure to truncate buffer to %zu bytes", n_have + 1); - } - } - - NM_SET_OUT (length, n_have); - } - - *contents = g_steal_pointer (&str); - return 0; -} - -/** - * nm_utils_file_get_contents: - * @dirfd: optional file descriptor to use openat(). If negative, use plain open(). - * @filename: the filename to open. Possibly relative to @dirfd. - * @max_length: allocate at most @max_length bytes. - * WARNING: see nm_utils_fd_get_contents() hint about @max_length. - * @flags: %NMUtilsFileGetContentsFlags for reading the file. - * @contents: the output buffer with the file read. It is always - * NUL terminated. The buffer is at most @max_length long, including - * the NUL byte. That is, it reads only files up to a length of - * @max_length - 1 bytes. - * @length: optional output argument of the read file size. - * - * A reimplementation of g_file_get_contents() with a few differences: - * - accepts an @dirfd to open @filename relative to that path via openat(). - * - limits the maxium filesize to max_length. - * - uses O_CLOEXEC on internal file descriptor - * - * Returns: a negative error code on failure. - */ -int -nm_utils_file_get_contents (int dirfd, - const char *filename, - gsize max_length, - NMUtilsFileGetContentsFlags flags, - char **contents, - gsize *length, - GError **error) -{ - int fd; - int errsv; - - g_return_val_if_fail (filename && filename[0], -EINVAL); - - if (dirfd >= 0) { - fd = openat (dirfd, filename, O_RDONLY | O_CLOEXEC); - if (fd < 0) { - errsv = errno; - - g_set_error (error, - G_FILE_ERROR, - g_file_error_from_errno (errsv), - "Failed to open file \"%s\" with openat: %s", - filename, - g_strerror (errsv)); - return -errsv; - } - } else { - fd = open (filename, O_RDONLY | O_CLOEXEC); - if (fd < 0) { - errsv = errno; - - g_set_error (error, - G_FILE_ERROR, - g_file_error_from_errno (errsv), - "Failed to open file \"%s\": %s", - filename, - g_strerror (errsv)); - return -errsv; - } - } - return nm_utils_fd_get_contents (fd, - TRUE, - max_length, - flags, - contents, - length, - error); -} - -/*****************************************************************************/ - static gboolean _secret_key_read (guint8 **out_secret_key, gsize *out_key_len) @@ -4028,110 +3733,6 @@ nm_utils_get_reverse_dns_domains_ip6 (const struct in6_addr *ip, guint8 plen, GP #undef N_SHIFT } -/** - * Copied from GLib's g_file_set_contents() et al., but allows - * specifying a mode for the new file. - */ -gboolean -nm_utils_file_set_contents (const char *filename, - const char *contents, - gssize length, - mode_t mode, - GError **error) -{ - gs_free char *tmp_name = NULL; - struct stat statbuf; - int errsv; - gssize s; - int fd; - - g_return_val_if_fail (filename, FALSE); - g_return_val_if_fail (contents || !length, FALSE); - g_return_val_if_fail (!error || !*error, FALSE); - g_return_val_if_fail (length >= -1, FALSE); - - if (length == -1) - length = strlen (contents); - - tmp_name = g_strdup_printf ("%s.XXXXXX", filename); - fd = g_mkstemp_full (tmp_name, O_RDWR, mode); - if (fd < 0) { - errsv = errno; - g_set_error (error, - G_FILE_ERROR, - g_file_error_from_errno (errsv), - "failed to create file %s: %s", - tmp_name, - g_strerror (errsv)); - return FALSE; - } - - while (length > 0) { - s = write (fd, contents, length); - if (s < 0) { - errsv = errno; - if (errsv == EINTR) - continue; - - nm_close (fd); - unlink (tmp_name); - - g_set_error (error, - G_FILE_ERROR, - g_file_error_from_errno (errsv), - "failed to write to file %s: %s", - tmp_name, - g_strerror (errsv)); - return FALSE; - } - - g_assert (s <= length); - - contents += s; - length -= s; - } - - /* If the final destination exists and is > 0 bytes, we want to sync the - * newly written file to ensure the data is on disk when we rename over - * the destination. Otherwise if we get a system crash we can lose both - * the new and the old file on some filesystems. (I.E. those that don't - * guarantee the data is written to the disk before the metadata.) - */ - if ( lstat (filename, &statbuf) == 0 - && statbuf.st_size > 0 - && fsync (fd) != 0) { - errsv = errno; - - nm_close (fd); - unlink (tmp_name); - - g_set_error (error, - G_FILE_ERROR, - g_file_error_from_errno (errsv), - "failed to fsync %s: %s", - tmp_name, - g_strerror (errsv)); - return FALSE; - } - - nm_close (fd); - - if (rename (tmp_name, filename)) { - errsv = errno; - unlink (tmp_name); - g_set_error (error, - G_FILE_ERROR, - g_file_error_from_errno (errsv), - "failed to rename %s to %s: %s", - tmp_name, - filename, - g_strerror (errsv)); - return FALSE; - } - - return TRUE; -} - struct plugin_info { char *path; struct stat st; diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h index af72b823b9..30d1360a1e 100644 --- a/src/nm-core-utils.h +++ b/src/nm-core-utils.h @@ -264,40 +264,6 @@ gboolean nm_utils_sysctl_ip_conf_is_path (int addr_family, const char *path, con gboolean nm_utils_is_specific_hostname (const char *name); -/** - * NMUtilsFileGetContentsFlags: - * @NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE: no flag - * @NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET: if present, ensure that no - * data is left in memory. Essentially, it means to call explicity_bzero() - * to not leave key material on the heap (when reading secrets). - */ -typedef enum { - NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE = 0, - NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET = (1 << 0), -} NMUtilsFileGetContentsFlags; - -int nm_utils_fd_get_contents (int fd, - gboolean close_fd, - gsize max_length, - NMUtilsFileGetContentsFlags flags, - char **contents, - gsize *length, - GError **error); - -int nm_utils_file_get_contents (int dirfd, - const char *filename, - gsize max_length, - NMUtilsFileGetContentsFlags flags, - char **contents, - gsize *length, - GError **error); - -gboolean nm_utils_file_set_contents (const char *filename, - const char *contents, - gssize length, - mode_t mode, - GError **error); - char *nm_utils_machine_id_read (void); gboolean nm_utils_machine_id_parse (const char *id_str, /*uuid_t*/ guchar *out_uuid); diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 6288b2c1de..a8a649c7b4 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -54,6 +54,7 @@ #include "wifi/nm-wifi-utils-wext.h" #include "wpan/nm-wpan-utils.h" #include "nm-utils/unaligned.h" +#include "nm-utils/nm-io-utils.h" #include "nm-utils/nm-udev-utils.h" /*****************************************************************************/ diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c index 5e6d02698a..057490c406 100644 --- a/src/platform/tests/test-link.c +++ b/src/platform/tests/test-link.c @@ -25,6 +25,7 @@ #include #include +#include "nm-utils/nm-io-utils.h" #include "platform/nmp-object.h" #include "platform/nmp-netns.h" #include "platform/nm-platform-utils.h" diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c index 74e8497578..530feba6ef 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c @@ -33,6 +33,7 @@ #include #include "nm-utils/nm-enum-utils.h" +#include "nm-utils/nm-io-utils.h" #include "nm-manager.h" #include "nm-setting-connection.h" #include "nm-setting-wired.h" diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c index 5c439d5292..fe82fbddae 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.c +++ b/src/settings/plugins/ifcfg-rh/shvar.c @@ -39,6 +39,7 @@ #include "nm-core-internal.h" #include "nm-core-utils.h" #include "nm-utils/nm-enum-utils.h" +#include "nm-utils/nm-io-utils.h" #include "c-list/src/c-list.h" /*****************************************************************************/ diff --git a/src/settings/plugins/keyfile/nms-keyfile-writer.c b/src/settings/plugins/keyfile/nms-keyfile-writer.c index 228f4d4469..1556f15840 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-writer.c +++ b/src/settings/plugins/keyfile/nms-keyfile-writer.c @@ -34,6 +34,8 @@ #include "nms-keyfile-utils.h" #include "nms-keyfile-reader.h" +#include "nm-utils/nm-io-utils.h" + /*****************************************************************************/ typedef struct { From c366c155f183b3d980206cd25ad548e9f01c14b5 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sat, 1 Sep 2018 19:18:51 +0200 Subject: [PATCH 12/61] shared: rename PROP_0 in NM_GOBJECT_PROPERTIES_DEFINE() and skip it in nm_gobject_notify_together() PROP_0 is how we commonly name this property when we don't use NM_GOBJECT_PROPERTIES_DEFINE(). Rename it. Also, allow to skip PROP_0 in nm_gobject_notify_together(), that is handy to optionally invoke a notification, like nm_gobject_notify_together (obj, PROP_SOMETHING, changed ? PROP_OTHER : PROP_0); --- shared/nm-utils/nm-macros-internal.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/shared/nm-utils/nm-macros-internal.h b/shared/nm-utils/nm-macros-internal.h index 6937994c38..084219b4bb 100644 --- a/shared/nm-utils/nm-macros-internal.h +++ b/shared/nm-utils/nm-macros-internal.h @@ -900,7 +900,7 @@ nm_str_realloc (char *str) #define NM_GOBJECT_PROPERTIES_DEFINE_BASE(...) \ typedef enum { \ - _PROPERTY_ENUMS_0, \ + PROP_0, \ __VA_ARGS__ \ _PROPERTY_ENUMS_LAST, \ } _PropertyEnums; \ @@ -921,8 +921,11 @@ _nm_gobject_notify_together_impl (obj_type *obj, guint n, const _PropertyEnums * while (n-- > 0) { \ const _PropertyEnums prop = *props++; \ \ - nm_assert ((gsize) prop < G_N_ELEMENTS (obj_properties)); \ - g_object_notify_by_pspec ((GObject *) obj, obj_properties[prop]); \ + if (prop != PROP_0) { \ + nm_assert ((gsize) prop < G_N_ELEMENTS (obj_properties)); \ + nm_assert (obj_properties[prop]); \ + g_object_notify_by_pspec ((GObject *) obj, obj_properties[prop]); \ + } \ } \ if (freeze_thaw) \ g_object_thaw_notify ((GObject *) obj); \ From 3536960eb766bb7d113a61db0ad2ae27e052bc9b Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 29 Aug 2018 16:12:40 +0200 Subject: [PATCH 13/61] libnm/crypto: minor cleanup confusing comment in crypto_decrypt_openssl_private_key_data() the comment and code made it sound like parse_old_openssl_key_file() would set @key_type if the parsing was only done partially. That is not the case, @key_type is only set, if parsing was successful. Adjust the code. While at it, don't require the caller to initialize @out_key_type. It's just an enum, if we care to always set it, just do it. --- libnm-core/crypto.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/libnm-core/crypto.c b/libnm-core/crypto.c index 319f8055fe..13afd4698f 100644 --- a/libnm-core/crypto.c +++ b/libnm-core/crypto.c @@ -487,17 +487,13 @@ crypto_decrypt_openssl_private_key_data (const guint8 *data, char *cipher = NULL; g_return_val_if_fail (data != NULL, NULL); - if (out_key_type) - g_return_val_if_fail (*out_key_type == NM_CRYPTO_KEY_TYPE_UNKNOWN, NULL); + + NM_SET_OUT (out_key_type, NM_CRYPTO_KEY_TYPE_UNKNOWN); if (!crypto_init (error)) return NULL; parsed = parse_old_openssl_key_file (data, data_len, &key_type, &cipher, &iv, NULL); - /* return the key type even if decryption failed */ - if (out_key_type) - *out_key_type = key_type; - if (!parsed) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, @@ -505,6 +501,8 @@ crypto_decrypt_openssl_private_key_data (const guint8 *data, return NULL; } + NM_SET_OUT (out_key_type, key_type); + if (password) { if (!cipher || !iv) { g_set_error (error, NM_CRYPTO_ERROR, From 3f7db94ffb7d0c25cb78827ac54a0409d4d122b6 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 28 Aug 2018 15:23:41 +0200 Subject: [PATCH 14/61] libnm/crypto: cleanup error paths and use cleanup-attribute --- libnm-core/crypto.c | 271 +++++++++++++++++++------------------------- 1 file changed, 114 insertions(+), 157 deletions(-) diff --git a/libnm-core/crypto.c b/libnm-core/crypto.c index 13afd4698f..e52c2cf89c 100644 --- a/libnm-core/crypto.c +++ b/libnm-core/crypto.c @@ -18,17 +18,20 @@ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA. * - * Copyright 2007 - 2011 Red Hat, Inc. + * Copyright 2007 - 2018 Red Hat, Inc. */ #include "nm-default.h" +#include "crypto.h" + #include #include #include #include -#include "crypto.h" +#include "nm-utils/nm-secret-utils.h" + #include "nm-errors.h" #define PEM_RSA_KEY_BEGIN "-----BEGIN RSA PRIVATE KEY-----" @@ -77,20 +80,20 @@ static GByteArray * parse_old_openssl_key_file (const guint8 *data, gsize data_len, NMCryptoKeyType *out_key_type, - char **out_cipher, + const char **out_cipher, char **out_iv, GError **error) { GByteArray *bindata = NULL; - char **lines = NULL; + gs_strfreev char **lines = NULL; char **ln = NULL; gsize start = 0, end = 0; - GString *str = NULL; + nm_auto_free_gstring GString *str = NULL; int enc_tags = 0; NMCryptoKeyType key_type; - char *iv = NULL; - char *cipher = NULL; - unsigned char *tmp = NULL; + gs_free char *iv = NULL; + const char *cipher = NULL; + gs_free unsigned char *tmp = NULL; gsize tmp_len = 0; const char *start_tag; const char *end_tag; @@ -109,7 +112,7 @@ parse_old_openssl_key_file (const guint8 *data, start_tag = PEM_DSA_KEY_BEGIN; end_tag = PEM_DSA_KEY_END; } else - goto parse_error; + return NULL; start += strlen (start_tag); if (!find_tag (end_tag, data, data_len, start, &end)) { @@ -117,7 +120,7 @@ parse_old_openssl_key_file (const guint8 *data, NM_CRYPTO_ERROR_INVALID_DATA, _("PEM key file had no end tag '%s'."), end_tag); - goto parse_error; + return NULL; } save_end = data[end]; @@ -129,7 +132,7 @@ parse_old_openssl_key_file (const guint8 *data, g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("Doesn't look like a PEM private key file.")); - goto parse_error; + return NULL; } str = g_string_new_len (NULL, end - start); @@ -146,7 +149,7 @@ parse_old_openssl_key_file (const guint8 *data, g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("Malformed PEM file: Proc-Type was not first tag.")); - goto parse_error; + return NULL; } p += strlen (PROC_TYPE_TAG); @@ -155,7 +158,7 @@ parse_old_openssl_key_file (const guint8 *data, NM_CRYPTO_ERROR_INVALID_DATA, _("Malformed PEM file: unknown Proc-Type tag '%s'."), p); - goto parse_error; + return NULL; } } else if (!strncmp (p, DEK_INFO_TAG, strlen (DEK_INFO_TAG))) { static const char *const known_ciphers[] = { CIPHER_DES_EDE3_CBC, @@ -170,7 +173,7 @@ parse_old_openssl_key_file (const guint8 *data, g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("Malformed PEM file: DEK-Info was not the second tag.")); - goto parse_error; + return NULL; } p += strlen (DEK_INFO_TAG); @@ -181,37 +184,37 @@ parse_old_openssl_key_file (const guint8 *data, g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("Malformed PEM file: no IV found in DEK-Info tag.")); - goto parse_error; + return NULL; } *comma++ = '\0'; if (!g_ascii_isxdigit (*comma)) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("Malformed PEM file: invalid format of IV in DEK-Info tag.")); - goto parse_error; + return NULL; } iv = g_strdup (comma); /* Get the private key cipher */ for (i = 0; i < G_N_ELEMENTS (known_ciphers); i++) { if (!g_ascii_strcasecmp (p, known_ciphers[i])) { - cipher = g_strdup (known_ciphers[i]); + cipher = known_ciphers[i]; break; } } - if (i == G_N_ELEMENTS (known_ciphers)) { + if (!cipher) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("Malformed PEM file: unknown private key cipher '%s'."), p); - goto parse_error; + return NULL; } } else { if (enc_tags == 1) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, "Malformed PEM file: both Proc-Type and DEK-Info tags are required."); - goto parse_error; + return NULL; } g_string_append (str, p); } @@ -222,31 +225,16 @@ parse_old_openssl_key_file (const guint8 *data, g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("Could not decode private key.")); - goto parse_error; + return NULL; } - g_string_free (str, TRUE); - - if (lines) - g_strfreev (lines); bindata = g_byte_array_sized_new (tmp_len); g_byte_array_append (bindata, tmp, tmp_len); - g_free (tmp); *out_key_type = key_type; - *out_iv = iv; + *out_iv = g_steal_pointer (&iv); *out_cipher = cipher; return bindata; - -parse_error: - g_free (tmp); - g_free (cipher); - g_free (iv); - if (str) - g_string_free (str, TRUE); - if (lines) - g_strfreev (lines); - return NULL; } static GByteArray * @@ -255,9 +243,9 @@ parse_pkcs8_key_file (const guint8 *data, gboolean *out_encrypted, GError **error) { - GByteArray *key = NULL; + GByteArray *key; gsize start = 0, end = 0; - unsigned char *der = NULL; + gs_free guchar *der = NULL; guint8 save_end; gsize length = 0; const char *start_tag = NULL, *end_tag = NULL; @@ -294,34 +282,31 @@ parse_pkcs8_key_file (const guint8 *data, der = g_base64_decode ((const char *) (data + start), &length); ((guint8 *)data)[end] = save_end; - if (der && length) { - key = g_byte_array_sized_new (length); - g_byte_array_append (key, der, length); - g_assert (key->len == length); - *out_encrypted = encrypted; - } else { + if (!der || !length) { g_set_error_literal (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("Failed to decode PKCS#8 private key.")); + return NULL; } - g_free (der); + key = g_byte_array_sized_new (length); + g_byte_array_append (key, der, length); + *out_encrypted = encrypted; return key; } static GByteArray * file_to_g_byte_array (const char *filename, GError **error) { - char *contents; + gs_free char *contents = NULL; GByteArray *array = NULL; gsize length = 0; - if (g_file_get_contents (filename, &contents, &length, error)) { - array = g_byte_array_sized_new (length); - g_byte_array_append (array, (guint8 *) contents, length); - g_assert (array->len == length); - g_free (contents); - } + if (!g_file_get_contents (filename, &contents, &length, error)) + return NULL; + + array = g_byte_array_sized_new (length); + g_byte_array_append (array, (guint8 *) contents, length); return array; } @@ -336,7 +321,7 @@ convert_iv (const char *src, int num; int i; char conv[3]; - char *c; + gs_free char *c = NULL; g_return_val_if_fail (src != NULL, NULL); @@ -359,17 +344,13 @@ convert_iv (const char *src, g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("IV contains non-hexadecimal digits.")); - goto error; + return FALSE; } c[i] = strtol(conv, NULL, 16); } *out_len = num; - return c; - -error: - g_free (c); - return NULL; + return g_steal_pointer (&c); } char * @@ -389,6 +370,8 @@ crypto_make_des_aes_key (const char *cipher, g_return_val_if_fail (password != NULL, NULL); g_return_val_if_fail (out_len != NULL, NULL); + *out_len = 0; + if (!strcmp (cipher, CIPHER_DES_EDE3_CBC)) digest_len = 24; else if (!strcmp (cipher, CIPHER_DES_CBC)) @@ -432,11 +415,10 @@ decrypt_key (const char *cipher, const char *password, GError **error) { - char *bin_iv = NULL; + gs_free char *bin_iv = NULL; gsize bin_iv_len = 0; - char *key = NULL; - gsize key_len = 0; - char *output = NULL; + nm_auto_clear_secret_ptr NMSecretPtr key = { 0 }; + gs_free char *output = NULL; gsize decrypted_len = 0; GByteArray *decrypted = NULL; @@ -447,14 +429,14 @@ decrypt_key (const char *cipher, return NULL; /* Convert the password and IV into a DES or AES key */ - key = crypto_make_des_aes_key (cipher, bin_iv, bin_iv_len, password, &key_len, error); - if (!key || !key_len) - goto out; + key.str = crypto_make_des_aes_key (cipher, bin_iv, bin_iv_len, password, &key.len, error); + if (!key.str || !key.len) + return NULL; output = crypto_decrypt (cipher, key_type, data, data_len, bin_iv, bin_iv_len, - key, key_len, + key.str, key.len, &decrypted_len, error); if (output && decrypted_len) { @@ -462,14 +444,6 @@ decrypt_key (const char *cipher, g_byte_array_append (decrypted, (guint8 *) output, decrypted_len); } -out: - /* Don't leak stale key material */ - if (key) - memset (key, 0, key_len); - g_free (output); - g_free (key); - g_free (bin_iv); - return decrypted; } @@ -480,11 +454,10 @@ crypto_decrypt_openssl_private_key_data (const guint8 *data, NMCryptoKeyType *out_key_type, GError **error) { - GByteArray *decrypted = NULL; NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN; - GByteArray *parsed; - char *iv = NULL; - char *cipher = NULL; + nm_auto_unref_bytearray GByteArray *parsed = NULL; + gs_free char *iv = NULL; + const char *cipher = NULL; g_return_val_if_fail (data != NULL, NULL); @@ -508,23 +481,22 @@ crypto_decrypt_openssl_private_key_data (const guint8 *data, g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_PASSWORD, _("Password provided, but key was not encrypted.")); - } else { - decrypted = decrypt_key (cipher, - key_type, - parsed->data, - parsed->len, - iv, - password, - error); + return NULL; } - } else if (!cipher && !iv) - decrypted = g_byte_array_ref (parsed); - g_byte_array_unref (parsed); - g_free (cipher); - g_free (iv); + return decrypt_key (cipher, + key_type, + parsed->data, + parsed->len, + iv, + password, + error); + } - return decrypted; + if (cipher || iv) + return NULL; + + return g_steal_pointer (&parsed); } GByteArray * @@ -533,19 +505,17 @@ crypto_decrypt_openssl_private_key (const char *file, NMCryptoKeyType *out_key_type, GError **error) { - GByteArray *contents; - GByteArray *key = NULL; + nm_auto_unref_bytearray GByteArray *contents = NULL; if (!crypto_init (error)) return NULL; contents = file_to_g_byte_array (file, error); - if (contents) { - key = crypto_decrypt_openssl_private_key_data (contents->data, contents->len, - password, out_key_type, error); - g_byte_array_free (contents, TRUE); - } - return key; + if (!contents) + return NULL; + + return crypto_decrypt_openssl_private_key_data (contents->data, contents->len, + password, out_key_type, error); } static GByteArray * @@ -553,7 +523,7 @@ extract_pem_cert_data (GByteArray *contents, GError **error) { GByteArray *cert = NULL; gsize start = 0, end = 0; - unsigned char *der = NULL; + gs_free unsigned char *der = NULL; guint8 save_end; gsize length = 0; @@ -562,7 +532,7 @@ extract_pem_cert_data (GByteArray *contents, GError **error) NM_CRYPTO_ERROR_INVALID_DATA, _("PEM certificate had no start tag '%s'."), PEM_CERT_BEGIN); - goto done; + return NULL; } start += strlen (PEM_CERT_BEGIN); @@ -571,7 +541,7 @@ extract_pem_cert_data (GByteArray *contents, GError **error) NM_CRYPTO_ERROR_INVALID_DATA, _("PEM certificate had no end tag '%s'."), PEM_CERT_END); - goto done; + return NULL; } /* g_base64_decode() wants a NULL-terminated string */ @@ -580,18 +550,15 @@ extract_pem_cert_data (GByteArray *contents, GError **error) der = g_base64_decode ((const char *) (contents->data + start), &length); contents->data[end] = save_end; - if (der && length) { - cert = g_byte_array_sized_new (length); - g_byte_array_append (cert, der, length); - g_assert (cert->len == length); - } else { + if (!der || !length) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("Failed to decode certificate.")); + return NULL; } -done: - g_free (der); + cert = g_byte_array_sized_new (length); + g_byte_array_append (cert, der, length); return cert; } @@ -600,7 +567,7 @@ crypto_load_and_verify_certificate (const char *file, NMCryptoFileFormat *out_file_format, GError **error) { - GByteArray *array, *contents; + nm_auto_unref_bytearray GByteArray *contents = NULL; g_return_val_if_fail (file != NULL, NULL); g_return_val_if_fail (out_file_format != NULL, NULL); @@ -616,29 +583,26 @@ crypto_load_and_verify_certificate (const char *file, /* Check for PKCS#12 */ if (crypto_is_pkcs12_data (contents->data, contents->len, NULL)) { *out_file_format = NM_CRYPTO_FILE_FORMAT_PKCS12; - return contents; + return g_steal_pointer (&contents); } /* Check for plain DER format */ if (contents->len > 2 && contents->data[0] == 0x30 && contents->data[1] == 0x82) { *out_file_format = crypto_verify_cert (contents->data, contents->len, error); } else { + nm_auto_unref_bytearray GByteArray *array = NULL; + array = extract_pem_cert_data (contents, error); - if (!array) { - g_byte_array_free (contents, TRUE); + if (!array) return NULL; - } *out_file_format = crypto_verify_cert (array->data, array->len, error); - g_byte_array_free (array, TRUE); } - if (*out_file_format != NM_CRYPTO_FILE_FORMAT_X509) { - g_byte_array_free (contents, TRUE); - contents = NULL; - } + if (*out_file_format != NM_CRYPTO_FILE_FORMAT_X509) + return NULL; - return contents; + return g_steal_pointer (&contents); } gboolean @@ -674,8 +638,7 @@ crypto_is_pkcs12_data (const guint8 *data, gboolean crypto_is_pkcs12_file (const char *file, GError **error) { - GByteArray *contents; - gboolean success = FALSE; + nm_auto_unref_bytearray GByteArray *contents = NULL; g_return_val_if_fail (file != NULL, FALSE); @@ -683,11 +646,10 @@ crypto_is_pkcs12_file (const char *file, GError **error) return FALSE; contents = file_to_g_byte_array (file, error); - if (contents) { - success = crypto_is_pkcs12_data (contents->data, contents->len, error); - g_byte_array_free (contents, TRUE); - } - return success; + if (!contents) + return FALSE; + + return crypto_is_pkcs12_data (contents->data, contents->len, error); } /* Verifies that a private key can be read, and if a password is given, that @@ -700,7 +662,6 @@ crypto_verify_private_key_data (const guint8 *data, gboolean *out_is_encrypted, GError **error) { - GByteArray *tmp; NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; NMCryptoKeyType ktype = NM_CRYPTO_KEY_TYPE_UNKNOWN; gboolean is_encrypted = FALSE; @@ -717,13 +678,16 @@ crypto_verify_private_key_data (const guint8 *data, if (!password || crypto_verify_pkcs12 (data, data_len, password, error)) format = NM_CRYPTO_FILE_FORMAT_PKCS12; } else { + nm_auto_unref_bytearray GByteArray *tmp = NULL; + /* Maybe it's PKCS#8 */ tmp = parse_pkcs8_key_file (data, data_len, &is_encrypted, NULL); if (tmp) { if (!password || crypto_verify_pkcs8 (tmp->data, tmp->len, is_encrypted, password, error)) format = NM_CRYPTO_FILE_FORMAT_RAW_KEY; } else { - char *cipher, *iv; + const char *cipher; + gs_free char *iv = NULL; /* Or it's old-style OpenSSL */ tmp = parse_old_openssl_key_file (data, data_len, &ktype, @@ -731,15 +695,12 @@ crypto_verify_private_key_data (const guint8 *data, if (tmp) { format = NM_CRYPTO_FILE_FORMAT_RAW_KEY; is_encrypted = (cipher && iv); - g_free (cipher); - g_free (iv); } } if (tmp) { /* Don't leave key data around */ memset (tmp->data, 0, tmp->len); - g_byte_array_free (tmp, TRUE); } } @@ -754,8 +715,7 @@ crypto_verify_private_key (const char *filename, gboolean *out_is_encrypted, GError **error) { - GByteArray *contents; - NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; + nm_auto_unref_bytearray GByteArray *contents = NULL; g_return_val_if_fail (filename != NULL, NM_CRYPTO_FILE_FORMAT_UNKNOWN); @@ -763,11 +723,10 @@ crypto_verify_private_key (const char *filename, return NM_CRYPTO_FILE_FORMAT_UNKNOWN; contents = file_to_g_byte_array (filename, error); - if (contents) { - format = crypto_verify_private_key_data (contents->data, contents->len, password, out_is_encrypted, error); - g_byte_array_free (contents, TRUE); - } - return format; + if (!contents) + return NM_CRYPTO_FILE_FORMAT_UNKNOWN; + + return crypto_verify_private_key_data (contents->data, contents->len, password, out_is_encrypted, error); } void @@ -778,13 +737,13 @@ crypto_md5_hash (const char *salt, char *buffer, gsize buflen) { - GChecksum *ctx; - gsize digest_len; - char digest[16]; + nm_auto_free_checksum GChecksum *ctx = NULL; +#define MD5_DIGEST_LEN 16 + nm_auto_clear_static_secret_ptr const NMSecretPtr digest = NM_SECRET_PTR_STATIC (MD5_DIGEST_LEN); gsize bufidx = 0; int i; - nm_assert (g_checksum_type_get_length (G_CHECKSUM_MD5) == sizeof (digest)); + nm_assert (g_checksum_type_get_length (G_CHECKSUM_MD5) == MD5_DIGEST_LEN); g_return_if_fail (password_len == 0 || password); g_return_if_fail (buffer != NULL); @@ -799,26 +758,24 @@ crypto_md5_hash (const char *salt, password_len = strlen (password); for (;;) { + gsize digest_len; + if (password_len > 0) g_checksum_update (ctx, (const guchar *) password, password_len); if (salt_len > 0) g_checksum_update (ctx, (const guchar *) salt, salt_len); - digest_len = sizeof (digest); - g_checksum_get_digest (ctx, (guchar *) digest, &digest_len); - nm_assert (digest_len == sizeof (digest)); + digest_len = MD5_DIGEST_LEN; + g_checksum_get_digest (ctx, digest.ptr, &digest_len); + nm_assert (digest_len == MD5_DIGEST_LEN); - for (i = 0; i < sizeof (digest); i++) { + for (i = 0; i < MD5_DIGEST_LEN; i++) { if (bufidx >= buflen) - goto done; - buffer[bufidx++] = digest[i]; + return; + buffer[bufidx++] = digest.str[i]; } g_checksum_reset (ctx); - g_checksum_update (ctx, (const guchar *) digest, sizeof (digest)); + g_checksum_update (ctx, digest.ptr, MD5_DIGEST_LEN); } - -done: - memset (digest, 0, sizeof (digest)); - g_checksum_free (ctx); } From 9ca12145a3a6d46b7f491459f8c1329a00798df0 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 29 Aug 2018 13:27:00 +0200 Subject: [PATCH 15/61] libnm/crypto: adjust argument types for crypto_md5_hash() There should be a clear distinction between whether an array is a NUL terminated string or binary with a length. crypto_md5_hash() is already complicated enough. Adjust it's API to only support binary arguments, and thus have "guint8 *" type. --- libnm-core/crypto.c | 27 +++++++++++---------------- libnm-core/crypto.h | 10 +++++----- libnm-core/nm-utils.c | 17 +++++++++++++++-- libnm-core/tests/test-crypto.c | 7 ++++--- 4 files changed, 35 insertions(+), 26 deletions(-) diff --git a/libnm-core/crypto.c b/libnm-core/crypto.c index e52c2cf89c..78f0802670 100644 --- a/libnm-core/crypto.c +++ b/libnm-core/crypto.c @@ -395,11 +395,11 @@ crypto_make_des_aes_key (const char *cipher, key = g_malloc0 (digest_len + 1); - crypto_md5_hash (salt, + crypto_md5_hash ((guint8 *) salt, 8, - password, + (guint8 *) password, strlen (password), - key, + (guint8 *) key, digest_len); *out_len = digest_len; @@ -730,11 +730,11 @@ crypto_verify_private_key (const char *filename, } void -crypto_md5_hash (const char *salt, - gssize salt_len, - const char *password, - gssize password_len, - char *buffer, +crypto_md5_hash (const guint8 *salt, + gsize salt_len, + const guint8 *password, + gsize password_len, + guint8 *buffer, gsize buflen) { nm_auto_free_checksum GChecksum *ctx = NULL; @@ -746,17 +746,12 @@ crypto_md5_hash (const char *salt, nm_assert (g_checksum_type_get_length (G_CHECKSUM_MD5) == MD5_DIGEST_LEN); g_return_if_fail (password_len == 0 || password); - g_return_if_fail (buffer != NULL); + g_return_if_fail (buffer); g_return_if_fail (buflen > 0); g_return_if_fail (salt_len == 0 || salt); ctx = g_checksum_new (G_CHECKSUM_MD5); - if (salt_len < 0) - salt_len = strlen (salt); - if (password_len < 0) - password_len = strlen (password); - for (;;) { gsize digest_len; @@ -766,13 +761,13 @@ crypto_md5_hash (const char *salt, g_checksum_update (ctx, (const guchar *) salt, salt_len); digest_len = MD5_DIGEST_LEN; - g_checksum_get_digest (ctx, digest.ptr, &digest_len); + g_checksum_get_digest (ctx, digest.bin, &digest_len); nm_assert (digest_len == MD5_DIGEST_LEN); for (i = 0; i < MD5_DIGEST_LEN; i++) { if (bufidx >= buflen) return; - buffer[bufidx++] = digest.str[i]; + buffer[bufidx++] = digest.bin[i]; } g_checksum_reset (ctx); diff --git a/libnm-core/crypto.h b/libnm-core/crypto.h index d20d6f3100..8021117ea6 100644 --- a/libnm-core/crypto.h +++ b/libnm-core/crypto.h @@ -82,11 +82,11 @@ NMCryptoFileFormat crypto_verify_private_key (const char *file, /* Internal utils API bits for crypto providers */ -void crypto_md5_hash (const char *salt, - gssize salt_len, - const char *password, - gssize password_len, - char *buffer, +void crypto_md5_hash (const guint8 *salt, + gsize salt_len, + const guint8 *password, + gsize password_len, + guint8 *buffer, gsize buflen); char *crypto_make_des_aes_key (const char *cipher, diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index cb71d6e77d..3fb1188fae 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -2838,9 +2838,17 @@ nm_utils_uuid_generate_from_string (const char *s, gssize slen, int uuid_type, g g_return_val_if_fail (uuid_type == NM_UTILS_UUID_TYPE_LEGACY || uuid_type == NM_UTILS_UUID_TYPE_VARIANT3, NULL); g_return_val_if_fail (!type_args || uuid_type == NM_UTILS_UUID_TYPE_VARIANT3, NULL); + if (slen < 0) + slen = s ? strlen (s) : 0; + switch (uuid_type) { case NM_UTILS_UUID_TYPE_LEGACY: - crypto_md5_hash (NULL, 0, s, slen, (char *) uuid, sizeof (uuid)); + crypto_md5_hash (NULL, + 0, + (guint8 *) s, + slen, + (guint8 *) uuid, + sizeof (uuid)); break; case NM_UTILS_UUID_TYPE_VARIANT3: { uuid_t ns_uuid = { 0 }; @@ -2851,7 +2859,12 @@ nm_utils_uuid_generate_from_string (const char *s, gssize slen, int uuid_type, g g_return_val_if_reached (NULL); } - crypto_md5_hash (s, slen, (char *) ns_uuid, sizeof (ns_uuid), (char *) uuid, sizeof (uuid)); + crypto_md5_hash ((guint8 *) s, + slen, + (guint8 *) ns_uuid, + sizeof (ns_uuid), + (guint8 *) uuid, + sizeof (uuid)); uuid[6] = (uuid[6] & 0x0F) | 0x30; uuid[8] = (uuid[8] & 0x3F) | 0x80; diff --git a/libnm-core/tests/test-crypto.c b/libnm-core/tests/test-crypto.c index 5fb26c1fcc..8865c47654 100644 --- a/libnm-core/tests/test-crypto.c +++ b/libnm-core/tests/test-crypto.c @@ -417,15 +417,16 @@ test_md5 (void) for (i = 0; i < G_N_ELEMENTS (md5_tests); i++) { memset (digest, 0, sizeof (digest)); - crypto_md5_hash (md5_tests[i].salt, + crypto_md5_hash ((const guint8 *) md5_tests[i].salt, /* crypto_md5_hash() used to clamp salt_len to 8. It * doesn't any more, so we need to do it here now to * get output that matches md5_tests[i].result. */ md5_tests[i].salt ? 8 : 0, - md5_tests[i].password, + (const guint8 *) md5_tests[i].password, strlen (md5_tests[i].password), - digest, md5_tests[i].digest_size); + (guint8 *) digest, + md5_tests[i].digest_size); hex = nm_utils_bin2hexstr (digest, md5_tests[i].digest_size, -1); g_assert_cmpstr (hex, ==, md5_tests[i].result); From 5ff12dc86d5fff74dfc920845990a1b4bd1171d4 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 29 Aug 2018 13:49:37 +0200 Subject: [PATCH 16/61] libnm/crypto: cleanup convert_iv() and handle more errors crypto_make_des_aes_key() asserts that iv-lenght is at least 8 characters. Whatever the reason. That means, decrypt_key() must check for that condition first, and gracefully fail. Also, don't use strtol() to convert a pair of hex digits to integer. Also, don't keep the IV in memory. Yes, it's not very critical, but this is crypto code, we should not leave data behind. --- libnm-core/crypto.c | 59 ++++++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/libnm-core/crypto.c b/libnm-core/crypto.c index 78f0802670..e0222eb280 100644 --- a/libnm-core/crypto.c +++ b/libnm-core/crypto.c @@ -313,20 +313,20 @@ file_to_g_byte_array (const char *filename, GError **error) /* * Convert a hex string into bytes. */ -static char * +static guint8 * convert_iv (const char *src, gsize *out_len, GError **error) { - int num; - int i; - char conv[3]; - gs_free char *c = NULL; + gsize i, num; + gs_free guint8 *c = NULL; + int c0, c1; - g_return_val_if_fail (src != NULL, NULL); + nm_assert (src); num = strlen (src); - if (num % 2) { + if ( num == 0 + || (num % 2) != 0) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("IV must be an even number of bytes in length.")); @@ -334,20 +334,23 @@ convert_iv (const char *src, } num /= 2; - c = g_malloc0 (num + 1); + c = g_malloc (num + 1); + + /* defensively add trailing NUL. This function returns binary data, + * do not assume it's NUL terminated. */ + c[num] = '\0'; - conv[2] = '\0'; for (i = 0; i < num; i++) { - conv[0] = src[(i * 2)]; - conv[1] = src[(i * 2) + 1]; - if (!g_ascii_isxdigit (conv[0]) || !g_ascii_isxdigit (conv[1])) { + if ( ((c0 = nm_utils_hexchar_to_int (*(src++))) < 0) + || ((c1 = nm_utils_hexchar_to_int (*(src++))) < 0)) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("IV contains non-hexadecimal digits.")); + nm_explicit_bzero (c, i); return FALSE; } - c[i] = strtol(conv, NULL, 16); + c[i] = (c0 << 4) + c1; } *out_len = num; return g_steal_pointer (&c); @@ -415,28 +418,40 @@ decrypt_key (const char *cipher, const char *password, GError **error) { - gs_free char *bin_iv = NULL; - gsize bin_iv_len = 0; + nm_auto_clear_secret_ptr NMSecretPtr bin_iv = { 0 }; nm_auto_clear_secret_ptr NMSecretPtr key = { 0 }; gs_free char *output = NULL; gsize decrypted_len = 0; GByteArray *decrypted = NULL; g_return_val_if_fail (password != NULL, NULL); + g_return_val_if_fail (iv, NULL); - bin_iv = convert_iv (iv, &bin_iv_len, error); - if (!bin_iv) + bin_iv.bin = convert_iv (iv, &bin_iv.len, error); + if (!bin_iv.bin) return NULL; + if (bin_iv.len < 8) { + g_set_error (error, + NM_CRYPTO_ERROR, + NM_CRYPTO_ERROR_INVALID_DATA, + _("IV must contain at least 8 characters")); + return NULL; + } + /* Convert the password and IV into a DES or AES key */ - key.str = crypto_make_des_aes_key (cipher, bin_iv, bin_iv_len, password, &key.len, error); + key.str = crypto_make_des_aes_key (cipher, bin_iv.str, bin_iv.len, password, &key.len, error); if (!key.str || !key.len) return NULL; - output = crypto_decrypt (cipher, key_type, - data, data_len, - bin_iv, bin_iv_len, - key.str, key.len, + output = crypto_decrypt (cipher, + key_type, + data, + data_len, + bin_iv.str, + bin_iv.len, + key.str, + key.len, &decrypted_len, error); if (output && decrypted_len) { From fbc0f599bc758a5e17583eea1c39f8c4c0f0d3d6 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 29 Aug 2018 14:42:03 +0200 Subject: [PATCH 17/61] libnm/crypto: rename crypto functions that are only used by tests These functions are only used by tests, hence they are much less important. Mark them as such, by naming them accordingly. --- libnm-core/crypto.c | 22 +++++++++++----------- libnm-core/crypto.h | 18 +++++++++--------- libnm-core/tests/test-crypto.c | 8 ++++---- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/libnm-core/crypto.c b/libnm-core/crypto.c index e0222eb280..b2255f59c0 100644 --- a/libnm-core/crypto.c +++ b/libnm-core/crypto.c @@ -463,11 +463,11 @@ decrypt_key (const char *cipher, } GByteArray * -crypto_decrypt_openssl_private_key_data (const guint8 *data, - gsize data_len, - const char *password, - NMCryptoKeyType *out_key_type, - GError **error) +nmtst_crypto_decrypt_openssl_private_key_data (const guint8 *data, + gsize data_len, + const char *password, + NMCryptoKeyType *out_key_type, + GError **error) { NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN; nm_auto_unref_bytearray GByteArray *parsed = NULL; @@ -515,10 +515,10 @@ crypto_decrypt_openssl_private_key_data (const guint8 *data, } GByteArray * -crypto_decrypt_openssl_private_key (const char *file, - const char *password, - NMCryptoKeyType *out_key_type, - GError **error) +nmtst_crypto_decrypt_openssl_private_key (const char *file, + const char *password, + NMCryptoKeyType *out_key_type, + GError **error) { nm_auto_unref_bytearray GByteArray *contents = NULL; @@ -529,8 +529,8 @@ crypto_decrypt_openssl_private_key (const char *file, if (!contents) return NULL; - return crypto_decrypt_openssl_private_key_data (contents->data, contents->len, - password, out_key_type, error); + return nmtst_crypto_decrypt_openssl_private_key_data (contents->data, contents->len, + password, out_key_type, error); } static GByteArray * diff --git a/libnm-core/crypto.h b/libnm-core/crypto.h index 8021117ea6..60d8e18973 100644 --- a/libnm-core/crypto.h +++ b/libnm-core/crypto.h @@ -50,16 +50,16 @@ typedef enum { gboolean crypto_init (GError **error); -GByteArray *crypto_decrypt_openssl_private_key_data (const guint8 *data, - gsize data_len, - const char *password, - NMCryptoKeyType *out_key_type, - GError **error); +GByteArray *nmtst_crypto_decrypt_openssl_private_key_data (const guint8 *data, + gsize data_len, + const char *password, + NMCryptoKeyType *out_key_type, + GError **error); -GByteArray *crypto_decrypt_openssl_private_key (const char *file, - const char *password, - NMCryptoKeyType *out_key_type, - GError **error); +GByteArray *nmtst_crypto_decrypt_openssl_private_key (const char *file, + const char *password, + NMCryptoKeyType *out_key_type, + GError **error); GByteArray *crypto_load_and_verify_certificate (const char *file, NMCryptoFileFormat *out_file_format, diff --git a/libnm-core/tests/test-crypto.c b/libnm-core/tests/test-crypto.c index 8865c47654..76414d1788 100644 --- a/libnm-core/tests/test-crypto.c +++ b/libnm-core/tests/test-crypto.c @@ -144,7 +144,7 @@ test_load_private_key (const char *path, g_assert (nm_utils_file_is_private_key (path, &is_encrypted)); g_assert (is_encrypted); - array = crypto_decrypt_openssl_private_key (path, password, &key_type, &error); + array = nmtst_crypto_decrypt_openssl_private_key (path, password, &key_type, &error); /* Even if the password is wrong, we should determine the key type */ g_assert_cmpint (key_type, ==, NM_CRYPTO_KEY_TYPE_RSA); @@ -264,7 +264,7 @@ test_encrypt_private_key (const char *path, GByteArray *array, *encrypted, *re_decrypted; GError *error = NULL; - array = crypto_decrypt_openssl_private_key (path, password, &key_type, &error); + array = nmtst_crypto_decrypt_openssl_private_key (path, password, &key_type, &error); g_assert_no_error (error); g_assert (array != NULL); g_assert_cmpint (key_type, ==, NM_CRYPTO_KEY_TYPE_RSA); @@ -276,8 +276,8 @@ test_encrypt_private_key (const char *path, /* Then re-decrypt the private key */ key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN; - re_decrypted = crypto_decrypt_openssl_private_key_data (encrypted->data, encrypted->len, - password, &key_type, &error); + re_decrypted = nmtst_crypto_decrypt_openssl_private_key_data (encrypted->data, encrypted->len, + password, &key_type, &error); g_assert_no_error (error); g_assert (re_decrypted != NULL); g_assert_cmpint (key_type, ==, NM_CRYPTO_KEY_TYPE_RSA); From 09d43b395969d59ce90bf6970c84a119872c46c4 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 29 Aug 2018 15:17:13 +0200 Subject: [PATCH 18/61] libnm/crypto: clear data loaded from files Data that we load from crypto files should be cleared once it's no longer used. Just a small step. There are many other places where we copy the data and leave it around. --- libnm-core/crypto.c | 128 ++++++++++++++++++++++++-------------------- 1 file changed, 71 insertions(+), 57 deletions(-) diff --git a/libnm-core/crypto.c b/libnm-core/crypto.c index b2255f59c0..ffa27eb666 100644 --- a/libnm-core/crypto.c +++ b/libnm-core/crypto.c @@ -49,6 +49,21 @@ #define PEM_PKCS8_DEC_KEY_BEGIN "-----BEGIN PRIVATE KEY-----" #define PEM_PKCS8_DEC_KEY_END "-----END PRIVATE KEY-----" +/*****************************************************************************/ + +static GByteArray * +to_gbyte_array_mem (gconstpointer mem, gsize len) +{ + GByteArray *arr; + + arr = g_byte_array_sized_new (len); + if (len > 0) + g_byte_array_append (arr, mem, len); + return arr; +} + +/*****************************************************************************/ + static gboolean find_tag (const char *tag, const guint8 *data, @@ -295,19 +310,16 @@ parse_pkcs8_key_file (const guint8 *data, return key; } -static GByteArray * -file_to_g_byte_array (const char *filename, GError **error) +static gboolean +file_read_contents (const char *filename, + NMSecretPtr *out_contents, + GError **error) { - gs_free char *contents = NULL; - GByteArray *array = NULL; - gsize length = 0; + nm_assert (out_contents); + nm_assert (out_contents->len == 0); + nm_assert (!out_contents->str); - if (!g_file_get_contents (filename, &contents, &length, error)) - return NULL; - - array = g_byte_array_sized_new (length); - g_byte_array_append (array, (guint8 *) contents, length); - return array; + return g_file_get_contents (filename, &out_contents->str, &out_contents->len, error); } /* @@ -520,61 +532,66 @@ nmtst_crypto_decrypt_openssl_private_key (const char *file, NMCryptoKeyType *out_key_type, GError **error) { - nm_auto_unref_bytearray GByteArray *contents = NULL; + nm_auto_clear_secret_ptr NMSecretPtr contents = { 0 }; if (!crypto_init (error)) return NULL; - contents = file_to_g_byte_array (file, error); - if (!contents) + if (!file_read_contents (file, &contents, error)) return NULL; - return nmtst_crypto_decrypt_openssl_private_key_data (contents->data, contents->len, - password, out_key_type, error); + return nmtst_crypto_decrypt_openssl_private_key_data (contents.bin, + contents.len, + password, + out_key_type, + error); } -static GByteArray * -extract_pem_cert_data (GByteArray *contents, GError **error) +static gboolean +extract_pem_cert_data (const guint8 *contents, + gsize contents_len, + NMSecretPtr *out_cert, + GError **error) { - GByteArray *cert = NULL; - gsize start = 0, end = 0; - gs_free unsigned char *der = NULL; - guint8 save_end; - gsize length = 0; + gsize start = 0; + gsize end = 0; + nm_auto_free_secret char *der_base64 = NULL; - if (!find_tag (PEM_CERT_BEGIN, contents->data, contents->len, 0, &start)) { + nm_assert (contents); + nm_assert (out_cert); + nm_assert (out_cert->len == 0); + nm_assert (!out_cert->ptr); + + if (!find_tag (PEM_CERT_BEGIN, contents, contents_len, 0, &start)) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("PEM certificate had no start tag '%s'."), PEM_CERT_BEGIN); - return NULL; + return FALSE; } start += strlen (PEM_CERT_BEGIN); - if (!find_tag (PEM_CERT_END, contents->data, contents->len, start, &end)) { + if (!find_tag (PEM_CERT_END, contents, contents_len, start, &end)) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("PEM certificate had no end tag '%s'."), PEM_CERT_END); - return NULL; + return FALSE; } /* g_base64_decode() wants a NULL-terminated string */ - save_end = contents->data[end]; - contents->data[end] = '\0'; - der = g_base64_decode ((const char *) (contents->data + start), &length); - contents->data[end] = save_end; + der_base64 = g_strndup ((const char *) &contents[start], end - start); - if (!der || !length) { + out_cert->bin = (guint8 *) g_base64_decode (der_base64, &out_cert->len); + if (!out_cert->bin || !out_cert->len) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("Failed to decode certificate.")); - return NULL; + nm_secret_ptr_clear (out_cert); + return FALSE; } - cert = g_byte_array_sized_new (length); - g_byte_array_append (cert, der, length); - return cert; + return TRUE; } GByteArray * @@ -582,42 +599,41 @@ crypto_load_and_verify_certificate (const char *file, NMCryptoFileFormat *out_file_format, GError **error) { - nm_auto_unref_bytearray GByteArray *contents = NULL; + nm_auto_clear_secret_ptr NMSecretPtr contents = { 0 }; g_return_val_if_fail (file != NULL, NULL); g_return_val_if_fail (out_file_format != NULL, NULL); - g_return_val_if_fail (*out_file_format == NM_CRYPTO_FILE_FORMAT_UNKNOWN, NULL); + + *out_file_format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; if (!crypto_init (error)) return NULL; - contents = file_to_g_byte_array (file, error); - if (!contents) + if (!file_read_contents (file, &contents, error)) return NULL; /* Check for PKCS#12 */ - if (crypto_is_pkcs12_data (contents->data, contents->len, NULL)) { + if (crypto_is_pkcs12_data (contents.bin, contents.len, NULL)) { *out_file_format = NM_CRYPTO_FILE_FORMAT_PKCS12; - return g_steal_pointer (&contents); + return to_gbyte_array_mem (contents.bin, contents.len); } /* Check for plain DER format */ - if (contents->len > 2 && contents->data[0] == 0x30 && contents->data[1] == 0x82) { - *out_file_format = crypto_verify_cert (contents->data, contents->len, error); + if (contents.len > 2 && contents.bin[0] == 0x30 && contents.bin[1] == 0x82) { + *out_file_format = crypto_verify_cert (contents.bin, contents.len, error); } else { - nm_auto_unref_bytearray GByteArray *array = NULL; + nm_auto_clear_secret_ptr NMSecretPtr pem_cert = { 0 }; - array = extract_pem_cert_data (contents, error); - if (!array) + if (!extract_pem_cert_data (contents.bin, contents.len, &pem_cert, error)) return NULL; - *out_file_format = crypto_verify_cert (array->data, array->len, error); + *out_file_format = crypto_verify_cert (pem_cert.bin, pem_cert.len, error); } if (*out_file_format != NM_CRYPTO_FILE_FORMAT_X509) return NULL; - return g_steal_pointer (&contents); + return to_gbyte_array_mem (contents.bin, contents.len); } gboolean @@ -653,18 +669,17 @@ crypto_is_pkcs12_data (const guint8 *data, gboolean crypto_is_pkcs12_file (const char *file, GError **error) { - nm_auto_unref_bytearray GByteArray *contents = NULL; + nm_auto_clear_secret_ptr NMSecretPtr contents = { 0 }; g_return_val_if_fail (file != NULL, FALSE); if (!crypto_init (error)) return FALSE; - contents = file_to_g_byte_array (file, error); - if (!contents) + if (!file_read_contents (file, &contents, error)) return FALSE; - return crypto_is_pkcs12_data (contents->data, contents->len, error); + return crypto_is_pkcs12_data (contents.bin, contents.len, error); } /* Verifies that a private key can be read, and if a password is given, that @@ -730,18 +745,17 @@ crypto_verify_private_key (const char *filename, gboolean *out_is_encrypted, GError **error) { - nm_auto_unref_bytearray GByteArray *contents = NULL; + nm_auto_clear_secret_ptr NMSecretPtr contents = { 0 }; g_return_val_if_fail (filename != NULL, NM_CRYPTO_FILE_FORMAT_UNKNOWN); if (!crypto_init (error)) return NM_CRYPTO_FILE_FORMAT_UNKNOWN; - contents = file_to_g_byte_array (filename, error); - if (!contents) + if (!file_read_contents (filename, &contents, error)) return NM_CRYPTO_FILE_FORMAT_UNKNOWN; - return crypto_verify_private_key_data (contents->data, contents->len, password, out_is_encrypted, error); + return crypto_verify_private_key_data (contents.bin, contents.len, password, out_is_encrypted, error); } void From 6550b36ed4726a4cf6d4379f5bd9b8c4230b588e Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 29 Aug 2018 15:46:42 +0200 Subject: [PATCH 19/61] libnm/crypto: refactor parse_old_openssl_key_file() to bzero loaded data Ensure that data processed by parse_old_openssl_key_file() is cleared from memory. --- libnm-core/crypto.c | 162 +++++++++++++++++++++++++++----------------- 1 file changed, 99 insertions(+), 63 deletions(-) diff --git a/libnm-core/crypto.c b/libnm-core/crypto.c index ffa27eb666..c18105c1d4 100644 --- a/libnm-core/crypto.c +++ b/libnm-core/crypto.c @@ -91,31 +91,66 @@ find_tag (const char *tag, #define DEK_INFO_TAG "DEK-Info: " #define PROC_TYPE_TAG "Proc-Type: " -static GByteArray * +static char * +_extract_line (const guint8 **p, const guint8 *p_end) +{ + const guint8 *x, *x0; + + nm_assert (p); + nm_assert (p_end); + nm_assert (*p); + nm_assert (*p < p_end); + + x = x0 = *p; + while (TRUE) { + if (x == p_end) { + *p = p_end; + break; + } + if (*x == '\0') { + /* the data contains embedded NUL. This is the end. */ + *p = p_end; + break; + } + if (*x == '\n') { + *p = x + 1; + break; + } + x++; + } + + if (x == x0) + return NULL; + return g_strndup ((char *) x0, x - x0); +} + +static gboolean parse_old_openssl_key_file (const guint8 *data, gsize data_len, + NMSecretPtr *out_parsed, NMCryptoKeyType *out_key_type, const char **out_cipher, char **out_iv, GError **error) { - GByteArray *bindata = NULL; - gs_strfreev char **lines = NULL; - char **ln = NULL; gsize start = 0, end = 0; - nm_auto_free_gstring GString *str = NULL; + nm_auto_free_secret char *str = NULL; + char *str_p; + gsize str_len; int enc_tags = 0; NMCryptoKeyType key_type; - gs_free char *iv = NULL; + nm_auto_clear_secret_ptr NMSecretPtr parsed = { 0 }; + nm_auto_clear_secret_ptr NMSecretPtr data_content = { 0 }; + nm_auto_free_secret char *iv = NULL; const char *cipher = NULL; - gs_free unsigned char *tmp = NULL; - gsize tmp_len = 0; const char *start_tag; const char *end_tag; - guint8 save_end = 0; + const guint8 *data_start, *data_end; - *out_key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN; - *out_iv = NULL; + nm_assert (!out_parsed || (out_parsed->len == 0 && !out_parsed->bin)); + nm_assert (!out_iv || !*out_iv); + + NM_SET_OUT (out_key_type, NM_CRYPTO_KEY_TYPE_UNKNOWN); *out_cipher = NULL; if (find_tag (PEM_RSA_KEY_BEGIN, data, data_len, 0, &start)) { @@ -126,8 +161,12 @@ parse_old_openssl_key_file (const guint8 *data, key_type = NM_CRYPTO_KEY_TYPE_DSA; start_tag = PEM_DSA_KEY_BEGIN; end_tag = PEM_DSA_KEY_END; - } else - return NULL; + } else { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERROR_INVALID_DATA, + _("PEM key file had no start tag")); + return FALSE; + } start += strlen (start_tag); if (!find_tag (end_tag, data, data_len, start, &end)) { @@ -135,36 +174,33 @@ parse_old_openssl_key_file (const guint8 *data, NM_CRYPTO_ERROR_INVALID_DATA, _("PEM key file had no end tag '%s'."), end_tag); - return NULL; + return FALSE; } - save_end = data[end]; - ((guint8 *)data)[end] = '\0'; - lines = g_strsplit ((const char *) (data + start), "\n", 0); - ((guint8 *)data)[end] = save_end; + str_len = end - start + 1; + str = g_new (char, str_len); + str[0] = '\0'; + str_p = str; - if (!lines || g_strv_length (lines) <= 1) { - g_set_error (error, NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, - _("Doesn't look like a PEM private key file.")); - return NULL; - } + data_start = &data[start]; + data_end = &data[end]; - str = g_string_new_len (NULL, end - start); - for (ln = lines; *ln; ln++) { - char *p = *ln; + while (data_start < data_end) { + nm_auto_free_secret char *line = NULL; + char *p; - /* Chug leading spaces */ - p = g_strstrip (p); - if (!*p) + line = _extract_line (&data_start, data_end); + if (!line) continue; + p = nm_secret_strchomp (nm_str_skip_leading_spaces (line)); + if (!strncmp (p, PROC_TYPE_TAG, strlen (PROC_TYPE_TAG))) { - if (enc_tags++ != 0 || str->len != 0) { + if (enc_tags++ != 0 || str_p != str) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("Malformed PEM file: Proc-Type was not first tag.")); - return NULL; + return FALSE; } p += strlen (PROC_TYPE_TAG); @@ -173,7 +209,7 @@ parse_old_openssl_key_file (const guint8 *data, NM_CRYPTO_ERROR_INVALID_DATA, _("Malformed PEM file: unknown Proc-Type tag '%s'."), p); - return NULL; + return FALSE; } } else if (!strncmp (p, DEK_INFO_TAG, strlen (DEK_INFO_TAG))) { static const char *const known_ciphers[] = { CIPHER_DES_EDE3_CBC, @@ -182,13 +218,14 @@ parse_old_openssl_key_file (const guint8 *data, CIPHER_AES_192_CBC, CIPHER_AES_256_CBC }; char *comma; + gsize p_len; guint i; - if (enc_tags++ != 1 || str->len != 0) { + if (enc_tags++ != 1 || str_p != str) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("Malformed PEM file: DEK-Info was not the second tag.")); - return NULL; + return FALSE; } p += strlen (DEK_INFO_TAG); @@ -199,20 +236,23 @@ parse_old_openssl_key_file (const guint8 *data, g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("Malformed PEM file: no IV found in DEK-Info tag.")); - return NULL; + return FALSE; } - *comma++ = '\0'; + p_len = comma - p; + comma++; if (!g_ascii_isxdigit (*comma)) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("Malformed PEM file: invalid format of IV in DEK-Info tag.")); - return NULL; + return FALSE; } + nm_free_secret (iv); iv = g_strdup (comma); /* Get the private key cipher */ for (i = 0; i < G_N_ELEMENTS (known_ciphers); i++) { - if (!g_ascii_strcasecmp (p, known_ciphers[i])) { + if ( strlen (known_ciphers[i]) == p_len + && !g_ascii_strncasecmp (p, known_ciphers[i], p_len)) { cipher = known_ciphers[i]; break; } @@ -222,34 +262,34 @@ parse_old_openssl_key_file (const guint8 *data, NM_CRYPTO_ERROR_INVALID_DATA, _("Malformed PEM file: unknown private key cipher '%s'."), p); - return NULL; + return FALSE; } } else { if (enc_tags == 1) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, "Malformed PEM file: both Proc-Type and DEK-Info tags are required."); - return NULL; + return FALSE; } - g_string_append (str, p); + nm_utils_strbuf_append_str (&str_p, &str_len, p); + nm_assert (str_len > 0); } } - tmp = g_base64_decode (str->str, &tmp_len); - if (tmp == NULL || !tmp_len) { + parsed.bin = (guint8 *) g_base64_decode (str, &parsed.len); + if (!parsed.bin || parsed.len == 0) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("Could not decode private key.")); - return NULL; + nm_secret_ptr_clear (&parsed); + return FALSE; } - bindata = g_byte_array_sized_new (tmp_len); - g_byte_array_append (bindata, tmp, tmp_len); - - *out_key_type = key_type; - *out_iv = g_steal_pointer (&iv); + NM_SET_OUT (out_key_type, key_type); + NM_SET_OUT (out_iv, g_steal_pointer (&iv)); *out_cipher = cipher; - return bindata; + nm_secret_ptr_move (out_parsed, &parsed); + return TRUE; } static GByteArray * @@ -482,8 +522,8 @@ nmtst_crypto_decrypt_openssl_private_key_data (const guint8 *data, GError **error) { NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN; - nm_auto_unref_bytearray GByteArray *parsed = NULL; - gs_free char *iv = NULL; + nm_auto_clear_secret_ptr NMSecretPtr parsed = { 0 }; + nm_auto_free_secret char *iv = NULL; const char *cipher = NULL; g_return_val_if_fail (data != NULL, NULL); @@ -493,8 +533,7 @@ nmtst_crypto_decrypt_openssl_private_key_data (const guint8 *data, if (!crypto_init (error)) return NULL; - parsed = parse_old_openssl_key_file (data, data_len, &key_type, &cipher, &iv, NULL); - if (!parsed) { + if (!parse_old_openssl_key_file (data, data_len, &parsed, &key_type, &cipher, &iv, NULL)) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("Unable to determine private key type.")); @@ -513,8 +552,8 @@ nmtst_crypto_decrypt_openssl_private_key_data (const guint8 *data, return decrypt_key (cipher, key_type, - parsed->data, - parsed->len, + parsed.bin, + parsed.len, iv, password, error); @@ -523,7 +562,7 @@ nmtst_crypto_decrypt_openssl_private_key_data (const guint8 *data, if (cipher || iv) return NULL; - return g_steal_pointer (&parsed); + return to_gbyte_array_mem (parsed.bin, parsed.len); } GByteArray * @@ -693,7 +732,6 @@ crypto_verify_private_key_data (const guint8 *data, GError **error) { NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; - NMCryptoKeyType ktype = NM_CRYPTO_KEY_TYPE_UNKNOWN; gboolean is_encrypted = FALSE; g_return_val_if_fail (data != NULL, NM_CRYPTO_FILE_FORMAT_UNKNOWN); @@ -717,12 +755,10 @@ crypto_verify_private_key_data (const guint8 *data, format = NM_CRYPTO_FILE_FORMAT_RAW_KEY; } else { const char *cipher; - gs_free char *iv = NULL; + nm_auto_free_secret char *iv = NULL; /* Or it's old-style OpenSSL */ - tmp = parse_old_openssl_key_file (data, data_len, &ktype, - &cipher, &iv, NULL); - if (tmp) { + if (parse_old_openssl_key_file (data, data_len, NULL, NULL, &cipher, &iv, NULL)) { format = NM_CRYPTO_FILE_FORMAT_RAW_KEY; is_encrypted = (cipher && iv); } From eef298108fa0eb31df6a4c432903a1d95ad10385 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 29 Aug 2018 17:50:52 +0200 Subject: [PATCH 20/61] libnm/crypto: refactor parse_pkcs8_key_file() to bzero loaded data --- libnm-core/crypto.c | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/libnm-core/crypto.c b/libnm-core/crypto.c index c18105c1d4..64a910d912 100644 --- a/libnm-core/crypto.c +++ b/libnm-core/crypto.c @@ -292,19 +292,23 @@ parse_old_openssl_key_file (const guint8 *data, return TRUE; } -static GByteArray * +static gboolean parse_pkcs8_key_file (const guint8 *data, gsize data_len, + NMSecretPtr *parsed, gboolean *out_encrypted, GError **error) { - GByteArray *key; gsize start = 0, end = 0; gs_free guchar *der = NULL; - guint8 save_end; - gsize length = 0; const char *start_tag = NULL, *end_tag = NULL; gboolean encrypted = FALSE; + nm_auto_free_secret char *der_base64 = NULL; + + nm_assert (parsed); + nm_assert (!parsed->bin); + nm_assert (parsed->len == 0); + nm_assert (out_encrypted); /* Try encrypted first, decrypted next */ if (find_tag (PEM_PKCS8_ENC_KEY_BEGIN, data, data_len, 0, &start)) { @@ -319,7 +323,7 @@ parse_pkcs8_key_file (const guint8 *data, g_set_error_literal (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("Failed to find expected PKCS#8 start tag.")); - return NULL; + return FALSE; } start += strlen (start_tag); @@ -328,26 +332,23 @@ parse_pkcs8_key_file (const guint8 *data, NM_CRYPTO_ERROR_INVALID_DATA, _("Failed to find expected PKCS#8 end tag '%s'."), end_tag); - return NULL; + return FALSE; } /* g_base64_decode() wants a NULL-terminated string */ - save_end = data[end]; - ((guint8 *)data)[end] = '\0'; - der = g_base64_decode ((const char *) (data + start), &length); - ((guint8 *)data)[end] = save_end; + der_base64 = g_strndup ((char *) &data[start], end - start); - if (!der || !length) { + parsed->bin = (guint8 *) g_base64_decode (der_base64, &parsed->len); + if (!parsed->bin || parsed->len == 0) { g_set_error_literal (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("Failed to decode PKCS#8 private key.")); - return NULL; + nm_secret_ptr_clear (parsed); + return FALSE; } - key = g_byte_array_sized_new (length); - g_byte_array_append (key, der, length); *out_encrypted = encrypted; - return key; + return TRUE; } static gboolean @@ -746,12 +747,12 @@ crypto_verify_private_key_data (const guint8 *data, if (!password || crypto_verify_pkcs12 (data, data_len, password, error)) format = NM_CRYPTO_FILE_FORMAT_PKCS12; } else { - nm_auto_unref_bytearray GByteArray *tmp = NULL; + nm_auto_clear_secret_ptr NMSecretPtr parsed = { 0 }; /* Maybe it's PKCS#8 */ - tmp = parse_pkcs8_key_file (data, data_len, &is_encrypted, NULL); - if (tmp) { - if (!password || crypto_verify_pkcs8 (tmp->data, tmp->len, is_encrypted, password, error)) + if (parse_pkcs8_key_file (data, data_len, &parsed, &is_encrypted, NULL)) { + if ( !password + || crypto_verify_pkcs8 (parsed.bin, parsed.len, is_encrypted, password, error)) format = NM_CRYPTO_FILE_FORMAT_RAW_KEY; } else { const char *cipher; @@ -763,11 +764,6 @@ crypto_verify_private_key_data (const guint8 *data, is_encrypted = (cipher && iv); } } - - if (tmp) { - /* Don't leave key data around */ - memset (tmp->data, 0, tmp->len); - } } if (out_is_encrypted) From 3c157d186c8f4c3338e69e9e9c60707ea3752aa6 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 29 Aug 2018 18:00:40 +0200 Subject: [PATCH 21/61] libnm/crypto: refactor decrypt_key() to use NMSecretPtr --- libnm-core/crypto.c | 67 +++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/libnm-core/crypto.c b/libnm-core/crypto.c index 64a910d912..88f8dde61e 100644 --- a/libnm-core/crypto.c +++ b/libnm-core/crypto.c @@ -462,57 +462,60 @@ crypto_make_des_aes_key (const char *cipher, return key; } -static GByteArray * +static gboolean decrypt_key (const char *cipher, int key_type, const guint8 *data, gsize data_len, const char *iv, const char *password, + NMSecretPtr *parsed, GError **error) { nm_auto_clear_secret_ptr NMSecretPtr bin_iv = { 0 }; nm_auto_clear_secret_ptr NMSecretPtr key = { 0 }; gs_free char *output = NULL; - gsize decrypted_len = 0; - GByteArray *decrypted = NULL; - g_return_val_if_fail (password != NULL, NULL); - g_return_val_if_fail (iv, NULL); + nm_assert (password); + nm_assert (cipher); + nm_assert (iv); + nm_assert (parsed); + nm_assert (!parsed->bin); + nm_assert (parsed->len == 0); bin_iv.bin = convert_iv (iv, &bin_iv.len, error); if (!bin_iv.bin) - return NULL; + return FALSE; if (bin_iv.len < 8) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("IV must contain at least 8 characters")); - return NULL; + return FALSE; } /* Convert the password and IV into a DES or AES key */ key.str = crypto_make_des_aes_key (cipher, bin_iv.str, bin_iv.len, password, &key.len, error); if (!key.str || !key.len) - return NULL; + return FALSE; - output = crypto_decrypt (cipher, - key_type, - data, - data_len, - bin_iv.str, - bin_iv.len, - key.str, - key.len, - &decrypted_len, - error); - if (output && decrypted_len) { - decrypted = g_byte_array_sized_new (decrypted_len); - g_byte_array_append (decrypted, (guint8 *) output, decrypted_len); + parsed->str = crypto_decrypt (cipher, + key_type, + data, + data_len, + bin_iv.str, + bin_iv.len, + key.str, + key.len, + &parsed->len, + error); + if (!parsed->str || parsed->len == 0) { + nm_secret_ptr_clear (parsed); + return FALSE; } - return decrypted; + return TRUE; } GByteArray * @@ -544,6 +547,8 @@ nmtst_crypto_decrypt_openssl_private_key_data (const guint8 *data, NM_SET_OUT (out_key_type, key_type); if (password) { + nm_auto_clear_secret_ptr NMSecretPtr parsed2 = { 0 }; + if (!cipher || !iv) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_PASSWORD, @@ -551,13 +556,17 @@ nmtst_crypto_decrypt_openssl_private_key_data (const guint8 *data, return NULL; } - return decrypt_key (cipher, - key_type, - parsed.bin, - parsed.len, - iv, - password, - error); + if (!decrypt_key (cipher, + key_type, + parsed.bin, + parsed.len, + iv, + password, + &parsed2, + error)) + return NULL; + + return to_gbyte_array_mem (parsed2.bin, parsed2.len); } if (cipher || iv) From 4106f2968d733702168175f332de011b9b4c83c2 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 29 Aug 2018 18:58:14 +0200 Subject: [PATCH 22/61] libnm/crypto: rename libnm's crypto files "crypto.h" did not follow our common NM style naming. Rename the files. --- Makefile.am | 12 ++++++------ docs/libnm/Makefile.am | 2 +- docs/libnm/meson.build | 2 +- libnm-core/meson.build | 4 ++-- libnm-core/{crypto_gnutls.c => nm-crypto-gnutls.c} | 3 ++- libnm-core/{crypto_nss.c => nm-crypto-nss.c} | 3 ++- libnm-core/{crypto.c => nm-crypto.c} | 2 +- libnm-core/{crypto.h => nm-crypto.h} | 6 +++--- libnm-core/nm-setting-8021x.c | 2 +- libnm-core/nm-utils.c | 2 +- libnm-core/tests/test-crypto.c | 2 +- po/POTFILES.in | 6 +++--- 12 files changed, 24 insertions(+), 22 deletions(-) rename libnm-core/{crypto_gnutls.c => nm-crypto-gnutls.c} (99%) rename libnm-core/{crypto_nss.c => nm-crypto-nss.c} (99%) rename libnm-core/{crypto.c => nm-crypto.c} (99%) rename libnm-core/{crypto.h => nm-crypto.h} (98%) diff --git a/Makefile.am b/Makefile.am index 1183d311b5..8fa1c19738 100644 --- a/Makefile.am +++ b/Makefile.am @@ -508,7 +508,7 @@ libnm_core_lib_h_priv = \ shared/nm-utils/nm-udev-utils.h \ shared/nm-ethtool-utils.h \ shared/nm-meta-setting.h \ - libnm-core/crypto.h \ + libnm-core/nm-crypto.h \ libnm-core/nm-connection-private.h \ libnm-core/nm-core-internal.h \ libnm-core/nm-core-types-internal.h \ @@ -576,7 +576,7 @@ libnm_core_lib_c_real = \ shared/nm-utils/nm-udev-utils.c \ shared/nm-ethtool-utils.c \ shared/nm-meta-setting.c \ - libnm-core/crypto.c \ + libnm-core/nm-crypto.c \ libnm-core/nm-connection.c \ libnm-core/nm-dbus-utils.c \ libnm-core/nm-errors.c \ @@ -683,18 +683,18 @@ libnm_core_libnm_core_la_LDFLAGS = \ $(SANITIZER_LIB_LDFLAGS) if WITH_GNUTLS -libnm_core_lib_c_real += libnm-core/crypto_gnutls.c +libnm_core_lib_c_real += libnm-core/nm-crypto-gnutls.c libnm_core_libnm_core_la_LIBADD += $(GNUTLS_LIBS) endif if WITH_NSS -libnm_core_lib_c_real += libnm-core/crypto_nss.c +libnm_core_lib_c_real += libnm-core/nm-crypto-nss.c libnm_core_libnm_core_la_LIBADD += $(NSS_LIBS) endif EXTRA_DIST += \ - libnm-core/crypto_gnutls.c \ - libnm-core/crypto_nss.c \ + libnm-core/nm-crypto-gnutls.c \ + libnm-core/nm-crypto-nss.c \ libnm-core/nm-core-enum-types.c.template \ libnm-core/nm-core-enum-types.h.template \ libnm-core/meson.build diff --git a/docs/libnm/Makefile.am b/docs/libnm/Makefile.am index c2bcff1be5..7c3b54b484 100644 --- a/docs/libnm/Makefile.am +++ b/docs/libnm/Makefile.am @@ -32,7 +32,7 @@ CFILE_GLOB=$(top_srcdir)/libnm-core/*.c $(top_srcdir)/libnm/*.c # Header files to ignore when scanning. IGNORE_HFILES= \ common.h \ - crypto.h \ + nm-crypto.h \ nm-dbus-helpers.h \ nm-core-internal.h \ nm-core-types-internal.h \ diff --git a/docs/libnm/meson.build b/docs/libnm/meson.build index 7baf09f823..35aae523be 100644 --- a/docs/libnm/meson.build +++ b/docs/libnm/meson.build @@ -2,7 +2,7 @@ doc_module = libnm_name private_headers = [ 'common.h', - 'crypto.h', + 'nm-crypto.h', 'nm-dbus-helpers.h', 'nm-core-internal.h', 'nm-core-types-internal.h', diff --git a/libnm-core/meson.build b/libnm-core/meson.build index ab570b6b47..5596ab5716 100644 --- a/libnm-core/meson.build +++ b/libnm-core/meson.build @@ -107,8 +107,8 @@ libnm_core_settings_sources = files( ) libnm_core_sources = libnm_core_settings_sources + files( - 'crypto.c', - 'crypto_' + crypto + '.c', + 'nm-crypto.c', + 'nm-crypto-' + crypto + '.c', 'nm-connection.c', 'nm-dbus-utils.c', 'nm-errors.c', diff --git a/libnm-core/crypto_gnutls.c b/libnm-core/nm-crypto-gnutls.c similarity index 99% rename from libnm-core/crypto_gnutls.c rename to libnm-core/nm-crypto-gnutls.c index 49181ee722..2cdb259af3 100644 --- a/libnm-core/crypto_gnutls.c +++ b/libnm-core/nm-crypto-gnutls.c @@ -23,12 +23,13 @@ #include "nm-default.h" +#include "nm-crypto.h" + #include #include #include #include -#include "crypto.h" #include "nm-errors.h" #define SALT_LEN 8 diff --git a/libnm-core/crypto_nss.c b/libnm-core/nm-crypto-nss.c similarity index 99% rename from libnm-core/crypto_nss.c rename to libnm-core/nm-crypto-nss.c index 3b457fbb30..eec61e0f87 100644 --- a/libnm-core/crypto_nss.c +++ b/libnm-core/nm-crypto-nss.c @@ -23,6 +23,8 @@ #include "nm-default.h" +#include "nm-crypto.h" + #include #include #include @@ -33,7 +35,6 @@ #include #include -#include "crypto.h" #include "nm-errors.h" static gboolean initialized = FALSE; diff --git a/libnm-core/crypto.c b/libnm-core/nm-crypto.c similarity index 99% rename from libnm-core/crypto.c rename to libnm-core/nm-crypto.c index 88f8dde61e..72e66a133c 100644 --- a/libnm-core/crypto.c +++ b/libnm-core/nm-crypto.c @@ -23,7 +23,7 @@ #include "nm-default.h" -#include "crypto.h" +#include "nm-crypto.h" #include #include diff --git a/libnm-core/crypto.h b/libnm-core/nm-crypto.h similarity index 98% rename from libnm-core/crypto.h rename to libnm-core/nm-crypto.h index 60d8e18973..cb74a8c953 100644 --- a/libnm-core/crypto.h +++ b/libnm-core/nm-crypto.h @@ -21,8 +21,8 @@ * Copyright 2007 - 2014 Red Hat, Inc. */ -#ifndef __CRYPTO_H__ -#define __CRYPTO_H__ +#ifndef __NM_CRYPTO_H__ +#define __NM_CRYPTO_H__ #if !((NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_PRIVATE) #error Cannot use this header. @@ -134,4 +134,4 @@ gboolean crypto_verify_pkcs8 (const guint8 *data, const char *password, GError **error); -#endif /* __CRYPTO_H__ */ +#endif /* __NM_CRYPTO_H__ */ diff --git a/libnm-core/nm-setting-8021x.c b/libnm-core/nm-setting-8021x.c index f8e0d270e6..583f98f201 100644 --- a/libnm-core/nm-setting-8021x.c +++ b/libnm-core/nm-setting-8021x.c @@ -27,7 +27,7 @@ #include #include "nm-utils.h" -#include "crypto.h" +#include "nm-crypto.h" #include "nm-utils-private.h" #include "nm-setting-private.h" #include "nm-core-enum-types.h" diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 3fb1188fae..253606ddcc 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -43,7 +43,7 @@ #include "nm-common-macros.h" #include "nm-utils-private.h" #include "nm-setting-private.h" -#include "crypto.h" +#include "nm-crypto.h" #include "nm-setting-bond.h" #include "nm-setting-bridge.h" #include "nm-setting-infiniband.h" diff --git a/libnm-core/tests/test-crypto.c b/libnm-core/tests/test-crypto.c index 76414d1788..8b447d2685 100644 --- a/libnm-core/tests/test-crypto.c +++ b/libnm-core/tests/test-crypto.c @@ -28,7 +28,7 @@ #include #include -#include "crypto.h" +#include "nm-crypto.h" #include "nm-utils.h" #include "nm-errors.h" #include "nm-core-internal.h" diff --git a/po/POTFILES.in b/po/POTFILES.in index ee78d29344..942b066131 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -51,9 +51,9 @@ clients/tui/nmtui-connect.c clients/tui/nmtui-edit.c clients/tui/nmtui-hostname.c clients/tui/nmtui.c -libnm-core/crypto.c -libnm-core/crypto_gnutls.c -libnm-core/crypto_nss.c +libnm-core/nm-crypto.c +libnm-core/nm-crypto-gnutls.c +libnm-core/nm-crypto-nss.c libnm-core/nm-connection.c libnm-core/nm-dbus-utils.c libnm-core/nm-keyfile.c From 6435040881937ab10c9e3532fb024600f25730c3 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 29 Aug 2018 20:46:17 +0200 Subject: [PATCH 23/61] libnm/crypto: add header "nm-crypto-impl.h" for crypto implementation There are two aspects: the public crypto API that is provided by "nm-crypto.h" header, and the internal header which crypto backends need to implement. Split them. --- Makefile.am | 1 + docs/libnm/Makefile.am | 1 + docs/libnm/meson.build | 1 + libnm-core/nm-crypto-gnutls.c | 2 +- libnm-core/nm-crypto-impl.h | 61 ++++++++++++++++++++++++++++++++++ libnm-core/nm-crypto-nss.c | 2 +- libnm-core/nm-crypto.c | 1 + libnm-core/nm-crypto.h | 31 +---------------- libnm-core/tests/test-crypto.c | 2 +- 9 files changed, 69 insertions(+), 33 deletions(-) create mode 100644 libnm-core/nm-crypto-impl.h diff --git a/Makefile.am b/Makefile.am index 8fa1c19738..f61b4e5cb8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -509,6 +509,7 @@ libnm_core_lib_h_priv = \ shared/nm-ethtool-utils.h \ shared/nm-meta-setting.h \ libnm-core/nm-crypto.h \ + libnm-core/nm-crypto-impl.h \ libnm-core/nm-connection-private.h \ libnm-core/nm-core-internal.h \ libnm-core/nm-core-types-internal.h \ diff --git a/docs/libnm/Makefile.am b/docs/libnm/Makefile.am index 7c3b54b484..c5f2b836ba 100644 --- a/docs/libnm/Makefile.am +++ b/docs/libnm/Makefile.am @@ -33,6 +33,7 @@ CFILE_GLOB=$(top_srcdir)/libnm-core/*.c $(top_srcdir)/libnm/*.c IGNORE_HFILES= \ common.h \ nm-crypto.h \ + nm-crypto-impl.h \ nm-dbus-helpers.h \ nm-core-internal.h \ nm-core-types-internal.h \ diff --git a/docs/libnm/meson.build b/docs/libnm/meson.build index 35aae523be..77f7ed0f94 100644 --- a/docs/libnm/meson.build +++ b/docs/libnm/meson.build @@ -3,6 +3,7 @@ doc_module = libnm_name private_headers = [ 'common.h', 'nm-crypto.h', + 'nm-crypto-impl.h', 'nm-dbus-helpers.h', 'nm-core-internal.h', 'nm-core-types-internal.h', diff --git a/libnm-core/nm-crypto-gnutls.c b/libnm-core/nm-crypto-gnutls.c index 2cdb259af3..72cc0a28c0 100644 --- a/libnm-core/nm-crypto-gnutls.c +++ b/libnm-core/nm-crypto-gnutls.c @@ -23,7 +23,7 @@ #include "nm-default.h" -#include "nm-crypto.h" +#include "nm-crypto-impl.h" #include #include diff --git a/libnm-core/nm-crypto-impl.h b/libnm-core/nm-crypto-impl.h new file mode 100644 index 0000000000..63ffb17097 --- /dev/null +++ b/libnm-core/nm-crypto-impl.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * Dan Williams + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2007 - 2018 Red Hat, Inc. + */ + +#ifndef __NM_CRYPTO_IMPL_H__ +#define __NM_CRYPTO_IMPL_H__ + +#if !((NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_PRIVATE) +#error Cannot use this header. +#endif + +#include "nm-crypto.h" + +gboolean crypto_init (GError **error); + +char * crypto_decrypt (const char *cipher, + int key_type, + const guint8 *data, + gsize data_len, + const char *iv, + const gsize iv_len, + const char *key, + const gsize key_len, + gsize *out_len, + GError **error); + +NMCryptoFileFormat crypto_verify_cert (const guint8 *data, + gsize len, + GError **error); + +gboolean crypto_verify_pkcs12 (const guint8 *data, + gsize data_len, + const char *password, + GError **error); + +gboolean crypto_verify_pkcs8 (const guint8 *data, + gsize data_len, + gboolean is_encrypted, + const char *password, + GError **error); + +#endif /* __NM_CRYPTO_IMPL_H__ */ diff --git a/libnm-core/nm-crypto-nss.c b/libnm-core/nm-crypto-nss.c index eec61e0f87..634022b00a 100644 --- a/libnm-core/nm-crypto-nss.c +++ b/libnm-core/nm-crypto-nss.c @@ -23,7 +23,7 @@ #include "nm-default.h" -#include "nm-crypto.h" +#include "nm-crypto-impl.h" #include #include diff --git a/libnm-core/nm-crypto.c b/libnm-core/nm-crypto.c index 72e66a133c..c4740f31f4 100644 --- a/libnm-core/nm-crypto.c +++ b/libnm-core/nm-crypto.c @@ -32,6 +32,7 @@ #include "nm-utils/nm-secret-utils.h" +#include "nm-crypto-impl.h" #include "nm-errors.h" #define PEM_RSA_KEY_BEGIN "-----BEGIN RSA PRIVATE KEY-----" diff --git a/libnm-core/nm-crypto.h b/libnm-core/nm-crypto.h index cb74a8c953..4e66454b81 100644 --- a/libnm-core/nm-crypto.h +++ b/libnm-core/nm-crypto.h @@ -29,6 +29,7 @@ #endif #define MD5_HASH_LEN 20 + #define CIPHER_DES_EDE3_CBC "DES-EDE3-CBC" #define CIPHER_DES_CBC "DES-CBC" #define CIPHER_AES_128_CBC "AES-128-CBC" @@ -48,8 +49,6 @@ typedef enum { NM_CRYPTO_FILE_FORMAT_PKCS12 } NMCryptoFileFormat; -gboolean crypto_init (GError **error); - GByteArray *nmtst_crypto_decrypt_openssl_private_key_data (const guint8 *data, gsize data_len, const char *password, @@ -80,8 +79,6 @@ NMCryptoFileFormat crypto_verify_private_key (const char *file, gboolean *out_is_encrypted, GError **error); -/* Internal utils API bits for crypto providers */ - void crypto_md5_hash (const guint8 *salt, gsize salt_len, const guint8 *password, @@ -96,17 +93,6 @@ char *crypto_make_des_aes_key (const char *cipher, gsize *out_len, GError **error); -char * crypto_decrypt (const char *cipher, - int key_type, - const guint8 *data, - gsize data_len, - const char *iv, - const gsize iv_len, - const char *key, - const gsize key_len, - gsize *out_len, - GError **error); - char * crypto_encrypt (const char *cipher, const guint8 *data, gsize data_len, @@ -119,19 +105,4 @@ char * crypto_encrypt (const char *cipher, gboolean crypto_randomize (void *buffer, gsize buffer_len, GError **error); -NMCryptoFileFormat crypto_verify_cert (const guint8 *data, - gsize len, - GError **error); - -gboolean crypto_verify_pkcs12 (const guint8 *data, - gsize data_len, - const char *password, - GError **error); - -gboolean crypto_verify_pkcs8 (const guint8 *data, - gsize data_len, - gboolean is_encrypted, - const char *password, - GError **error); - #endif /* __NM_CRYPTO_H__ */ diff --git a/libnm-core/tests/test-crypto.c b/libnm-core/tests/test-crypto.c index 8b447d2685..c180b987f3 100644 --- a/libnm-core/tests/test-crypto.c +++ b/libnm-core/tests/test-crypto.c @@ -28,7 +28,7 @@ #include #include -#include "nm-crypto.h" +#include "nm-crypto-impl.h" #include "nm-utils.h" #include "nm-errors.h" #include "nm-core-internal.h" From c172675c1390ba003908e90b9819e2b3d6285032 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 29 Aug 2018 21:01:48 +0200 Subject: [PATCH 24/61] libnm/crypto: rename libnm crypto API to have consistent NM prefix Follow our convention, that items in headers are all named with an "NM" prefix. Also, "nm-crypto-impl.h" contains internal functions that are to be implemented by the corresponding crypto backends. Distinguish their names as well. --- libnm-core/nm-crypto-gnutls.c | 78 +++++++++--------- libnm-core/nm-crypto-impl.h | 58 +++++++------ libnm-core/nm-crypto-nss.c | 78 +++++++++--------- libnm-core/nm-crypto.c | 143 +++++++++++++++++++-------------- libnm-core/nm-crypto.h | 76 +++++++++--------- libnm-core/nm-setting-8021x.c | 42 +++++----- libnm-core/nm-utils.c | 38 ++++----- libnm-core/tests/test-crypto.c | 32 ++++---- 8 files changed, 289 insertions(+), 256 deletions(-) diff --git a/libnm-core/nm-crypto-gnutls.c b/libnm-core/nm-crypto-gnutls.c index 72cc0a28c0..86535ee4b1 100644 --- a/libnm-core/nm-crypto-gnutls.c +++ b/libnm-core/nm-crypto-gnutls.c @@ -37,7 +37,7 @@ static gboolean initialized = FALSE; gboolean -crypto_init (GError **error) +_nm_crypto_init (GError **error) { if (initialized) return TRUE; @@ -55,16 +55,16 @@ crypto_init (GError **error) } char * -crypto_decrypt (const char *cipher, - int key_type, - const guint8 *data, - gsize data_len, - const char *iv, - const gsize iv_len, - const char *key, - const gsize key_len, - gsize *out_len, - GError **error) +_nm_crypto_decrypt (const char *cipher, + int key_type, + const guint8 *data, + gsize data_len, + const char *iv, + const gsize iv_len, + const char *key, + const gsize key_len, + gsize *out_len, + GError **error) { gnutls_cipher_hd_t ctx; gnutls_datum_t key_dt, iv_dt; @@ -74,7 +74,7 @@ crypto_decrypt (const char *cipher, gboolean success = FALSE; gsize pad_len, real_iv_len; - if (!crypto_init (error)) + if (!_nm_crypto_init (error)) return NULL; if (!strcmp (cipher, CIPHER_DES_EDE3_CBC)) { @@ -171,15 +171,15 @@ out: } char * -crypto_encrypt (const char *cipher, - const guint8 *data, - gsize data_len, - const char *iv, - const gsize iv_len, - const char *key, - gsize key_len, - gsize *out_len, - GError **error) +_nm_crypto_encrypt (const char *cipher, + const guint8 *data, + gsize data_len, + const char *iv, + const gsize iv_len, + const char *key, + gsize key_len, + gsize *out_len, + GError **error) { gnutls_cipher_hd_t ctx; gnutls_datum_t key_dt, iv_dt; @@ -191,7 +191,7 @@ crypto_encrypt (const char *cipher, char *padded_buf = NULL; guint32 i; - if (!crypto_init (error)) + if (!_nm_crypto_init (error)) return NULL; if (!strcmp (cipher, CIPHER_DES_EDE3_CBC)) @@ -269,15 +269,15 @@ out: } NMCryptoFileFormat -crypto_verify_cert (const unsigned char *data, - gsize len, - GError **error) +_nm_crypto_verify_cert (const unsigned char *data, + gsize len, + GError **error) { gnutls_x509_crt_t der; gnutls_datum_t dt; int err; - if (!crypto_init (error)) + if (!_nm_crypto_init (error)) return NM_CRYPTO_FILE_FORMAT_UNKNOWN; err = gnutls_x509_crt_init (&der); @@ -312,10 +312,10 @@ crypto_verify_cert (const unsigned char *data, } gboolean -crypto_verify_pkcs12 (const guint8 *data, - gsize data_len, - const char *password, - GError **error) +_nm_crypto_verify_pkcs12 (const guint8 *data, + gsize data_len, + const char *password, + GError **error) { gnutls_pkcs12_t p12; gnutls_datum_t dt; @@ -324,7 +324,7 @@ crypto_verify_pkcs12 (const guint8 *data, g_return_val_if_fail (data != NULL, FALSE); - if (!crypto_init (error)) + if (!_nm_crypto_init (error)) return FALSE; dt.data = (unsigned char *) data; @@ -369,11 +369,11 @@ out: } gboolean -crypto_verify_pkcs8 (const guint8 *data, - gsize data_len, - gboolean is_encrypted, - const char *password, - GError **error) +_nm_crypto_verify_pkcs8 (const guint8 *data, + gsize data_len, + gboolean is_encrypted, + const char *password, + GError **error) { gnutls_x509_privkey_t p8; gnutls_datum_t dt; @@ -381,7 +381,7 @@ crypto_verify_pkcs8 (const guint8 *data, g_return_val_if_fail (data != NULL, FALSE); - if (!crypto_init (error)) + if (!_nm_crypto_init (error)) return FALSE; dt.data = (unsigned char *) data; @@ -424,9 +424,9 @@ crypto_verify_pkcs8 (const guint8 *data, } gboolean -crypto_randomize (void *buffer, gsize buffer_len, GError **error) +_nm_crypto_randomize (void *buffer, gsize buffer_len, GError **error) { - if (!crypto_init (error)) + if (!_nm_crypto_init (error)) return FALSE; gnutls_rnd (GNUTLS_RND_RANDOM, buffer, buffer_len); diff --git a/libnm-core/nm-crypto-impl.h b/libnm-core/nm-crypto-impl.h index 63ffb17097..93186cb6cf 100644 --- a/libnm-core/nm-crypto-impl.h +++ b/libnm-core/nm-crypto-impl.h @@ -30,32 +30,44 @@ #include "nm-crypto.h" -gboolean crypto_init (GError **error); +gboolean _nm_crypto_init (GError **error); -char * crypto_decrypt (const char *cipher, - int key_type, - const guint8 *data, - gsize data_len, - const char *iv, - const gsize iv_len, - const char *key, - const gsize key_len, - gsize *out_len, - GError **error); +gboolean _nm_crypto_randomize (void *buffer, gsize buffer_len, GError **error); -NMCryptoFileFormat crypto_verify_cert (const guint8 *data, - gsize len, - GError **error); +char *_nm_crypto_encrypt (const char *cipher, + const guint8 *data, + gsize data_len, + const char *iv, + gsize iv_len, + const char *key, + gsize key_len, + gsize *out_len, + GError **error); -gboolean crypto_verify_pkcs12 (const guint8 *data, - gsize data_len, - const char *password, - GError **error); +char *_nm_crypto_decrypt (const char *cipher, + int key_type, + const guint8 *data, + gsize data_len, + const char *iv, + const gsize iv_len, + const char *key, + const gsize key_len, + gsize *out_len, + GError **error); -gboolean crypto_verify_pkcs8 (const guint8 *data, - gsize data_len, - gboolean is_encrypted, - const char *password, - GError **error); +NMCryptoFileFormat _nm_crypto_verify_cert (const guint8 *data, + gsize len, + GError **error); + +gboolean _nm_crypto_verify_pkcs12 (const guint8 *data, + gsize data_len, + const char *password, + GError **error); + +gboolean _nm_crypto_verify_pkcs8 (const guint8 *data, + gsize data_len, + gboolean is_encrypted, + const char *password, + GError **error); #endif /* __NM_CRYPTO_IMPL_H__ */ diff --git a/libnm-core/nm-crypto-nss.c b/libnm-core/nm-crypto-nss.c index 634022b00a..df0fa58ea9 100644 --- a/libnm-core/nm-crypto-nss.c +++ b/libnm-core/nm-crypto-nss.c @@ -40,7 +40,7 @@ static gboolean initialized = FALSE; gboolean -crypto_init (GError **error) +_nm_crypto_init (GError **error) { SECStatus ret; @@ -71,16 +71,16 @@ crypto_init (GError **error) } char * -crypto_decrypt (const char *cipher, - int key_type, - const guint8 *data, - gsize data_len, - const char *iv, - const gsize iv_len, - const char *key, - const gsize key_len, - gsize *out_len, - GError **error) +_nm_crypto_decrypt (const char *cipher, + int key_type, + const guint8 *data, + gsize data_len, + const char *iv, + const gsize iv_len, + const char *key, + const gsize key_len, + gsize *out_len, + GError **error) { char *output = NULL; int decrypted_len = 0; @@ -95,7 +95,7 @@ crypto_decrypt (const char *cipher, unsigned pad_len = 0, extra = 0; guint32 i, real_iv_len = 0; - if (!crypto_init (error)) + if (!_nm_crypto_init (error)) return NULL; if (!strcmp (cipher, CIPHER_DES_EDE3_CBC)) { @@ -243,15 +243,15 @@ out: } char * -crypto_encrypt (const char *cipher, - const guint8 *data, - gsize data_len, - const char *iv, - gsize iv_len, - const char *key, - gsize key_len, - gsize *out_len, - GError **error) +_nm_crypto_encrypt (const char *cipher, + const guint8 *data, + gsize data_len, + const char *iv, + gsize iv_len, + const char *key, + gsize key_len, + gsize *out_len, + GError **error) { SECStatus ret; CK_MECHANISM_TYPE cipher_mech = CKM_DES3_CBC_PAD; @@ -267,7 +267,7 @@ crypto_encrypt (const char *cipher, gboolean success = FALSE; gsize padded_buf_len, pad_len; - if (!crypto_init (error)) + if (!_nm_crypto_init (error)) return NULL; if (!strcmp (cipher, CIPHER_DES_EDE3_CBC)) @@ -371,13 +371,13 @@ out: } NMCryptoFileFormat -crypto_verify_cert (const unsigned char *data, - gsize len, - GError **error) +_nm_crypto_verify_cert (const unsigned char *data, + gsize len, + GError **error) { CERTCertificate *cert; - if (!crypto_init (error)) + if (!_nm_crypto_init (error)) return NM_CRYPTO_FILE_FORMAT_UNKNOWN; /* Try DER/PEM first */ @@ -395,10 +395,10 @@ crypto_verify_cert (const unsigned char *data, } gboolean -crypto_verify_pkcs12 (const guint8 *data, - gsize data_len, - const char *password, - GError **error) +_nm_crypto_verify_pkcs12 (const guint8 *data, + gsize data_len, + const char *password, + GError **error) { SEC_PKCS12DecoderContext *p12ctx = NULL; SECItem pw = { 0 }; @@ -413,7 +413,7 @@ crypto_verify_pkcs12 (const guint8 *data, if (error) g_return_val_if_fail (*error == NULL, FALSE); - if (!crypto_init (error)) + if (!_nm_crypto_init (error)) return FALSE; /* PKCS#12 passwords are apparently UCS2 BIG ENDIAN, and NSS doesn't do @@ -492,15 +492,15 @@ error: } gboolean -crypto_verify_pkcs8 (const guint8 *data, - gsize data_len, - gboolean is_encrypted, - const char *password, - GError **error) +_nm_crypto_verify_pkcs8 (const guint8 *data, + gsize data_len, + gboolean is_encrypted, + const char *password, + GError **error) { g_return_val_if_fail (data != NULL, FALSE); - if (!crypto_init (error)) + if (!_nm_crypto_init (error)) return FALSE; /* NSS apparently doesn't do PKCS#8 natively, but you have to put the @@ -511,11 +511,11 @@ crypto_verify_pkcs8 (const guint8 *data, } gboolean -crypto_randomize (void *buffer, gsize buffer_len, GError **error) +_nm_crypto_randomize (void *buffer, gsize buffer_len, GError **error) { SECStatus s; - if (!crypto_init (error)) + if (!_nm_crypto_init (error)) return FALSE; s = PK11_GenerateRandom (buffer, buffer_len); diff --git a/libnm-core/nm-crypto.c b/libnm-core/nm-crypto.c index c4740f31f4..fcef7960cb 100644 --- a/libnm-core/nm-crypto.c +++ b/libnm-core/nm-crypto.c @@ -411,12 +411,12 @@ convert_iv (const char *src, } char * -crypto_make_des_aes_key (const char *cipher, - const char *salt, - const gsize salt_len, - const char *password, - gsize *out_len, - GError **error) +nm_crypto_make_des_aes_key (const char *cipher, + const char *salt, + const gsize salt_len, + const char *password, + gsize *out_len, + GError **error) { char *key; guint32 digest_len; @@ -452,12 +452,12 @@ crypto_make_des_aes_key (const char *cipher, key = g_malloc0 (digest_len + 1); - crypto_md5_hash ((guint8 *) salt, - 8, - (guint8 *) password, - strlen (password), - (guint8 *) key, - digest_len); + nm_crypto_md5_hash ((guint8 *) salt, + 8, + (guint8 *) password, + strlen (password), + (guint8 *) key, + digest_len); *out_len = digest_len; return key; @@ -497,20 +497,20 @@ decrypt_key (const char *cipher, } /* Convert the password and IV into a DES or AES key */ - key.str = crypto_make_des_aes_key (cipher, bin_iv.str, bin_iv.len, password, &key.len, error); + key.str = nm_crypto_make_des_aes_key (cipher, bin_iv.str, bin_iv.len, password, &key.len, error); if (!key.str || !key.len) return FALSE; - parsed->str = crypto_decrypt (cipher, - key_type, - data, - data_len, - bin_iv.str, - bin_iv.len, - key.str, - key.len, - &parsed->len, - error); + parsed->str = _nm_crypto_decrypt (cipher, + key_type, + data, + data_len, + bin_iv.str, + bin_iv.len, + key.str, + key.len, + &parsed->len, + error); if (!parsed->str || parsed->len == 0) { nm_secret_ptr_clear (parsed); return FALSE; @@ -535,7 +535,7 @@ nmtst_crypto_decrypt_openssl_private_key_data (const guint8 *data, NM_SET_OUT (out_key_type, NM_CRYPTO_KEY_TYPE_UNKNOWN); - if (!crypto_init (error)) + if (!_nm_crypto_init (error)) return NULL; if (!parse_old_openssl_key_file (data, data_len, &parsed, &key_type, &cipher, &iv, NULL)) { @@ -584,7 +584,7 @@ nmtst_crypto_decrypt_openssl_private_key (const char *file, { nm_auto_clear_secret_ptr NMSecretPtr contents = { 0 }; - if (!crypto_init (error)) + if (!_nm_crypto_init (error)) return NULL; if (!file_read_contents (file, &contents, error)) @@ -645,9 +645,9 @@ extract_pem_cert_data (const guint8 *contents, } GByteArray * -crypto_load_and_verify_certificate (const char *file, - NMCryptoFileFormat *out_file_format, - GError **error) +nm_crypto_load_and_verify_certificate (const char *file, + NMCryptoFileFormat *out_file_format, + GError **error) { nm_auto_clear_secret_ptr NMSecretPtr contents = { 0 }; @@ -656,28 +656,28 @@ crypto_load_and_verify_certificate (const char *file, *out_file_format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; - if (!crypto_init (error)) + if (!_nm_crypto_init (error)) return NULL; if (!file_read_contents (file, &contents, error)) return NULL; /* Check for PKCS#12 */ - if (crypto_is_pkcs12_data (contents.bin, contents.len, NULL)) { + if (nm_crypto_is_pkcs12_data (contents.bin, contents.len, NULL)) { *out_file_format = NM_CRYPTO_FILE_FORMAT_PKCS12; return to_gbyte_array_mem (contents.bin, contents.len); } /* Check for plain DER format */ if (contents.len > 2 && contents.bin[0] == 0x30 && contents.bin[1] == 0x82) { - *out_file_format = crypto_verify_cert (contents.bin, contents.len, error); + *out_file_format = _nm_crypto_verify_cert (contents.bin, contents.len, error); } else { nm_auto_clear_secret_ptr NMSecretPtr pem_cert = { 0 }; if (!extract_pem_cert_data (contents.bin, contents.len, &pem_cert, error)) return NULL; - *out_file_format = crypto_verify_cert (pem_cert.bin, pem_cert.len, error); + *out_file_format = _nm_crypto_verify_cert (pem_cert.bin, pem_cert.len, error); } if (*out_file_format != NM_CRYPTO_FILE_FORMAT_X509) @@ -687,9 +687,9 @@ crypto_load_and_verify_certificate (const char *file, } gboolean -crypto_is_pkcs12_data (const guint8 *data, - gsize data_len, - GError **error) +nm_crypto_is_pkcs12_data (const guint8 *data, + gsize data_len, + GError **error) { GError *local = NULL; gboolean success; @@ -699,10 +699,10 @@ crypto_is_pkcs12_data (const guint8 *data, g_return_val_if_fail (data != NULL, FALSE); - if (!crypto_init (error)) + if (!_nm_crypto_init (error)) return FALSE; - success = crypto_verify_pkcs12 (data, data_len, NULL, &local); + success = _nm_crypto_verify_pkcs12 (data, data_len, NULL, &local); if (success == FALSE) { /* If the error was just a decryption error, then it's pkcs#12 */ if (local) { @@ -717,30 +717,30 @@ crypto_is_pkcs12_data (const guint8 *data, } gboolean -crypto_is_pkcs12_file (const char *file, GError **error) +nm_crypto_is_pkcs12_file (const char *file, GError **error) { nm_auto_clear_secret_ptr NMSecretPtr contents = { 0 }; g_return_val_if_fail (file != NULL, FALSE); - if (!crypto_init (error)) + if (!_nm_crypto_init (error)) return FALSE; if (!file_read_contents (file, &contents, error)) return FALSE; - return crypto_is_pkcs12_data (contents.bin, contents.len, error); + return nm_crypto_is_pkcs12_data (contents.bin, contents.len, error); } /* Verifies that a private key can be read, and if a password is given, that * the private key can be decrypted with that password. */ NMCryptoFileFormat -crypto_verify_private_key_data (const guint8 *data, - gsize data_len, - const char *password, - gboolean *out_is_encrypted, - GError **error) +nm_crypto_verify_private_key_data (const guint8 *data, + gsize data_len, + const char *password, + gboolean *out_is_encrypted, + GError **error) { NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; gboolean is_encrypted = FALSE; @@ -748,13 +748,14 @@ crypto_verify_private_key_data (const guint8 *data, g_return_val_if_fail (data != NULL, NM_CRYPTO_FILE_FORMAT_UNKNOWN); g_return_val_if_fail (out_is_encrypted == NULL || *out_is_encrypted == FALSE, NM_CRYPTO_FILE_FORMAT_UNKNOWN); - if (!crypto_init (error)) + if (!_nm_crypto_init (error)) return NM_CRYPTO_FILE_FORMAT_UNKNOWN; /* Check for PKCS#12 first */ - if (crypto_is_pkcs12_data (data, data_len, NULL)) { + if (nm_crypto_is_pkcs12_data (data, data_len, NULL)) { is_encrypted = TRUE; - if (!password || crypto_verify_pkcs12 (data, data_len, password, error)) + if ( !password + || _nm_crypto_verify_pkcs12 (data, data_len, password, error)) format = NM_CRYPTO_FILE_FORMAT_PKCS12; } else { nm_auto_clear_secret_ptr NMSecretPtr parsed = { 0 }; @@ -762,7 +763,7 @@ crypto_verify_private_key_data (const guint8 *data, /* Maybe it's PKCS#8 */ if (parse_pkcs8_key_file (data, data_len, &parsed, &is_encrypted, NULL)) { if ( !password - || crypto_verify_pkcs8 (parsed.bin, parsed.len, is_encrypted, password, error)) + || _nm_crypto_verify_pkcs8 (parsed.bin, parsed.len, is_encrypted, password, error)) format = NM_CRYPTO_FILE_FORMAT_RAW_KEY; } else { const char *cipher; @@ -782,31 +783,31 @@ crypto_verify_private_key_data (const guint8 *data, } NMCryptoFileFormat -crypto_verify_private_key (const char *filename, - const char *password, - gboolean *out_is_encrypted, - GError **error) +nm_crypto_verify_private_key (const char *filename, + const char *password, + gboolean *out_is_encrypted, + GError **error) { nm_auto_clear_secret_ptr NMSecretPtr contents = { 0 }; g_return_val_if_fail (filename != NULL, NM_CRYPTO_FILE_FORMAT_UNKNOWN); - if (!crypto_init (error)) + if (!_nm_crypto_init (error)) return NM_CRYPTO_FILE_FORMAT_UNKNOWN; if (!file_read_contents (filename, &contents, error)) return NM_CRYPTO_FILE_FORMAT_UNKNOWN; - return crypto_verify_private_key_data (contents.bin, contents.len, password, out_is_encrypted, error); + return nm_crypto_verify_private_key_data (contents.bin, contents.len, password, out_is_encrypted, error); } void -crypto_md5_hash (const guint8 *salt, - gsize salt_len, - const guint8 *password, - gsize password_len, - guint8 *buffer, - gsize buflen) +nm_crypto_md5_hash (const guint8 *salt, + gsize salt_len, + const guint8 *password, + gsize password_len, + guint8 *buffer, + gsize buflen) { nm_auto_free_checksum GChecksum *ctx = NULL; #define MD5_DIGEST_LEN 16 @@ -845,3 +846,23 @@ crypto_md5_hash (const guint8 *salt, g_checksum_update (ctx, digest.ptr, MD5_DIGEST_LEN); } } + +char * +nm_crypto_encrypt (const char *cipher, + const guint8 *data, + gsize data_len, + const char *iv, + gsize iv_len, + const char *key, + gsize key_len, + gsize *out_len, + GError **error) +{ + return _nm_crypto_encrypt (cipher, data, data_len, iv, iv_len, key, key_len, out_len, error); +} + +gboolean +nm_crypto_randomize (void *buffer, gsize buffer_len, GError **error) +{ + return _nm_crypto_randomize (buffer, buffer_len, error); +} diff --git a/libnm-core/nm-crypto.h b/libnm-core/nm-crypto.h index 4e66454b81..3c073a9916 100644 --- a/libnm-core/nm-crypto.h +++ b/libnm-core/nm-crypto.h @@ -60,49 +60,49 @@ GByteArray *nmtst_crypto_decrypt_openssl_private_key (const char *file, NMCryptoKeyType *out_key_type, GError **error); -GByteArray *crypto_load_and_verify_certificate (const char *file, - NMCryptoFileFormat *out_file_format, - GError **error); - -gboolean crypto_is_pkcs12_file (const char *file, GError **error); - -gboolean crypto_is_pkcs12_data (const guint8 *data, gsize len, GError **error); - -NMCryptoFileFormat crypto_verify_private_key_data (const guint8 *data, - gsize data_len, - const char *password, - gboolean *out_is_encrypted, +GByteArray *nm_crypto_load_and_verify_certificate (const char *file, + NMCryptoFileFormat *out_file_format, GError **error); -NMCryptoFileFormat crypto_verify_private_key (const char *file, - const char *password, - gboolean *out_is_encrypted, - GError **error); +gboolean nm_crypto_is_pkcs12_file (const char *file, GError **error); -void crypto_md5_hash (const guint8 *salt, - gsize salt_len, - const guint8 *password, - gsize password_len, - guint8 *buffer, - gsize buflen); +gboolean nm_crypto_is_pkcs12_data (const guint8 *data, gsize len, GError **error); -char *crypto_make_des_aes_key (const char *cipher, - const char *salt, - const gsize salt_len, - const char *password, - gsize *out_len, - GError **error); +NMCryptoFileFormat nm_crypto_verify_private_key_data (const guint8 *data, + gsize data_len, + const char *password, + gboolean *out_is_encrypted, + GError **error); -char * crypto_encrypt (const char *cipher, - const guint8 *data, - gsize data_len, - const char *iv, - gsize iv_len, - const char *key, - gsize key_len, - gsize *out_len, - GError **error); +NMCryptoFileFormat nm_crypto_verify_private_key (const char *file, + const char *password, + gboolean *out_is_encrypted, + GError **error); -gboolean crypto_randomize (void *buffer, gsize buffer_len, GError **error); +void nm_crypto_md5_hash (const guint8 *salt, + gsize salt_len, + const guint8 *password, + gsize password_len, + guint8 *buffer, + gsize buflen); + +char *nm_crypto_make_des_aes_key (const char *cipher, + const char *salt, + const gsize salt_len, + const char *password, + gsize *out_len, + GError **error); + +char * nm_crypto_encrypt (const char *cipher, + const guint8 *data, + gsize data_len, + const char *iv, + gsize iv_len, + const char *key, + gsize key_len, + gsize *out_len, + GError **error); + +gboolean nm_crypto_randomize (void *buffer, gsize buffer_len, GError **error); #endif /* __NM_CRYPTO_H__ */ diff --git a/libnm-core/nm-setting-8021x.c b/libnm-core/nm-setting-8021x.c index 583f98f201..30f4537e9c 100644 --- a/libnm-core/nm-setting-8021x.c +++ b/libnm-core/nm-setting-8021x.c @@ -529,7 +529,7 @@ load_and_verify_certificate (const char *cert_path, NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; GByteArray *array; - array = crypto_load_and_verify_certificate (cert_path, &format, error); + array = nm_crypto_load_and_verify_certificate (cert_path, &format, error); if (!array || !array->len || format == NM_CRYPTO_FILE_FORMAT_UNKNOWN) { /* the array is empty or the format is already unknown. */ @@ -2264,7 +2264,7 @@ nm_setting_802_1x_set_private_key (NMSetting8021x *setting, * given, that it decrypts the private key. */ if (value && scheme != NM_SETTING_802_1X_CK_SCHEME_PKCS11) { - format = crypto_verify_private_key (value, password, NULL, &local_err); + format = nm_crypto_verify_private_key (value, password, NULL, &local_err); if (format == NM_CRYPTO_FILE_FORMAT_UNKNOWN) { g_set_error_literal (error, NM_CONNECTION_ERROR, @@ -2379,14 +2379,14 @@ nm_setting_802_1x_get_private_key_format (NMSetting8021x *setting) switch (nm_setting_802_1x_get_private_key_scheme (setting)) { case NM_SETTING_802_1X_CK_SCHEME_BLOB: - if (crypto_is_pkcs12_data (g_bytes_get_data (priv->private_key, NULL), - g_bytes_get_size (priv->private_key), - NULL)) + if (nm_crypto_is_pkcs12_data (g_bytes_get_data (priv->private_key, NULL), + g_bytes_get_size (priv->private_key), + NULL)) return NM_SETTING_802_1X_CK_FORMAT_PKCS12; return NM_SETTING_802_1X_CK_FORMAT_RAW_KEY; case NM_SETTING_802_1X_CK_SCHEME_PATH: path = nm_setting_802_1x_get_private_key_path (setting); - if (crypto_is_pkcs12_file (path, &error)) + if (nm_crypto_is_pkcs12_file (path, &error)) return NM_SETTING_802_1X_CK_FORMAT_PKCS12; if (error && error->domain == G_FILE_ERROR) { g_error_free (error); @@ -2606,7 +2606,7 @@ nm_setting_802_1x_set_phase2_private_key (NMSetting8021x *setting, * given, that it decrypts the private key. */ if (value && scheme != NM_SETTING_802_1X_CK_SCHEME_PKCS11) { - format = crypto_verify_private_key (value, password, NULL, &local_err); + format = nm_crypto_verify_private_key (value, password, NULL, &local_err); if (format == NM_CRYPTO_FILE_FORMAT_UNKNOWN) { g_set_error_literal (error, NM_CONNECTION_ERROR, @@ -2691,14 +2691,14 @@ nm_setting_802_1x_get_phase2_private_key_format (NMSetting8021x *setting) switch (nm_setting_802_1x_get_phase2_private_key_scheme (setting)) { case NM_SETTING_802_1X_CK_SCHEME_BLOB: - if (crypto_is_pkcs12_data (g_bytes_get_data (priv->phase2_private_key, NULL), - g_bytes_get_size (priv->phase2_private_key), - NULL)) + if (nm_crypto_is_pkcs12_data (g_bytes_get_data (priv->phase2_private_key, NULL), + g_bytes_get_size (priv->phase2_private_key), + NULL)) return NM_SETTING_802_1X_CK_FORMAT_PKCS12; return NM_SETTING_802_1X_CK_FORMAT_RAW_KEY; case NM_SETTING_802_1X_CK_SCHEME_PATH: path = nm_setting_802_1x_get_phase2_private_key_path (setting); - if (crypto_is_pkcs12_file (path, &error)) + if (nm_crypto_is_pkcs12_file (path, &error)) return NM_SETTING_802_1X_CK_FORMAT_PKCS12; if (error && error->domain == G_FILE_ERROR) { g_error_free (error); @@ -2776,11 +2776,11 @@ need_private_key_password (GBytes *blob, /* Private key password is required */ if (password) { if (path) - format = crypto_verify_private_key (path, password, NULL, NULL); + format = nm_crypto_verify_private_key (path, password, NULL, NULL); else if (blob) - format = crypto_verify_private_key_data (g_bytes_get_data (blob, NULL), - g_bytes_get_size (blob), - password, NULL, NULL); + format = nm_crypto_verify_private_key_data (g_bytes_get_data (blob, NULL), + g_bytes_get_size (blob), + password, NULL, NULL); else return FALSE; } @@ -2895,9 +2895,9 @@ verify_tls (NMSetting8021x *self, gboolean phase2, GError **error) } /* If the private key is PKCS#12, check that it matches the client cert */ - if (crypto_is_pkcs12_data (g_bytes_get_data (priv->phase2_private_key, NULL), - g_bytes_get_size (priv->phase2_private_key), - NULL)) { + if (nm_crypto_is_pkcs12_data (g_bytes_get_data (priv->phase2_private_key, NULL), + g_bytes_get_size (priv->phase2_private_key), + NULL)) { if (!g_bytes_equal (priv->phase2_private_key, priv->phase2_client_cert)) { g_set_error (error, NM_CONNECTION_ERROR, @@ -2943,9 +2943,9 @@ verify_tls (NMSetting8021x *self, gboolean phase2, GError **error) } /* If the private key is PKCS#12, check that it matches the client cert */ - if (crypto_is_pkcs12_data (g_bytes_get_data (priv->private_key, NULL), - g_bytes_get_size (priv->private_key), - NULL)) { + if (nm_crypto_is_pkcs12_data (g_bytes_get_data (priv->private_key, NULL), + g_bytes_get_size (priv->private_key), + NULL)) { if (!g_bytes_equal (priv->private_key, priv->client_cert)) { g_set_error (error, NM_CONNECTION_ERROR, diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 253606ddcc..21de5624a8 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -2843,12 +2843,12 @@ nm_utils_uuid_generate_from_string (const char *s, gssize slen, int uuid_type, g switch (uuid_type) { case NM_UTILS_UUID_TYPE_LEGACY: - crypto_md5_hash (NULL, - 0, - (guint8 *) s, - slen, - (guint8 *) uuid, - sizeof (uuid)); + nm_crypto_md5_hash (NULL, + 0, + (guint8 *) s, + slen, + (guint8 *) uuid, + sizeof (uuid)); break; case NM_UTILS_UUID_TYPE_VARIANT3: { uuid_t ns_uuid = { 0 }; @@ -2859,12 +2859,12 @@ nm_utils_uuid_generate_from_string (const char *s, gssize slen, int uuid_type, g g_return_val_if_reached (NULL); } - crypto_md5_hash ((guint8 *) s, - slen, - (guint8 *) ns_uuid, - sizeof (ns_uuid), - (guint8 *) uuid, - sizeof (uuid)); + nm_crypto_md5_hash ((guint8 *) s, + slen, + (guint8 *) ns_uuid, + sizeof (ns_uuid), + (guint8 *) uuid, + sizeof (uuid)); uuid[6] = (uuid[6] & 0x0F) | 0x30; uuid[8] = (uuid[8] & 0x3F) | 0x80; @@ -2964,20 +2964,20 @@ nm_utils_rsa_key_encrypt (const guint8 *data, /* Make the password if needed */ if (!in_password) { - if (!crypto_randomize (pw_buf, sizeof (pw_buf), error)) + if (!nm_crypto_randomize (pw_buf, sizeof (pw_buf), error)) return NULL; in_password = tmp_password = nm_utils_bin2hexstr (pw_buf, sizeof (pw_buf), -1); } salt_len = 8; - if (!crypto_randomize (salt, salt_len, error)) + if (!nm_crypto_randomize (salt, salt_len, error)) goto out; - key = crypto_make_des_aes_key (CIPHER_DES_EDE3_CBC, &salt[0], salt_len, in_password, &key_len, NULL); + key = nm_crypto_make_des_aes_key (CIPHER_DES_EDE3_CBC, &salt[0], salt_len, in_password, &key_len, NULL); if (!key) g_return_val_if_reached (NULL); - enc = crypto_encrypt (CIPHER_DES_EDE3_CBC, data, len, salt, salt_len, key, key_len, &enc_len, error); + enc = nm_crypto_encrypt (CIPHER_DES_EDE3_CBC, data, len, salt, salt_len, key, key_len, &enc_len, error); if (!enc) goto out; @@ -3068,7 +3068,7 @@ nm_utils_file_is_certificate (const char *filename) if (!file_has_extension (filename, extensions)) return FALSE; - cert = crypto_load_and_verify_certificate (filename, &file_format, NULL); + cert = nm_crypto_load_and_verify_certificate (filename, &file_format, NULL); if (cert) g_byte_array_unref (cert); @@ -3097,7 +3097,7 @@ nm_utils_file_is_private_key (const char *filename, gboolean *out_encrypted) if (!file_has_extension (filename, extensions)) return FALSE; - return crypto_verify_private_key (filename, NULL, out_encrypted, NULL) != NM_CRYPTO_FILE_FORMAT_UNKNOWN; + return nm_crypto_verify_private_key (filename, NULL, out_encrypted, NULL) != NM_CRYPTO_FILE_FORMAT_UNKNOWN; } /** @@ -3113,7 +3113,7 @@ nm_utils_file_is_pkcs12 (const char *filename) { g_return_val_if_fail (filename != NULL, FALSE); - return crypto_is_pkcs12_file (filename, NULL); + return nm_crypto_is_pkcs12_file (filename, NULL); } /*****************************************************************************/ diff --git a/libnm-core/tests/test-crypto.c b/libnm-core/tests/test-crypto.c index c180b987f3..937fc6c3b0 100644 --- a/libnm-core/tests/test-crypto.c +++ b/libnm-core/tests/test-crypto.c @@ -105,7 +105,7 @@ test_cert (gconstpointer test_data) path = g_build_filename (TEST_CERT_DIR, (const char *) test_data, NULL); - array = crypto_load_and_verify_certificate (path, &format, &error); + array = nm_crypto_load_and_verify_certificate (path, &format, &error); g_assert_no_error (error); g_assert_cmpint (format, ==, NM_CRYPTO_FILE_FORMAT_X509); @@ -187,7 +187,7 @@ test_load_pkcs12 (const char *path, g_assert (nm_utils_file_is_private_key (path, NULL)); - format = crypto_verify_private_key (path, password, &is_encrypted, &error); + format = nm_crypto_verify_private_key (path, password, &is_encrypted, &error); if (expected_error != -1) { g_assert_error (error, NM_CRYPTO_ERROR, expected_error); g_assert_cmpint (format, ==, NM_CRYPTO_FILE_FORMAT_UNKNOWN); @@ -209,7 +209,7 @@ test_load_pkcs12_no_password (const char *path) g_assert (nm_utils_file_is_private_key (path, NULL)); /* We should still get a valid returned crypto file format */ - format = crypto_verify_private_key (path, NULL, &is_encrypted, &error); + format = nm_crypto_verify_private_key (path, NULL, &is_encrypted, &error); g_assert_no_error (error); g_assert_cmpint (format, ==, NM_CRYPTO_FILE_FORMAT_PKCS12); g_assert (is_encrypted); @@ -221,7 +221,7 @@ test_is_pkcs12 (const char *path, gboolean expect_fail) gboolean is_pkcs12; GError *error = NULL; - is_pkcs12 = crypto_is_pkcs12_file (path, &error); + is_pkcs12 = nm_crypto_is_pkcs12_file (path, &error); if (expect_fail) { g_assert_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA); @@ -244,7 +244,7 @@ test_load_pkcs8 (const char *path, g_assert (nm_utils_file_is_private_key (path, NULL)); - format = crypto_verify_private_key (path, password, &is_encrypted, &error); + format = nm_crypto_verify_private_key (path, password, &is_encrypted, &error); if (expected_error != -1) { g_assert_error (error, NM_CRYPTO_ERROR, expected_error); g_assert_cmpint (format, ==, NM_CRYPTO_FILE_FORMAT_UNKNOWN); @@ -417,16 +417,16 @@ test_md5 (void) for (i = 0; i < G_N_ELEMENTS (md5_tests); i++) { memset (digest, 0, sizeof (digest)); - crypto_md5_hash ((const guint8 *) md5_tests[i].salt, - /* crypto_md5_hash() used to clamp salt_len to 8. It - * doesn't any more, so we need to do it here now to - * get output that matches md5_tests[i].result. - */ - md5_tests[i].salt ? 8 : 0, - (const guint8 *) md5_tests[i].password, - strlen (md5_tests[i].password), - (guint8 *) digest, - md5_tests[i].digest_size); + nm_crypto_md5_hash ((const guint8 *) md5_tests[i].salt, + /* nm_crypto_md5_hash() used to clamp salt_len to 8. It + * doesn't any more, so we need to do it here now to + * get output that matches md5_tests[i].result. + */ + md5_tests[i].salt ? 8 : 0, + (const guint8 *) md5_tests[i].password, + strlen (md5_tests[i].password), + (guint8 *) digest, + md5_tests[i].digest_size); hex = nm_utils_bin2hexstr (digest, md5_tests[i].digest_size, -1); g_assert_cmpstr (hex, ==, md5_tests[i].result); @@ -445,7 +445,7 @@ main (int argc, char **argv) nmtst_init (&argc, &argv, TRUE); - success = crypto_init (&error); + success = _nm_crypto_init (&error); g_assert_no_error (error); g_assert (success); From e01f7f2c6dccad7a950c1af4c31737a9628e809e Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 30 Aug 2018 08:30:19 +0200 Subject: [PATCH 25/61] build: enable building both crypto backends for tests If the library is available, let's at least compile both crypto backends. That is helpful when developing on crypto backends, so that one does not have to configure the build twice. With autotools, the build is only run during `make check`. Not for meson, but that is generally the case with our meson setup, that it also builds tests during the regular build step. --- Makefile.am | 65 ++++++++++++++++------- configure.ac | 36 ++++++++++--- libnm-core/meson.build | 29 +++++++++- meson.build | 13 +++-- src/settings/plugins/ifcfg-rh/meson.build | 1 - 5 files changed, 110 insertions(+), 34 deletions(-) diff --git a/Makefile.am b/Makefile.am index f61b4e5cb8..c1615108a1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -620,14 +620,6 @@ dflt_cppflags_libnm_core = \ $(SANITIZER_LIB_CFLAGS) \ $(NULL) -if WITH_GNUTLS -dflt_cppflags_libnm_core += $(GNUTLS_CFLAGS) -endif - -if WITH_NSS -dflt_cppflags_libnm_core += $(NSS_CFLAGS) -endif - noinst_LTLIBRARIES += libnm-core/libnm-core.la GLIB_GENERATED += \ @@ -673,7 +665,6 @@ nodist_libnm_core_libnm_core_la_SOURCES = \ $(libnm_core_lib_c_mkenums) libnm_core_libnm_core_la_LIBADD = \ - shared/libcsiphash.la \ $(GLIB_LIBS) \ $(UUID_LIBS) \ $(LIBUDEV_LIBS) \ @@ -683,16 +674,6 @@ libnm_core_libnm_core_la_LDFLAGS = \ $(CODE_COVERAGE_LDFLAGS) \ $(SANITIZER_LIB_LDFLAGS) -if WITH_GNUTLS -libnm_core_lib_c_real += libnm-core/nm-crypto-gnutls.c -libnm_core_libnm_core_la_LIBADD += $(GNUTLS_LIBS) -endif - -if WITH_NSS -libnm_core_lib_c_real += libnm-core/nm-crypto-nss.c -libnm_core_libnm_core_la_LIBADD += $(NSS_LIBS) -endif - EXTRA_DIST += \ libnm-core/nm-crypto-gnutls.c \ libnm-core/nm-crypto-nss.c \ @@ -718,6 +699,46 @@ dist_dependencies += \ ############################################################################### +if HAVE_CRYPTO_GNUTLS +if WITH_GNUTLS +libnm_crypto_lib = libnm-core/libnm-crypto-gnutls.la +else +check_ltlibraries += libnm-core/libnm-crypto-gnutls.la +endif + +libnm_core_libnm_crypto_gnutls_la_SOURCES = libnm-core/nm-crypto-gnutls.c +libnm_core_libnm_crypto_gnutls_la_CPPFLAGS = \ + $(libnm_core_libnm_core_la_CPPFLAGS) \ + $(GNUTLS_CFLAGS) +libnm_core_libnm_crypto_gnutls_la_LDFLAGS = \ + $(libnm_core_libnm_core_la_LDFLAGS) +libnm_core_libnm_crypto_gnutls_la_LIBADD = \ + $(libnm_core_libnm_core_la_LIBADD) \ + $(GNUTLS_LIBS) +endif + +if HAVE_CRYPTO_NSS +if WITH_NSS +libnm_crypto_lib = libnm-core/libnm-crypto-nss.la +else +check_ltlibraries += libnm-core/libnm-crypto-nss.la +endif + +libnm_core_libnm_crypto_nss_la_SOURCES = libnm-core/nm-crypto-nss.c +libnm_core_libnm_crypto_nss_la_CPPFLAGS = \ + $(libnm_core_libnm_core_la_CPPFLAGS) \ + $(NSS_CFLAGS) +libnm_core_libnm_crypto_nss_la_LDFLAGS = \ + $(libnm_core_libnm_core_la_LDFLAGS) +libnm_core_libnm_crypto_nss_la_LIBADD = \ + $(libnm_core_libnm_core_la_LIBADD) \ + $(NSS_LIBS) +endif + +noinst_LTLIBRARIES += $(libnm_crypto_lib) + +############################################################################### + check_programs += \ libnm-core/tests/test-compare \ libnm-core/tests/test-crypto \ @@ -764,6 +785,8 @@ nodist_libnm_core_tests_test_general_SOURCES = \ libnm_core_tests_ldadd = \ libnm-core/libnm-core.la \ + shared/libcsiphash.la \ + $(libnm_crypto_lib) \ $(GLIB_LIBS) libnm_core_tests_ldflags = \ @@ -968,6 +991,8 @@ libnm_libnm_utils_la_SOURCES = \ libnm_libnm_utils_la_LIBADD = \ libnm-core/libnm-core.la \ + shared/libcsiphash.la \ + $(libnm_crypto_lib) \ introspection/libnmdbus.la \ $(GLIB_LIBS) @@ -1593,6 +1618,8 @@ endif src_libNetworkManagerBase_la_LIBADD = \ libnm-core/libnm-core.la \ + shared/libcsiphash.la \ + $(libnm_crypto_lib) \ $(GLIB_LIBS) \ $(SYSTEMD_JOURNAL_LIBS) \ $(LIBUDEV_LIBS) \ diff --git a/configure.ac b/configure.ac index ea1c43f12f..62c7e0392e 100644 --- a/configure.ac +++ b/configure.ac @@ -668,21 +668,41 @@ else fi AC_SUBST(NM_MODIFY_SYSTEM_POLICY) +PKG_CHECK_MODULES(GNUTLS, [gnutls >= 2.12], [have_crypto_gnutls=yes], [have_crypto_gnutls=no]) +PKG_CHECK_MODULES(NSS, [nss], [have_crypto_nss=yes], [have_crypto_nss=yes]) +if test "${have_crypto_nss}" = "yes"; then + # Work around a pkg-config bug (fdo #29801) where exists != usable + FOO=`$PKG_CONFIG --cflags --libs nss` + if test x"$?" != "x0"; then + have_crypto_nss=no + fi +fi +AM_CONDITIONAL(HAVE_CRYPTO_GNUTLS, test "${have_crypto_gnutls}" = 'yes') +AM_CONDITIONAL(HAVE_CRYPTO_NSS, test "${have_crypto_nss}" = 'yes') +if test "${have_crypto_gnutls}" = 'yes'; then + AC_DEFINE(HAVE_CRYPTO_GNUTLS, 1, [Define if you have gnutls support]) +else + AC_DEFINE(HAVE_CRYPTO_GNUTLS, 0, [Define if you have gnutls support]) +fi +if test "${have_crypto_nss}" = 'yes'; then + AC_DEFINE(HAVE_CRYPTO_NSS, 1, [Define if you have nss support]) +else + AC_DEFINE(HAVE_CRYPTO_NSS, 0, [Define if you have nss support]) +fi + AC_ARG_WITH(crypto, AS_HELP_STRING([--with-crypto=nss|gnutls], [Cryptography library to use for certificate and key operations]), with_crypto=$withval, with_crypto=nss) if test "$with_crypto" = 'nss'; then - PKG_CHECK_MODULES(NSS, [nss]) - - # Work around a pkg-config bug (fdo #29801) where exists != usable - FOO=`$PKG_CONFIG --cflags --libs nss` - if test x"$?" != "x0"; then - AC_MSG_ERROR([No usable NSS found]) + if test "${have_crypto_nss}" != "yes"; then + AC_MSG_ERROR([No usable NSS found for --with-crypto=nss]) fi elif test "$with_crypto" = 'gnutls'; then - PKG_CHECK_MODULES(GNUTLS, [gnutls >= 2.12]) + if test "${have_crypto_gnutls}" != "yes"; then + AC_MSG_ERROR([No usable gnutls found for --with-crypto=gnutls]) + fi else AC_MSG_ERROR([Please choose either 'nss' or 'gnutls' for certificate and crypto operations]) fi @@ -1385,7 +1405,7 @@ echo " code coverage: $enable_code_coverage" echo " LTO: $enable_lto" echo " linker garbage collection: $enable_ld_gc" echo " JSON validation for libnm: $enable_json_validation" -echo " crypto: $with_crypto" +echo " crypto: $with_crypto (have-gnutls: $have_crypto_gnutls, have-nss: $have_crypto_nss)" echo " sanitizers: $sanitizers" echo " Mozilla Public Suffix List: $with_libpsl" echo diff --git a/libnm-core/meson.build b/libnm-core/meson.build index 5596ab5716..eb6fcce94b 100644 --- a/libnm-core/meson.build +++ b/libnm-core/meson.build @@ -108,7 +108,6 @@ libnm_core_settings_sources = files( libnm_core_sources = libnm_core_settings_sources + files( 'nm-crypto.c', - 'nm-crypto-' + crypto + '.c', 'nm-connection.c', 'nm-dbus-utils.c', 'nm-errors.c', @@ -136,7 +135,6 @@ libnm_core_enum = gnome.mkenums( ) deps = [ - crypto_dep, dl_dep, libudev_dep, shared_dep, @@ -154,6 +152,32 @@ if enable_json_validation deps += jansson_dep endif +if (crypto_gnutls_dep.found()) + libnm_crypto_gnutls = static_library( + 'nm-crypto-gnutls', + sources: [ 'nm-crypto-gnutls.c' ], + dependencies: deps + [ crypto_gnutls_dep ], + c_args: cflags + ) +endif + +if (crypto_nss_dep.found()) + libnm_crypto_nss = static_library( + 'nm-crypto-nss', + sources: [ 'nm-crypto-nss.c' ], + dependencies: deps + [ crypto_nss_dep ], + c_args: cflags + ) +endif + +if crypto == 'gnutls' + libnm_crypto = libnm_crypto_gnutls +elif crypto == 'nss' + libnm_crypto = libnm_crypto_nss +else + error('bug') +endif + libnm_core_sources_all = libnm_core_sources libnm_core_sources_all += libnm_core_enum libnm_core_sources_all += shared_nm_meta_setting_c @@ -165,6 +189,7 @@ libnm_core = static_library( 'nm-core', sources: libnm_core_sources_all, dependencies: deps, + link_with: libnm_crypto, c_args: cflags ) diff --git a/meson.build b/meson.build index 4695efb2a2..44a7ee7ff5 100644 --- a/meson.build +++ b/meson.build @@ -470,12 +470,16 @@ if enable_polkit_agent endif config_h.set10('WITH_POLKIT_AGENT', enable_polkit_agent) -# crypto +crypto_gnutls_dep = dependency('gnutls', version: '>= 2.12', required: false) +crypto_nss_dep = dependency('nss', required: false) + crypto = get_option('crypto') if crypto == 'nss' - crypto_dep = dependency('nss') + assert(crypto_nss_dep.found(), 'Requires gnutls crypto support') +elif crypto == 'gnutls' + assert(crypto_gnutls_dep.found(), 'Requires gnutls crypto support') else - crypto_dep = dependency('gnutls', version: '>= 2.12') + error('bug') endif dbus_conf_dir = get_option('dbus_conf_dir') @@ -1022,7 +1026,8 @@ output += '\n' output += ' code coverage: ' + get_option('b_coverage').to_string() + '\n' output += ' LTO: ' + get_option('b_lto').to_string() + '\n' output += ' Linker garbage collection: ' + enable_ld_gc.to_string() + '\n' -output += ' JSON validation for libnm: ' + enable_json_validation.to_string () + '\n' +output += ' JSON validation for libnm: ' + enable_json_validation.to_string() + '\n' +output += ' crypto: ' + crypto + ' (have-gnutls: ' + crypto_gnutls_dep.found().to_string() + ', have-nss: ' + crypto_nss_dep.found().to_string() + ')\n' output += ' sanitizers: ' + get_option('b_sanitize') + '\n' output += ' Mozilla Public Suffix List: ' + enable_libpsl.to_string() + '\n' message(output) diff --git a/src/settings/plugins/ifcfg-rh/meson.build b/src/settings/plugins/ifcfg-rh/meson.build index 00892c22f4..5d9689a52c 100644 --- a/src/settings/plugins/ifcfg-rh/meson.build +++ b/src/settings/plugins/ifcfg-rh/meson.build @@ -27,7 +27,6 @@ sources = files( ) deps = [ - crypto_dep, nm_dep ] From 6b8280f6a903880a6dce3b1a41f962fac84e482f Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 31 Aug 2018 07:59:24 +0200 Subject: [PATCH 26/61] build/travis: build both against crypto "gnutls" and "nss" We already do matrix-builds with autotools|meson and gcc|clang. Make the selected crypto backend depending on the compiler, so that we get more coverage. --- .travis.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index bba6844210..c5108aee11 100644 --- a/.travis.yml +++ b/.travis.yml @@ -79,6 +79,11 @@ before_install: script: - | if test "$BUILD_TYPE" == 'meson'; then + if [ "$CC" == gcc ]; then + CRYPTO=nss + else + CRYPTO=gnutls + fi && meson build \ \ -D ld_gc=false \ @@ -91,6 +96,7 @@ script: -D vapi=false \ -D introspection=false \ -D qt=false \ + -D crypto=$CRYPTO \ \ -D docs=true \ \ @@ -103,11 +109,16 @@ script: fi - | if test "$BUILD_TYPE" == 'autotools'; then + if [ "$CC" == gcc ]; then + CRYPTO=gnutls + else + CRYPTO=nss + fi && git clean -fdx && NOCONFIGURE=1 ./autogen.sh && mkdir ./build && pushd ./build && - ../configure --prefix="$PWD/INST" --with-systemd-logind=no --enable-more-warnings=no --enable-ifcfg-rh --enable-config-plugin-ibft --enable-ifupdown --enable-tests && + ../configure --prefix="$PWD/INST" --with-systemd-logind=no --enable-more-warnings=no --enable-ifcfg-rh --enable-config-plugin-ibft --enable-ifupdown --enable-tests --with-crypto=$CRYPTO && make -j4 && if [ "$CC" == gcc ]; then sudo locale-gen pl_PL.UTF-8 && From 105254a15b3310a70ab43e5298cd3ebfebc47b51 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 30 Aug 2018 08:18:44 +0200 Subject: [PATCH 27/61] libnm/crypto: rework _nm_crypto_verify_cert() to return boolean Rename _nm_crypto_verify_cert() to _nm_crypto_verify_x509(). Also, don't let it return a NMCryptoFileFormat result. This function only checks for a particular format, hence it should only return true/false. Also, fix setting error output argument when the function fails. --- libnm-core/nm-crypto-gnutls.c | 14 +++++------ libnm-core/nm-crypto-impl.h | 6 ++--- libnm-core/nm-crypto-nss.c | 10 ++++---- libnm-core/nm-crypto.c | 46 +++++++++++++++++++++++++---------- 4 files changed, 48 insertions(+), 28 deletions(-) diff --git a/libnm-core/nm-crypto-gnutls.c b/libnm-core/nm-crypto-gnutls.c index 86535ee4b1..1cfb628bb2 100644 --- a/libnm-core/nm-crypto-gnutls.c +++ b/libnm-core/nm-crypto-gnutls.c @@ -268,8 +268,8 @@ out: return output; } -NMCryptoFileFormat -_nm_crypto_verify_cert (const unsigned char *data, +gboolean +_nm_crypto_verify_x509 (const unsigned char *data, gsize len, GError **error) { @@ -278,7 +278,7 @@ _nm_crypto_verify_cert (const unsigned char *data, int err; if (!_nm_crypto_init (error)) - return NM_CRYPTO_FILE_FORMAT_UNKNOWN; + return FALSE; err = gnutls_x509_crt_init (&der); if (err < 0) { @@ -286,7 +286,7 @@ _nm_crypto_verify_cert (const unsigned char *data, NM_CRYPTO_ERROR_INVALID_DATA, _("Error initializing certificate data: %s"), gnutls_strerror (err)); - return NM_CRYPTO_FILE_FORMAT_UNKNOWN; + return FALSE; } /* Try DER first */ @@ -295,20 +295,20 @@ _nm_crypto_verify_cert (const unsigned char *data, err = gnutls_x509_crt_import (der, &dt, GNUTLS_X509_FMT_DER); if (err == GNUTLS_E_SUCCESS) { gnutls_x509_crt_deinit (der); - return NM_CRYPTO_FILE_FORMAT_X509; + return TRUE; } /* And PEM next */ err = gnutls_x509_crt_import (der, &dt, GNUTLS_X509_FMT_PEM); gnutls_x509_crt_deinit (der); if (err == GNUTLS_E_SUCCESS) - return NM_CRYPTO_FILE_FORMAT_X509; + return TRUE; g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("Couldn't decode certificate: %s"), gnutls_strerror (err)); - return NM_CRYPTO_FILE_FORMAT_UNKNOWN; + return FALSE; } gboolean diff --git a/libnm-core/nm-crypto-impl.h b/libnm-core/nm-crypto-impl.h index 93186cb6cf..50b9ad4f52 100644 --- a/libnm-core/nm-crypto-impl.h +++ b/libnm-core/nm-crypto-impl.h @@ -55,9 +55,9 @@ char *_nm_crypto_decrypt (const char *cipher, gsize *out_len, GError **error); -NMCryptoFileFormat _nm_crypto_verify_cert (const guint8 *data, - gsize len, - GError **error); +gboolean _nm_crypto_verify_x509 (const guint8 *data, + gsize len, + GError **error); gboolean _nm_crypto_verify_pkcs12 (const guint8 *data, gsize data_len, diff --git a/libnm-core/nm-crypto-nss.c b/libnm-core/nm-crypto-nss.c index df0fa58ea9..6e1b9e176b 100644 --- a/libnm-core/nm-crypto-nss.c +++ b/libnm-core/nm-crypto-nss.c @@ -370,15 +370,15 @@ out: return (char *) output; } -NMCryptoFileFormat -_nm_crypto_verify_cert (const unsigned char *data, +gboolean +_nm_crypto_verify_x509 (const unsigned char *data, gsize len, GError **error) { CERTCertificate *cert; if (!_nm_crypto_init (error)) - return NM_CRYPTO_FILE_FORMAT_UNKNOWN; + return FALSE; /* Try DER/PEM first */ cert = CERT_DecodeCertFromPackage ((char *) data, len); @@ -387,11 +387,11 @@ _nm_crypto_verify_cert (const unsigned char *data, NM_CRYPTO_ERROR_INVALID_DATA, _("Couldn't decode certificate: %d"), PORT_GetError()); - return NM_CRYPTO_FILE_FORMAT_UNKNOWN; + return FALSE; } CERT_DestroyCertificate (cert); - return NM_CRYPTO_FILE_FORMAT_X509; + return TRUE; } gboolean diff --git a/libnm-core/nm-crypto.c b/libnm-core/nm-crypto.c index fcef7960cb..2f222aa3f0 100644 --- a/libnm-core/nm-crypto.c +++ b/libnm-core/nm-crypto.c @@ -654,13 +654,19 @@ nm_crypto_load_and_verify_certificate (const char *file, g_return_val_if_fail (file != NULL, NULL); g_return_val_if_fail (out_file_format != NULL, NULL); - *out_file_format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; - if (!_nm_crypto_init (error)) - return NULL; + goto out; if (!file_read_contents (file, &contents, error)) - return NULL; + goto out; + + if (contents.len == 0) { + g_set_error (error, + NM_CRYPTO_ERROR, + NM_CRYPTO_ERROR_INVALID_DATA, + _("Certificate file is empty")); + goto out; + } /* Check for PKCS#12 */ if (nm_crypto_is_pkcs12_data (contents.bin, contents.len, NULL)) { @@ -670,20 +676,29 @@ nm_crypto_load_and_verify_certificate (const char *file, /* Check for plain DER format */ if (contents.len > 2 && contents.bin[0] == 0x30 && contents.bin[1] == 0x82) { - *out_file_format = _nm_crypto_verify_cert (contents.bin, contents.len, error); + if (_nm_crypto_verify_x509 (contents.bin, contents.len, NULL)) { + *out_file_format = NM_CRYPTO_FILE_FORMAT_X509; + return to_gbyte_array_mem (contents.bin, contents.len); + } } else { nm_auto_clear_secret_ptr NMSecretPtr pem_cert = { 0 }; - if (!extract_pem_cert_data (contents.bin, contents.len, &pem_cert, error)) - return NULL; - - *out_file_format = _nm_crypto_verify_cert (pem_cert.bin, pem_cert.len, error); + if (extract_pem_cert_data (contents.bin, contents.len, &pem_cert, NULL)) { + if (_nm_crypto_verify_x509 (pem_cert.bin, pem_cert.len, NULL)) { + *out_file_format = NM_CRYPTO_FILE_FORMAT_X509; + return to_gbyte_array_mem (contents.bin, contents.len); + } + } } - if (*out_file_format != NM_CRYPTO_FILE_FORMAT_X509) - return NULL; + g_set_error (error, + NM_CRYPTO_ERROR, + NM_CRYPTO_ERROR_INVALID_DATA, + _("Failed to recognize certificate")); - return to_gbyte_array_mem (contents.bin, contents.len); +out: + *out_file_format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; + return NULL; } gboolean @@ -694,8 +709,13 @@ nm_crypto_is_pkcs12_data (const guint8 *data, GError *local = NULL; gboolean success; - if (!data_len) + if (!data_len) { + g_set_error (error, + NM_CRYPTO_ERROR, + NM_CRYPTO_ERROR_INVALID_DATA, + _("Certificate file is empty")); return FALSE; + } g_return_val_if_fail (data != NULL, FALSE); From 4c996da5bcdb4c82b5398e8310194cbe3d28d157 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 30 Aug 2018 11:27:04 +0200 Subject: [PATCH 28/61] libnm/crypto: use nm_explicit_bzero() instead of plain memset() --- libnm-core/nm-crypto-gnutls.c | 9 ++++----- libnm-core/nm-crypto-nss.c | 10 +++++----- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/libnm-core/nm-crypto-gnutls.c b/libnm-core/nm-crypto-gnutls.c index 1cfb628bb2..1cf48aeec9 100644 --- a/libnm-core/nm-crypto-gnutls.c +++ b/libnm-core/nm-crypto-gnutls.c @@ -30,6 +30,7 @@ #include #include +#include "nm-utils/nm-secret-utils.h" #include "nm-errors.h" #define SALT_LEN 8 @@ -160,8 +161,7 @@ _nm_crypto_decrypt (const char *cipher, out: if (!success) { if (output) { - /* Don't expose key material */ - memset (output, 0, data_len); + nm_explicit_bzero (output, data_len); g_free (output); output = NULL; } @@ -251,15 +251,14 @@ _nm_crypto_encrypt (const char *cipher, out: if (padded_buf) { - memset (padded_buf, 0, padded_buf_len); + nm_explicit_bzero (padded_buf, padded_buf_len); g_free (padded_buf); padded_buf = NULL; } if (!success) { if (output) { - /* Don't expose key material */ - memset (output, 0, output_len); + nm_explicit_bzero (output, output_len); g_free (output); output = NULL; } diff --git a/libnm-core/nm-crypto-nss.c b/libnm-core/nm-crypto-nss.c index 6e1b9e176b..fb55b95896 100644 --- a/libnm-core/nm-crypto-nss.c +++ b/libnm-core/nm-crypto-nss.c @@ -35,6 +35,7 @@ #include #include +#include "nm-utils/nm-secret-utils.h" #include "nm-errors.h" static gboolean initialized = FALSE; @@ -233,8 +234,7 @@ out: if (!success) { if (output) { - /* Don't expose key material */ - memset (output, 0, data_len); + nm_explicit_bzero (output, data_len); g_free (output); output = NULL; } @@ -359,11 +359,11 @@ out: if (slot) PK11_FreeSlot (slot); - memset (padded_buf, 0, padded_buf_len); + nm_explicit_bzero (padded_buf, padded_buf_len); g_free (padded_buf); if (!success) { - memset (output, 0, output_len); + nm_explicit_bzero (output, output_len); g_free (output); output = NULL; } @@ -435,7 +435,7 @@ _nm_crypto_verify_pkcs12 (const guint8 *data, memcpy (pw.data, ucs2_password, ucs2_chars); pw.len = ucs2_chars + 2; /* include terminating NULL */ - memset (ucs2_password, 0, ucs2_chars); + nm_explicit_bzero (ucs2_password, ucs2_chars); g_free (ucs2_password); #ifndef WORDS_BIGENDIAN From 896a47da53d03f98d11c554789411c0d83afad72 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 30 Aug 2018 10:13:40 +0200 Subject: [PATCH 29/61] libnm/crypto: refactor nm_crypto_load_and_verify_certificate() and return GBytes The GBytes has a suitable cleanup function, which zeros the certificate from memory. Also, all callers that require the certificate, actually later converted it into a GBytes anyway. This way, they can re-used the same instance (avoiding an additionaly copying of the data), and they will properly clear the memory when freed. --- libnm-core/nm-crypto.c | 27 ++++++---- libnm-core/nm-crypto.h | 7 +-- libnm-core/nm-setting-8021x.c | 92 ++++++++++++++++------------------ libnm-core/nm-utils.c | 9 ++-- libnm-core/tests/test-crypto.c | 9 ++-- 5 files changed, 71 insertions(+), 73 deletions(-) diff --git a/libnm-core/nm-crypto.c b/libnm-core/nm-crypto.c index 2f222aa3f0..7f84614cc7 100644 --- a/libnm-core/nm-crypto.c +++ b/libnm-core/nm-crypto.c @@ -644,15 +644,16 @@ extract_pem_cert_data (const guint8 *contents, return TRUE; } -GByteArray * +gboolean nm_crypto_load_and_verify_certificate (const char *file, NMCryptoFileFormat *out_file_format, + GBytes **out_certificate, GError **error) { nm_auto_clear_secret_ptr NMSecretPtr contents = { 0 }; - g_return_val_if_fail (file != NULL, NULL); - g_return_val_if_fail (out_file_format != NULL, NULL); + g_return_val_if_fail (file, FALSE); + nm_assert (!error || !*error); if (!_nm_crypto_init (error)) goto out; @@ -670,23 +671,26 @@ nm_crypto_load_and_verify_certificate (const char *file, /* Check for PKCS#12 */ if (nm_crypto_is_pkcs12_data (contents.bin, contents.len, NULL)) { - *out_file_format = NM_CRYPTO_FILE_FORMAT_PKCS12; - return to_gbyte_array_mem (contents.bin, contents.len); + NM_SET_OUT (out_file_format, NM_CRYPTO_FILE_FORMAT_PKCS12); + NM_SET_OUT (out_certificate, nm_secret_copy_to_gbytes (contents.bin, contents.len)); + return TRUE; } /* Check for plain DER format */ if (contents.len > 2 && contents.bin[0] == 0x30 && contents.bin[1] == 0x82) { if (_nm_crypto_verify_x509 (contents.bin, contents.len, NULL)) { - *out_file_format = NM_CRYPTO_FILE_FORMAT_X509; - return to_gbyte_array_mem (contents.bin, contents.len); + NM_SET_OUT (out_file_format, NM_CRYPTO_FILE_FORMAT_X509); + NM_SET_OUT (out_certificate, nm_secret_copy_to_gbytes (contents.bin, contents.len)); + return TRUE; } } else { nm_auto_clear_secret_ptr NMSecretPtr pem_cert = { 0 }; if (extract_pem_cert_data (contents.bin, contents.len, &pem_cert, NULL)) { if (_nm_crypto_verify_x509 (pem_cert.bin, pem_cert.len, NULL)) { - *out_file_format = NM_CRYPTO_FILE_FORMAT_X509; - return to_gbyte_array_mem (contents.bin, contents.len); + NM_SET_OUT (out_file_format, NM_CRYPTO_FILE_FORMAT_X509); + NM_SET_OUT (out_certificate, nm_secret_copy_to_gbytes (contents.bin, contents.len)); + return TRUE; } } } @@ -697,8 +701,9 @@ nm_crypto_load_and_verify_certificate (const char *file, _("Failed to recognize certificate")); out: - *out_file_format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; - return NULL; + NM_SET_OUT (out_file_format, NM_CRYPTO_FILE_FORMAT_UNKNOWN); + NM_SET_OUT (out_certificate, NULL); + return FALSE; } gboolean diff --git a/libnm-core/nm-crypto.h b/libnm-core/nm-crypto.h index 3c073a9916..669f9660eb 100644 --- a/libnm-core/nm-crypto.h +++ b/libnm-core/nm-crypto.h @@ -60,9 +60,10 @@ GByteArray *nmtst_crypto_decrypt_openssl_private_key (const char *file, NMCryptoKeyType *out_key_type, GError **error); -GByteArray *nm_crypto_load_and_verify_certificate (const char *file, - NMCryptoFileFormat *out_file_format, - GError **error); +gboolean nm_crypto_load_and_verify_certificate (const char *file, + NMCryptoFileFormat *out_file_format, + GBytes **out_certificat, + GError **error); gboolean nm_crypto_is_pkcs12_file (const char *file, GError **error); diff --git a/libnm-core/nm-setting-8021x.c b/libnm-core/nm-setting-8021x.c index 30f4537e9c..4fae2c64e6 100644 --- a/libnm-core/nm-setting-8021x.c +++ b/libnm-core/nm-setting-8021x.c @@ -520,33 +520,41 @@ nm_setting_802_1x_check_cert_scheme (gconstpointer pdata, gsize length, GError * return scheme; } -static GByteArray * +static GBytes * load_and_verify_certificate (const char *cert_path, NMSetting8021xCKScheme scheme, NMCryptoFileFormat *out_file_format, GError **error) { NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; - GByteArray *array; + gs_unref_bytes GBytes *cert = NULL; - array = nm_crypto_load_and_verify_certificate (cert_path, &format, error); + if (!nm_crypto_load_and_verify_certificate (cert_path, &format, &cert, error)) { + NM_SET_OUT (out_file_format, NM_CRYPTO_FILE_FORMAT_UNKNOWN); + return NULL; + } - if (!array || !array->len || format == NM_CRYPTO_FILE_FORMAT_UNKNOWN) { - /* the array is empty or the format is already unknown. */ - format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; - } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { + nm_assert (format != NM_CRYPTO_FILE_FORMAT_UNKNOWN); + nm_assert (cert); + + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { + const guint8 *bin; + gsize len; + + bin = g_bytes_get_data (cert, &len); /* If we load the file as blob, we must ensure that the binary data does not * start with file://. NMSetting8021x cannot represent blobs that start with * file://. * If that's the case, coerce the format to UNKNOWN. The callers will take care * of that and not set the blob. */ - if (nm_setting_802_1x_check_cert_scheme (array->data, array->len, NULL) != NM_SETTING_802_1X_CK_SCHEME_BLOB) - format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; + if (nm_setting_802_1x_check_cert_scheme (bin, len, NULL) != NM_SETTING_802_1X_CK_SCHEME_BLOB) { + NM_SET_OUT (out_file_format, NM_CRYPTO_FILE_FORMAT_UNKNOWN); + return NULL; + } } - if (out_file_format) - *out_file_format = format; - return array; + NM_SET_OUT (out_file_format, format); + return g_steal_pointer (&cert); } /** @@ -700,7 +708,7 @@ nm_setting_802_1x_set_ca_cert (NMSetting8021x *setting, { NMSetting8021xPrivate *priv; NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; - GByteArray *data; + gs_unref_bytes GBytes *cert = NULL; g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE); @@ -730,17 +738,16 @@ nm_setting_802_1x_set_ca_cert (NMSetting8021x *setting, return TRUE; } - data = load_and_verify_certificate (value, scheme, &format, error); - if (data) { + cert = load_and_verify_certificate (value, scheme, &format, error); + if (cert) { /* wpa_supplicant can only use raw x509 CA certs */ if (format == NM_CRYPTO_FILE_FORMAT_X509) { if (out_format) *out_format = NM_SETTING_802_1X_CK_FORMAT_X509; - if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { - priv->ca_cert = g_byte_array_free_to_bytes (data); - data = NULL; - } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) + priv->ca_cert = g_bytes_ref (cert); + else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) priv->ca_cert = path_to_scheme_value (value); else g_assert_not_reached (); @@ -751,8 +758,6 @@ nm_setting_802_1x_set_ca_cert (NMSetting8021x *setting, _("CA certificate must be in X.509 format")); g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CA_CERT); } - if (data) - g_byte_array_unref (data); } g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_CA_CERT); @@ -1104,7 +1109,7 @@ nm_setting_802_1x_set_client_cert (NMSetting8021x *setting, { NMSetting8021xPrivate *priv; NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; - GByteArray *data; + gs_unref_bytes GBytes *cert = NULL; g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE); @@ -1134,8 +1139,8 @@ nm_setting_802_1x_set_client_cert (NMSetting8021x *setting, return TRUE; } - data = load_and_verify_certificate (value, scheme, &format, error); - if (data) { + cert = load_and_verify_certificate (value, scheme, &format, error); + if (cert) { gboolean valid = FALSE; switch (format) { @@ -1159,16 +1164,13 @@ nm_setting_802_1x_set_client_cert (NMSetting8021x *setting, } if (valid) { - if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { - priv->client_cert = g_byte_array_free_to_bytes (data); - data = NULL; - } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) + priv->client_cert = g_bytes_ref (cert); + else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) priv->client_cert = path_to_scheme_value (value); else g_assert_not_reached (); } - if (data) - g_byte_array_unref (data); } g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_CLIENT_CERT); @@ -1459,7 +1461,7 @@ nm_setting_802_1x_set_phase2_ca_cert (NMSetting8021x *setting, { NMSetting8021xPrivate *priv; NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; - GByteArray *data; + gs_unref_bytes GBytes *cert = NULL; g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE); @@ -1489,17 +1491,16 @@ nm_setting_802_1x_set_phase2_ca_cert (NMSetting8021x *setting, return TRUE; } - data = load_and_verify_certificate (value, scheme, &format, error); - if (data) { + cert = load_and_verify_certificate (value, scheme, &format, error); + if (cert) { /* wpa_supplicant can only use raw x509 CA certs */ if (format == NM_CRYPTO_FILE_FORMAT_X509) { if (out_format) *out_format = NM_SETTING_802_1X_CK_FORMAT_X509; - if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { - priv->phase2_ca_cert = g_byte_array_free_to_bytes (data); - data = NULL; - } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) + priv->phase2_ca_cert = g_bytes_ref (cert); + else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) priv->phase2_ca_cert = path_to_scheme_value (value); else g_assert_not_reached (); @@ -1510,8 +1511,6 @@ nm_setting_802_1x_set_phase2_ca_cert (NMSetting8021x *setting, _("invalid certificate format")); g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_CA_CERT); } - if (data) - g_byte_array_unref (data); } g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_CA_CERT); @@ -1867,7 +1866,7 @@ nm_setting_802_1x_set_phase2_client_cert (NMSetting8021x *setting, { NMSetting8021xPrivate *priv; NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; - GByteArray *data; + gs_unref_bytes GBytes *cert = NULL; g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE); @@ -1897,8 +1896,8 @@ nm_setting_802_1x_set_phase2_client_cert (NMSetting8021x *setting, return TRUE; } - data = load_and_verify_certificate (value, scheme, &format, error); - if (data) { + cert = load_and_verify_certificate (value, scheme, &format, error); + if (cert) { gboolean valid = FALSE; /* wpa_supplicant can only use raw x509 CA certs */ @@ -1923,16 +1922,13 @@ nm_setting_802_1x_set_phase2_client_cert (NMSetting8021x *setting, } if (valid) { - if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { - priv->phase2_client_cert = g_byte_array_free_to_bytes (data); - data = NULL; - } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) + priv->phase2_client_cert = g_bytes_ref (cert); + else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) priv->phase2_client_cert = path_to_scheme_value (value); else g_assert_not_reached (); } - if (data) - g_byte_array_unref (data); } g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_CLIENT_CERT); diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 21de5624a8..1542625c01 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -3060,18 +3060,15 @@ gboolean nm_utils_file_is_certificate (const char *filename) { const char *extensions[] = { ".der", ".pem", ".crt", ".cer", NULL }; - NMCryptoFileFormat file_format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; - GByteArray *cert; + NMCryptoFileFormat file_format; g_return_val_if_fail (filename != NULL, FALSE); if (!file_has_extension (filename, extensions)) return FALSE; - cert = nm_crypto_load_and_verify_certificate (filename, &file_format, NULL); - if (cert) - g_byte_array_unref (cert); - + if (!nm_crypto_load_and_verify_certificate (filename, &file_format, NULL, NULL)) + return FALSE; return file_format = NM_CRYPTO_FILE_FORMAT_X509; } diff --git a/libnm-core/tests/test-crypto.c b/libnm-core/tests/test-crypto.c index 937fc6c3b0..26ef08f56c 100644 --- a/libnm-core/tests/test-crypto.c +++ b/libnm-core/tests/test-crypto.c @@ -99,18 +99,17 @@ static void test_cert (gconstpointer test_data) { gs_free char *path = NULL; - GByteArray *array; + gs_unref_bytes GBytes *cert = NULL; NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; GError *error = NULL; + gboolean success; path = g_build_filename (TEST_CERT_DIR, (const char *) test_data, NULL); - array = nm_crypto_load_and_verify_certificate (path, &format, &error); - g_assert_no_error (error); + success = nm_crypto_load_and_verify_certificate (path, &format, &cert, &error); + nmtst_assert_success (success, error); g_assert_cmpint (format, ==, NM_CRYPTO_FILE_FORMAT_X509); - g_byte_array_free (array, TRUE); - g_assert (nm_utils_file_is_certificate (path)); } From 639e6de6e37d5bf150c82273d02e0a898dd75d51 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 30 Aug 2018 10:13:40 +0200 Subject: [PATCH 30/61] libnm/crypto: refactor crypto test functions to return GBytes Using GBytes consistently simplifies the code. Also use it for the test related functions. --- libnm-core/nm-crypto.c | 21 +++-------- libnm-core/nm-crypto.h | 18 +++++----- libnm-core/tests/test-crypto.c | 65 +++++++++++++--------------------- 3 files changed, 37 insertions(+), 67 deletions(-) diff --git a/libnm-core/nm-crypto.c b/libnm-core/nm-crypto.c index 7f84614cc7..ab52c94d69 100644 --- a/libnm-core/nm-crypto.c +++ b/libnm-core/nm-crypto.c @@ -52,19 +52,6 @@ /*****************************************************************************/ -static GByteArray * -to_gbyte_array_mem (gconstpointer mem, gsize len) -{ - GByteArray *arr; - - arr = g_byte_array_sized_new (len); - if (len > 0) - g_byte_array_append (arr, mem, len); - return arr; -} - -/*****************************************************************************/ - static gboolean find_tag (const char *tag, const guint8 *data, @@ -519,7 +506,7 @@ decrypt_key (const char *cipher, return TRUE; } -GByteArray * +GBytes * nmtst_crypto_decrypt_openssl_private_key_data (const guint8 *data, gsize data_len, const char *password, @@ -567,16 +554,16 @@ nmtst_crypto_decrypt_openssl_private_key_data (const guint8 *data, error)) return NULL; - return to_gbyte_array_mem (parsed2.bin, parsed2.len); + return nm_secret_copy_to_gbytes (parsed2.bin, parsed2.len); } if (cipher || iv) return NULL; - return to_gbyte_array_mem (parsed.bin, parsed.len); + return nm_secret_copy_to_gbytes (parsed.bin, parsed.len); } -GByteArray * +GBytes * nmtst_crypto_decrypt_openssl_private_key (const char *file, const char *password, NMCryptoKeyType *out_key_type, diff --git a/libnm-core/nm-crypto.h b/libnm-core/nm-crypto.h index 669f9660eb..88fec819e7 100644 --- a/libnm-core/nm-crypto.h +++ b/libnm-core/nm-crypto.h @@ -49,16 +49,16 @@ typedef enum { NM_CRYPTO_FILE_FORMAT_PKCS12 } NMCryptoFileFormat; -GByteArray *nmtst_crypto_decrypt_openssl_private_key_data (const guint8 *data, - gsize data_len, - const char *password, - NMCryptoKeyType *out_key_type, - GError **error); +GBytes *nmtst_crypto_decrypt_openssl_private_key_data (const guint8 *data, + gsize data_len, + const char *password, + NMCryptoKeyType *out_key_type, + GError **error); -GByteArray *nmtst_crypto_decrypt_openssl_private_key (const char *file, - const char *password, - NMCryptoKeyType *out_key_type, - GError **error); +GBytes *nmtst_crypto_decrypt_openssl_private_key (const char *file, + const char *password, + NMCryptoKeyType *out_key_type, + GError **error); gboolean nm_crypto_load_and_verify_certificate (const char *file, NMCryptoFileFormat *out_file_format, diff --git a/libnm-core/tests/test-crypto.c b/libnm-core/tests/test-crypto.c index 26ef08f56c..93970f3adc 100644 --- a/libnm-core/tests/test-crypto.c +++ b/libnm-core/tests/test-crypto.c @@ -113,22 +113,6 @@ test_cert (gconstpointer test_data) g_assert (nm_utils_file_is_certificate (path)); } -static GByteArray * -file_to_byte_array (const char *filename) -{ - char *contents; - GByteArray *array = NULL; - gsize length = 0; - - if (g_file_get_contents (filename, &contents, &length, NULL)) { - array = g_byte_array_sized_new (length); - g_byte_array_append (array, (guint8 *) contents, length); - g_assert (array->len == length); - g_free (contents); - } - return array; -} - static void test_load_private_key (const char *path, const char *password, @@ -137,7 +121,7 @@ test_load_private_key (const char *path, { NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN; gboolean is_encrypted = FALSE; - GByteArray *array, *decrypted; + gs_unref_bytes GBytes *array = NULL; GError *error = NULL; g_assert (nm_utils_file_is_private_key (path, &is_encrypted)); @@ -163,16 +147,14 @@ test_load_private_key (const char *path, g_assert (array != NULL); if (decrypted_path) { + gs_free char *contents = NULL; + gsize length; + /* Compare the crypto decrypted key against a known-good decryption */ - decrypted = file_to_byte_array (decrypted_path); - g_assert (decrypted != NULL); - g_assert (decrypted->len == array->len); - g_assert (memcmp (decrypted->data, array->data, array->len) == 0); - - g_byte_array_free (decrypted, TRUE); + if (!g_file_get_contents (decrypted_path, &contents, &length, NULL)) + g_assert_not_reached (); + g_assert (nm_utils_gbytes_equal_mem (array, contents, length)); } - - g_byte_array_free (array, TRUE); } static void @@ -260,34 +242,35 @@ test_encrypt_private_key (const char *path, const char *password) { NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN; - GByteArray *array, *encrypted, *re_decrypted; + gs_unref_bytes GBytes *array = NULL; + nm_auto_unref_bytearray GByteArray *encrypted = NULL; + gs_unref_bytes GBytes *re_decrypted = NULL; GError *error = NULL; array = nmtst_crypto_decrypt_openssl_private_key (path, password, &key_type, &error); - g_assert_no_error (error); - g_assert (array != NULL); + nmtst_assert_success (array, error); g_assert_cmpint (key_type, ==, NM_CRYPTO_KEY_TYPE_RSA); /* Now re-encrypt the private key */ - encrypted = nm_utils_rsa_key_encrypt (array->data, array->len, password, NULL, &error); - g_assert_no_error (error); - g_assert (encrypted != NULL); + encrypted = nm_utils_rsa_key_encrypt (g_bytes_get_data (array, NULL), + g_bytes_get_size (array), + password, + NULL, + &error); + nmtst_assert_success (encrypted, error); /* Then re-decrypt the private key */ key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN; - re_decrypted = nmtst_crypto_decrypt_openssl_private_key_data (encrypted->data, encrypted->len, - password, &key_type, &error); - g_assert_no_error (error); - g_assert (re_decrypted != NULL); + re_decrypted = nmtst_crypto_decrypt_openssl_private_key_data (encrypted->data, + encrypted->len, + password, + &key_type, + &error); + nmtst_assert_success (re_decrypted, error); g_assert_cmpint (key_type, ==, NM_CRYPTO_KEY_TYPE_RSA); /* Compare the original decrypted key with the re-decrypted key */ - g_assert_cmpint (array->len, ==, re_decrypted->len); - g_assert (!memcmp (array->data, re_decrypted->data, array->len)); - - g_byte_array_free (re_decrypted, TRUE); - g_byte_array_free (encrypted, TRUE); - g_byte_array_free (array, TRUE); + g_assert (g_bytes_equal (array, re_decrypted)); } static void From 9153d9e2ea2a696221c4b160625c3b7f7f8753a7 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 30 Aug 2018 12:32:29 +0200 Subject: [PATCH 31/61] libnm-core/trivial: rename testing related functions in crypto code In nm-crypto.c we have functions that are only called from tests. Maybe these functions should move away from libnm-core to the test. Leave it, but at least rename them to make it clear that these functions are not relevant for libnm's actual usage. For a reviewer that makes a big difference as crypto functions in libnm have a significantly higher requirement for quality. There is nothing new here. We already have other *nmtst* functions beside our regular code. The concention is, that functions that are only for testing are named explicitly ("nmtst"), and that they can only be called by test functions themselves. --- libnm-core/nm-crypto.c | 40 ++++++++++++++++++++-------------------- libnm-core/nm-crypto.h | 26 ++++++++++++++++---------- 2 files changed, 36 insertions(+), 30 deletions(-) diff --git a/libnm-core/nm-crypto.c b/libnm-core/nm-crypto.c index ab52c94d69..c529cf458d 100644 --- a/libnm-core/nm-crypto.c +++ b/libnm-core/nm-crypto.c @@ -355,9 +355,9 @@ file_read_contents (const char *filename, * Convert a hex string into bytes. */ static guint8 * -convert_iv (const char *src, - gsize *out_len, - GError **error) +_nmtst_convert_iv (const char *src, + gsize *out_len, + GError **error) { gsize i, num; gs_free guint8 *c = NULL; @@ -451,14 +451,14 @@ nm_crypto_make_des_aes_key (const char *cipher, } static gboolean -decrypt_key (const char *cipher, - int key_type, - const guint8 *data, - gsize data_len, - const char *iv, - const char *password, - NMSecretPtr *parsed, - GError **error) +_nmtst_crypto_decrypt_key (const char *cipher, + int key_type, + const guint8 *data, + gsize data_len, + const char *iv, + const char *password, + NMSecretPtr *parsed, + GError **error) { nm_auto_clear_secret_ptr NMSecretPtr bin_iv = { 0 }; nm_auto_clear_secret_ptr NMSecretPtr key = { 0 }; @@ -471,7 +471,7 @@ decrypt_key (const char *cipher, nm_assert (!parsed->bin); nm_assert (parsed->len == 0); - bin_iv.bin = convert_iv (iv, &bin_iv.len, error); + bin_iv.bin = _nmtst_convert_iv (iv, &bin_iv.len, error); if (!bin_iv.bin) return FALSE; @@ -544,14 +544,14 @@ nmtst_crypto_decrypt_openssl_private_key_data (const guint8 *data, return NULL; } - if (!decrypt_key (cipher, - key_type, - parsed.bin, - parsed.len, - iv, - password, - &parsed2, - error)) + if (!_nmtst_crypto_decrypt_key (cipher, + key_type, + parsed.bin, + parsed.len, + iv, + password, + &parsed2, + error)) return NULL; return nm_secret_copy_to_gbytes (parsed2.bin, parsed2.len); diff --git a/libnm-core/nm-crypto.h b/libnm-core/nm-crypto.h index 88fec819e7..2b56f4371b 100644 --- a/libnm-core/nm-crypto.h +++ b/libnm-core/nm-crypto.h @@ -49,16 +49,7 @@ typedef enum { NM_CRYPTO_FILE_FORMAT_PKCS12 } NMCryptoFileFormat; -GBytes *nmtst_crypto_decrypt_openssl_private_key_data (const guint8 *data, - gsize data_len, - const char *password, - NMCryptoKeyType *out_key_type, - GError **error); - -GBytes *nmtst_crypto_decrypt_openssl_private_key (const char *file, - const char *password, - NMCryptoKeyType *out_key_type, - GError **error); +/*****************************************************************************/ gboolean nm_crypto_load_and_verify_certificate (const char *file, NMCryptoFileFormat *out_file_format, @@ -106,4 +97,19 @@ char * nm_crypto_encrypt (const char *cipher, gboolean nm_crypto_randomize (void *buffer, gsize buffer_len, GError **error); +/*****************************************************************************/ + +GBytes *nmtst_crypto_decrypt_openssl_private_key_data (const guint8 *data, + gsize data_len, + const char *password, + NMCryptoKeyType *out_key_type, + GError **error); + +GBytes *nmtst_crypto_decrypt_openssl_private_key (const char *file, + const char *password, + NMCryptoKeyType *out_key_type, + GError **error); + +/*****************************************************************************/ + #endif /* __NM_CRYPTO_H__ */ From f961dcb8063b4e8d613609f8b11fb4b20d4a9989 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 30 Aug 2018 12:42:10 +0200 Subject: [PATCH 32/61] libnm/crypto: move and mark nm_utils_rsa_key_encrypt() as test code nm_utils_rsa_key_encrypt() is internal API which is only uesd for testing. Move it to nm-crypto.h (where it fits better) and rename it to make the testing-aspect obvious. --- libnm-core/nm-core-internal.h | 6 -- libnm-core/nm-crypto.c | 106 +++++++++++++++++++++++++++++++++ libnm-core/nm-crypto.h | 6 ++ libnm-core/nm-utils.c | 104 -------------------------------- libnm-core/tests/test-crypto.c | 10 ++-- 5 files changed, 117 insertions(+), 115 deletions(-) diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index 3dbbb7aa64..614e017490 100644 --- a/libnm-core/nm-core-internal.h +++ b/libnm-core/nm-core-internal.h @@ -287,12 +287,6 @@ void _nm_dbus_errors_init (void); extern gboolean _nm_utils_is_manager_process; -GByteArray *nm_utils_rsa_key_encrypt (const guint8 *data, - gsize len, - const char *in_password, - char **out_password, - GError **error); - gulong _nm_dbus_signal_connect_data (GDBusProxy *proxy, const char *signal_name, const GVariantType *signature, diff --git a/libnm-core/nm-crypto.c b/libnm-core/nm-crypto.c index c529cf458d..b626b07654 100644 --- a/libnm-core/nm-crypto.c +++ b/libnm-core/nm-crypto.c @@ -33,6 +33,7 @@ #include "nm-utils/nm-secret-utils.h" #include "nm-crypto-impl.h" +#include "nm-utils.h" #include "nm-errors.h" #define PEM_RSA_KEY_BEGIN "-----BEGIN RSA PRIVATE KEY-----" @@ -878,3 +879,108 @@ nm_crypto_randomize (void *buffer, gsize buffer_len, GError **error) { return _nm_crypto_randomize (buffer, buffer_len, error); } + + +/** + * nmtst_crypto_rsa_key_encrypt: + * @data: (array length=len): RSA private key data to be encrypted + * @len: length of @data + * @in_password: (allow-none): existing password to use, if any + * @out_password: (out) (allow-none): if @in_password was %NULL, a random + * password will be generated and returned in this argument + * @error: detailed error information on return, if an error occurred + * + * Encrypts the given RSA private key data with the given password (or generates + * a password if no password was given) and converts the data to PEM format + * suitable for writing to a file. It uses Triple DES cipher for the encryption. + * + * Returns: (transfer full): on success, PEM-formatted data suitable for writing + * to a PEM-formatted certificate/private key file. + **/ +GByteArray * +nmtst_crypto_rsa_key_encrypt (const guint8 *data, + gsize len, + const char *in_password, + char **out_password, + GError **error) +{ + char salt[16]; + int salt_len; + char *key = NULL, *enc = NULL, *pw_buf[32]; + gsize key_len = 0, enc_len = 0; + GString *pem = NULL; + char *tmp, *tmp_password = NULL; + int left; + const char *p; + GByteArray *ret = NULL; + + g_return_val_if_fail (data != NULL, NULL); + g_return_val_if_fail (len > 0, NULL); + if (out_password) + g_return_val_if_fail (*out_password == NULL, NULL); + + /* Make the password if needed */ + if (!in_password) { + if (!nm_crypto_randomize (pw_buf, sizeof (pw_buf), error)) + return NULL; + in_password = tmp_password = nm_utils_bin2hexstr (pw_buf, sizeof (pw_buf), -1); + } + + salt_len = 8; + if (!nm_crypto_randomize (salt, salt_len, error)) + goto out; + + key = nm_crypto_make_des_aes_key (CIPHER_DES_EDE3_CBC, &salt[0], salt_len, in_password, &key_len, NULL); + if (!key) + g_return_val_if_reached (NULL); + + enc = nm_crypto_encrypt (CIPHER_DES_EDE3_CBC, data, len, salt, salt_len, key, key_len, &enc_len, error); + if (!enc) + goto out; + + pem = g_string_sized_new (enc_len * 2 + 100); + g_string_append (pem, "-----BEGIN RSA PRIVATE KEY-----\n"); + g_string_append (pem, "Proc-Type: 4,ENCRYPTED\n"); + + /* Convert the salt to a hex string */ + tmp = nm_utils_bin2hexstr (salt, salt_len, salt_len * 2); + g_string_append_printf (pem, "DEK-Info: %s,%s\n\n", CIPHER_DES_EDE3_CBC, tmp); + g_free (tmp); + + /* Convert the encrypted key to a base64 string */ + p = tmp = g_base64_encode ((const guchar *) enc, enc_len); + left = strlen (tmp); + while (left > 0) { + g_string_append_len (pem, p, (left < 64) ? left : 64); + g_string_append_c (pem, '\n'); + left -= 64; + p += 64; + } + g_free (tmp); + + g_string_append (pem, "-----END RSA PRIVATE KEY-----\n"); + + ret = g_byte_array_sized_new (pem->len); + g_byte_array_append (ret, (const unsigned char *) pem->str, pem->len); + if (tmp_password && out_password) + *out_password = g_strdup (tmp_password); + +out: + if (key) { + memset (key, 0, key_len); + g_free (key); + } + if (enc) { + memset (enc, 0, enc_len); + g_free (enc); + } + if (pem) + g_string_free (pem, TRUE); + + if (tmp_password) { + memset (tmp_password, 0, strlen (tmp_password)); + g_free (tmp_password); + } + + return ret; +} diff --git a/libnm-core/nm-crypto.h b/libnm-core/nm-crypto.h index 2b56f4371b..d8b69a7411 100644 --- a/libnm-core/nm-crypto.h +++ b/libnm-core/nm-crypto.h @@ -110,6 +110,12 @@ GBytes *nmtst_crypto_decrypt_openssl_private_key (const char *file, NMCryptoKeyType *out_key_type, GError **error); +GByteArray *nmtst_crypto_rsa_key_encrypt (const guint8 *data, + gsize len, + const char *in_password, + char **out_password, + GError **error); + /*****************************************************************************/ #endif /* __NM_CRYPTO_H__ */ diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 1542625c01..1ca78421c3 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -2924,110 +2924,6 @@ _nm_utils_uuid_generate_from_strings (const char *string1, ...) /*****************************************************************************/ -/** - * nm_utils_rsa_key_encrypt: - * @data: (array length=len): RSA private key data to be encrypted - * @len: length of @data - * @in_password: (allow-none): existing password to use, if any - * @out_password: (out) (allow-none): if @in_password was %NULL, a random - * password will be generated and returned in this argument - * @error: detailed error information on return, if an error occurred - * - * Encrypts the given RSA private key data with the given password (or generates - * a password if no password was given) and converts the data to PEM format - * suitable for writing to a file. It uses Triple DES cipher for the encryption. - * - * Returns: (transfer full): on success, PEM-formatted data suitable for writing - * to a PEM-formatted certificate/private key file. - **/ -GByteArray * -nm_utils_rsa_key_encrypt (const guint8 *data, - gsize len, - const char *in_password, - char **out_password, - GError **error) -{ - char salt[16]; - int salt_len; - char *key = NULL, *enc = NULL, *pw_buf[32]; - gsize key_len = 0, enc_len = 0; - GString *pem = NULL; - char *tmp, *tmp_password = NULL; - int left; - const char *p; - GByteArray *ret = NULL; - - g_return_val_if_fail (data != NULL, NULL); - g_return_val_if_fail (len > 0, NULL); - if (out_password) - g_return_val_if_fail (*out_password == NULL, NULL); - - /* Make the password if needed */ - if (!in_password) { - if (!nm_crypto_randomize (pw_buf, sizeof (pw_buf), error)) - return NULL; - in_password = tmp_password = nm_utils_bin2hexstr (pw_buf, sizeof (pw_buf), -1); - } - - salt_len = 8; - if (!nm_crypto_randomize (salt, salt_len, error)) - goto out; - - key = nm_crypto_make_des_aes_key (CIPHER_DES_EDE3_CBC, &salt[0], salt_len, in_password, &key_len, NULL); - if (!key) - g_return_val_if_reached (NULL); - - enc = nm_crypto_encrypt (CIPHER_DES_EDE3_CBC, data, len, salt, salt_len, key, key_len, &enc_len, error); - if (!enc) - goto out; - - pem = g_string_sized_new (enc_len * 2 + 100); - g_string_append (pem, "-----BEGIN RSA PRIVATE KEY-----\n"); - g_string_append (pem, "Proc-Type: 4,ENCRYPTED\n"); - - /* Convert the salt to a hex string */ - tmp = nm_utils_bin2hexstr (salt, salt_len, salt_len * 2); - g_string_append_printf (pem, "DEK-Info: %s,%s\n\n", CIPHER_DES_EDE3_CBC, tmp); - g_free (tmp); - - /* Convert the encrypted key to a base64 string */ - p = tmp = g_base64_encode ((const guchar *) enc, enc_len); - left = strlen (tmp); - while (left > 0) { - g_string_append_len (pem, p, (left < 64) ? left : 64); - g_string_append_c (pem, '\n'); - left -= 64; - p += 64; - } - g_free (tmp); - - g_string_append (pem, "-----END RSA PRIVATE KEY-----\n"); - - ret = g_byte_array_sized_new (pem->len); - g_byte_array_append (ret, (const unsigned char *) pem->str, pem->len); - if (tmp_password && out_password) - *out_password = g_strdup (tmp_password); - -out: - if (key) { - memset (key, 0, key_len); - g_free (key); - } - if (enc) { - memset (enc, 0, enc_len); - g_free (enc); - } - if (pem) - g_string_free (pem, TRUE); - - if (tmp_password) { - memset (tmp_password, 0, strlen (tmp_password)); - g_free (tmp_password); - } - - return ret; -} - static gboolean file_has_extension (const char *filename, const char *extensions[]) { diff --git a/libnm-core/tests/test-crypto.c b/libnm-core/tests/test-crypto.c index 93970f3adc..b2474f88e7 100644 --- a/libnm-core/tests/test-crypto.c +++ b/libnm-core/tests/test-crypto.c @@ -252,11 +252,11 @@ test_encrypt_private_key (const char *path, g_assert_cmpint (key_type, ==, NM_CRYPTO_KEY_TYPE_RSA); /* Now re-encrypt the private key */ - encrypted = nm_utils_rsa_key_encrypt (g_bytes_get_data (array, NULL), - g_bytes_get_size (array), - password, - NULL, - &error); + encrypted = nmtst_crypto_rsa_key_encrypt (g_bytes_get_data (array, NULL), + g_bytes_get_size (array), + password, + NULL, + &error); nmtst_assert_success (encrypted, error); /* Then re-decrypt the private key */ From c0a1f09a2665b064eae2348c9d2852da77c6fbff Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 30 Aug 2018 13:06:51 +0200 Subject: [PATCH 33/61] libnm/crypto: refactor nmtst_crypto_rsa_key_encrypt() and clear memory It's only used for testing, so this change is not very relevant. Anyway, I think our crypto code should succeed in not leaving key material in memory. Refactor the code to do that, though, how the pem file gets composed is quite a hack (for tests good enough though). --- libnm-core/nm-crypto.c | 125 +++++++++++++++++---------------- libnm-core/nm-crypto.h | 10 +-- libnm-core/tests/test-crypto.c | 6 +- 3 files changed, 72 insertions(+), 69 deletions(-) diff --git a/libnm-core/nm-crypto.c b/libnm-core/nm-crypto.c index b626b07654..9dd32fe7f3 100644 --- a/libnm-core/nm-crypto.c +++ b/libnm-core/nm-crypto.c @@ -897,90 +897,93 @@ nm_crypto_randomize (void *buffer, gsize buffer_len, GError **error) * Returns: (transfer full): on success, PEM-formatted data suitable for writing * to a PEM-formatted certificate/private key file. **/ -GByteArray * +GBytes * nmtst_crypto_rsa_key_encrypt (const guint8 *data, gsize len, const char *in_password, char **out_password, GError **error) { - char salt[16]; - int salt_len; - char *key = NULL, *enc = NULL, *pw_buf[32]; - gsize key_len = 0, enc_len = 0; - GString *pem = NULL; - char *tmp, *tmp_password = NULL; - int left; + char salt[8]; + nm_auto_clear_secret_ptr NMSecretPtr key = { 0 }; + nm_auto_clear_secret_ptr NMSecretPtr enc = { 0 }; + gs_unref_ptrarray GPtrArray *pem = NULL; + nm_auto_free_secret char *tmp_password = NULL; + nm_auto_free_secret char *enc_base64 = NULL; + gsize enc_base64_len; const char *p; - GByteArray *ret = NULL; + gsize ret_len, ret_idx; + guint i; + NMSecretBuf *ret; - g_return_val_if_fail (data != NULL, NULL); + g_return_val_if_fail (data, NULL); g_return_val_if_fail (len > 0, NULL); - if (out_password) - g_return_val_if_fail (*out_password == NULL, NULL); + g_return_val_if_fail (!out_password || !*out_password, NULL); /* Make the password if needed */ if (!in_password) { - if (!nm_crypto_randomize (pw_buf, sizeof (pw_buf), error)) + nm_auto_clear_static_secret_ptr NMSecretPtr pw_buf = NM_SECRET_PTR_STATIC (32); + + if (!nm_crypto_randomize (pw_buf.bin, pw_buf.len, error)) return NULL; - in_password = tmp_password = nm_utils_bin2hexstr (pw_buf, sizeof (pw_buf), -1); + tmp_password = nm_utils_bin2hexstr (pw_buf.bin, pw_buf.len, -1); + in_password = tmp_password; } - salt_len = 8; - if (!nm_crypto_randomize (salt, salt_len, error)) - goto out; + if (!nm_crypto_randomize (salt, sizeof (salt), error)) + return NULL; - key = nm_crypto_make_des_aes_key (CIPHER_DES_EDE3_CBC, &salt[0], salt_len, in_password, &key_len, NULL); - if (!key) + key.str = nm_crypto_make_des_aes_key (CIPHER_DES_EDE3_CBC, &salt[0], sizeof (salt), in_password, &key.len, NULL); + if (!key.str) g_return_val_if_reached (NULL); - enc = nm_crypto_encrypt (CIPHER_DES_EDE3_CBC, data, len, salt, salt_len, key, key_len, &enc_len, error); - if (!enc) - goto out; + enc.str = nm_crypto_encrypt (CIPHER_DES_EDE3_CBC, data, len, salt, sizeof (salt), key.str, key.len, &enc.len, error); + if (!enc.str) + return NULL; - pem = g_string_sized_new (enc_len * 2 + 100); - g_string_append (pem, "-----BEGIN RSA PRIVATE KEY-----\n"); - g_string_append (pem, "Proc-Type: 4,ENCRYPTED\n"); + /* What follows is not the most efficient way to construct the pem + * file line-by-line. At least, it makes sure, that the data will be cleared + * again and not left around in memory. + * + * If this would not be test code, we should improve the implementation + * to avoid some of the copying. */ + pem = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_free_secret); + + g_ptr_array_add (pem, g_strdup ("-----BEGIN RSA PRIVATE KEY-----\n")); + g_ptr_array_add (pem, g_strdup ("Proc-Type: 4,ENCRYPTED\n")); /* Convert the salt to a hex string */ - tmp = nm_utils_bin2hexstr (salt, salt_len, salt_len * 2); - g_string_append_printf (pem, "DEK-Info: %s,%s\n\n", CIPHER_DES_EDE3_CBC, tmp); - g_free (tmp); + g_ptr_array_add (pem, g_strdup ("DEK-Info: "CIPHER_DES_EDE3_CBC",")); + g_ptr_array_add (pem, nm_utils_bin2hexstr (salt, sizeof (salt), sizeof (salt) * 2)); + g_ptr_array_add (pem, g_strdup ("\n\n")); /* Convert the encrypted key to a base64 string */ - p = tmp = g_base64_encode ((const guchar *) enc, enc_len); - left = strlen (tmp); - while (left > 0) { - g_string_append_len (pem, p, (left < 64) ? left : 64); - g_string_append_c (pem, '\n'); - left -= 64; - p += 64; - } - g_free (tmp); - - g_string_append (pem, "-----END RSA PRIVATE KEY-----\n"); - - ret = g_byte_array_sized_new (pem->len); - g_byte_array_append (ret, (const unsigned char *) pem->str, pem->len); - if (tmp_password && out_password) - *out_password = g_strdup (tmp_password); - -out: - if (key) { - memset (key, 0, key_len); - g_free (key); - } - if (enc) { - memset (enc, 0, enc_len); - g_free (enc); - } - if (pem) - g_string_free (pem, TRUE); - - if (tmp_password) { - memset (tmp_password, 0, strlen (tmp_password)); - g_free (tmp_password); + enc_base64 = g_base64_encode ((const guchar *) enc.str, enc.len); + enc_base64_len = strlen (enc_base64); + for (p = enc_base64; (p - enc_base64) < (ptrdiff_t) enc_base64_len; p += 64) { + g_ptr_array_add (pem, g_strndup (p, 64)); + g_ptr_array_add (pem, g_strdup ("\n")); } - return ret; + g_ptr_array_add (pem, g_strdup ("-----END RSA PRIVATE KEY-----\n")); + + ret_len = 0; + for (i = 0; i < pem->len; i++) + ret_len += strlen (pem->pdata[i]); + + ret = nm_secret_buf_new (ret_len + 1); + ret_idx = 0; + for (i = 0; i < pem->len; i++) { + const char *line = pem->pdata[i]; + gsize line_l = strlen (line); + + memcpy (&ret->bin[ret_idx], line, line_l); + ret_idx += line_l; + nm_assert (ret_idx <= ret_len); + } + nm_assert (ret_idx == ret_len); + ret->bin[ret_len] = '\0'; + + NM_SET_OUT (out_password, g_strdup (tmp_password)); + return nm_secret_buf_to_gbytes_take (ret, ret_len); } diff --git a/libnm-core/nm-crypto.h b/libnm-core/nm-crypto.h index d8b69a7411..1b9e039484 100644 --- a/libnm-core/nm-crypto.h +++ b/libnm-core/nm-crypto.h @@ -110,11 +110,11 @@ GBytes *nmtst_crypto_decrypt_openssl_private_key (const char *file, NMCryptoKeyType *out_key_type, GError **error); -GByteArray *nmtst_crypto_rsa_key_encrypt (const guint8 *data, - gsize len, - const char *in_password, - char **out_password, - GError **error); +GBytes *nmtst_crypto_rsa_key_encrypt (const guint8 *data, + gsize len, + const char *in_password, + char **out_password, + GError **error); /*****************************************************************************/ diff --git a/libnm-core/tests/test-crypto.c b/libnm-core/tests/test-crypto.c index b2474f88e7..6b63e6fdd4 100644 --- a/libnm-core/tests/test-crypto.c +++ b/libnm-core/tests/test-crypto.c @@ -243,7 +243,7 @@ test_encrypt_private_key (const char *path, { NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN; gs_unref_bytes GBytes *array = NULL; - nm_auto_unref_bytearray GByteArray *encrypted = NULL; + gs_unref_bytes GBytes *encrypted = NULL; gs_unref_bytes GBytes *re_decrypted = NULL; GError *error = NULL; @@ -261,8 +261,8 @@ test_encrypt_private_key (const char *path, /* Then re-decrypt the private key */ key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN; - re_decrypted = nmtst_crypto_decrypt_openssl_private_key_data (encrypted->data, - encrypted->len, + re_decrypted = nmtst_crypto_decrypt_openssl_private_key_data (g_bytes_get_data (encrypted, NULL), + g_bytes_get_size (encrypted), password, &key_type, &error); From b5abc8a1d539f9b7af9cf53a8ea4a1be9616e0eb Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 30 Aug 2018 15:23:34 +0200 Subject: [PATCH 34/61] libnm/crypto: ensure not leaking sensitive information when loading files g_file_get_contents() may use re-alloc to load the file. Each time it re-allocated the buffer, it does not bother clearing the loaded buffer from memory. Alternatively, g_file_get_contents() may use stat() and only allocate one buffer. But also in this mode, without realloc(), it does not clear the buffer if reading the file fails with IO error later. Use nm_utils_file_get_contents() which does that. While at it, don't load files larger that 100 MB. --- libnm-core/nm-crypto.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/libnm-core/nm-crypto.c b/libnm-core/nm-crypto.c index 9dd32fe7f3..e1235ccbbe 100644 --- a/libnm-core/nm-crypto.c +++ b/libnm-core/nm-crypto.c @@ -31,6 +31,7 @@ #include #include "nm-utils/nm-secret-utils.h" +#include "nm-utils/nm-io-utils.h" #include "nm-crypto-impl.h" #include "nm-utils.h" @@ -349,7 +350,13 @@ file_read_contents (const char *filename, nm_assert (out_contents->len == 0); nm_assert (!out_contents->str); - return g_file_get_contents (filename, &out_contents->str, &out_contents->len, error); + return nm_utils_file_get_contents (-1, + filename, + 100*1024*1024, + NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET, + &out_contents->str, + &out_contents->len, + error) >= 0; } /* From 2be0bb828717bf78a3d976eeecbeeead37903bf1 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 30 Aug 2018 15:38:47 +0200 Subject: [PATCH 35/61] libnm/crypto: fix loading certificates from file securely file_to_secure_bytes() tried to load the file from disk and ensure that the data will be cleared. It did so poorely, because g_file_get_contents() cannot be used for that. Add a helper function nm_crypto_read_file() to get this right. --- libnm-core/nm-crypto.c | 13 +++++++++++++ libnm-core/nm-crypto.h | 3 +++ libnm-core/nm-setting-8021x.c | 30 ++---------------------------- 3 files changed, 18 insertions(+), 28 deletions(-) diff --git a/libnm-core/nm-crypto.c b/libnm-core/nm-crypto.c index e1235ccbbe..776a6d6858 100644 --- a/libnm-core/nm-crypto.c +++ b/libnm-core/nm-crypto.c @@ -359,6 +359,19 @@ file_read_contents (const char *filename, error) >= 0; } +GBytes * +nm_crypto_read_file (const char *filename, + GError **error) +{ + nm_auto_clear_secret_ptr NMSecretPtr contents = { 0 }; + + g_return_val_if_fail (filename, NULL); + + if (!file_read_contents (filename, &contents, error)) + return NULL; + return nm_secret_copy_to_gbytes (contents.bin, contents.len); +} + /* * Convert a hex string into bytes. */ diff --git a/libnm-core/nm-crypto.h b/libnm-core/nm-crypto.h index 1b9e039484..59c2f7c3e1 100644 --- a/libnm-core/nm-crypto.h +++ b/libnm-core/nm-crypto.h @@ -51,6 +51,9 @@ typedef enum { /*****************************************************************************/ +GBytes *nm_crypto_read_file (const char *filename, + GError **error); + gboolean nm_crypto_load_and_verify_certificate (const char *file, NMCryptoFileFormat *out_file_format, GBytes **out_certificat, diff --git a/libnm-core/nm-setting-8021x.c b/libnm-core/nm-setting-8021x.c index 4fae2c64e6..7218e7e536 100644 --- a/libnm-core/nm-setting-8021x.c +++ b/libnm-core/nm-setting-8021x.c @@ -2165,32 +2165,6 @@ nm_setting_802_1x_get_private_key_uri (NMSetting8021x *setting) return (const char *)data; } -static void -free_secure_bytes (gpointer data) -{ - GByteArray *array = data; - - memset (array->data, 0, array->len); - g_byte_array_unref (array); -} - -static GBytes * -file_to_secure_bytes (const char *filename) -{ - char *contents; - GByteArray *array = NULL; - gsize length = 0; - - if (g_file_get_contents (filename, &contents, &length, NULL)) { - array = g_byte_array_sized_new (length); - g_byte_array_append (array, (guint8 *) contents, length); - memset (contents, 0, length); - g_free (contents); - return g_bytes_new_with_free_func (array->data, array->len, free_secure_bytes, array); - } - return NULL; -} - /** * nm_setting_802_1x_set_private_key: * @setting: the #NMSetting8021x @@ -2295,7 +2269,7 @@ nm_setting_802_1x_set_private_key (NMSetting8021x *setting, if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { /* FIXME: potential race after verifying the private key above */ /* FIXME: ensure blob doesn't start with file:// */ - priv->private_key = file_to_secure_bytes (value); + priv->private_key = nm_crypto_read_file (value, NULL); nm_assert (priv->private_key); } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) priv->private_key = path_to_scheme_value (value); @@ -2637,7 +2611,7 @@ nm_setting_802_1x_set_phase2_private_key (NMSetting8021x *setting, if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { /* FIXME: potential race after verifying the private key above */ /* FIXME: ensure blob doesn't start with file:// */ - priv->phase2_private_key = file_to_secure_bytes (value); + priv->phase2_private_key = nm_crypto_read_file (value, NULL); nm_assert (priv->phase2_private_key); } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) priv->phase2_private_key = path_to_scheme_value (value); From b0c3af6c840db4934782d6bbbd022d18ba506d1f Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 30 Aug 2018 15:40:05 +0200 Subject: [PATCH 36/61] libnm: avoid intermediate GByteArray in path_to_scheme_value() It's not that to directly initialize the GBytes without an intermediate GByteArray. --- libnm-core/nm-setting-8021x.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/libnm-core/nm-setting-8021x.c b/libnm-core/nm-setting-8021x.c index 7218e7e536..376f86da2c 100644 --- a/libnm-core/nm-setting-8021x.c +++ b/libnm-core/nm-setting-8021x.c @@ -664,20 +664,19 @@ nm_setting_802_1x_get_ca_cert_uri (NMSetting8021x *setting) static GBytes * path_to_scheme_value (const char *path) { - GByteArray *array; - gsize len; + guint8 *mem; + gsize len, total_len; g_return_val_if_fail (path != NULL && path[0], NULL); len = strlen (path); + total_len = (NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH) + 1) + len; /* Add the path scheme tag to the front, then the filename */ - array = g_byte_array_sized_new (len + strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH) + 1); - g_byte_array_append (array, (const guint8 *) NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH, strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)); - g_byte_array_append (array, (const guint8 *) path, len); - g_byte_array_append (array, (const guint8 *) "\0", 1); - - return g_byte_array_free_to_bytes (array); + mem = g_new (guint8, total_len); + memcpy (mem, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH, NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)); + memcpy (&mem[NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)], path, len + 1); + return g_bytes_new_take (mem, total_len); } /** From 0fdd42e24c38a1366b09ab18e03455cb66d61ca0 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 30 Aug 2018 15:47:15 +0200 Subject: [PATCH 37/61] libnm/keyfile: avoid GByteArray to construct path uri in nm_keyfile_detect_unqualified_path_scheme() --- libnm-core/nm-keyfile.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/libnm-core/nm-keyfile.c b/libnm-core/nm-keyfile.c index 924777f503..d337854bda 100644 --- a/libnm-core/nm-keyfile.c +++ b/libnm-core/nm-keyfile.c @@ -1147,8 +1147,9 @@ nm_keyfile_detect_unqualified_path_scheme (const char *base_dir, const char *data = pdata; gboolean exists = FALSE; gsize validate_len; + gsize path_len, pathuri_len; gs_free char *path = NULL; - GByteArray *tmp; + gs_free char *pathuri = NULL; g_return_val_if_fail (base_dir && base_dir[0] == '/', NULL); @@ -1191,18 +1192,16 @@ nm_keyfile_detect_unqualified_path_scheme (const char *base_dir, * 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. */ - 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_byte_array_unref (tmp); + path_len = strlen (path); + pathuri_len = (NM_STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH) + 1) + path_len; + pathuri = g_new (char, pathuri_len); + memcpy (pathuri, NM_KEYFILE_CERT_SCHEME_PREFIX_PATH, NM_STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH)); + memcpy (&pathuri[NM_STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH)], path, path_len + 1); + if (nm_setting_802_1x_check_cert_scheme (pathuri, pathuri_len, NULL) != NM_SETTING_802_1X_CK_SCHEME_PATH) return NULL; - } - g_free (path); - path = (char *) g_byte_array_free (tmp, FALSE); NM_SET_OUT (out_exists, exists); - return g_steal_pointer (&path); + return g_steal_pointer (&pathuri); } #define HAS_SCHEME_PREFIX(bin, bin_len, scheme) \ From b6377b8082b74f3510873f75f3ac546d068f6da5 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 30 Aug 2018 17:21:26 +0200 Subject: [PATCH 38/61] libnm: clear private-key passwords in NMSetting8021x Yes, there are countless other places where we don't get this right and leave sensitive data in memory. Anyway, fix these places. --- libnm-core/nm-setting-8021x.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/libnm-core/nm-setting-8021x.c b/libnm-core/nm-setting-8021x.c index 376f86da2c..4f2232a7b1 100644 --- a/libnm-core/nm-setting-8021x.c +++ b/libnm-core/nm-setting-8021x.c @@ -26,6 +26,7 @@ #include +#include "nm-utils/nm-secret-utils.h" #include "nm-utils.h" #include "nm-crypto.h" #include "nm-utils-private.h" @@ -2252,14 +2253,14 @@ nm_setting_802_1x_set_private_key (NMSetting8021x *setting, g_clear_pointer (&priv->private_key, g_bytes_unref); g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PRIVATE_KEY); } - if (nm_clear_g_free (&priv->private_key_password)) + if (nm_clear_pointer (&priv->private_key_password, nm_free_secret)) g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD); return TRUE; } /* this makes password self-assignment safe. */ if (!nm_streq0 (priv->private_key_password, password)) { - g_free (priv->private_key_password); + nm_free_secret (priv->private_key_password); priv->private_key_password = g_strdup (password); password_changed = TRUE; } @@ -2594,14 +2595,14 @@ nm_setting_802_1x_set_phase2_private_key (NMSetting8021x *setting, g_clear_pointer (&priv->phase2_private_key, g_bytes_unref); g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY); } - if (nm_clear_g_free (&priv->phase2_private_key_password)) + if (nm_clear_pointer (&priv->phase2_private_key_password, nm_free_secret)) g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD); return TRUE; } /* this makes password self-assignment safe. */ if (!nm_streq0 (priv->phase2_private_key_password, password)) { - g_free (priv->phase2_private_key_password); + nm_free_secret (priv->phase2_private_key_password); priv->phase2_private_key_password = g_strdup (password); password_changed = TRUE; } @@ -3342,7 +3343,7 @@ finalize (GObject *object) g_free (priv->client_cert_password); if (priv->private_key) g_bytes_unref (priv->private_key); - g_free (priv->private_key_password); + nm_free_secret (priv->private_key_password); if (priv->phase2_ca_cert) g_bytes_unref (priv->phase2_ca_cert); g_free (priv->phase2_ca_cert_password); @@ -3351,7 +3352,7 @@ finalize (GObject *object) g_free (priv->phase2_client_cert_password); if (priv->phase2_private_key) g_bytes_unref (priv->phase2_private_key); - g_free (priv->phase2_private_key_password); + nm_free_secret (priv->phase2_private_key_password); G_OBJECT_CLASS (nm_setting_802_1x_parent_class)->finalize (object); } @@ -3542,7 +3543,7 @@ set_property (GObject *object, guint prop_id, } break; case PROP_PRIVATE_KEY_PASSWORD: - g_free (priv->private_key_password); + nm_free_secret (priv->private_key_password); priv->private_key_password = g_value_dup_string (value); break; case PROP_PRIVATE_KEY_PASSWORD_FLAGS: @@ -3558,7 +3559,7 @@ set_property (GObject *object, guint prop_id, } break; case PROP_PHASE2_PRIVATE_KEY_PASSWORD: - g_free (priv->phase2_private_key_password); + nm_free_secret (priv->phase2_private_key_password); priv->phase2_private_key_password = g_value_dup_string (value); break; case PROP_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS: From 44ceb161956cb0af43fda8220ec89881f6efc984 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 30 Aug 2018 16:40:01 +0200 Subject: [PATCH 39/61] libnm: fix race in nm-setting-x802-1x's setting private key functions Do not first load the file during nm_crypto_verify_private_key(), and later re-load it, in case we are setting a blob. Instead, ensure we only load the file once. This fixes a race, and also the very wrong assertion: priv->phase2_private_key = nm_crypto_read_file (value, NULL); nm_assert (priv->phase2_private_key); We should never assert that an IO operation succeeds. Also, we encode blobs, paths, and pkcs11 URIs all inside a binary field. Unfortunately, there is no defined prefix for blobs (TODO). That means, if you have a blob that happens to start with "file://" it cannot be expressed. At least, check that the binary field that we are setting gets detected as correct scheme type. --- libnm-core/nm-setting-8021x.c | 192 +++++++++++++++++++++------------- 1 file changed, 121 insertions(+), 71 deletions(-) diff --git a/libnm-core/nm-setting-8021x.c b/libnm-core/nm-setting-8021x.c index 4f2232a7b1..59ce97ba17 100644 --- a/libnm-core/nm-setting-8021x.c +++ b/libnm-core/nm-setting-8021x.c @@ -511,9 +511,9 @@ nm_setting_802_1x_check_cert_scheme (gconstpointer pdata, gsize length, GError * if (!g_utf8_validate (data + prefix_length, length - prefix_length, NULL)) { g_set_error_literal (error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("URI is not valid UTF-8")); + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("URI is not valid UTF-8")); return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN; } } @@ -2214,8 +2214,10 @@ nm_setting_802_1x_set_private_key (NMSetting8021x *setting, { NMSetting8021xPrivate *priv; NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; + gs_unref_bytes GBytes *file_content = NULL; + gs_unref_bytes GBytes *private_key_new = NULL; + gboolean private_key_changed = FALSE; gboolean password_changed = FALSE; - GError *local_err = NULL; g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE); @@ -2227,14 +2229,32 @@ nm_setting_802_1x_set_private_key (NMSetting8021x *setting, FALSE); } - if (out_format) - g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE); + NM_SET_OUT (out_format, NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); + + priv = NM_SETTING_802_1X_GET_PRIVATE (setting); + + if (!value) { + if (nm_clear_pointer (&priv->private_key, g_bytes_unref)) + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PRIVATE_KEY); + if (nm_clear_pointer (&priv->private_key_password, nm_free_secret)) + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD); + return TRUE; + } /* Ensure the private key is a recognized format and if the password was * given, that it decrypts the private key. */ - if (value && scheme != NM_SETTING_802_1X_CK_SCHEME_PKCS11) { - format = nm_crypto_verify_private_key (value, password, NULL, &local_err); + if (scheme != NM_SETTING_802_1X_CK_SCHEME_PKCS11) { + GError *local_err = NULL; + + file_content = nm_crypto_read_file (value, &local_err); + if (file_content) { + format = nm_crypto_verify_private_key_data (g_bytes_get_data (file_content, NULL), + g_bytes_get_size (file_content), + password, + NULL, + &local_err); + } if (format == NM_CRYPTO_FILE_FORMAT_UNKNOWN) { g_set_error_literal (error, NM_CONNECTION_ERROR, @@ -2246,54 +2266,58 @@ nm_setting_802_1x_set_private_key (NMSetting8021x *setting, } } - priv = NM_SETTING_802_1X_GET_PRIVATE (setting); - - if (value == NULL) { - if (priv->private_key) { - g_clear_pointer (&priv->private_key, g_bytes_unref); - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PRIVATE_KEY); - } - if (nm_clear_pointer (&priv->private_key_password, nm_free_secret)) - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD); - return TRUE; + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { + private_key_new = file_content + ? g_steal_pointer (&file_content) + : nm_crypto_read_file (value, NULL); + } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) + private_key_new = path_to_scheme_value (value); + else { + nm_assert (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11); + private_key_new = g_bytes_new (value, strlen (value) + 1); + } + + if ( !private_key_new + || nm_setting_802_1x_check_cert_scheme (g_bytes_get_data (private_key_new, NULL), + g_bytes_get_size (private_key_new), + NULL) != scheme) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("invalid private key")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY); + return FALSE; + } + + if ( !priv->private_key + || !g_bytes_equal (priv->private_key, private_key_new)) { + g_bytes_unref (priv->private_key); + priv->private_key = g_steal_pointer (&private_key_new); + private_key_changed = TRUE; } - /* this makes password self-assignment safe. */ if (!nm_streq0 (priv->private_key_password, password)) { nm_free_secret (priv->private_key_password); priv->private_key_password = g_strdup (password); password_changed = TRUE; } - g_bytes_unref (priv->private_key); - if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { - /* FIXME: potential race after verifying the private key above */ - /* FIXME: ensure blob doesn't start with file:// */ - priv->private_key = nm_crypto_read_file (value, NULL); - nm_assert (priv->private_key); - } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) - priv->private_key = path_to_scheme_value (value); - else { - nm_assert (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11); - priv->private_key = g_bytes_new (value, strlen (value) + 1); - } - /* As required by NM and wpa_supplicant, set the client-cert * property to the same PKCS#12 data. */ if (format == NM_CRYPTO_FILE_FORMAT_PKCS12) { - if (priv->client_cert) - g_bytes_unref (priv->client_cert); + g_bytes_unref (priv->client_cert); priv->client_cert = g_bytes_ref (priv->private_key); g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_CLIENT_CERT); } - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PRIVATE_KEY); + if (private_key_changed) + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PRIVATE_KEY); if (password_changed) g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD); NM_SET_OUT (out_format, (NMSetting8021xCKFormat) format); - return priv->private_key != NULL; + return TRUE; } /** @@ -2556,8 +2580,10 @@ nm_setting_802_1x_set_phase2_private_key (NMSetting8021x *setting, { NMSetting8021xPrivate *priv; NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; + gs_unref_bytes GBytes *file_content = NULL; + gs_unref_bytes GBytes *private_key_new = NULL; + gboolean private_key_changed = FALSE; gboolean password_changed = FALSE; - GError *local_err = NULL; g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE); @@ -2569,14 +2595,33 @@ nm_setting_802_1x_set_phase2_private_key (NMSetting8021x *setting, FALSE); } - if (out_format) - g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE); + NM_SET_OUT (out_format, NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); + + priv = NM_SETTING_802_1X_GET_PRIVATE (setting); + + if (!value) { + if (nm_clear_pointer (&priv->phase2_private_key, g_bytes_unref)) + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY); + if (nm_clear_pointer (&priv->phase2_private_key_password, nm_free_secret)) + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD); + return TRUE; + } /* Ensure the private key is a recognized format and if the password was * given, that it decrypts the private key. */ - if (value && scheme != NM_SETTING_802_1X_CK_SCHEME_PKCS11) { - format = nm_crypto_verify_private_key (value, password, NULL, &local_err); + if (scheme != NM_SETTING_802_1X_CK_SCHEME_PKCS11) { + GError *local_err = NULL; + + file_content = nm_crypto_read_file (value, &local_err); + if (file_content) { + format = nm_crypto_verify_private_key_data (g_bytes_get_data (file_content, NULL), + g_bytes_get_size (file_content), + password, + NULL, + &local_err); + } + if (format == NM_CRYPTO_FILE_FORMAT_UNKNOWN) { g_set_error_literal (error, NM_CONNECTION_ERROR, @@ -2588,55 +2633,60 @@ nm_setting_802_1x_set_phase2_private_key (NMSetting8021x *setting, } } - priv = NM_SETTING_802_1X_GET_PRIVATE (setting); - - if (value == NULL) { - if (priv->phase2_private_key) { - g_clear_pointer (&priv->phase2_private_key, g_bytes_unref); - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY); - } - if (nm_clear_pointer (&priv->phase2_private_key_password, nm_free_secret)) - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD); - return TRUE; + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { + private_key_new = file_content + ? g_steal_pointer (&file_content) + : nm_crypto_read_file (value, NULL); + } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) + private_key_new = path_to_scheme_value (value); + else { + nm_assert (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11); + private_key_new = g_bytes_new (value, strlen (value) + 1); + } + + if ( !private_key_new + || nm_setting_802_1x_check_cert_scheme (g_bytes_get_data (private_key_new, NULL), + g_bytes_get_size (private_key_new), + NULL) != scheme) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("invalid phase2 private key")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY); + return FALSE; + } + + if ( !priv->phase2_private_key + || !g_bytes_equal (priv->phase2_private_key, private_key_new)) { + g_bytes_unref (priv->phase2_private_key); + priv->phase2_private_key = g_steal_pointer (&private_key_new); + private_key_changed = TRUE; } - /* this makes password self-assignment safe. */ if (!nm_streq0 (priv->phase2_private_key_password, password)) { nm_free_secret (priv->phase2_private_key_password); priv->phase2_private_key_password = g_strdup (password); password_changed = TRUE; } - g_bytes_unref (priv->phase2_private_key); - if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { - /* FIXME: potential race after verifying the private key above */ - /* FIXME: ensure blob doesn't start with file:// */ - priv->phase2_private_key = nm_crypto_read_file (value, NULL); - nm_assert (priv->phase2_private_key); - } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) - priv->phase2_private_key = path_to_scheme_value (value); - else { - nm_assert (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11); - priv->phase2_private_key = g_bytes_new (value, strlen (value) + 1); - } - /* As required by NM and wpa_supplicant, set the client-cert * property to the same PKCS#12 data. */ - if (format == NM_CRYPTO_FILE_FORMAT_PKCS12) { - if (priv->phase2_client_cert) - g_bytes_unref (priv->phase2_client_cert); - + if ( format == NM_CRYPTO_FILE_FORMAT_PKCS12 + && ( !priv->phase2_client_cert + || g_bytes_equal (priv->phase2_client_cert, priv->phase2_private_key))) { + g_bytes_unref (priv->phase2_client_cert); priv->phase2_client_cert = g_bytes_ref (priv->phase2_private_key); g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_CLIENT_CERT); } - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY); + if (private_key_changed) + g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY); if (password_changed) g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD); NM_SET_OUT (out_format, (NMSetting8021xCKFormat) format); - return priv->phase2_private_key != NULL; + return TRUE; } /** From b91e60b1d686c48455a8b231e6eb7a81d0f29340 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 30 Aug 2018 17:35:05 +0200 Subject: [PATCH 40/61] libnm: cleanup conversion from NMCryptoFileFormat to NMSetting8021xCKFormat enum --- libnm-core/nm-setting-8021x.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/libnm-core/nm-setting-8021x.c b/libnm-core/nm-setting-8021x.c index 59ce97ba17..f8ef6c962d 100644 --- a/libnm-core/nm-setting-8021x.c +++ b/libnm-core/nm-setting-8021x.c @@ -61,15 +61,29 @@ * ISBN: 978-1587051548 **/ +/*****************************************************************************/ + +static NMSetting8021xCKFormat +_crypto_format_to_ck (NMCryptoFileFormat format) +{ + G_STATIC_ASSERT ( (NM_SETTING_802_1X_CK_FORMAT_UNKNOWN == (NMSetting8021xCKFormat) NM_CRYPTO_FILE_FORMAT_UNKNOWN) ); + G_STATIC_ASSERT ( (NM_SETTING_802_1X_CK_FORMAT_X509 == (NMSetting8021xCKFormat) NM_CRYPTO_FILE_FORMAT_X509) ); + G_STATIC_ASSERT ( (NM_SETTING_802_1X_CK_FORMAT_RAW_KEY == (NMSetting8021xCKFormat) NM_CRYPTO_FILE_FORMAT_RAW_KEY) ); + G_STATIC_ASSERT ( (NM_SETTING_802_1X_CK_FORMAT_PKCS12 == (NMSetting8021xCKFormat) NM_CRYPTO_FILE_FORMAT_PKCS12) ); + + nm_assert (NM_IN_SET (format, NM_CRYPTO_FILE_FORMAT_UNKNOWN, + NM_CRYPTO_FILE_FORMAT_X509, + NM_CRYPTO_FILE_FORMAT_RAW_KEY, + NM_CRYPTO_FILE_FORMAT_PKCS12)); + return (NMSetting8021xCKFormat) format; +} + +/*****************************************************************************/ + G_DEFINE_TYPE (NMSetting8021x, nm_setting_802_1x, NM_TYPE_SETTING) #define NM_SETTING_802_1X_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_802_1X, NMSetting8021xPrivate)) -G_STATIC_ASSERT ( (NM_SETTING_802_1X_CK_FORMAT_UNKNOWN == (NMSetting8021xCKFormat) NM_CRYPTO_FILE_FORMAT_UNKNOWN) ); -G_STATIC_ASSERT ( (NM_SETTING_802_1X_CK_FORMAT_X509 == (NMSetting8021xCKFormat) NM_CRYPTO_FILE_FORMAT_X509) ); -G_STATIC_ASSERT ( (NM_SETTING_802_1X_CK_FORMAT_RAW_KEY == (NMSetting8021xCKFormat) NM_CRYPTO_FILE_FORMAT_RAW_KEY) ); -G_STATIC_ASSERT ( (NM_SETTING_802_1X_CK_FORMAT_PKCS12 == (NMSetting8021xCKFormat) NM_CRYPTO_FILE_FORMAT_PKCS12) ); - typedef struct { GSList *eap; /* GSList of strings */ char *identity; @@ -2316,7 +2330,7 @@ nm_setting_802_1x_set_private_key (NMSetting8021x *setting, if (password_changed) g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD); - NM_SET_OUT (out_format, (NMSetting8021xCKFormat) format); + NM_SET_OUT (out_format, _crypto_format_to_ck (format)); return TRUE; } @@ -2685,7 +2699,7 @@ nm_setting_802_1x_set_phase2_private_key (NMSetting8021x *setting, if (password_changed) g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD); - NM_SET_OUT (out_format, (NMSetting8021xCKFormat) format); + NM_SET_OUT (out_format, _crypto_format_to_ck (format)); return TRUE; } From 67f36f880e58d1c13377cd985e6735087c88bf7a Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 30 Aug 2018 21:08:47 +0200 Subject: [PATCH 41/61] libnm/crypto: rename crypto functions used for testing only - drop nm_crypto_encrypt(). It's not actually used outside of "nm-crypto.c". - rename internal _nm_crypto_*() functions that are only used in tests. It's so much nicer to visually recognize functions that are used for testing only. --- libnm-core/nm-crypto-gnutls.c | 38 ++++++++++---------- libnm-core/nm-crypto-impl.h | 44 ++++++++++++----------- libnm-core/nm-crypto-nss.c | 38 ++++++++++---------- libnm-core/nm-crypto.c | 68 ++++++++++++++--------------------- libnm-core/nm-crypto.h | 10 ------ 5 files changed, 88 insertions(+), 110 deletions(-) diff --git a/libnm-core/nm-crypto-gnutls.c b/libnm-core/nm-crypto-gnutls.c index 1cf48aeec9..dea578fd29 100644 --- a/libnm-core/nm-crypto-gnutls.c +++ b/libnm-core/nm-crypto-gnutls.c @@ -56,16 +56,16 @@ _nm_crypto_init (GError **error) } char * -_nm_crypto_decrypt (const char *cipher, - int key_type, - const guint8 *data, - gsize data_len, - const char *iv, - const gsize iv_len, - const char *key, - const gsize key_len, - gsize *out_len, - GError **error) +_nmtst_crypto_decrypt (const char *cipher, + int key_type, + const guint8 *data, + gsize data_len, + const char *iv, + const gsize iv_len, + const char *key, + const gsize key_len, + gsize *out_len, + GError **error) { gnutls_cipher_hd_t ctx; gnutls_datum_t key_dt, iv_dt; @@ -171,15 +171,15 @@ out: } char * -_nm_crypto_encrypt (const char *cipher, - const guint8 *data, - gsize data_len, - const char *iv, - const gsize iv_len, - const char *key, - gsize key_len, - gsize *out_len, - GError **error) +_nmtst_crypto_encrypt (const char *cipher, + const guint8 *data, + gsize data_len, + const char *iv, + const gsize iv_len, + const char *key, + gsize key_len, + gsize *out_len, + GError **error) { gnutls_cipher_hd_t ctx; gnutls_datum_t key_dt, iv_dt; diff --git a/libnm-core/nm-crypto-impl.h b/libnm-core/nm-crypto-impl.h index 50b9ad4f52..49c5c7a328 100644 --- a/libnm-core/nm-crypto-impl.h +++ b/libnm-core/nm-crypto-impl.h @@ -34,27 +34,6 @@ gboolean _nm_crypto_init (GError **error); gboolean _nm_crypto_randomize (void *buffer, gsize buffer_len, GError **error); -char *_nm_crypto_encrypt (const char *cipher, - const guint8 *data, - gsize data_len, - const char *iv, - gsize iv_len, - const char *key, - gsize key_len, - gsize *out_len, - GError **error); - -char *_nm_crypto_decrypt (const char *cipher, - int key_type, - const guint8 *data, - gsize data_len, - const char *iv, - const gsize iv_len, - const char *key, - const gsize key_len, - gsize *out_len, - GError **error); - gboolean _nm_crypto_verify_x509 (const guint8 *data, gsize len, GError **error); @@ -70,4 +49,27 @@ gboolean _nm_crypto_verify_pkcs8 (const guint8 *data, const char *password, GError **error); +/*****************************************************************************/ + +char *_nmtst_crypto_encrypt (const char *cipher, + const guint8 *data, + gsize data_len, + const char *iv, + gsize iv_len, + const char *key, + gsize key_len, + gsize *out_len, + GError **error); + +char *_nmtst_crypto_decrypt (const char *cipher, + int key_type, + const guint8 *data, + gsize data_len, + const char *iv, + const gsize iv_len, + const char *key, + const gsize key_len, + gsize *out_len, + GError **error); + #endif /* __NM_CRYPTO_IMPL_H__ */ diff --git a/libnm-core/nm-crypto-nss.c b/libnm-core/nm-crypto-nss.c index fb55b95896..6ddc442862 100644 --- a/libnm-core/nm-crypto-nss.c +++ b/libnm-core/nm-crypto-nss.c @@ -72,16 +72,16 @@ _nm_crypto_init (GError **error) } char * -_nm_crypto_decrypt (const char *cipher, - int key_type, - const guint8 *data, - gsize data_len, - const char *iv, - const gsize iv_len, - const char *key, - const gsize key_len, - gsize *out_len, - GError **error) +_nmtst_crypto_decrypt (const char *cipher, + int key_type, + const guint8 *data, + gsize data_len, + const char *iv, + const gsize iv_len, + const char *key, + const gsize key_len, + gsize *out_len, + GError **error) { char *output = NULL; int decrypted_len = 0; @@ -243,15 +243,15 @@ out: } char * -_nm_crypto_encrypt (const char *cipher, - const guint8 *data, - gsize data_len, - const char *iv, - gsize iv_len, - const char *key, - gsize key_len, - gsize *out_len, - GError **error) +_nmtst_crypto_encrypt (const char *cipher, + const guint8 *data, + gsize data_len, + const char *iv, + gsize iv_len, + const char *key, + gsize key_len, + gsize *out_len, + GError **error) { SECStatus ret; CK_MECHANISM_TYPE cipher_mech = CKM_DES3_CBC_PAD; diff --git a/libnm-core/nm-crypto.c b/libnm-core/nm-crypto.c index 776a6d6858..684867593d 100644 --- a/libnm-core/nm-crypto.c +++ b/libnm-core/nm-crypto.c @@ -472,14 +472,14 @@ nm_crypto_make_des_aes_key (const char *cipher, } static gboolean -_nmtst_crypto_decrypt_key (const char *cipher, - int key_type, - const guint8 *data, - gsize data_len, - const char *iv, - const char *password, - NMSecretPtr *parsed, - GError **error) +_nmtst_decrypt_key (const char *cipher, + int key_type, + const guint8 *data, + gsize data_len, + const char *iv, + const char *password, + NMSecretPtr *parsed, + GError **error) { nm_auto_clear_secret_ptr NMSecretPtr bin_iv = { 0 }; nm_auto_clear_secret_ptr NMSecretPtr key = { 0 }; @@ -509,16 +509,16 @@ _nmtst_crypto_decrypt_key (const char *cipher, if (!key.str || !key.len) return FALSE; - parsed->str = _nm_crypto_decrypt (cipher, - key_type, - data, - data_len, - bin_iv.str, - bin_iv.len, - key.str, - key.len, - &parsed->len, - error); + parsed->str = _nmtst_crypto_decrypt (cipher, + key_type, + data, + data_len, + bin_iv.str, + bin_iv.len, + key.str, + key.len, + &parsed->len, + error); if (!parsed->str || parsed->len == 0) { nm_secret_ptr_clear (parsed); return FALSE; @@ -565,14 +565,14 @@ nmtst_crypto_decrypt_openssl_private_key_data (const guint8 *data, return NULL; } - if (!_nmtst_crypto_decrypt_key (cipher, - key_type, - parsed.bin, - parsed.len, - iv, - password, - &parsed2, - error)) + if (!_nmtst_decrypt_key (cipher, + key_type, + parsed.bin, + parsed.len, + iv, + password, + &parsed2, + error)) return NULL; return nm_secret_copy_to_gbytes (parsed2.bin, parsed2.len); @@ -880,20 +880,6 @@ nm_crypto_md5_hash (const guint8 *salt, } } -char * -nm_crypto_encrypt (const char *cipher, - const guint8 *data, - gsize data_len, - const char *iv, - gsize iv_len, - const char *key, - gsize key_len, - gsize *out_len, - GError **error) -{ - return _nm_crypto_encrypt (cipher, data, data_len, iv, iv_len, key, key_len, out_len, error); -} - gboolean nm_crypto_randomize (void *buffer, gsize buffer_len, GError **error) { @@ -957,7 +943,7 @@ nmtst_crypto_rsa_key_encrypt (const guint8 *data, if (!key.str) g_return_val_if_reached (NULL); - enc.str = nm_crypto_encrypt (CIPHER_DES_EDE3_CBC, data, len, salt, sizeof (salt), key.str, key.len, &enc.len, error); + enc.str = _nmtst_crypto_encrypt (CIPHER_DES_EDE3_CBC, data, len, salt, sizeof (salt), key.str, key.len, &enc.len, error); if (!enc.str) return NULL; diff --git a/libnm-core/nm-crypto.h b/libnm-core/nm-crypto.h index 59c2f7c3e1..8294bdbe1b 100644 --- a/libnm-core/nm-crypto.h +++ b/libnm-core/nm-crypto.h @@ -88,16 +88,6 @@ char *nm_crypto_make_des_aes_key (const char *cipher, gsize *out_len, GError **error); -char * nm_crypto_encrypt (const char *cipher, - const guint8 *data, - gsize data_len, - const char *iv, - gsize iv_len, - const char *key, - gsize key_len, - gsize *out_len, - GError **error); - gboolean nm_crypto_randomize (void *buffer, gsize buffer_len, GError **error); /*****************************************************************************/ From 2456298da02579ca87b836d6552779c8803cfce2 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 31 Aug 2018 07:40:52 +0200 Subject: [PATCH 42/61] libnm/crypto: adjust signature of crypto functions - avoid "const gsize" as type for function arguments. - consistently use "guint8 *" type for binary data, instead of "char *", which indicates a NUL terminated C string. --- libnm-core/nm-crypto-gnutls.c | 24 +++++++++++----------- libnm-core/nm-crypto-impl.h | 38 +++++++++++++++++------------------ libnm-core/nm-crypto-nss.c | 22 ++++++++++---------- libnm-core/nm-crypto.c | 38 +++++++++++++++++------------------ libnm-core/nm-crypto.h | 12 +++++------ 5 files changed, 67 insertions(+), 67 deletions(-) diff --git a/libnm-core/nm-crypto-gnutls.c b/libnm-core/nm-crypto-gnutls.c index dea578fd29..be4ba572ed 100644 --- a/libnm-core/nm-crypto-gnutls.c +++ b/libnm-core/nm-crypto-gnutls.c @@ -55,15 +55,15 @@ _nm_crypto_init (GError **error) return TRUE; } -char * +guint8 * _nmtst_crypto_decrypt (const char *cipher, int key_type, const guint8 *data, gsize data_len, - const char *iv, - const gsize iv_len, - const char *key, - const gsize key_len, + const guint8 *iv, + gsize iv_len, + const guint8 *key, + gsize key_len, gsize *out_len, GError **error) { @@ -167,16 +167,16 @@ out: } } gnutls_cipher_deinit (ctx); - return output; + return (guint8 *) output; } -char * +guint8 * _nmtst_crypto_encrypt (const char *cipher, const guint8 *data, gsize data_len, - const char *iv, - const gsize iv_len, - const char *key, + const guint8 *iv, + gsize iv_len, + const guint8 *key, gsize key_len, gsize *out_len, GError **error) @@ -264,11 +264,11 @@ out: } } gnutls_cipher_deinit (ctx); - return output; + return (guint8 *) output; } gboolean -_nm_crypto_verify_x509 (const unsigned char *data, +_nm_crypto_verify_x509 (const guint8 *data, gsize len, GError **error) { diff --git a/libnm-core/nm-crypto-impl.h b/libnm-core/nm-crypto-impl.h index 49c5c7a328..65db9a28d3 100644 --- a/libnm-core/nm-crypto-impl.h +++ b/libnm-core/nm-crypto-impl.h @@ -51,25 +51,25 @@ gboolean _nm_crypto_verify_pkcs8 (const guint8 *data, /*****************************************************************************/ -char *_nmtst_crypto_encrypt (const char *cipher, - const guint8 *data, - gsize data_len, - const char *iv, - gsize iv_len, - const char *key, - gsize key_len, - gsize *out_len, - GError **error); +guint8 *_nmtst_crypto_encrypt (const char *cipher, + const guint8 *data, + gsize data_len, + const guint8 *iv, + gsize iv_len, + const guint8 *key, + gsize key_len, + gsize *out_len, + GError **error); -char *_nmtst_crypto_decrypt (const char *cipher, - int key_type, - const guint8 *data, - gsize data_len, - const char *iv, - const gsize iv_len, - const char *key, - const gsize key_len, - gsize *out_len, - GError **error); +guint8 *_nmtst_crypto_decrypt (const char *cipher, + int key_type, + const guint8 *data, + gsize data_len, + const guint8 *iv, + gsize iv_len, + const guint8 *key, + gsize key_len, + gsize *out_len, + GError **error); #endif /* __NM_CRYPTO_IMPL_H__ */ diff --git a/libnm-core/nm-crypto-nss.c b/libnm-core/nm-crypto-nss.c index 6ddc442862..a0c3eb1d39 100644 --- a/libnm-core/nm-crypto-nss.c +++ b/libnm-core/nm-crypto-nss.c @@ -71,15 +71,15 @@ _nm_crypto_init (GError **error) return TRUE; } -char * +guint8 * _nmtst_crypto_decrypt (const char *cipher, int key_type, const guint8 *data, gsize data_len, - const char *iv, - const gsize iv_len, - const char *key, - const gsize key_len, + const guint8 *iv, + gsize iv_len, + const guint8 *key, + gsize key_len, gsize *out_len, GError **error) { @@ -239,16 +239,16 @@ out: output = NULL; } } - return output; + return (guint8 *) output; } -char * +guint8 * _nmtst_crypto_encrypt (const char *cipher, const guint8 *data, gsize data_len, - const char *iv, + const guint8 *iv, gsize iv_len, - const char *key, + const guint8 *key, gsize key_len, gsize *out_len, GError **error) @@ -367,11 +367,11 @@ out: g_free (output); output = NULL; } - return (char *) output; + return (guint8 *) output; } gboolean -_nm_crypto_verify_x509 (const unsigned char *data, +_nm_crypto_verify_x509 (const guint8 *data, gsize len, GError **error) { diff --git a/libnm-core/nm-crypto.c b/libnm-core/nm-crypto.c index 684867593d..5fc5fc8b91 100644 --- a/libnm-core/nm-crypto.c +++ b/libnm-core/nm-crypto.c @@ -418,16 +418,16 @@ _nmtst_convert_iv (const char *src, return g_steal_pointer (&c); } -char * +guint8 * nm_crypto_make_des_aes_key (const char *cipher, - const char *salt, - const gsize salt_len, + const guint8 *salt, + gsize salt_len, const char *password, gsize *out_len, GError **error) { - char *key; - guint32 digest_len; + guint8 *key; + gsize digest_len; g_return_val_if_fail (cipher != NULL, NULL); g_return_val_if_fail (salt != NULL, NULL); @@ -460,11 +460,11 @@ nm_crypto_make_des_aes_key (const char *cipher, key = g_malloc0 (digest_len + 1); - nm_crypto_md5_hash ((guint8 *) salt, + nm_crypto_md5_hash (salt, 8, (guint8 *) password, strlen (password), - (guint8 *) key, + key, digest_len); *out_len = digest_len; @@ -505,21 +505,21 @@ _nmtst_decrypt_key (const char *cipher, } /* Convert the password and IV into a DES or AES key */ - key.str = nm_crypto_make_des_aes_key (cipher, bin_iv.str, bin_iv.len, password, &key.len, error); - if (!key.str || !key.len) + key.bin = nm_crypto_make_des_aes_key (cipher, bin_iv.bin, bin_iv.len, password, &key.len, error); + if (!key.bin || !key.len) return FALSE; - parsed->str = _nmtst_crypto_decrypt (cipher, + parsed->bin = _nmtst_crypto_decrypt (cipher, key_type, data, data_len, - bin_iv.str, + bin_iv.bin, bin_iv.len, - key.str, + key.bin, key.len, &parsed->len, error); - if (!parsed->str || parsed->len == 0) { + if (!parsed->bin || parsed->len == 0) { nm_secret_ptr_clear (parsed); return FALSE; } @@ -910,7 +910,7 @@ nmtst_crypto_rsa_key_encrypt (const guint8 *data, char **out_password, GError **error) { - char salt[8]; + guint8 salt[8]; nm_auto_clear_secret_ptr NMSecretPtr key = { 0 }; nm_auto_clear_secret_ptr NMSecretPtr enc = { 0 }; gs_unref_ptrarray GPtrArray *pem = NULL; @@ -939,12 +939,12 @@ nmtst_crypto_rsa_key_encrypt (const guint8 *data, if (!nm_crypto_randomize (salt, sizeof (salt), error)) return NULL; - key.str = nm_crypto_make_des_aes_key (CIPHER_DES_EDE3_CBC, &salt[0], sizeof (salt), in_password, &key.len, NULL); - if (!key.str) + key.bin = nm_crypto_make_des_aes_key (CIPHER_DES_EDE3_CBC, salt, sizeof (salt), in_password, &key.len, NULL); + if (!key.bin) g_return_val_if_reached (NULL); - enc.str = _nmtst_crypto_encrypt (CIPHER_DES_EDE3_CBC, data, len, salt, sizeof (salt), key.str, key.len, &enc.len, error); - if (!enc.str) + enc.bin = _nmtst_crypto_encrypt (CIPHER_DES_EDE3_CBC, data, len, salt, sizeof (salt), key.bin, key.len, &enc.len, error); + if (!enc.bin) return NULL; /* What follows is not the most efficient way to construct the pem @@ -964,7 +964,7 @@ nmtst_crypto_rsa_key_encrypt (const guint8 *data, g_ptr_array_add (pem, g_strdup ("\n\n")); /* Convert the encrypted key to a base64 string */ - enc_base64 = g_base64_encode ((const guchar *) enc.str, enc.len); + enc_base64 = g_base64_encode ((const guchar *) enc.bin, enc.len); enc_base64_len = strlen (enc_base64); for (p = enc_base64; (p - enc_base64) < (ptrdiff_t) enc_base64_len; p += 64) { g_ptr_array_add (pem, g_strndup (p, 64)); diff --git a/libnm-core/nm-crypto.h b/libnm-core/nm-crypto.h index 8294bdbe1b..70019211b3 100644 --- a/libnm-core/nm-crypto.h +++ b/libnm-core/nm-crypto.h @@ -81,12 +81,12 @@ void nm_crypto_md5_hash (const guint8 *salt, guint8 *buffer, gsize buflen); -char *nm_crypto_make_des_aes_key (const char *cipher, - const char *salt, - const gsize salt_len, - const char *password, - gsize *out_len, - GError **error); +guint8 *nm_crypto_make_des_aes_key (const char *cipher, + const guint8 *salt, + gsize salt_len, + const char *password, + gsize *out_len, + GError **error); gboolean nm_crypto_randomize (void *buffer, gsize buffer_len, GError **error); From 10724b38a884eabdccc90064c5c0f78c9c9a4a07 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 31 Aug 2018 07:51:10 +0200 Subject: [PATCH 43/61] libnm/crypto: don't initialize buffer for nm_crypto_make_des_aes_key() with zero @key is directly passed to nm_crypto_md5_hash(), which cannot (by API design) fail. No need to initialize it. Also, no need to allocate an additional trailing NUL byte. The key is binary, every attempt to use it as a string will horribly fail. --- libnm-core/nm-crypto.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libnm-core/nm-crypto.c b/libnm-core/nm-crypto.c index 5fc5fc8b91..1bd7236cf2 100644 --- a/libnm-core/nm-crypto.c +++ b/libnm-core/nm-crypto.c @@ -458,7 +458,7 @@ nm_crypto_make_des_aes_key (const char *cipher, if (password[0] == '\0') return NULL; - key = g_malloc0 (digest_len + 1); + key = g_malloc (digest_len); nm_crypto_md5_hash (salt, 8, From 1f550790bb731abbb83ec306283a805c5b3f0461 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 31 Aug 2018 09:50:02 +0200 Subject: [PATCH 44/61] libnm/crypto: remove unused argument key_type for decrypt functions --- libnm-core/nm-crypto-gnutls.c | 1 - libnm-core/nm-crypto-impl.h | 1 - libnm-core/nm-crypto-nss.c | 1 - libnm-core/nm-crypto.c | 3 --- 4 files changed, 6 deletions(-) diff --git a/libnm-core/nm-crypto-gnutls.c b/libnm-core/nm-crypto-gnutls.c index be4ba572ed..c8ee85b0d3 100644 --- a/libnm-core/nm-crypto-gnutls.c +++ b/libnm-core/nm-crypto-gnutls.c @@ -57,7 +57,6 @@ _nm_crypto_init (GError **error) guint8 * _nmtst_crypto_decrypt (const char *cipher, - int key_type, const guint8 *data, gsize data_len, const guint8 *iv, diff --git a/libnm-core/nm-crypto-impl.h b/libnm-core/nm-crypto-impl.h index 65db9a28d3..7502e55872 100644 --- a/libnm-core/nm-crypto-impl.h +++ b/libnm-core/nm-crypto-impl.h @@ -62,7 +62,6 @@ guint8 *_nmtst_crypto_encrypt (const char *cipher, GError **error); guint8 *_nmtst_crypto_decrypt (const char *cipher, - int key_type, const guint8 *data, gsize data_len, const guint8 *iv, diff --git a/libnm-core/nm-crypto-nss.c b/libnm-core/nm-crypto-nss.c index a0c3eb1d39..b083439092 100644 --- a/libnm-core/nm-crypto-nss.c +++ b/libnm-core/nm-crypto-nss.c @@ -73,7 +73,6 @@ _nm_crypto_init (GError **error) guint8 * _nmtst_crypto_decrypt (const char *cipher, - int key_type, const guint8 *data, gsize data_len, const guint8 *iv, diff --git a/libnm-core/nm-crypto.c b/libnm-core/nm-crypto.c index 1bd7236cf2..b9be68a989 100644 --- a/libnm-core/nm-crypto.c +++ b/libnm-core/nm-crypto.c @@ -473,7 +473,6 @@ nm_crypto_make_des_aes_key (const char *cipher, static gboolean _nmtst_decrypt_key (const char *cipher, - int key_type, const guint8 *data, gsize data_len, const char *iv, @@ -510,7 +509,6 @@ _nmtst_decrypt_key (const char *cipher, return FALSE; parsed->bin = _nmtst_crypto_decrypt (cipher, - key_type, data, data_len, bin_iv.bin, @@ -566,7 +564,6 @@ nmtst_crypto_decrypt_openssl_private_key_data (const guint8 *data, } if (!_nmtst_decrypt_key (cipher, - key_type, parsed.bin, parsed.len, iv, From 858d5c3e917a60f076c80873149a911c0dc2f497 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 31 Aug 2018 09:01:08 +0200 Subject: [PATCH 45/61] libnm/crypto: refactor to use enum for supported ciphers We need to (and already did) define our own identifier for ciphers, because the gnutls/nss identifiers must be abstracted. Don't use a string for that. The number of supported ciphers is not generic but fixed and known at compiler time. An enum is better suited. --- libnm-core/nm-crypto-gnutls.c | 88 +++++++++++----------- libnm-core/nm-crypto-impl.h | 4 +- libnm-core/nm-crypto-nss.c | 77 ++++++++++--------- libnm-core/nm-crypto.c | 136 +++++++++++++++++++++------------- libnm-core/nm-crypto.h | 25 +++++-- 5 files changed, 193 insertions(+), 137 deletions(-) diff --git a/libnm-core/nm-crypto-gnutls.c b/libnm-core/nm-crypto-gnutls.c index c8ee85b0d3..dd68ffca58 100644 --- a/libnm-core/nm-crypto-gnutls.c +++ b/libnm-core/nm-crypto-gnutls.c @@ -33,7 +33,32 @@ #include "nm-utils/nm-secret-utils.h" #include "nm-errors.h" -#define SALT_LEN 8 +/*****************************************************************************/ + +static gboolean +_get_cipher_info (NMCryptoCipherType cipher, + int *out_cipher_mech, + guint8 *out_real_iv_len) +{ + static const int cipher_mechs[] = { + [NM_CRYPTO_CIPHER_DES_EDE3_CBC] = GNUTLS_CIPHER_3DES_CBC, + [NM_CRYPTO_CIPHER_DES_CBC] = GNUTLS_CIPHER_DES_CBC, + [NM_CRYPTO_CIPHER_AES_128_CBC] = GNUTLS_CIPHER_AES_128_CBC, + [NM_CRYPTO_CIPHER_AES_192_CBC] = GNUTLS_CIPHER_AES_192_CBC, + [NM_CRYPTO_CIPHER_AES_256_CBC] = GNUTLS_CIPHER_AES_256_CBC, + }; + + g_return_val_if_fail (_NM_INT_NOT_NEGATIVE (cipher) && (gsize) cipher < G_N_ELEMENTS (cipher_mechs), FALSE); + + if (cipher_mechs[cipher] == 0) + return FALSE; + + NM_SET_OUT (out_cipher_mech, cipher_mechs[cipher]); + NM_SET_OUT (out_real_iv_len, nm_crypto_cipher_get_info (cipher)->real_iv_len); + return TRUE; +} + +/*****************************************************************************/ static gboolean initialized = FALSE; @@ -56,7 +81,7 @@ _nm_crypto_init (GError **error) } guint8 * -_nmtst_crypto_decrypt (const char *cipher, +_nmtst_crypto_decrypt (NMCryptoCipherType cipher, const guint8 *data, gsize data_len, const guint8 *iv, @@ -72,39 +97,24 @@ _nmtst_crypto_decrypt (const char *cipher, int cipher_mech, i; char *output = NULL; gboolean success = FALSE; - gsize pad_len, real_iv_len; + gsize pad_len; + guint8 real_iv_len; + + if (!_get_cipher_info (cipher, &cipher_mech, &real_iv_len)) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERROR_UNKNOWN_CIPHER, + _("Unsupported key cipher for decryption")); + return NULL; + } if (!_nm_crypto_init (error)) return NULL; - if (!strcmp (cipher, CIPHER_DES_EDE3_CBC)) { - cipher_mech = GNUTLS_CIPHER_3DES_CBC; - real_iv_len = SALT_LEN; - } else if (!strcmp (cipher, CIPHER_DES_CBC)) { - cipher_mech = GNUTLS_CIPHER_DES_CBC; - real_iv_len = SALT_LEN; - } else if (!strcmp (cipher, CIPHER_AES_128_CBC)) { - cipher_mech = GNUTLS_CIPHER_AES_128_CBC; - real_iv_len = 16; - } else if (!strcmp (cipher, CIPHER_AES_192_CBC)) { - cipher_mech = GNUTLS_CIPHER_AES_192_CBC; - real_iv_len = 16; - } else if (!strcmp (cipher, CIPHER_AES_256_CBC)) { - cipher_mech = GNUTLS_CIPHER_AES_256_CBC; - real_iv_len = 16; - } else { - g_set_error (error, NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_UNKNOWN_CIPHER, - _("Private key cipher '%s' was unknown."), - cipher); - return NULL; - } - if (iv_len < real_iv_len) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, - _("Invalid IV length (must be at least %zd)."), - real_iv_len); + _("Invalid IV length (must be at least %u)."), + (guint) real_iv_len); return NULL; } @@ -170,7 +180,7 @@ out: } guint8 * -_nmtst_crypto_encrypt (const char *cipher, +_nmtst_crypto_encrypt (NMCryptoCipherType cipher, const guint8 *data, gsize data_len, const guint8 *iv, @@ -190,25 +200,17 @@ _nmtst_crypto_encrypt (const char *cipher, char *padded_buf = NULL; guint32 i; - if (!_nm_crypto_init (error)) - return NULL; - - if (!strcmp (cipher, CIPHER_DES_EDE3_CBC)) - cipher_mech = GNUTLS_CIPHER_3DES_CBC; - else if (!strcmp (cipher, CIPHER_AES_128_CBC)) - cipher_mech = GNUTLS_CIPHER_AES_128_CBC; - else if (!strcmp (cipher, CIPHER_AES_192_CBC)) - cipher_mech = GNUTLS_CIPHER_AES_192_CBC; - else if (!strcmp (cipher, CIPHER_AES_256_CBC)) - cipher_mech = GNUTLS_CIPHER_AES_256_CBC; - else { + if ( cipher == NM_CRYPTO_CIPHER_DES_CBC + || !_get_cipher_info (cipher, &cipher_mech, NULL)) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_UNKNOWN_CIPHER, - _("Private key cipher '%s' was unknown."), - cipher); + _("Unsupported key cipher for encryption")); return NULL; } + if (!_nm_crypto_init (error)) + return NULL; + /* If data_len % ivlen == 0, then we add another complete block * onto the end so that the decrypter knows there's padding. */ diff --git a/libnm-core/nm-crypto-impl.h b/libnm-core/nm-crypto-impl.h index 7502e55872..918651525b 100644 --- a/libnm-core/nm-crypto-impl.h +++ b/libnm-core/nm-crypto-impl.h @@ -51,7 +51,7 @@ gboolean _nm_crypto_verify_pkcs8 (const guint8 *data, /*****************************************************************************/ -guint8 *_nmtst_crypto_encrypt (const char *cipher, +guint8 *_nmtst_crypto_encrypt (NMCryptoCipherType cipher, const guint8 *data, gsize data_len, const guint8 *iv, @@ -61,7 +61,7 @@ guint8 *_nmtst_crypto_encrypt (const char *cipher, gsize *out_len, GError **error); -guint8 *_nmtst_crypto_decrypt (const char *cipher, +guint8 *_nmtst_crypto_decrypt (NMCryptoCipherType cipher, const guint8 *data, gsize data_len, const guint8 *iv, diff --git a/libnm-core/nm-crypto-nss.c b/libnm-core/nm-crypto-nss.c index b083439092..1260b3b45f 100644 --- a/libnm-core/nm-crypto-nss.c +++ b/libnm-core/nm-crypto-nss.c @@ -38,6 +38,33 @@ #include "nm-utils/nm-secret-utils.h" #include "nm-errors.h" +/*****************************************************************************/ + +static gboolean +_get_cipher_info (NMCryptoCipherType cipher, + CK_MECHANISM_TYPE *out_cipher_mech, + guint8 *out_real_iv_len) +{ + static const CK_MECHANISM_TYPE cipher_mechs[] = { + [NM_CRYPTO_CIPHER_DES_EDE3_CBC] = CKM_DES3_CBC_PAD, + [NM_CRYPTO_CIPHER_DES_CBC] = CKM_DES_CBC_PAD, + [NM_CRYPTO_CIPHER_AES_128_CBC] = CKM_AES_CBC_PAD, + [NM_CRYPTO_CIPHER_AES_192_CBC] = CKM_AES_CBC_PAD, + [NM_CRYPTO_CIPHER_AES_256_CBC] = CKM_AES_CBC_PAD, + }; + + g_return_val_if_fail (_NM_INT_NOT_NEGATIVE (cipher) && (gsize) cipher < G_N_ELEMENTS (cipher_mechs), FALSE); + + if (!cipher_mechs[cipher]) + return FALSE; + + NM_SET_OUT (out_cipher_mech, cipher_mechs[cipher]); + NM_SET_OUT (out_real_iv_len, nm_crypto_cipher_get_info (cipher)->real_iv_len); + return TRUE; +} + +/*****************************************************************************/ + static gboolean initialized = FALSE; gboolean @@ -72,7 +99,7 @@ _nm_crypto_init (GError **error) } guint8 * -_nmtst_crypto_decrypt (const char *cipher, +_nmtst_crypto_decrypt (NMCryptoCipherType cipher, const guint8 *data, gsize data_len, const guint8 *iv, @@ -93,35 +120,24 @@ _nmtst_crypto_decrypt (const char *cipher, SECStatus s; gboolean success = FALSE; unsigned pad_len = 0, extra = 0; - guint32 i, real_iv_len = 0; + guint32 i; + guint8 real_iv_len; + + if (!_get_cipher_info (cipher, &cipher_mech, &real_iv_len)) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERROR_UNKNOWN_CIPHER, + _("Unsupported key cipher for decryption")); + return NULL; + } if (!_nm_crypto_init (error)) return NULL; - if (!strcmp (cipher, CIPHER_DES_EDE3_CBC)) { - cipher_mech = CKM_DES3_CBC_PAD; - real_iv_len = 8; - } else if (!strcmp (cipher, CIPHER_DES_CBC)) { - cipher_mech = CKM_DES_CBC_PAD; - real_iv_len = 8; - } else if (NM_IN_STRSET (cipher, CIPHER_AES_128_CBC, - CIPHER_AES_192_CBC, - CIPHER_AES_256_CBC)) { - cipher_mech = CKM_AES_CBC_PAD; - real_iv_len = 16; - } else { - g_set_error (error, NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_UNKNOWN_CIPHER, - _("Private key cipher '%s' was unknown."), - cipher); - return NULL; - } - if (iv_len < real_iv_len) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, - _("Invalid IV length (must be at least %d)."), - real_iv_len); + _("Invalid IV length (must be at least %u)."), + (guint) real_iv_len); return NULL; } @@ -242,7 +258,7 @@ out: } guint8 * -_nmtst_crypto_encrypt (const char *cipher, +_nmtst_crypto_encrypt (NMCryptoCipherType cipher, const guint8 *data, gsize data_len, const guint8 *iv, @@ -269,18 +285,11 @@ _nmtst_crypto_encrypt (const char *cipher, if (!_nm_crypto_init (error)) return NULL; - if (!strcmp (cipher, CIPHER_DES_EDE3_CBC)) - cipher_mech = CKM_DES3_CBC_PAD; - else if (NM_IN_STRSET (cipher, - CIPHER_AES_128_CBC, - CIPHER_AES_192_CBC, - CIPHER_AES_256_CBC)) - cipher_mech = CKM_AES_CBC_PAD; - else { + if ( cipher == NM_CRYPTO_CIPHER_DES_CBC + || !_get_cipher_info (cipher, &cipher_mech, NULL)) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_UNKNOWN_CIPHER, - _("Private key cipher '%s' was unknown."), - cipher); + _("Unsupported key cipher for encryption")); return NULL; } diff --git a/libnm-core/nm-crypto.c b/libnm-core/nm-crypto.c index b9be68a989..7fca22b8c7 100644 --- a/libnm-core/nm-crypto.c +++ b/libnm-core/nm-crypto.c @@ -54,6 +54,64 @@ /*****************************************************************************/ +static const NMCryptoCipherInfo cipher_infos[] = { +#define _CI(_cipher, _name, _digest_len, _real_iv_len) \ + [(_cipher) - 1] = { .cipher = _cipher, .name = ""_name"", .digest_len = _digest_len, .real_iv_len = _real_iv_len } + _CI (NM_CRYPTO_CIPHER_DES_EDE3_CBC, "DES-EDE3-CBC", 24, 8), + _CI (NM_CRYPTO_CIPHER_DES_CBC, "DES-CBC", 8, 8), + _CI (NM_CRYPTO_CIPHER_AES_128_CBC, "AES-128-CBC", 16, 16), + _CI (NM_CRYPTO_CIPHER_AES_192_CBC, "AES-192-CBC", 24, 16), + _CI (NM_CRYPTO_CIPHER_AES_256_CBC, "AES-256-CBC", 32, 16), +}; + +const NMCryptoCipherInfo * +nm_crypto_cipher_get_info (NMCryptoCipherType cipher) +{ + g_return_val_if_fail (cipher > NM_CRYPTO_CIPHER_UNKNOWN && (gsize) cipher < G_N_ELEMENTS (cipher_infos) + 1, NULL); + +#if NM_MORE_ASSERTS > 10 + { + int i, j; + + for (i = 0; i < (int) G_N_ELEMENTS (cipher_infos); i++) { + const NMCryptoCipherInfo *info = &cipher_infos[i]; + + nm_assert (info->cipher == (NMCryptoCipherType) (i + 1)); + nm_assert (info->name && info->name[0]); + for (j = 0; j < i; j++) + nm_assert (g_ascii_strcasecmp (info->name, cipher_infos[j].name) != 0); + } + } +#endif + + return &cipher_infos[cipher - 1]; +} + +const NMCryptoCipherInfo * +nm_crypto_cipher_get_info_by_name (const char *cipher_name, gssize p_len) +{ + int i; + + nm_assert (nm_crypto_cipher_get_info (NM_CRYPTO_CIPHER_DES_CBC)->cipher == NM_CRYPTO_CIPHER_DES_CBC); + + if (p_len < 0) { + if (!cipher_name) + return FALSE; + p_len = strlen (cipher_name); + } + + for (i = 0; i < (int) G_N_ELEMENTS (cipher_infos); i++) { + const NMCryptoCipherInfo *info = &cipher_infos[i]; + + if ( (gsize) p_len == strlen (info->name) + && g_ascii_strncasecmp (info->name, cipher_name, p_len) == 0) + return info; + } + return NULL; +} + +/*****************************************************************************/ + static gboolean find_tag (const char *tag, const guint8 *data, @@ -119,7 +177,7 @@ parse_old_openssl_key_file (const guint8 *data, gsize data_len, NMSecretPtr *out_parsed, NMCryptoKeyType *out_key_type, - const char **out_cipher, + NMCryptoCipherType *out_cipher, char **out_iv, GError **error) { @@ -132,7 +190,7 @@ parse_old_openssl_key_file (const guint8 *data, nm_auto_clear_secret_ptr NMSecretPtr parsed = { 0 }; nm_auto_clear_secret_ptr NMSecretPtr data_content = { 0 }; nm_auto_free_secret char *iv = NULL; - const char *cipher = NULL; + NMCryptoCipherType cipher = NM_CRYPTO_CIPHER_UNKNOWN; const char *start_tag; const char *end_tag; const guint8 *data_start, *data_end; @@ -141,7 +199,7 @@ parse_old_openssl_key_file (const guint8 *data, nm_assert (!out_iv || !*out_iv); NM_SET_OUT (out_key_type, NM_CRYPTO_KEY_TYPE_UNKNOWN); - *out_cipher = NULL; + NM_SET_OUT (out_cipher, NM_CRYPTO_CIPHER_UNKNOWN); if (find_tag (PEM_RSA_KEY_BEGIN, data, data_len, 0, &start)) { key_type = NM_CRYPTO_KEY_TYPE_RSA; @@ -202,14 +260,9 @@ parse_old_openssl_key_file (const guint8 *data, return FALSE; } } else if (!strncmp (p, DEK_INFO_TAG, strlen (DEK_INFO_TAG))) { - static const char *const known_ciphers[] = { CIPHER_DES_EDE3_CBC, - CIPHER_DES_CBC, - CIPHER_AES_128_CBC, - CIPHER_AES_192_CBC, - CIPHER_AES_256_CBC }; + const NMCryptoCipherInfo *cipher_info; char *comma; gsize p_len; - guint i; if (enc_tags++ != 1 || str_p != str) { g_set_error (error, NM_CRYPTO_ERROR, @@ -240,20 +293,15 @@ parse_old_openssl_key_file (const guint8 *data, iv = g_strdup (comma); /* Get the private key cipher */ - for (i = 0; i < G_N_ELEMENTS (known_ciphers); i++) { - if ( strlen (known_ciphers[i]) == p_len - && !g_ascii_strncasecmp (p, known_ciphers[i], p_len)) { - cipher = known_ciphers[i]; - break; - } - } - if (!cipher) { + cipher_info = nm_crypto_cipher_get_info_by_name (p, p_len); + if (!cipher_info) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("Malformed PEM file: unknown private key cipher '%s'."), p); return FALSE; } + cipher = cipher_info->cipher; } else { if (enc_tags == 1) { g_set_error (error, NM_CRYPTO_ERROR, @@ -277,7 +325,7 @@ parse_old_openssl_key_file (const guint8 *data, NM_SET_OUT (out_key_type, key_type); NM_SET_OUT (out_iv, g_steal_pointer (&iv)); - *out_cipher = cipher; + NM_SET_OUT (out_cipher, cipher); nm_secret_ptr_move (out_parsed, &parsed); return TRUE; } @@ -419,7 +467,7 @@ _nmtst_convert_iv (const char *src, } guint8 * -nm_crypto_make_des_aes_key (const char *cipher, +nm_crypto_make_des_aes_key (NMCryptoCipherType cipher, const guint8 *salt, gsize salt_len, const char *password, @@ -427,9 +475,8 @@ nm_crypto_make_des_aes_key (const char *cipher, GError **error) { guint8 *key; - gsize digest_len; + const NMCryptoCipherInfo *cipher_info; - g_return_val_if_fail (cipher != NULL, NULL); g_return_val_if_fail (salt != NULL, NULL); g_return_val_if_fail (salt_len >= 8, NULL); g_return_val_if_fail (password != NULL, NULL); @@ -437,42 +484,28 @@ nm_crypto_make_des_aes_key (const char *cipher, *out_len = 0; - if (!strcmp (cipher, CIPHER_DES_EDE3_CBC)) - digest_len = 24; - else if (!strcmp (cipher, CIPHER_DES_CBC)) - digest_len = 8; - else if (!strcmp (cipher, CIPHER_AES_128_CBC)) - digest_len = 16; - else if (!strcmp (cipher, CIPHER_AES_192_CBC)) - digest_len = 24; - else if (!strcmp (cipher, CIPHER_AES_256_CBC)) - digest_len = 32; - else { - g_set_error (error, NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_UNKNOWN_CIPHER, - _("Private key cipher '%s' was unknown."), - cipher); - return NULL; - } + cipher_info = nm_crypto_cipher_get_info (cipher); + + g_return_val_if_fail (cipher_info, NULL); if (password[0] == '\0') return NULL; - key = g_malloc (digest_len); + key = g_malloc (cipher_info->digest_len); nm_crypto_md5_hash (salt, 8, (guint8 *) password, strlen (password), key, - digest_len); + cipher_info->digest_len); - *out_len = digest_len; + *out_len = cipher_info->digest_len; return key; } static gboolean -_nmtst_decrypt_key (const char *cipher, +_nmtst_decrypt_key (NMCryptoCipherType cipher, const guint8 *data, gsize data_len, const char *iv, @@ -485,7 +518,7 @@ _nmtst_decrypt_key (const char *cipher, gs_free char *output = NULL; nm_assert (password); - nm_assert (cipher); + nm_assert (cipher != NM_CRYPTO_CIPHER_UNKNOWN); nm_assert (iv); nm_assert (parsed); nm_assert (!parsed->bin); @@ -535,7 +568,7 @@ nmtst_crypto_decrypt_openssl_private_key_data (const guint8 *data, NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN; nm_auto_clear_secret_ptr NMSecretPtr parsed = { 0 }; nm_auto_free_secret char *iv = NULL; - const char *cipher = NULL; + NMCryptoCipherType cipher = NM_CRYPTO_CIPHER_UNKNOWN; g_return_val_if_fail (data != NULL, NULL); @@ -556,7 +589,7 @@ nmtst_crypto_decrypt_openssl_private_key_data (const guint8 *data, if (password) { nm_auto_clear_secret_ptr NMSecretPtr parsed2 = { 0 }; - if (!cipher || !iv) { + if (cipher == NM_CRYPTO_CIPHER_UNKNOWN || !iv) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_PASSWORD, _("Password provided, but key was not encrypted.")); @@ -575,7 +608,7 @@ nmtst_crypto_decrypt_openssl_private_key_data (const guint8 *data, return nm_secret_copy_to_gbytes (parsed2.bin, parsed2.len); } - if (cipher || iv) + if (cipher != NM_CRYPTO_CIPHER_UNKNOWN || iv) return NULL; return nm_secret_copy_to_gbytes (parsed.bin, parsed.len); @@ -796,13 +829,13 @@ nm_crypto_verify_private_key_data (const guint8 *data, || _nm_crypto_verify_pkcs8 (parsed.bin, parsed.len, is_encrypted, password, error)) format = NM_CRYPTO_FILE_FORMAT_RAW_KEY; } else { - const char *cipher; + NMCryptoCipherType cipher; nm_auto_free_secret char *iv = NULL; /* Or it's old-style OpenSSL */ if (parse_old_openssl_key_file (data, data_len, NULL, NULL, &cipher, &iv, NULL)) { format = NM_CRYPTO_FILE_FORMAT_RAW_KEY; - is_encrypted = (cipher && iv); + is_encrypted = (cipher != NM_CRYPTO_CIPHER_UNKNOWN && iv); } } } @@ -936,11 +969,11 @@ nmtst_crypto_rsa_key_encrypt (const guint8 *data, if (!nm_crypto_randomize (salt, sizeof (salt), error)) return NULL; - key.bin = nm_crypto_make_des_aes_key (CIPHER_DES_EDE3_CBC, salt, sizeof (salt), in_password, &key.len, NULL); + key.bin = nm_crypto_make_des_aes_key (NM_CRYPTO_CIPHER_DES_EDE3_CBC, salt, sizeof (salt), in_password, &key.len, NULL); if (!key.bin) g_return_val_if_reached (NULL); - enc.bin = _nmtst_crypto_encrypt (CIPHER_DES_EDE3_CBC, data, len, salt, sizeof (salt), key.bin, key.len, &enc.len, error); + enc.bin = _nmtst_crypto_encrypt (NM_CRYPTO_CIPHER_DES_EDE3_CBC, data, len, salt, sizeof (salt), key.bin, key.len, &enc.len, error); if (!enc.bin) return NULL; @@ -956,7 +989,8 @@ nmtst_crypto_rsa_key_encrypt (const guint8 *data, g_ptr_array_add (pem, g_strdup ("Proc-Type: 4,ENCRYPTED\n")); /* Convert the salt to a hex string */ - g_ptr_array_add (pem, g_strdup ("DEK-Info: "CIPHER_DES_EDE3_CBC",")); + g_ptr_array_add (pem, g_strdup_printf ("DEK-Info: %s,", + nm_crypto_cipher_get_info (NM_CRYPTO_CIPHER_DES_EDE3_CBC)->name)); g_ptr_array_add (pem, nm_utils_bin2hexstr (salt, sizeof (salt), sizeof (salt) * 2)); g_ptr_array_add (pem, g_strdup ("\n\n")); diff --git a/libnm-core/nm-crypto.h b/libnm-core/nm-crypto.h index 70019211b3..3ce9476488 100644 --- a/libnm-core/nm-crypto.h +++ b/libnm-core/nm-crypto.h @@ -28,13 +28,24 @@ #error Cannot use this header. #endif -#define MD5_HASH_LEN 20 +typedef enum { + NM_CRYPTO_CIPHER_UNKNOWN, + NM_CRYPTO_CIPHER_DES_EDE3_CBC, + NM_CRYPTO_CIPHER_DES_CBC, + NM_CRYPTO_CIPHER_AES_128_CBC, + NM_CRYPTO_CIPHER_AES_192_CBC, + NM_CRYPTO_CIPHER_AES_256_CBC, +} NMCryptoCipherType; -#define CIPHER_DES_EDE3_CBC "DES-EDE3-CBC" -#define CIPHER_DES_CBC "DES-CBC" -#define CIPHER_AES_128_CBC "AES-128-CBC" -#define CIPHER_AES_192_CBC "AES-192-CBC" -#define CIPHER_AES_256_CBC "AES-256-CBC" +typedef struct { + const char *name; + NMCryptoCipherType cipher; + guint8 digest_len; + guint8 real_iv_len; +} NMCryptoCipherInfo; + +const NMCryptoCipherInfo *nm_crypto_cipher_get_info (NMCryptoCipherType cipher); +const NMCryptoCipherInfo *nm_crypto_cipher_get_info_by_name (const char *cipher_name, gssize p_len); typedef enum { NM_CRYPTO_KEY_TYPE_UNKNOWN = 0, @@ -81,7 +92,7 @@ void nm_crypto_md5_hash (const guint8 *salt, guint8 *buffer, gsize buflen); -guint8 *nm_crypto_make_des_aes_key (const char *cipher, +guint8 *nm_crypto_make_des_aes_key (NMCryptoCipherType cipher, const guint8 *salt, gsize salt_len, const char *password, From 08c80dd2e32d4b50786c8c8fd5308e513c1bc90d Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 31 Aug 2018 10:59:31 +0200 Subject: [PATCH 46/61] libnm/crypto: rework endianness detection for crypto_verify_pkcs12() At other places, we already use __BYTE_ORDER define to detect endianness. We don't need multiple mechanisms. Also note that meson did not do the correct thing as AC_C_BIGENDIAN, so meson + nss + big-endian was possibly broken. --- config.h.meson | 12 ------------ configure.ac | 5 ----- libnm-core/nm-crypto-nss.c | 15 ++++++++------- libnm-util/crypto_nss.c | 15 ++++++++------- 4 files changed, 16 insertions(+), 31 deletions(-) diff --git a/config.h.meson b/config.h.meson index c5fbf5fab3..ce77952bec 100644 --- a/config.h.meson +++ b/config.h.meson @@ -226,18 +226,6 @@ /* Define if you have iwd support */ #mesondefine WITH_IWD -/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most - significant byte first (like Motorola and SPARC, unlike Intel). */ -#if defined AC_APPLE_UNIVERSAL_BUILD -# if defined __BIG_ENDIAN__ -# define WORDS_BIGENDIAN 1 -# endif -#else -# ifndef WORDS_BIGENDIAN -/* # undef WORDS_BIGENDIAN */ -# endif -#endif - /* Define to 1 if on MINIX. */ #mesondefine _MINIX diff --git a/configure.ac b/configure.ac index 62c7e0392e..c104c9b930 100644 --- a/configure.ac +++ b/configure.ac @@ -109,11 +109,6 @@ GETTEXT_PACKAGE=NetworkManager AC_SUBST(GETTEXT_PACKAGE) AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE", [Gettext package]) -dnl -dnl Make sha1.c happy on big endian systems -dnl -AC_C_BIGENDIAN - # Add runstatedir if not specified manually in autoconf < 2.70 AS_IF([test -z "$runstatedir"], runstatedir="$localstatedir/run") AC_SUBST(runstatedir) diff --git a/libnm-core/nm-crypto-nss.c b/libnm-core/nm-crypto-nss.c index 1260b3b45f..feb081e621 100644 --- a/libnm-core/nm-crypto-nss.c +++ b/libnm-core/nm-crypto-nss.c @@ -414,9 +414,6 @@ _nm_crypto_verify_pkcs12 (const guint8 *data, SECStatus s; gunichar2 *ucs2_password; long ucs2_chars = 0; -#ifndef WORDS_BIGENDIAN - guint16 *p; -#endif /* WORDS_BIGENDIAN */ if (error) g_return_val_if_fail (*error == NULL, FALSE); @@ -446,10 +443,14 @@ _nm_crypto_verify_pkcs12 (const guint8 *data, nm_explicit_bzero (ucs2_password, ucs2_chars); g_free (ucs2_password); -#ifndef WORDS_BIGENDIAN - for (p = (guint16 *) pw.data; p < (guint16 *) (pw.data + pw.len); p++) - *p = GUINT16_SWAP_LE_BE (*p); -#endif /* WORDS_BIGENDIAN */ +#if __BYTE_ORDER == __LITTLE_ENDIAN + { + guint16 *p; + + for (p = (guint16 *) pw.data; p < (guint16 *) (pw.data + pw.len); p++) + *p = GUINT16_SWAP_LE_BE (*p); + } +#endif } else { /* NULL password */ pw.data = NULL; diff --git a/libnm-util/crypto_nss.c b/libnm-util/crypto_nss.c index 5736db4f89..01bb28c33d 100644 --- a/libnm-util/crypto_nss.c +++ b/libnm-util/crypto_nss.c @@ -442,9 +442,6 @@ crypto_verify_pkcs12 (const GByteArray *data, SECStatus s; char *ucs2_password; long ucs2_chars = 0; -#ifndef WORDS_BIGENDIAN - guint16 *p; -#endif /* WORDS_BIGENDIAN */ if (error) g_return_val_if_fail (*error == NULL, FALSE); @@ -470,10 +467,14 @@ crypto_verify_pkcs12 (const GByteArray *data, memset (ucs2_password, 0, ucs2_chars); g_free (ucs2_password); -#ifndef WORDS_BIGENDIAN - for (p = (guint16 *) pw.data; p < (guint16 *) (pw.data + pw.len); p++) - *p = GUINT16_SWAP_LE_BE (*p); -#endif /* WORDS_BIGENDIAN */ +#if __BYTE_ORDER == __LITTLE_ENDIAN + { + guint16 *p; + + for (p = (guint16 *) pw.data; p < (guint16 *) (pw.data + pw.len); p++) + *p = GUINT16_SWAP_LE_BE (*p); + } +#endif } else { /* NULL password */ pw.data = NULL; From 116ee7a4bf183656736755ad9de0c123ca2779b8 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 30 Aug 2018 21:03:01 +0200 Subject: [PATCH 47/61] libnm/crypto: clean crypto implementations for gnutls/nss - refactor to use cleanup attribute and return-early - reorder some code --- libnm-core/nm-crypto-gnutls.c | 150 ++++++++++++------------- libnm-core/nm-crypto-nss.c | 202 +++++++++++++++++----------------- 2 files changed, 171 insertions(+), 181 deletions(-) diff --git a/libnm-core/nm-crypto-gnutls.c b/libnm-core/nm-crypto-gnutls.c index dd68ffca58..6c897e6dd3 100644 --- a/libnm-core/nm-crypto-gnutls.c +++ b/libnm-core/nm-crypto-gnutls.c @@ -60,16 +60,16 @@ _get_cipher_info (NMCryptoCipherType cipher, /*****************************************************************************/ -static gboolean initialized = FALSE; - gboolean _nm_crypto_init (GError **error) { + static gboolean initialized = FALSE; + if (initialized) return TRUE; - if (gnutls_global_init() != 0) { - gnutls_global_deinit(); + if (gnutls_global_init () != 0) { + gnutls_global_deinit (); g_set_error_literal (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_FAILED, _("Failed to initialize the crypto engine.")); @@ -80,6 +80,8 @@ _nm_crypto_init (GError **error) return TRUE; } +/*****************************************************************************/ + guint8 * _nmtst_crypto_decrypt (NMCryptoCipherType cipher, const guint8 *data, @@ -94,10 +96,9 @@ _nmtst_crypto_decrypt (NMCryptoCipherType cipher, gnutls_cipher_hd_t ctx; gnutls_datum_t key_dt, iv_dt; int err; - int cipher_mech, i; - char *output = NULL; - gboolean success = FALSE; - gsize pad_len; + int cipher_mech; + nm_auto_clear_secret_ptr NMSecretPtr output = { 0 }; + guint8 pad_i, pad_len; guint8 real_iv_len; if (!_get_cipher_info (cipher, &cipher_mech, &real_iv_len)) { @@ -118,7 +119,8 @@ _nmtst_crypto_decrypt (NMCryptoCipherType cipher, return NULL; } - output = g_malloc0 (data_len); + output.len = data_len; + output.bin = g_malloc (data_len); key_dt.data = (unsigned char *) key; key_dt.size = key_len; @@ -131,52 +133,48 @@ _nmtst_crypto_decrypt (NMCryptoCipherType cipher, NM_CRYPTO_ERROR_DECRYPTION_FAILED, _("Failed to initialize the decryption cipher context: %s (%s)"), gnutls_strerror_name (err), gnutls_strerror (err)); - goto out; + return NULL; } - err = gnutls_cipher_decrypt2 (ctx, data, data_len, output, data_len); + err = gnutls_cipher_decrypt2 (ctx, data, data_len, output.bin, output.len); + + gnutls_cipher_deinit (ctx); + if (err < 0) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_DECRYPTION_FAILED, _("Failed to decrypt the private key: %s (%s)"), gnutls_strerror_name (err), gnutls_strerror (err)); - goto out; + return NULL; } - pad_len = output[data_len - 1]; + + pad_len = output.len > 0 + ? output.bin[output.len - 1] + : 0; /* Check if the padding at the end of the decrypted data is valid */ - if (pad_len == 0 || pad_len > real_iv_len) { + if ( pad_len == 0 + || pad_len > real_iv_len) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_DECRYPTION_FAILED, _("Failed to decrypt the private key: unexpected padding length.")); - goto out; + return NULL; } /* Validate tail padding; last byte is the padding size, and all pad bytes * should contain the padding size. */ - for (i = 1; i <= pad_len; ++i) { - if (output[data_len - i] != pad_len) { + for (pad_i = 1; pad_i <= pad_len; ++pad_i) { + if (output.bin[data_len - pad_i] != pad_len) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_DECRYPTION_FAILED, _("Failed to decrypt the private key.")); - goto out; + return NULL; } } - *out_len = data_len - pad_len; - success = TRUE; - -out: - if (!success) { - if (output) { - nm_explicit_bzero (output, data_len); - g_free (output); - output = NULL; - } - } - gnutls_cipher_deinit (ctx); - return (guint8 *) output; + *out_len = output.len - pad_len; + return g_steal_pointer (&output.bin); } guint8 * @@ -194,11 +192,11 @@ _nmtst_crypto_encrypt (NMCryptoCipherType cipher, gnutls_datum_t key_dt, iv_dt; int err; int cipher_mech; - char *output = NULL; - gboolean success = FALSE; - gsize padded_buf_len, pad_len, output_len; - char *padded_buf = NULL; - guint32 i; + nm_auto_clear_secret_ptr NMSecretPtr output = { 0 }; + nm_auto_clear_secret_ptr NMSecretPtr padded_buf = { 0 }; + gsize i, pad_len; + + nm_assert (iv_len); if ( cipher == NM_CRYPTO_CIPHER_DES_CBC || !_get_cipher_info (cipher, &cipher_mech, NULL)) { @@ -211,19 +209,6 @@ _nmtst_crypto_encrypt (NMCryptoCipherType cipher, if (!_nm_crypto_init (error)) return NULL; - /* If data_len % ivlen == 0, then we add another complete block - * onto the end so that the decrypter knows there's padding. - */ - pad_len = iv_len - (data_len % iv_len); - output_len = padded_buf_len = data_len + pad_len; - padded_buf = g_malloc0 (padded_buf_len); - - memcpy (padded_buf, data, data_len); - for (i = 0; i < pad_len; i++) - padded_buf[data_len + i] = (guint8) (pad_len & 0xFF); - - output = g_malloc0 (output_len); - key_dt.data = (unsigned char *) key; key_dt.size = key_len; iv_dt.data = (unsigned char *) iv; @@ -235,37 +220,37 @@ _nmtst_crypto_encrypt (NMCryptoCipherType cipher, NM_CRYPTO_ERROR_ENCRYPTION_FAILED, _("Failed to initialize the encryption cipher context: %s (%s)"), gnutls_strerror_name (err), gnutls_strerror (err)); - goto out; + return NULL; } - err = gnutls_cipher_encrypt2 (ctx, padded_buf, padded_buf_len, output, output_len); + /* If data_len % ivlen == 0, then we add another complete block + * onto the end so that the decrypter knows there's padding. + */ + pad_len = iv_len - (data_len % iv_len); + + padded_buf.len = data_len + pad_len; + padded_buf.bin = g_malloc (padded_buf.len); + memcpy (padded_buf.bin, data, data_len); + for (i = 0; i < pad_len; i++) + padded_buf.bin[data_len + i] = (guint8) (pad_len & 0xFF); + + output.len = padded_buf.len; + output.bin = g_malloc (output.len); + + err = gnutls_cipher_encrypt2 (ctx, padded_buf.bin, padded_buf.len, output.bin, output.len); + + gnutls_cipher_deinit (ctx); + if (err < 0) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_ENCRYPTION_FAILED, _("Failed to encrypt the data: %s (%s)"), gnutls_strerror_name (err), gnutls_strerror (err)); - goto out; + return NULL; } - *out_len = output_len; - success = TRUE; - -out: - if (padded_buf) { - nm_explicit_bzero (padded_buf, padded_buf_len); - g_free (padded_buf); - padded_buf = NULL; - } - - if (!success) { - if (output) { - nm_explicit_bzero (output, output_len); - g_free (output); - output = NULL; - } - } - gnutls_cipher_deinit (ctx); - return (guint8 *) output; + *out_len = output.len; + return g_steal_pointer (&output.bin); } gboolean @@ -319,7 +304,6 @@ _nm_crypto_verify_pkcs12 (const guint8 *data, { gnutls_pkcs12_t p12; gnutls_datum_t dt; - gboolean success = FALSE; int err; g_return_val_if_fail (data != NULL, FALSE); @@ -349,23 +333,24 @@ _nm_crypto_verify_pkcs12 (const guint8 *data, NM_CRYPTO_ERROR_INVALID_DATA, _("Couldn't decode PKCS#12 file: %s"), gnutls_strerror (err)); - goto out; + gnutls_pkcs12_deinit (p12); + return FALSE; } } err = gnutls_pkcs12_verify_mac (p12, password); - if (err == GNUTLS_E_SUCCESS) - success = TRUE; - else { + + gnutls_pkcs12_deinit (p12); + + if (err != GNUTLS_E_SUCCESS) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_DECRYPTION_FAILED, _("Couldn't verify PKCS#12 file: %s"), gnutls_strerror (err)); + return FALSE; } -out: - gnutls_pkcs12_deinit (p12); - return success; + return TRUE; } gboolean @@ -384,9 +369,6 @@ _nm_crypto_verify_pkcs8 (const guint8 *data, if (!_nm_crypto_init (error)) return FALSE; - dt.data = (unsigned char *) data; - dt.size = data_len; - err = gnutls_x509_privkey_init (&p8); if (err < 0) { g_set_error (error, NM_CRYPTO_ERROR, @@ -396,11 +378,15 @@ _nm_crypto_verify_pkcs8 (const guint8 *data, return FALSE; } + dt.data = (unsigned char *) data; + dt.size = data_len; + err = gnutls_x509_privkey_import_pkcs8 (p8, &dt, GNUTLS_X509_FMT_DER, is_encrypted ? password : NULL, is_encrypted ? 0 : GNUTLS_PKCS_PLAIN); + gnutls_x509_privkey_deinit (p8); if (err < 0) { diff --git a/libnm-core/nm-crypto-nss.c b/libnm-core/nm-crypto-nss.c index feb081e621..711dde4baf 100644 --- a/libnm-core/nm-crypto-nss.c +++ b/libnm-core/nm-crypto-nss.c @@ -65,17 +65,16 @@ _get_cipher_info (NMCryptoCipherType cipher, /*****************************************************************************/ -static gboolean initialized = FALSE; - gboolean _nm_crypto_init (GError **error) { + static gboolean initialized = FALSE; SECStatus ret; if (initialized) return TRUE; - PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 1); + PR_Init (PR_USER_THREAD, PR_PRIORITY_NORMAL, 1); ret = NSS_NoDB_Init (NULL); if (ret != SECSuccess) { g_set_error (error, NM_CRYPTO_ERROR, @@ -86,13 +85,13 @@ _nm_crypto_init (GError **error) return FALSE; } - SEC_PKCS12EnableCipher(PKCS12_RC4_40, 1); - SEC_PKCS12EnableCipher(PKCS12_RC4_128, 1); - SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_40, 1); - SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_128, 1); - SEC_PKCS12EnableCipher(PKCS12_DES_56, 1); - SEC_PKCS12EnableCipher(PKCS12_DES_EDE3_168, 1); - SEC_PKCS12SetPreferredCipher(PKCS12_DES_EDE3_168, 1); + SEC_PKCS12EnableCipher (PKCS12_RC4_40, 1); + SEC_PKCS12EnableCipher (PKCS12_RC4_128, 1); + SEC_PKCS12EnableCipher (PKCS12_RC2_CBC_40, 1); + SEC_PKCS12EnableCipher (PKCS12_RC2_CBC_128, 1); + SEC_PKCS12EnableCipher (PKCS12_DES_56, 1); + SEC_PKCS12EnableCipher (PKCS12_DES_EDE3_168, 1); + SEC_PKCS12SetPreferredCipher (PKCS12_DES_EDE3_168, 1); initialized = TRUE; return TRUE; @@ -109,17 +108,18 @@ _nmtst_crypto_decrypt (NMCryptoCipherType cipher, gsize *out_len, GError **error) { - char *output = NULL; - int decrypted_len = 0; CK_MECHANISM_TYPE cipher_mech; PK11SlotInfo *slot = NULL; SECItem key_item; PK11SymKey *sym_key = NULL; SECItem *sec_param = NULL; PK11Context *ctx = NULL; + nm_auto_clear_secret_ptr NMSecretPtr output = { 0 }; SECStatus s; gboolean success = FALSE; - unsigned pad_len = 0, extra = 0; + int decrypted_len = 0; + unsigned extra = 0; + unsigned pad_len = 0; guint32 i; guint8 real_iv_len; @@ -130,9 +130,6 @@ _nmtst_crypto_decrypt (NMCryptoCipherType cipher, return NULL; } - if (!_nm_crypto_init (error)) - return NULL; - if (iv_len < real_iv_len) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, @@ -141,7 +138,8 @@ _nmtst_crypto_decrypt (NMCryptoCipherType cipher, return NULL; } - output = g_malloc0 (data_len); + if (!_nm_crypto_init (error)) + return NULL; slot = PK11_GetBestSlot (cipher_mech, NULL); if (!slot) { @@ -179,10 +177,13 @@ _nmtst_crypto_decrypt (NMCryptoCipherType cipher, goto out; } + output.len = data_len; + output.bin = g_malloc (data_len); + s = PK11_CipherOp (ctx, - (unsigned char *) output, + (unsigned char *) output.bin, &decrypted_len, - data_len, + output.len, data, data_len); if (s != SECSuccess) { @@ -201,7 +202,7 @@ _nmtst_crypto_decrypt (NMCryptoCipherType cipher, } s = PK11_DigestFinal (ctx, - (unsigned char *) (output + decrypted_len), + (unsigned char *) &output.bin[decrypted_len], &extra, data_len - decrypted_len); if (s != SECSuccess) { @@ -211,6 +212,7 @@ _nmtst_crypto_decrypt (NMCryptoCipherType cipher, PORT_GetError ()); goto out; } + decrypted_len += extra; pad_len = data_len - decrypted_len; @@ -226,7 +228,7 @@ _nmtst_crypto_decrypt (NMCryptoCipherType cipher, * should contain the padding size. */ for (i = pad_len; i > 0; i--) { - if (output[data_len - i] != pad_len) { + if (output.bin[data_len - i] != pad_len) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_DECRYPTION_FAILED, _("Failed to decrypt the private key.")); @@ -234,7 +236,6 @@ _nmtst_crypto_decrypt (NMCryptoCipherType cipher, } } - *out_len = decrypted_len; success = TRUE; out: @@ -247,14 +248,13 @@ out: if (slot) PK11_FreeSlot (slot); - if (!success) { - if (output) { - nm_explicit_bzero (output, data_len); - g_free (output); - output = NULL; - } - } - return (guint8 *) output; + if (!success) + return NULL; + + if (decrypted_len < output.len) + nm_explicit_bzero (&output.bin[decrypted_len], output.len - decrypted_len); + *out_len = decrypted_len; + return g_steal_pointer (&output.bin); } guint8 * @@ -276,14 +276,11 @@ _nmtst_crypto_encrypt (NMCryptoCipherType cipher, PK11SymKey *sym_key = NULL; SECItem *sec_param = NULL; PK11Context *ctx = NULL; - unsigned char *output, *padded_buf; - gsize output_len; + nm_auto_clear_secret_ptr NMSecretPtr padded_buf = { 0 }; + nm_auto_clear_secret_ptr NMSecretPtr output = { 0 }; int encrypted_len = 0, i; gboolean success = FALSE; - gsize padded_buf_len, pad_len; - - if (!_nm_crypto_init (error)) - return NULL; + gsize pad_len; if ( cipher == NM_CRYPTO_CIPHER_DES_CBC || !_get_cipher_info (cipher, &cipher_mech, NULL)) { @@ -293,25 +290,15 @@ _nmtst_crypto_encrypt (NMCryptoCipherType cipher, return NULL; } - /* If data->len % ivlen == 0, then we add another complete block - * onto the end so that the decrypter knows there's padding. - */ - pad_len = iv_len - (data_len % iv_len); - output_len = padded_buf_len = data_len + pad_len; - padded_buf = g_malloc0 (padded_buf_len); - - memcpy (padded_buf, data, data_len); - for (i = 0; i < pad_len; i++) - padded_buf[data_len + i] = (guint8) (pad_len & 0xFF); - - output = g_malloc0 (output_len); + if (!_nm_crypto_init (error)) + return NULL; slot = PK11_GetBestSlot (cipher_mech, NULL); if (!slot) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_FAILED, _("Failed to initialize the encryption cipher slot.")); - goto out; + return NULL; } sym_key = PK11_ImportSymKey (slot, cipher_mech, PK11_OriginUnwrap, CKA_ENCRYPT, &key_item, NULL); @@ -338,7 +325,22 @@ _nmtst_crypto_encrypt (NMCryptoCipherType cipher, goto out; } - ret = PK11_CipherOp (ctx, output, &encrypted_len, output_len, padded_buf, padded_buf_len); + /* If data->len % ivlen == 0, then we add another complete block + * onto the end so that the decrypter knows there's padding. + */ + pad_len = iv_len - (data_len % iv_len); + + padded_buf.len = data_len + pad_len; + padded_buf.bin = g_malloc (padded_buf.len); + + memcpy (padded_buf.bin, data, data_len); + for (i = 0; i < pad_len; i++) + padded_buf.bin[data_len + i] = (guint8) (pad_len & 0xFF); + + output.len = padded_buf.len; + output.bin = g_malloc (output.len); + + ret = PK11_CipherOp (ctx, output.bin, &encrypted_len, output.len, padded_buf.bin, padded_buf.len); if (ret != SECSuccess) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_ENCRYPTION_FAILED, @@ -347,35 +349,30 @@ _nmtst_crypto_encrypt (NMCryptoCipherType cipher, goto out; } - if (encrypted_len != output_len) { + if (encrypted_len != output.len) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_ENCRYPTION_FAILED, _("Unexpected amount of data after encrypting.")); goto out; } - *out_len = encrypted_len; success = TRUE; out: if (ctx) PK11_DestroyContext (ctx, PR_TRUE); - if (sym_key) - PK11_FreeSymKey (sym_key); if (sec_param) SECITEM_FreeItem (sec_param, PR_TRUE); + if (sym_key) + PK11_FreeSymKey (sym_key); if (slot) PK11_FreeSlot (slot); - nm_explicit_bzero (padded_buf, padded_buf_len); - g_free (padded_buf); + if (!success) + return NULL; - if (!success) { - nm_explicit_bzero (output, output_len); - g_free (output); - output = NULL; - } - return (guint8 *) output; + *out_len = output.len; + return g_steal_pointer (&output.bin); } gboolean @@ -394,7 +391,7 @@ _nm_crypto_verify_x509 (const guint8 *data, g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("Couldn't decode certificate: %d"), - PORT_GetError()); + PORT_GetError ()); return FALSE; } @@ -412,11 +409,9 @@ _nm_crypto_verify_pkcs12 (const guint8 *data, SECItem pw = { 0 }; PK11SlotInfo *slot = NULL; SECStatus s; - gunichar2 *ucs2_password; - long ucs2_chars = 0; + gboolean success = FALSE; - if (error) - g_return_val_if_fail (*error == NULL, FALSE); + g_return_val_if_fail (!error || !*error, FALSE); if (!_nm_crypto_init (error)) return FALSE; @@ -425,46 +420,56 @@ _nm_crypto_verify_pkcs12 (const guint8 *data, * any conversions for us. */ if (password && *password) { - if (!g_utf8_validate (password, -1, NULL)) { + nm_auto_clear_secret_ptr NMSecretPtr ucs2_password = { 0 }; + + if (g_utf8_validate (password, -1, NULL)) { + long ucs2_chars; + + ucs2_password.bin = (guint8 *) g_utf8_to_utf16 (password, strlen (password), NULL, &ucs2_chars, NULL); + + /* cannot fail, because password is valid UTF-8*/ + nm_assert (ucs2_password.bin && ucs2_chars > 0); + + ucs2_password.len = ucs2_chars * 2; + } + + if (!ucs2_password.bin || ucs2_password.len == 0) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_PASSWORD, _("Password must be UTF-8")); return FALSE; } - ucs2_password = g_utf8_to_utf16 (password, strlen (password), NULL, &ucs2_chars, NULL); - /* Can't fail if g_utf8_validate() succeeded */ - g_return_val_if_fail (ucs2_password != NULL && ucs2_chars != 0, FALSE); - ucs2_chars *= 2; /* convert # UCS2 characters -> bytes */ - pw.data = PORT_ZAlloc(ucs2_chars + 2); - memcpy (pw.data, ucs2_password, ucs2_chars); - pw.len = ucs2_chars + 2; /* include terminating NULL */ - - nm_explicit_bzero (ucs2_password, ucs2_chars); - g_free (ucs2_password); + pw.data = PORT_ZAlloc (ucs2_password.len + 2); + memcpy (pw.data, ucs2_password.bin, ucs2_password.len); + pw.len = ucs2_password.len + 2; #if __BYTE_ORDER == __LITTLE_ENDIAN { - guint16 *p; + guint16 *p, *p_end; - for (p = (guint16 *) pw.data; p < (guint16 *) (pw.data + pw.len); p++) + p_end = (guint16 *) &(((guint8 *) pw.data)[ucs2_password.len]); + for (p = (guint16 *) pw.data; p < p_end; p++) *p = GUINT16_SWAP_LE_BE (*p); } #endif - } else { - /* NULL password */ - pw.data = NULL; - pw.len = 0; } - slot = PK11_GetInternalKeySlot(); + slot = PK11_GetInternalKeySlot (); + if (!slot) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERROR_FAILED, + _("Couldn't initialize slot")); + goto out; + } + p12ctx = SEC_PKCS12DecoderStart (&pw, slot, NULL, NULL, NULL, NULL, NULL, NULL); if (!p12ctx) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_FAILED, _("Couldn't initialize PKCS#12 decoder: %d"), - PORT_GetError()); - goto error; + PORT_GetError ()); + goto out; } s = SEC_PKCS12DecoderUpdate (p12ctx, (guint8 *)data, data_len); @@ -472,8 +477,8 @@ _nm_crypto_verify_pkcs12 (const guint8 *data, g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("Couldn't decode PKCS#12 file: %d"), - PORT_GetError()); - goto error; + PORT_GetError ()); + goto out; } s = SEC_PKCS12DecoderVerify (p12ctx); @@ -481,23 +486,22 @@ _nm_crypto_verify_pkcs12 (const guint8 *data, g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_DECRYPTION_FAILED, _("Couldn't verify PKCS#12 file: %d"), - PORT_GetError()); - goto error; + PORT_GetError ()); + goto out; } - SEC_PKCS12DecoderFinish (p12ctx); - SECITEM_ZfreeItem (&pw, PR_FALSE); - return TRUE; + success = TRUE; -error: +out: if (p12ctx) SEC_PKCS12DecoderFinish (p12ctx); - if (slot) - PK11_FreeSlot(slot); + PK11_FreeSlot (slot); - SECITEM_ZfreeItem (&pw, PR_FALSE); - return FALSE; + if (pw.data) + SECITEM_ZfreeItem (&pw, PR_FALSE); + + return success; } gboolean From fa4f27372c4ba54bd6ba4cec53e387ea7c5a246b Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 3 Sep 2018 17:44:11 +0200 Subject: [PATCH 48/61] libnm/crypto: mark nm_crypto_make_des_aes_key() as test-only function --- libnm-core/nm-crypto.c | 16 ++++++++-------- libnm-core/nm-crypto.h | 14 +++++++------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/libnm-core/nm-crypto.c b/libnm-core/nm-crypto.c index 7fca22b8c7..b582c880e3 100644 --- a/libnm-core/nm-crypto.c +++ b/libnm-core/nm-crypto.c @@ -467,12 +467,12 @@ _nmtst_convert_iv (const char *src, } guint8 * -nm_crypto_make_des_aes_key (NMCryptoCipherType cipher, - const guint8 *salt, - gsize salt_len, - const char *password, - gsize *out_len, - GError **error) +nmtst_crypto_make_des_aes_key (NMCryptoCipherType cipher, + const guint8 *salt, + gsize salt_len, + const char *password, + gsize *out_len, + GError **error) { guint8 *key; const NMCryptoCipherInfo *cipher_info; @@ -537,7 +537,7 @@ _nmtst_decrypt_key (NMCryptoCipherType cipher, } /* Convert the password and IV into a DES or AES key */ - key.bin = nm_crypto_make_des_aes_key (cipher, bin_iv.bin, bin_iv.len, password, &key.len, error); + key.bin = nmtst_crypto_make_des_aes_key (cipher, bin_iv.bin, bin_iv.len, password, &key.len, error); if (!key.bin || !key.len) return FALSE; @@ -969,7 +969,7 @@ nmtst_crypto_rsa_key_encrypt (const guint8 *data, if (!nm_crypto_randomize (salt, sizeof (salt), error)) return NULL; - key.bin = nm_crypto_make_des_aes_key (NM_CRYPTO_CIPHER_DES_EDE3_CBC, salt, sizeof (salt), in_password, &key.len, NULL); + key.bin = nmtst_crypto_make_des_aes_key (NM_CRYPTO_CIPHER_DES_EDE3_CBC, salt, sizeof (salt), in_password, &key.len, NULL); if (!key.bin) g_return_val_if_reached (NULL); diff --git a/libnm-core/nm-crypto.h b/libnm-core/nm-crypto.h index 3ce9476488..54fbbc5f57 100644 --- a/libnm-core/nm-crypto.h +++ b/libnm-core/nm-crypto.h @@ -92,13 +92,6 @@ void nm_crypto_md5_hash (const guint8 *salt, guint8 *buffer, gsize buflen); -guint8 *nm_crypto_make_des_aes_key (NMCryptoCipherType cipher, - const guint8 *salt, - gsize salt_len, - const char *password, - gsize *out_len, - GError **error); - gboolean nm_crypto_randomize (void *buffer, gsize buffer_len, GError **error); /*****************************************************************************/ @@ -120,6 +113,13 @@ GBytes *nmtst_crypto_rsa_key_encrypt (const guint8 *data, char **out_password, GError **error); +guint8 *nmtst_crypto_make_des_aes_key (NMCryptoCipherType cipher, + const guint8 *salt, + gsize salt_len, + const char *password, + gsize *out_len, + GError **error); + /*****************************************************************************/ #endif /* __NM_CRYPTO_H__ */ From 1cd1bf7d2fc6c045f8d70bfc2965e7fc91a4565b Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 31 Aug 2018 20:08:48 +0200 Subject: [PATCH 49/61] libnm/802-1x: refactor GObject properties of NMSetting8021x --- libnm-core/nm-setting-8021x.c | 603 ++++++++++++++++------------------ 1 file changed, 280 insertions(+), 323 deletions(-) diff --git a/libnm-core/nm-setting-8021x.c b/libnm-core/nm-setting-8021x.c index f8ef6c962d..7b79f7bfd8 100644 --- a/libnm-core/nm-setting-8021x.c +++ b/libnm-core/nm-setting-8021x.c @@ -131,8 +131,7 @@ typedef struct { int auth_timeout; } NMSetting8021xPrivate; -enum { - PROP_0, +NM_GOBJECT_PROPERTIES_DEFINE (NMSetting8021x, PROP_EAP, PROP_IDENTITY, PROP_ANONYMOUS_IDENTITY, @@ -177,9 +176,9 @@ enum { PROP_PIN_FLAGS, PROP_SYSTEM_CA_CERTS, PROP_AUTH_TIMEOUT, +); - LAST_PROP -}; +/*****************************************************************************/ /** * nm_setting_802_1x_new: @@ -266,7 +265,7 @@ nm_setting_802_1x_add_eap_method (NMSetting8021x *setting, const char *eap) } priv->eap = g_slist_append (priv->eap, g_ascii_strdown (eap, -1)); - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_EAP); + _notify (setting, PROP_EAP); return TRUE; } @@ -291,7 +290,7 @@ nm_setting_802_1x_remove_eap_method (NMSetting8021x *setting, guint32 i) g_free (elt->data); priv->eap = g_slist_delete_link (priv->eap, elt); - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_EAP); + _notify (setting, PROP_EAP); } /** @@ -317,7 +316,7 @@ nm_setting_802_1x_remove_eap_method_by_value (NMSetting8021x *setting, for (iter = priv->eap; iter; iter = g_slist_next (iter)) { if (!strcmp (eap, (char *) iter->data)) { priv->eap = g_slist_delete_link (priv->eap, iter); - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_EAP); + _notify (setting, PROP_EAP); return TRUE; } } @@ -340,7 +339,7 @@ nm_setting_802_1x_clear_eap_methods (NMSetting8021x *setting) priv = NM_SETTING_802_1X_GET_PRIVATE (setting); g_slist_free_full (priv->eap, g_free); priv->eap = NULL; - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_EAP); + _notify (setting, PROP_EAP); } /** @@ -742,13 +741,13 @@ nm_setting_802_1x_set_ca_cert (NMSetting8021x *setting, g_clear_pointer (&priv->ca_cert, g_bytes_unref); if (!value) { - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_CA_CERT); + _notify (setting, PROP_CA_CERT); return TRUE; } if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) { priv->ca_cert = g_bytes_new (value, strlen (value) + 1); - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_CA_CERT); + _notify (setting, PROP_CA_CERT); return TRUE; } @@ -774,7 +773,7 @@ nm_setting_802_1x_set_ca_cert (NMSetting8021x *setting, } } - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_CA_CERT); + _notify (setting, PROP_CA_CERT); return priv->ca_cert != NULL; } @@ -899,7 +898,7 @@ nm_setting_802_1x_add_altsubject_match (NMSetting8021x *setting, priv->altsubject_matches = g_slist_append (priv->altsubject_matches, g_strdup (altsubject_match)); - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_ALTSUBJECT_MATCHES); + _notify (setting, PROP_ALTSUBJECT_MATCHES); return TRUE; } @@ -924,7 +923,7 @@ nm_setting_802_1x_remove_altsubject_match (NMSetting8021x *setting, guint32 i) g_free (elt->data); priv->altsubject_matches = g_slist_delete_link (priv->altsubject_matches, elt); - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_ALTSUBJECT_MATCHES); + _notify (setting, PROP_ALTSUBJECT_MATCHES); } /** @@ -951,7 +950,7 @@ nm_setting_802_1x_remove_altsubject_match_by_value (NMSetting8021x *setting, for (iter = priv->altsubject_matches; iter; iter = g_slist_next (iter)) { if (!strcmp (altsubject_match, (char *) iter->data)) { priv->altsubject_matches = g_slist_delete_link (priv->altsubject_matches, iter); - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_ALTSUBJECT_MATCHES); + _notify (setting, PROP_ALTSUBJECT_MATCHES); return TRUE; } } @@ -974,7 +973,7 @@ nm_setting_802_1x_clear_altsubject_matches (NMSetting8021x *setting) priv = NM_SETTING_802_1X_GET_PRIVATE (setting); g_slist_free_full (priv->altsubject_matches, g_free); priv->altsubject_matches = NULL; - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_ALTSUBJECT_MATCHES); + _notify (setting, PROP_ALTSUBJECT_MATCHES); } /** @@ -1143,13 +1142,13 @@ nm_setting_802_1x_set_client_cert (NMSetting8021x *setting, g_clear_pointer (&priv->client_cert, g_bytes_unref); if (!value) { - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_CLIENT_CERT); + _notify (setting, PROP_CLIENT_CERT); return TRUE; } if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) { priv->client_cert = g_bytes_new (value, strlen (value) + 1); - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_CLIENT_CERT); + _notify (setting, PROP_CLIENT_CERT); return TRUE; } @@ -1187,7 +1186,7 @@ nm_setting_802_1x_set_client_cert (NMSetting8021x *setting, } } - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_CLIENT_CERT); + _notify (setting, PROP_CLIENT_CERT); return priv->client_cert != NULL; } @@ -1495,13 +1494,13 @@ nm_setting_802_1x_set_phase2_ca_cert (NMSetting8021x *setting, g_clear_pointer (&priv->phase2_ca_cert, g_bytes_unref); if (!value) { - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_CA_CERT); + _notify (setting, PROP_PHASE2_CA_CERT); return TRUE; } if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) { priv->phase2_ca_cert = g_bytes_new (value, strlen (value) + 1); - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_CA_CERT); + _notify (setting, PROP_PHASE2_CA_CERT); return TRUE; } @@ -1527,7 +1526,7 @@ nm_setting_802_1x_set_phase2_ca_cert (NMSetting8021x *setting, } } - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_CA_CERT); + _notify (setting, PROP_PHASE2_CA_CERT); return priv->phase2_ca_cert != NULL; } @@ -1669,7 +1668,7 @@ nm_setting_802_1x_add_phase2_altsubject_match (NMSetting8021x *setting, priv->phase2_altsubject_matches = g_slist_append (priv->phase2_altsubject_matches, g_strdup (phase2_altsubject_match)); - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES); + _notify (setting, PROP_PHASE2_ALTSUBJECT_MATCHES); return TRUE; } @@ -1694,7 +1693,7 @@ nm_setting_802_1x_remove_phase2_altsubject_match (NMSetting8021x *setting, guint g_free (elt->data); priv->phase2_altsubject_matches = g_slist_delete_link (priv->phase2_altsubject_matches, elt); - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES); + _notify (setting, PROP_PHASE2_ALTSUBJECT_MATCHES); } /** @@ -1721,7 +1720,7 @@ nm_setting_802_1x_remove_phase2_altsubject_match_by_value (NMSetting8021x *setti for (iter = priv->phase2_altsubject_matches; iter; iter = g_slist_next (iter)) { if (!strcmp (phase2_altsubject_match, (char *) iter->data)) { priv->phase2_altsubject_matches = g_slist_delete_link (priv->phase2_altsubject_matches, iter); - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES); + _notify (setting, PROP_PHASE2_ALTSUBJECT_MATCHES); return TRUE; } } @@ -1744,7 +1743,7 @@ nm_setting_802_1x_clear_phase2_altsubject_matches (NMSetting8021x *setting) priv = NM_SETTING_802_1X_GET_PRIVATE (setting); g_slist_free_full (priv->phase2_altsubject_matches, g_free); priv->phase2_altsubject_matches = NULL; - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES); + _notify (setting, PROP_PHASE2_ALTSUBJECT_MATCHES); } /** @@ -1900,13 +1899,13 @@ nm_setting_802_1x_set_phase2_client_cert (NMSetting8021x *setting, g_clear_pointer (&priv->phase2_client_cert, g_bytes_unref); if (!value) { - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_CLIENT_CERT); + _notify (setting, PROP_PHASE2_CLIENT_CERT); return TRUE; } if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) { priv->phase2_client_cert = g_bytes_new (value, strlen (value) + 1); - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_CLIENT_CERT); + _notify (setting, PROP_PHASE2_CLIENT_CERT); return TRUE; } @@ -1945,7 +1944,7 @@ nm_setting_802_1x_set_phase2_client_cert (NMSetting8021x *setting, } } - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_CLIENT_CERT); + _notify (setting, PROP_PHASE2_CLIENT_CERT); return priv->phase2_client_cert != NULL; } @@ -2249,9 +2248,9 @@ nm_setting_802_1x_set_private_key (NMSetting8021x *setting, if (!value) { if (nm_clear_pointer (&priv->private_key, g_bytes_unref)) - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PRIVATE_KEY); + _notify (setting, PROP_PRIVATE_KEY); if (nm_clear_pointer (&priv->private_key_password, nm_free_secret)) - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD); + _notify (setting, PROP_PRIVATE_KEY_PASSWORD); return TRUE; } @@ -2322,13 +2321,13 @@ nm_setting_802_1x_set_private_key (NMSetting8021x *setting, if (format == NM_CRYPTO_FILE_FORMAT_PKCS12) { g_bytes_unref (priv->client_cert); priv->client_cert = g_bytes_ref (priv->private_key); - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_CLIENT_CERT); + _notify (setting, PROP_CLIENT_CERT); } if (private_key_changed) - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PRIVATE_KEY); + _notify (setting, PROP_PRIVATE_KEY); if (password_changed) - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD); + _notify (setting, PROP_PRIVATE_KEY_PASSWORD); NM_SET_OUT (out_format, _crypto_format_to_ck (format)); return TRUE; @@ -2615,9 +2614,9 @@ nm_setting_802_1x_set_phase2_private_key (NMSetting8021x *setting, if (!value) { if (nm_clear_pointer (&priv->phase2_private_key, g_bytes_unref)) - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY); + _notify (setting, PROP_PHASE2_PRIVATE_KEY); if (nm_clear_pointer (&priv->phase2_private_key_password, nm_free_secret)) - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD); + _notify (setting, PROP_PHASE2_PRIVATE_KEY_PASSWORD); return TRUE; } @@ -2691,13 +2690,13 @@ nm_setting_802_1x_set_phase2_private_key (NMSetting8021x *setting, || g_bytes_equal (priv->phase2_client_cert, priv->phase2_private_key))) { g_bytes_unref (priv->phase2_client_cert); priv->phase2_client_cert = g_bytes_ref (priv->phase2_private_key); - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_CLIENT_CERT); + _notify (setting, PROP_PHASE2_CLIENT_CERT); } if (private_key_changed) - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY); + _notify (setting, PROP_PHASE2_PRIVATE_KEY); if (password_changed) - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD); + _notify (setting, PROP_PHASE2_PRIVATE_KEY_PASSWORD); NM_SET_OUT (out_format, _crypto_format_to_ck (format)); return TRUE; @@ -3826,12 +3825,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * example: IEEE_8021X_EAP_METHODS=PEAP * ---end--- */ - g_object_class_install_property - (object_class, PROP_EAP, - g_param_spec_boxed (NM_SETTING_802_1X_EAP, "", "", - G_TYPE_STRV, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_EAP] = + g_param_spec_boxed (NM_SETTING_802_1X_EAP, "", "", + G_TYPE_STRV, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:identity: @@ -3846,12 +3844,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * example: IEEE_8021X_IDENTITY=itsme * ---end--- */ - g_object_class_install_property - (object_class, PROP_IDENTITY, - g_param_spec_string (NM_SETTING_802_1X_IDENTITY, "", "", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_IDENTITY] = + g_param_spec_string (NM_SETTING_802_1X_IDENTITY, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:anonymous-identity: @@ -3866,12 +3863,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * description: Anonymous identity for EAP authentication methods. * ---end--- */ - g_object_class_install_property - (object_class, PROP_ANONYMOUS_IDENTITY, - g_param_spec_string (NM_SETTING_802_1X_ANONYMOUS_IDENTITY, "", "", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_ANONYMOUS_IDENTITY] = + g_param_spec_string (NM_SETTING_802_1X_ANONYMOUS_IDENTITY, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:pac-file: @@ -3885,12 +3881,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * example: IEEE_8021X_PAC_FILE=/home/joe/my-fast.pac * ---end--- */ - g_object_class_install_property - (object_class, PROP_PAC_FILE, - g_param_spec_string (NM_SETTING_802_1X_PAC_FILE, "", "", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_PAC_FILE] = + g_param_spec_string (NM_SETTING_802_1X_PAC_FILE, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:ca-cert: @@ -3917,12 +3912,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * example: IEEE_8021X_CA_CERT=/home/joe/cacert.crt * ---end--- */ - g_object_class_install_property - (object_class, PROP_CA_CERT, - g_param_spec_boxed (NM_SETTING_802_1X_CA_CERT, "", "", - G_TYPE_BYTES, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_CA_CERT] = + g_param_spec_boxed (NM_SETTING_802_1X_CA_CERT, "", "", + G_TYPE_BYTES, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:ca-cert-password: @@ -3936,13 +3930,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) /* ---ifcfg-rh--- * ---end--- */ - g_object_class_install_property - (object_class, PROP_CA_CERT_PASSWORD, - g_param_spec_string (NM_SETTING_802_1X_CA_CERT_PASSWORD, "", "", - NULL, - G_PARAM_READWRITE | - NM_SETTING_PARAM_SECRET | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_CA_CERT_PASSWORD] = + g_param_spec_string (NM_SETTING_802_1X_CA_CERT_PASSWORD, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_SECRET | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:ca-cert-password-flags: @@ -3954,13 +3947,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) /* ---ifcfg-rh--- * ---end--- */ - g_object_class_install_property - (object_class, PROP_CA_CERT_PASSWORD_FLAGS, - g_param_spec_flags (NM_SETTING_802_1X_CA_CERT_PASSWORD_FLAGS, "", "", - NM_TYPE_SETTING_SECRET_FLAGS, - NM_SETTING_SECRET_FLAG_NONE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_CA_CERT_PASSWORD_FLAGS] = + g_param_spec_flags (NM_SETTING_802_1X_CA_CERT_PASSWORD_FLAGS, "", "", + NM_TYPE_SETTING_SECRET_FLAGS, + NM_SETTING_SECRET_FLAG_NONE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:ca-path: @@ -3975,12 +3967,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * description: The property is not handled by ifcfg-rh plugin. * ---end--- */ - g_object_class_install_property - (object_class, PROP_CA_PATH, - g_param_spec_string (NM_SETTING_802_1X_CA_PATH, "", "", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_CA_PATH] = + g_param_spec_string (NM_SETTING_802_1X_CA_PATH, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:subject-match: @@ -3998,12 +3989,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * example: IEEE_8021X_SUBJECT_MATCH="Red Hat" * ---end--- */ - g_object_class_install_property - (object_class, PROP_SUBJECT_MATCH, - g_param_spec_string (NM_SETTING_802_1X_SUBJECT_MATCH, "", "", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_SUBJECT_MATCH] = + g_param_spec_string (NM_SETTING_802_1X_SUBJECT_MATCH, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:altsubject-matches: @@ -4019,12 +4009,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * example: IEEE_8021X_ALTSUBJECT_MATCHES="s1.domain.cc" * ---end--- */ - g_object_class_install_property - (object_class, PROP_ALTSUBJECT_MATCHES, - g_param_spec_boxed (NM_SETTING_802_1X_ALTSUBJECT_MATCHES, "", "", - G_TYPE_STRV, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_ALTSUBJECT_MATCHES] = + g_param_spec_boxed (NM_SETTING_802_1X_ALTSUBJECT_MATCHES, "", "", + G_TYPE_STRV, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:domain-suffix-match: @@ -4043,12 +4032,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * variable: IEEE_8021X_DOMAIN_SUFFIX_MATCH(+) * ---end--- */ - g_object_class_install_property - (object_class, PROP_DOMAIN_SUFFIX_MATCH, - g_param_spec_string (NM_SETTING_802_1X_DOMAIN_SUFFIX_MATCH, "", "", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_DOMAIN_SUFFIX_MATCH] = + g_param_spec_string (NM_SETTING_802_1X_DOMAIN_SUFFIX_MATCH, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:client-cert: @@ -4073,12 +4061,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * example: IEEE_8021X_CLIENT_CERT=/home/joe/mycert.crt * ---end--- */ - g_object_class_install_property - (object_class, PROP_CLIENT_CERT, - g_param_spec_boxed (NM_SETTING_802_1X_CLIENT_CERT, "", "", - G_TYPE_BYTES, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_CLIENT_CERT] = + g_param_spec_boxed (NM_SETTING_802_1X_CLIENT_CERT, "", "", + G_TYPE_BYTES, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:client-cert-password: @@ -4092,13 +4079,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) /* ---ifcfg-rh--- * ---end--- */ - g_object_class_install_property - (object_class, PROP_CLIENT_CERT_PASSWORD, - g_param_spec_string (NM_SETTING_802_1X_CLIENT_CERT_PASSWORD, "", "", - NULL, - G_PARAM_READWRITE | - NM_SETTING_PARAM_SECRET | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_CLIENT_CERT_PASSWORD] = + g_param_spec_string (NM_SETTING_802_1X_CLIENT_CERT_PASSWORD, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_SECRET | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:client-cert-password-flags: @@ -4110,13 +4096,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) /* ---ifcfg-rh--- * ---end--- */ - g_object_class_install_property - (object_class, PROP_CLIENT_CERT_PASSWORD_FLAGS, - g_param_spec_flags (NM_SETTING_802_1X_CLIENT_CERT_PASSWORD_FLAGS, "", "", - NM_TYPE_SETTING_SECRET_FLAGS, - NM_SETTING_SECRET_FLAG_NONE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_CLIENT_CERT_PASSWORD_FLAGS] = + g_param_spec_flags (NM_SETTING_802_1X_CLIENT_CERT_PASSWORD_FLAGS, "", "", + NM_TYPE_SETTING_SECRET_FLAGS, + NM_SETTING_SECRET_FLAG_NONE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:phase1-peapver: @@ -4135,12 +4120,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * description: Use to force a specific PEAP version. * ---end--- */ - g_object_class_install_property - (object_class, PROP_PHASE1_PEAPVER, - g_param_spec_string (NM_SETTING_802_1X_PHASE1_PEAPVER, "", "", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_PHASE1_PEAPVER] = + g_param_spec_string (NM_SETTING_802_1X_PHASE1_PEAPVER, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:phase1-peaplabel: @@ -4158,12 +4142,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * description: Use to force the new PEAP label during key derivation. * ---end--- */ - g_object_class_install_property - (object_class, PROP_PHASE1_PEAPLABEL, - g_param_spec_string (NM_SETTING_802_1X_PHASE1_PEAPLABEL, "", "", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_PHASE1_PEAPLABEL] = + g_param_spec_string (NM_SETTING_802_1X_PHASE1_PEAPLABEL, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:phase1-fast-provisioning: @@ -4183,12 +4166,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * example: IEEE_8021X_FAST_PROVISIONING="allow-auth allow-unauth" * ---end--- */ - g_object_class_install_property - (object_class, PROP_PHASE1_FAST_PROVISIONING, - g_param_spec_string (NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING, "", "", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_PHASE1_FAST_PROVISIONING] = + g_param_spec_string (NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:phase1-auth-flags: @@ -4210,13 +4192,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * example: IEEE_8021X_PHASE1_AUTH_FLAGS="tls-1-0-disable tls-1-1-disable" * ---end--- */ - g_object_class_install_property - (object_class, PROP_PHASE1_AUTH_FLAGS, - g_param_spec_uint (NM_SETTING_802_1X_PHASE1_AUTH_FLAGS, "", "", - 0, G_MAXUINT32, NM_SETTING_802_1X_AUTH_FLAGS_NONE, - G_PARAM_CONSTRUCT | - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_PHASE1_AUTH_FLAGS] = + g_param_spec_uint (NM_SETTING_802_1X_PHASE1_AUTH_FLAGS, "", "", + 0, G_MAXUINT32, NM_SETTING_802_1X_AUTH_FLAGS_NONE, + G_PARAM_CONSTRUCT | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:phase2-auth: @@ -4237,12 +4218,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * example: IEEE_8021X_INNER_AUTH_METHODS=PAP * ---end--- */ - g_object_class_install_property - (object_class, PROP_PHASE2_AUTH, - g_param_spec_string (NM_SETTING_802_1X_PHASE2_AUTH, "", "", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_PHASE2_AUTH] = + g_param_spec_string (NM_SETTING_802_1X_PHASE2_AUTH, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:phase2-autheap: @@ -4263,12 +4243,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * example: IEEE_8021X_INNER_AUTH_METHODS="MSCHAPV2 EAP-TLS" * ---end--- */ - g_object_class_install_property - (object_class, PROP_PHASE2_AUTHEAP, - g_param_spec_string (NM_SETTING_802_1X_PHASE2_AUTHEAP, "", "", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_PHASE2_AUTHEAP] = + g_param_spec_string (NM_SETTING_802_1X_PHASE2_AUTHEAP, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:phase2-ca-cert: @@ -4289,12 +4268,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * Setting this property directly is discouraged; use the * nm_setting_802_1x_set_phase2_ca_cert() function instead. **/ - g_object_class_install_property - (object_class, PROP_PHASE2_CA_CERT, - g_param_spec_boxed (NM_SETTING_802_1X_PHASE2_CA_CERT, "", "", - G_TYPE_BYTES, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_PHASE2_CA_CERT] = + g_param_spec_boxed (NM_SETTING_802_1X_PHASE2_CA_CERT, "", "", + G_TYPE_BYTES, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:phase2-ca-cert-password: @@ -4308,13 +4286,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) /* ---ifcfg-rh--- * ---end--- */ - g_object_class_install_property - (object_class, PROP_PHASE2_CA_CERT_PASSWORD, - g_param_spec_string (NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD, "", "", - NULL, - G_PARAM_READWRITE | - NM_SETTING_PARAM_SECRET | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_PHASE2_CA_CERT_PASSWORD] = + g_param_spec_string (NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_SECRET | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:phase2-ca-cert-password-flags: @@ -4326,13 +4303,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) /* ---ifcfg-rh--- * ---end--- */ - g_object_class_install_property - (object_class, PROP_PHASE2_CA_CERT_PASSWORD_FLAGS, - g_param_spec_flags (NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD_FLAGS, "", "", - NM_TYPE_SETTING_SECRET_FLAGS, - NM_SETTING_SECRET_FLAG_NONE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_PHASE2_CA_CERT_PASSWORD_FLAGS] = + g_param_spec_flags (NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD_FLAGS, "", "", + NM_TYPE_SETTING_SECRET_FLAGS, + NM_SETTING_SECRET_FLAG_NONE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:phase2-ca-path: @@ -4341,12 +4317,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * certificates to be added to the verification chain in addition to the * certificate specified in the #NMSetting8021x:phase2-ca-cert property. **/ - g_object_class_install_property - (object_class, PROP_PHASE2_CA_PATH, - g_param_spec_string (NM_SETTING_802_1X_PHASE2_CA_PATH, "", "", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_PHASE2_CA_PATH] = + g_param_spec_string (NM_SETTING_802_1X_PHASE2_CA_PATH, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:phase2-subject-match: @@ -4365,12 +4340,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * example: IEEE_8021X_PHASE2_SUBJECT_MATCH="Red Hat" * ---end--- */ - g_object_class_install_property - (object_class, PROP_PHASE2_SUBJECT_MATCH, - g_param_spec_string (NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH, "", "", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_PHASE2_SUBJECT_MATCH] = + g_param_spec_string (NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:phase2-altsubject-matches: @@ -4385,12 +4359,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * variable: IEEE_8021X_PHASE2_ALTSUBJECT_MATCHES(+) * ---end--- */ - g_object_class_install_property - (object_class, PROP_PHASE2_ALTSUBJECT_MATCHES, - g_param_spec_boxed (NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES, "", "", - G_TYPE_STRV, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_PHASE2_ALTSUBJECT_MATCHES] = + g_param_spec_boxed (NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES, "", "", + G_TYPE_STRV, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:phase2-domain-suffix-match: @@ -4410,12 +4383,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * variable: IEEE_8021X_PHASE2_DOMAIN_SUFFIX_MATCH(+) * ---end--- */ - g_object_class_install_property - (object_class, PROP_PHASE2_DOMAIN_SUFFIX_MATCH, - g_param_spec_string (NM_SETTING_802_1X_PHASE2_DOMAIN_SUFFIX_MATCH, "", "", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_PHASE2_DOMAIN_SUFFIX_MATCH] = + g_param_spec_string (NM_SETTING_802_1X_PHASE2_DOMAIN_SUFFIX_MATCH, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:phase2-client-cert: @@ -4443,12 +4415,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * example: IEEE_8021X_INNER_CLIENT_CERT=/home/joe/mycert.crt * ---end--- */ - g_object_class_install_property - (object_class, PROP_PHASE2_CLIENT_CERT, - g_param_spec_boxed (NM_SETTING_802_1X_PHASE2_CLIENT_CERT, "", "", - G_TYPE_BYTES, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_PHASE2_CLIENT_CERT] = + g_param_spec_boxed (NM_SETTING_802_1X_PHASE2_CLIENT_CERT, "", "", + G_TYPE_BYTES, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:phase2-client-cert-password: @@ -4462,13 +4433,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) /* ---ifcfg-rh--- * ---end--- */ - g_object_class_install_property - (object_class, PROP_PHASE2_CLIENT_CERT_PASSWORD, - g_param_spec_string (NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD, "", "", - NULL, - G_PARAM_READWRITE | - NM_SETTING_PARAM_SECRET | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_PHASE2_CLIENT_CERT_PASSWORD] = + g_param_spec_string (NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_SECRET | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:phase2-client-cert-password-flags: @@ -4480,13 +4450,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) /* ---ifcfg-rh--- * ---end--- */ - g_object_class_install_property - (object_class, PROP_PHASE2_CLIENT_CERT_PASSWORD_FLAGS, - g_param_spec_flags (NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD_FLAGS, "", "", - NM_TYPE_SETTING_SECRET_FLAGS, - NM_SETTING_SECRET_FLAG_NONE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_PHASE2_CLIENT_CERT_PASSWORD_FLAGS] = + g_param_spec_flags (NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD_FLAGS, "", "", + NM_TYPE_SETTING_SECRET_FLAGS, + NM_SETTING_SECRET_FLAG_NONE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:password: @@ -4502,13 +4471,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * lookaside file, or it can be owned by a secret agent. * ---end--- */ - g_object_class_install_property - (object_class, PROP_PASSWORD, - g_param_spec_string (NM_SETTING_802_1X_PASSWORD, "", "", - NULL, - G_PARAM_READWRITE | - NM_SETTING_PARAM_SECRET | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_PASSWORD] = + g_param_spec_string (NM_SETTING_802_1X_PASSWORD, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_SECRET | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:password-flags: @@ -4522,13 +4490,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * description: Password flags for IEEE_8021X_PASSWORD password. * ---end--- */ - g_object_class_install_property - (object_class, PROP_PASSWORD_FLAGS, - g_param_spec_flags (NM_SETTING_802_1X_PASSWORD_FLAGS, "", "", - NM_TYPE_SETTING_SECRET_FLAGS, - NM_SETTING_SECRET_FLAG_NONE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_PASSWORD_FLAGS] = + g_param_spec_flags (NM_SETTING_802_1X_PASSWORD_FLAGS, "", "", + NM_TYPE_SETTING_SECRET_FLAGS, + NM_SETTING_SECRET_FLAG_NONE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:password-raw: @@ -4546,13 +4513,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * example: IEEE_8021X_PASSWORD_RAW=041c8320083aa4bf * ---end--- */ - g_object_class_install_property - (object_class, PROP_PASSWORD_RAW, - g_param_spec_boxed (NM_SETTING_802_1X_PASSWORD_RAW, "", "", - G_TYPE_BYTES, - G_PARAM_READWRITE | - NM_SETTING_PARAM_SECRET | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_PASSWORD_RAW] = + g_param_spec_boxed (NM_SETTING_802_1X_PASSWORD_RAW, "", "", + G_TYPE_BYTES, + G_PARAM_READWRITE | + NM_SETTING_PARAM_SECRET | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:password-raw-flags: @@ -4565,13 +4531,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * description: The property is not handled by ifcfg-rh plugin. * ---end--- */ - g_object_class_install_property - (object_class, PROP_PASSWORD_RAW_FLAGS, - g_param_spec_flags (NM_SETTING_802_1X_PASSWORD_RAW_FLAGS, "", "", - NM_TYPE_SETTING_SECRET_FLAGS, - NM_SETTING_SECRET_FLAG_NONE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_PASSWORD_RAW_FLAGS] = + g_param_spec_flags (NM_SETTING_802_1X_PASSWORD_RAW_FLAGS, "", "", + NM_TYPE_SETTING_SECRET_FLAGS, + NM_SETTING_SECRET_FLAG_NONE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:private-key: @@ -4611,12 +4576,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * example: IEEE_8021X_PRIVATE_KEY=/home/joe/mykey.p12 * ---end--- */ - g_object_class_install_property - (object_class, PROP_PRIVATE_KEY, - g_param_spec_boxed (NM_SETTING_802_1X_PRIVATE_KEY, "", "", - G_TYPE_BYTES, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_PRIVATE_KEY] = + g_param_spec_boxed (NM_SETTING_802_1X_PRIVATE_KEY, "", "", + G_TYPE_BYTES, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:private-key-password: @@ -4635,13 +4599,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * lookaside file, or it can be owned by a secret agent. * ---end--- */ - g_object_class_install_property - (object_class, PROP_PRIVATE_KEY_PASSWORD, - g_param_spec_string (NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD, "", "", - NULL, - G_PARAM_READWRITE | - NM_SETTING_PARAM_SECRET | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_PRIVATE_KEY_PASSWORD] = + g_param_spec_string (NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_SECRET | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:private-key-password-flags: @@ -4656,13 +4619,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * description: Password flags for IEEE_8021X_PRIVATE_KEY_PASSWORD password. * ---end--- */ - g_object_class_install_property - (object_class, PROP_PRIVATE_KEY_PASSWORD_FLAGS, - g_param_spec_flags (NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS, "", "", - NM_TYPE_SETTING_SECRET_FLAGS, - NM_SETTING_SECRET_FLAG_NONE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_PRIVATE_KEY_PASSWORD_FLAGS] = + g_param_spec_flags (NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS, "", "", + NM_TYPE_SETTING_SECRET_FLAGS, + NM_SETTING_SECRET_FLAG_NONE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:phase2-private-key: @@ -4696,12 +4658,11 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * description: Private key for inner authentication method for EAP-TLS. * ---end--- */ - g_object_class_install_property - (object_class, PROP_PHASE2_PRIVATE_KEY, - g_param_spec_boxed (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, "", "", - G_TYPE_BYTES, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_PHASE2_PRIVATE_KEY] = + g_param_spec_boxed (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, "", "", + G_TYPE_BYTES, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:phase2-private-key-password: @@ -4721,13 +4682,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * lookaside file, or it can be owned by a secret agent. * ---end--- */ - g_object_class_install_property - (object_class, PROP_PHASE2_PRIVATE_KEY_PASSWORD, - g_param_spec_string (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD, "", "", - NULL, - G_PARAM_READWRITE | - NM_SETTING_PARAM_SECRET | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_PHASE2_PRIVATE_KEY_PASSWORD] = + g_param_spec_string (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_SECRET | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:phase2-private-key-password-flags: @@ -4742,13 +4702,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * description: Password flags for IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD password. * ---end--- */ - g_object_class_install_property - (object_class, PROP_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS, - g_param_spec_flags (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS, "", "", - NM_TYPE_SETTING_SECRET_FLAGS, - NM_SETTING_SECRET_FLAG_NONE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS] = + g_param_spec_flags (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS, "", "", + NM_TYPE_SETTING_SECRET_FLAGS, + NM_SETTING_SECRET_FLAG_NONE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:pin: @@ -4761,13 +4720,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * description: The property is not handled by ifcfg-rh plugin. * ---end--- */ - g_object_class_install_property - (object_class, PROP_PIN, - g_param_spec_string (NM_SETTING_802_1X_PIN, "", "", - NULL, - G_PARAM_READWRITE | - NM_SETTING_PARAM_SECRET | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_PIN] = + g_param_spec_string (NM_SETTING_802_1X_PIN, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_SECRET | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:pin-flags: @@ -4780,13 +4738,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * description: The property is not handled by ifcfg-rh plugin. * ---end--- */ - g_object_class_install_property - (object_class, PROP_PIN_FLAGS, - g_param_spec_flags (NM_SETTING_802_1X_PIN_FLAGS, "", "", - NM_TYPE_SETTING_SECRET_FLAGS, - NM_SETTING_SECRET_FLAG_NONE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_PIN_FLAGS] = + g_param_spec_flags (NM_SETTING_802_1X_PIN_FLAGS, "", "", + NM_TYPE_SETTING_SECRET_FLAGS, + NM_SETTING_SECRET_FLAG_NONE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:system-ca-certs: @@ -4807,13 +4764,12 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * description: The property is not handled by ifcfg-rh plugin. * ---end--- */ - g_object_class_install_property - (object_class, PROP_SYSTEM_CA_CERTS, - g_param_spec_boolean (NM_SETTING_802_1X_SYSTEM_CA_CERTS, "", "", - FALSE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_SYSTEM_CA_CERTS] = + g_param_spec_boolean (NM_SETTING_802_1X_SYSTEM_CA_CERTS, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); /** * NMSetting8021x:auth-timeout: @@ -4830,13 +4786,14 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass) * description: Timeout in seconds for the 802.1X authentication. Zero means the global default or 25. * ---end--- */ - g_object_class_install_property - (object_class, PROP_AUTH_TIMEOUT, - g_param_spec_int (NM_SETTING_802_1X_AUTH_TIMEOUT, "", "", - 0, G_MAXINT32, 0, - G_PARAM_READWRITE | - NM_SETTING_PARAM_FUZZY_IGNORE | - G_PARAM_STATIC_STRINGS)); + obj_properties[PROP_AUTH_TIMEOUT] = + g_param_spec_int (NM_SETTING_802_1X_AUTH_TIMEOUT, "", "", + 0, G_MAXINT32, 0, + G_PARAM_READWRITE | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); _nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_802_1X); } From d0a26c67132db5b1d3bb317859154141c830d975 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 31 Aug 2018 20:13:39 +0200 Subject: [PATCH 50/61] libnm/802-1x/trivial: reorder code --- libnm-core/nm-setting-8021x.c | 1023 +++++++++++++++++---------------- 1 file changed, 516 insertions(+), 507 deletions(-) diff --git a/libnm-core/nm-setting-8021x.c b/libnm-core/nm-setting-8021x.c index 7b79f7bfd8..6923e50eac 100644 --- a/libnm-core/nm-setting-8021x.c +++ b/libnm-core/nm-setting-8021x.c @@ -80,56 +80,23 @@ _crypto_format_to_ck (NMCryptoFileFormat format) /*****************************************************************************/ -G_DEFINE_TYPE (NMSetting8021x, nm_setting_802_1x, NM_TYPE_SETTING) +typedef void (*EAPMethodNeedSecretsFunc) (NMSetting8021x *self, + GPtrArray *secrets, + gboolean phase2); -#define NM_SETTING_802_1X_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_802_1X, NMSetting8021xPrivate)) +typedef gboolean (*EAPMethodValidateFunc)(NMSetting8021x *self, + gboolean phase2, + GError **error); typedef struct { - GSList *eap; /* GSList of strings */ - char *identity; - char *anonymous_identity; - char *pac_file; - GBytes *ca_cert; - char *ca_cert_password; - NMSettingSecretFlags ca_cert_password_flags; - char *ca_path; - char *subject_match; - GSList *altsubject_matches; - char *domain_suffix_match; - GBytes *client_cert; - char *client_cert_password; - NMSettingSecretFlags client_cert_password_flags; - char *phase1_peapver; - char *phase1_peaplabel; - char *phase1_fast_provisioning; - NMSetting8021xAuthFlags phase1_auth_flags; - char *phase2_auth; - char *phase2_autheap; - GBytes *phase2_ca_cert; - char *phase2_ca_cert_password; - NMSettingSecretFlags phase2_ca_cert_password_flags; - char *phase2_ca_path; - char *phase2_subject_match; - GSList *phase2_altsubject_matches; - char *phase2_domain_suffix_match; - GBytes *phase2_client_cert; - char *phase2_client_cert_password; - NMSettingSecretFlags phase2_client_cert_password_flags; - char *password; - NMSettingSecretFlags password_flags; - GBytes *password_raw; - NMSettingSecretFlags password_raw_flags; - char *pin; - NMSettingSecretFlags pin_flags; - GBytes *private_key; - char *private_key_password; - NMSettingSecretFlags private_key_password_flags; - GBytes *phase2_private_key; - char *phase2_private_key_password; - NMSettingSecretFlags phase2_private_key_password_flags; - gboolean system_ca_certs; - int auth_timeout; -} NMSetting8021xPrivate; + const char *method; + EAPMethodNeedSecretsFunc ns_func; + EAPMethodValidateFunc v_func; +} EAPMethodsTable; + +static EAPMethodsTable eap_methods_table[]; + +/*****************************************************************************/ NM_GOBJECT_PROPERTIES_DEFINE (NMSetting8021x, PROP_EAP, @@ -178,19 +145,245 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMSetting8021x, PROP_AUTH_TIMEOUT, ); +typedef struct { + GSList *eap; /* GSList of strings */ + char *identity; + char *anonymous_identity; + char *pac_file; + GBytes *ca_cert; + char *ca_cert_password; + NMSettingSecretFlags ca_cert_password_flags; + char *ca_path; + char *subject_match; + GSList *altsubject_matches; + char *domain_suffix_match; + GBytes *client_cert; + char *client_cert_password; + NMSettingSecretFlags client_cert_password_flags; + char *phase1_peapver; + char *phase1_peaplabel; + char *phase1_fast_provisioning; + NMSetting8021xAuthFlags phase1_auth_flags; + char *phase2_auth; + char *phase2_autheap; + GBytes *phase2_ca_cert; + char *phase2_ca_cert_password; + NMSettingSecretFlags phase2_ca_cert_password_flags; + char *phase2_ca_path; + char *phase2_subject_match; + GSList *phase2_altsubject_matches; + char *phase2_domain_suffix_match; + GBytes *phase2_client_cert; + char *phase2_client_cert_password; + NMSettingSecretFlags phase2_client_cert_password_flags; + char *password; + NMSettingSecretFlags password_flags; + GBytes *password_raw; + NMSettingSecretFlags password_raw_flags; + char *pin; + NMSettingSecretFlags pin_flags; + GBytes *private_key; + char *private_key_password; + NMSettingSecretFlags private_key_password_flags; + GBytes *phase2_private_key; + char *phase2_private_key_password; + NMSettingSecretFlags phase2_private_key_password_flags; + gboolean system_ca_certs; + int auth_timeout; +} NMSetting8021xPrivate; + +G_DEFINE_TYPE (NMSetting8021x, nm_setting_802_1x, NM_TYPE_SETTING) + +#define NM_SETTING_802_1X_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_802_1X, NMSetting8021xPrivate)) + /*****************************************************************************/ /** - * nm_setting_802_1x_new: + * nm_setting_802_1x_check_cert_scheme: + * @pdata: (allow-none): the data pointer + * @length: the length of the data + * @error: (allow-none): (out): validation reason * - * Creates a new #NMSetting8021x object with default values. + * Determines and verifies the blob type. + * When setting certificate properties of NMSetting8021x + * the blob must be not UNKNOWN (or NULL). * - * Returns: the new empty #NMSetting8021x object + * Returns: the scheme of the blob or %NM_SETTING_802_1X_CK_SCHEME_UNKNOWN. + * For NULL it also returns NM_SETTING_802_1X_CK_SCHEME_UNKNOWN. + * + * Since: 1.2 **/ -NMSetting * -nm_setting_802_1x_new (void) +NMSetting8021xCKScheme +nm_setting_802_1x_check_cert_scheme (gconstpointer pdata, gsize length, GError **error) { - return (NMSetting *) g_object_new (NM_TYPE_SETTING_802_1X, NULL); + const char *data = pdata; + NMSetting8021xCKScheme scheme; + gsize prefix_length; + + g_return_val_if_fail (!length || data, NM_SETTING_802_1X_CK_SCHEME_UNKNOWN); + + if (!length || !data) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("binary data missing")); + return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN; + } + + if ( length >= NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH) + && !memcmp (data, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH, NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH))) { + scheme = NM_SETTING_802_1X_CK_SCHEME_PATH; + prefix_length = NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH); + } else if ( length >= NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PKCS11) + && !memcmp (data, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PKCS11, NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PKCS11))) { + scheme = NM_SETTING_802_1X_CK_SCHEME_PKCS11; + prefix_length = NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PKCS11); + } else { + scheme = NM_SETTING_802_1X_CK_SCHEME_BLOB; + prefix_length = 0; + } + + if (scheme != NM_SETTING_802_1X_CK_SCHEME_BLOB) { + /* An actual URI must be NUL terminated, contain at least + * one non-NUL character, and contain only one trailing NUL + * chracter. + * And ensure it's UTF-8 valid too so we can pass it through + * D-Bus and stuff like that. */ + + if (data[length - 1] != '\0') { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("URI not NUL terminated")); + return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN; + } + length--; + + if (length <= prefix_length) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("URI is empty")); + return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN; + } + + if (!g_utf8_validate (data + prefix_length, length - prefix_length, NULL)) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("URI is not valid UTF-8")); + return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN; + } + } + + return scheme; +} + +static NMSetting8021xCKScheme +get_cert_scheme (GBytes *bytes, GError **error) +{ + const char *data; + gsize length; + + if (!bytes) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("data missing")); + return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN; + } + + data = g_bytes_get_data (bytes, &length); + return nm_setting_802_1x_check_cert_scheme (data, length, error); +} + +static GBytes * +load_and_verify_certificate (const char *cert_path, + NMSetting8021xCKScheme scheme, + NMCryptoFileFormat *out_file_format, + GError **error) +{ + NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; + gs_unref_bytes GBytes *cert = NULL; + + if (!nm_crypto_load_and_verify_certificate (cert_path, &format, &cert, error)) { + NM_SET_OUT (out_file_format, NM_CRYPTO_FILE_FORMAT_UNKNOWN); + return NULL; + } + + nm_assert (format != NM_CRYPTO_FILE_FORMAT_UNKNOWN); + nm_assert (cert); + + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { + const guint8 *bin; + gsize len; + + bin = g_bytes_get_data (cert, &len); + /* If we load the file as blob, we must ensure that the binary data does not + * start with file://. NMSetting8021x cannot represent blobs that start with + * file://. + * If that's the case, coerce the format to UNKNOWN. The callers will take care + * of that and not set the blob. */ + if (nm_setting_802_1x_check_cert_scheme (bin, len, NULL) != NM_SETTING_802_1X_CK_SCHEME_BLOB) { + NM_SET_OUT (out_file_format, NM_CRYPTO_FILE_FORMAT_UNKNOWN); + return NULL; + } + } + + NM_SET_OUT (out_file_format, format); + return g_steal_pointer (&cert); +} + +static GBytes * +path_to_scheme_value (const char *path) +{ + guint8 *mem; + gsize len, total_len; + + g_return_val_if_fail (path != NULL && path[0], NULL); + + len = strlen (path); + total_len = (NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH) + 1) + len; + + /* Add the path scheme tag to the front, then the filename */ + mem = g_new (guint8, total_len); + memcpy (mem, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH, NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)); + memcpy (&mem[NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)], path, len + 1); + return g_bytes_new_take (mem, total_len); +} + +static gboolean +verify_cert (GBytes *bytes, const char *prop_name, + const char *password, const char *password_prop_name, GError **error) +{ + GError *local = NULL; + NMSetting8021xCKScheme scheme; + + if (bytes) + scheme = get_cert_scheme (bytes, &local); + else + return TRUE; + + if (scheme == NM_SETTING_802_1X_CK_SCHEME_UNKNOWN) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("certificate is invalid: %s"), local->message); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, prop_name); + g_error_free (local); + return FALSE; + } + + if (password && (scheme != NM_SETTING_802_1X_CK_SCHEME_PKCS11)) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("password is not supported when certificate is not on a PKCS#11 token")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, password_prop_name); + return FALSE; + } + + return TRUE; } /*****************************************************************************/ @@ -435,142 +628,6 @@ nm_setting_802_1x_get_system_ca_certs (NMSetting8021x *setting) return NM_SETTING_802_1X_GET_PRIVATE (setting)->system_ca_certs; } -static NMSetting8021xCKScheme -get_cert_scheme (GBytes *bytes, GError **error) -{ - const char *data; - gsize length; - - if (!bytes) { - g_set_error_literal (error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("data missing")); - return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN; - } - - data = g_bytes_get_data (bytes, &length); - return nm_setting_802_1x_check_cert_scheme (data, length, error); -} - -/** - * nm_setting_802_1x_check_cert_scheme: - * @pdata: (allow-none): the data pointer - * @length: the length of the data - * @error: (allow-none): (out): validation reason - * - * Determines and verifies the blob type. - * When setting certificate properties of NMSetting8021x - * the blob must be not UNKNOWN (or NULL). - * - * Returns: the scheme of the blob or %NM_SETTING_802_1X_CK_SCHEME_UNKNOWN. - * For NULL it also returns NM_SETTING_802_1X_CK_SCHEME_UNKNOWN. - * - * Since: 1.2 - **/ -NMSetting8021xCKScheme -nm_setting_802_1x_check_cert_scheme (gconstpointer pdata, gsize length, GError **error) -{ - const char *data = pdata; - NMSetting8021xCKScheme scheme; - gsize prefix_length; - - g_return_val_if_fail (!length || data, NM_SETTING_802_1X_CK_SCHEME_UNKNOWN); - - if (!length || !data) { - g_set_error_literal (error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("binary data missing")); - return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN; - } - - if ( length >= NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH) - && !memcmp (data, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH, NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH))) { - scheme = NM_SETTING_802_1X_CK_SCHEME_PATH; - prefix_length = NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH); - } else if ( length >= NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PKCS11) - && !memcmp (data, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PKCS11, NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PKCS11))) { - scheme = NM_SETTING_802_1X_CK_SCHEME_PKCS11; - prefix_length = NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PKCS11); - } else { - scheme = NM_SETTING_802_1X_CK_SCHEME_BLOB; - prefix_length = 0; - } - - if (scheme != NM_SETTING_802_1X_CK_SCHEME_BLOB) { - /* An actual URI must be NUL terminated, contain at least - * one non-NUL character, and contain only one trailing NUL - * chracter. - * And ensure it's UTF-8 valid too so we can pass it through - * D-Bus and stuff like that. */ - - if (data[length - 1] != '\0') { - g_set_error_literal (error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("URI not NUL terminated")); - return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN; - } - length--; - - if (length <= prefix_length) { - g_set_error_literal (error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("URI is empty")); - return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN; - } - - if (!g_utf8_validate (data + prefix_length, length - prefix_length, NULL)) { - g_set_error_literal (error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("URI is not valid UTF-8")); - return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN; - } - } - - return scheme; -} - -static GBytes * -load_and_verify_certificate (const char *cert_path, - NMSetting8021xCKScheme scheme, - NMCryptoFileFormat *out_file_format, - GError **error) -{ - NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; - gs_unref_bytes GBytes *cert = NULL; - - if (!nm_crypto_load_and_verify_certificate (cert_path, &format, &cert, error)) { - NM_SET_OUT (out_file_format, NM_CRYPTO_FILE_FORMAT_UNKNOWN); - return NULL; - } - - nm_assert (format != NM_CRYPTO_FILE_FORMAT_UNKNOWN); - nm_assert (cert); - - if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { - const guint8 *bin; - gsize len; - - bin = g_bytes_get_data (cert, &len); - /* If we load the file as blob, we must ensure that the binary data does not - * start with file://. NMSetting8021x cannot represent blobs that start with - * file://. - * If that's the case, coerce the format to UNKNOWN. The callers will take care - * of that and not set the blob. */ - if (nm_setting_802_1x_check_cert_scheme (bin, len, NULL) != NM_SETTING_802_1X_CK_SCHEME_BLOB) { - NM_SET_OUT (out_file_format, NM_CRYPTO_FILE_FORMAT_UNKNOWN); - return NULL; - } - } - - NM_SET_OUT (out_file_format, format); - return g_steal_pointer (&cert); -} - /** * nm_setting_802_1x_get_ca_cert_scheme: * @setting: the #NMSetting8021x @@ -675,24 +732,6 @@ nm_setting_802_1x_get_ca_cert_uri (NMSetting8021x *setting) return (const char *)data; } -static GBytes * -path_to_scheme_value (const char *path) -{ - guint8 *mem; - gsize len, total_len; - - g_return_val_if_fail (path != NULL && path[0], NULL); - - len = strlen (path); - total_len = (NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH) + 1) + len; - - /* Add the path scheme tag to the front, then the filename */ - mem = g_new (guint8, total_len); - memcpy (mem, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH, NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)); - memcpy (&mem[NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)], path, len + 1); - return g_bytes_new_take (mem, total_len); -} - /** * nm_setting_802_1x_set_ca_cert: * @setting: the #NMSetting8021x @@ -2765,6 +2804,8 @@ nm_setting_802_1x_get_auth_timeout (NMSetting8021x *setting) return NM_SETTING_802_1X_GET_PRIVATE (setting)->auth_timeout; } +/*****************************************************************************/ + static void need_secrets_password (NMSetting8021x *self, GPtrArray *secrets, @@ -3086,43 +3127,6 @@ verify_identity (NMSetting8021x *self, gboolean phase2, GError **error) return TRUE; } -/* Implemented below... */ -static void need_secrets_phase2 (NMSetting8021x *self, - GPtrArray *secrets, - gboolean phase2); - -typedef void (*EAPMethodNeedSecretsFunc) (NMSetting8021x *self, - GPtrArray *secrets, - gboolean phase2); - -typedef gboolean (*EAPMethodValidateFunc)(NMSetting8021x *self, - gboolean phase2, - GError **error); - -typedef struct { - const char *method; - EAPMethodNeedSecretsFunc ns_func; - EAPMethodValidateFunc v_func; -} EAPMethodsTable; - -static EAPMethodsTable eap_methods_table[] = { - { "leap", need_secrets_password, verify_identity }, - { "pwd", need_secrets_password, verify_identity }, - { "md5", need_secrets_password, verify_identity }, - { "pap", need_secrets_password, verify_identity }, - { "chap", need_secrets_password, verify_identity }, - { "mschap", need_secrets_password, verify_identity }, - { "mschapv2", need_secrets_password, verify_identity }, - { "fast", need_secrets_password, verify_identity }, - { "tls", need_secrets_tls, verify_tls }, - { "peap", need_secrets_phase2, verify_ttls }, - { "ttls", need_secrets_phase2, verify_ttls }, - { "sim", need_secrets_sim, NULL }, - { "gtc", need_secrets_password, verify_identity }, - { "otp", NULL, NULL }, // FIXME: implement - { NULL, NULL, NULL } -}; - static void need_secrets_phase2 (NMSetting8021x *self, GPtrArray *secrets, @@ -3156,79 +3160,23 @@ need_secrets_phase2 (NMSetting8021x *self, } } -static GPtrArray * -need_secrets (NMSetting *setting) -{ - NMSetting8021x *self = NM_SETTING_802_1X (setting); - NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self); - GSList *iter; - GPtrArray *secrets; - gboolean eap_method_found = FALSE; - - secrets = g_ptr_array_sized_new (4); - - /* Ask each configured EAP method if it needs secrets */ - for (iter = priv->eap; iter && !eap_method_found; iter = g_slist_next (iter)) { - const char *method = (const char *) iter->data; - int i; - - for (i = 0; eap_methods_table[i].method; i++) { - if (eap_methods_table[i].ns_func == NULL) - continue; - if (!strcmp (eap_methods_table[i].method, method)) { - (*eap_methods_table[i].ns_func) (self, secrets, FALSE); - - /* Only break out of the outer loop if this EAP method - * needed secrets. - */ - if (secrets->len > 0) - eap_method_found = TRUE; - break; - } - } - } - - if (secrets->len == 0) { - g_ptr_array_free (secrets, TRUE); - secrets = NULL; - } - - return secrets; -} - -static gboolean -verify_cert (GBytes *bytes, const char *prop_name, - const char *password, const char *password_prop_name, GError **error) -{ - GError *local = NULL; - NMSetting8021xCKScheme scheme; - - if (bytes) - scheme = get_cert_scheme (bytes, &local); - else - return TRUE; - - if (scheme == NM_SETTING_802_1X_CK_SCHEME_UNKNOWN) { - g_set_error (error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("certificate is invalid: %s"), local->message); - g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, prop_name); - g_error_free (local); - return FALSE; - } - - if (password && (scheme != NM_SETTING_802_1X_CK_SCHEME_PKCS11)) { - g_set_error (error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("password is not supported when certificate is not on a PKCS#11 token")); - g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, password_prop_name); - return FALSE; - } - - return TRUE; -} +static EAPMethodsTable eap_methods_table[] = { + { "leap", need_secrets_password, verify_identity }, + { "pwd", need_secrets_password, verify_identity }, + { "md5", need_secrets_password, verify_identity }, + { "pap", need_secrets_password, verify_identity }, + { "chap", need_secrets_password, verify_identity }, + { "mschap", need_secrets_password, verify_identity }, + { "mschapv2", need_secrets_password, verify_identity }, + { "fast", need_secrets_password, verify_identity }, + { "tls", need_secrets_tls, verify_tls }, + { "peap", need_secrets_phase2, verify_ttls }, + { "ttls", need_secrets_phase2, verify_ttls }, + { "sim", need_secrets_sim, NULL }, + { "gtc", need_secrets_password, verify_identity }, + { "otp", NULL, NULL }, // FIXME: implement + { NULL, NULL, NULL } +}; static gboolean verify (NMSetting *setting, NMConnection *connection, GError **error) @@ -3363,63 +3311,50 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return TRUE; } -static void -nm_setting_802_1x_init (NMSetting8021x *setting) -{ -} +/*****************************************************************************/ -static void -finalize (GObject *object) +static GPtrArray * +need_secrets (NMSetting *setting) { - NMSetting8021x *self = NM_SETTING_802_1X (object); + NMSetting8021x *self = NM_SETTING_802_1X (setting); NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self); + GSList *iter; + GPtrArray *secrets; + gboolean eap_method_found = FALSE; - /* Strings first. g_free() already checks for NULLs so we don't have to */ + secrets = g_ptr_array_sized_new (4); - g_free (priv->identity); - g_free (priv->anonymous_identity); - g_free (priv->ca_path); - g_free (priv->subject_match); - g_free (priv->domain_suffix_match); - g_free (priv->phase1_peapver); - g_free (priv->phase1_peaplabel); - g_free (priv->phase1_fast_provisioning); - g_free (priv->phase2_auth); - g_free (priv->phase2_autheap); - g_free (priv->phase2_ca_path); - g_free (priv->phase2_subject_match); - g_free (priv->phase2_domain_suffix_match); - g_free (priv->password); - if (priv->password_raw) - g_bytes_unref (priv->password_raw); - g_free (priv->pin); + /* Ask each configured EAP method if it needs secrets */ + for (iter = priv->eap; iter && !eap_method_found; iter = g_slist_next (iter)) { + const char *method = (const char *) iter->data; + int i; - g_slist_free_full (priv->eap, g_free); - g_slist_free_full (priv->altsubject_matches, g_free); - g_slist_free_full (priv->phase2_altsubject_matches, g_free); + for (i = 0; eap_methods_table[i].method; i++) { + if (eap_methods_table[i].ns_func == NULL) + continue; + if (!strcmp (eap_methods_table[i].method, method)) { + (*eap_methods_table[i].ns_func) (self, secrets, FALSE); - if (priv->ca_cert) - g_bytes_unref (priv->ca_cert); - g_free (priv->ca_cert_password); - if (priv->client_cert) - g_bytes_unref (priv->client_cert); - g_free (priv->client_cert_password); - if (priv->private_key) - g_bytes_unref (priv->private_key); - nm_free_secret (priv->private_key_password); - if (priv->phase2_ca_cert) - g_bytes_unref (priv->phase2_ca_cert); - g_free (priv->phase2_ca_cert_password); - if (priv->phase2_client_cert) - g_bytes_unref (priv->phase2_client_cert); - g_free (priv->phase2_client_cert_password); - if (priv->phase2_private_key) - g_bytes_unref (priv->phase2_private_key); - nm_free_secret (priv->phase2_private_key_password); + /* Only break out of the outer loop if this EAP method + * needed secrets. + */ + if (secrets->len > 0) + eap_method_found = TRUE; + break; + } + } + } - G_OBJECT_CLASS (nm_setting_802_1x_parent_class)->finalize (object); + if (secrets->len == 0) { + g_ptr_array_free (secrets, TRUE); + secrets = NULL; + } + + return secrets; } +/*****************************************************************************/ + static GBytes * set_cert_prop_helper (const GValue *value, const char *prop_name, GError **error) { @@ -3436,6 +3371,154 @@ set_cert_prop_helper (const GValue *value, const char *prop_name, GError **error return bytes; } +/*****************************************************************************/ + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMSetting8021x *setting = NM_SETTING_802_1X (object); + NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (setting); + + switch (prop_id) { + case PROP_EAP: + g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->eap, TRUE)); + break; + case PROP_IDENTITY: + g_value_set_string (value, priv->identity); + break; + case PROP_ANONYMOUS_IDENTITY: + g_value_set_string (value, priv->anonymous_identity); + break; + case PROP_PAC_FILE: + g_value_set_string (value, priv->pac_file); + break; + case PROP_CA_CERT: + g_value_set_boxed (value, priv->ca_cert); + break; + case PROP_CA_CERT_PASSWORD: + g_value_set_string (value, priv->ca_cert_password); + break; + case PROP_CA_CERT_PASSWORD_FLAGS: + g_value_set_flags (value, priv->ca_cert_password_flags); + break; + case PROP_CA_PATH: + g_value_set_string (value, priv->ca_path); + break; + case PROP_SUBJECT_MATCH: + g_value_set_string (value, priv->subject_match); + break; + case PROP_ALTSUBJECT_MATCHES: + g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->altsubject_matches, TRUE)); + break; + case PROP_DOMAIN_SUFFIX_MATCH: + g_value_set_string (value, priv->domain_suffix_match); + break; + case PROP_CLIENT_CERT: + g_value_set_boxed (value, priv->client_cert); + break; + case PROP_CLIENT_CERT_PASSWORD: + g_value_set_string (value, priv->client_cert_password); + break; + case PROP_CLIENT_CERT_PASSWORD_FLAGS: + g_value_set_flags (value, priv->client_cert_password_flags); + break; + case PROP_PHASE1_PEAPVER: + g_value_set_string (value, priv->phase1_peapver); + break; + case PROP_PHASE1_PEAPLABEL: + g_value_set_string (value, priv->phase1_peaplabel); + break; + case PROP_PHASE1_FAST_PROVISIONING: + g_value_set_string (value, priv->phase1_fast_provisioning); + break; + case PROP_PHASE1_AUTH_FLAGS: + g_value_set_uint (value, priv->phase1_auth_flags); + break; + case PROP_PHASE2_AUTH: + g_value_set_string (value, priv->phase2_auth); + break; + case PROP_PHASE2_AUTHEAP: + g_value_set_string (value, priv->phase2_autheap); + break; + case PROP_PHASE2_CA_CERT: + g_value_set_boxed (value, priv->phase2_ca_cert); + break; + case PROP_PHASE2_CA_CERT_PASSWORD: + g_value_set_string (value, priv->phase2_ca_cert_password); + break; + case PROP_PHASE2_CA_CERT_PASSWORD_FLAGS: + g_value_set_flags (value, priv->phase2_ca_cert_password_flags); + break; + case PROP_PHASE2_CA_PATH: + g_value_set_string (value, priv->phase2_ca_path); + break; + case PROP_PHASE2_SUBJECT_MATCH: + g_value_set_string (value, priv->phase2_subject_match); + break; + case PROP_PHASE2_ALTSUBJECT_MATCHES: + g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->phase2_altsubject_matches, TRUE)); + break; + case PROP_PHASE2_DOMAIN_SUFFIX_MATCH: + g_value_set_string (value, priv->phase2_domain_suffix_match); + break; + case PROP_PHASE2_CLIENT_CERT: + g_value_set_boxed (value, priv->phase2_client_cert); + break; + case PROP_PHASE2_CLIENT_CERT_PASSWORD: + g_value_set_string (value, priv->phase2_client_cert_password); + break; + case PROP_PHASE2_CLIENT_CERT_PASSWORD_FLAGS: + g_value_set_flags (value, priv->phase2_client_cert_password_flags); + break; + case PROP_PASSWORD: + g_value_set_string (value, priv->password); + break; + case PROP_PASSWORD_FLAGS: + g_value_set_flags (value, priv->password_flags); + break; + case PROP_PASSWORD_RAW: + g_value_set_boxed (value, priv->password_raw); + break; + case PROP_PASSWORD_RAW_FLAGS: + g_value_set_flags (value, priv->password_raw_flags); + break; + case PROP_PRIVATE_KEY: + g_value_set_boxed (value, priv->private_key); + break; + case PROP_PRIVATE_KEY_PASSWORD: + g_value_set_string (value, priv->private_key_password); + break; + case PROP_PRIVATE_KEY_PASSWORD_FLAGS: + g_value_set_flags (value, priv->private_key_password_flags); + break; + case PROP_PHASE2_PRIVATE_KEY: + g_value_set_boxed (value, priv->phase2_private_key); + break; + case PROP_PHASE2_PRIVATE_KEY_PASSWORD: + g_value_set_string (value, priv->phase2_private_key_password); + break; + case PROP_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS: + g_value_set_flags (value, priv->phase2_private_key_password_flags); + break; + case PROP_PIN: + g_value_set_string (value, priv->pin); + break; + case PROP_PIN_FLAGS: + g_value_set_flags (value, priv->pin_flags); + break; + case PROP_SYSTEM_CA_CERTS: + g_value_set_boolean (value, priv->system_ca_certs); + break; + case PROP_AUTH_TIMEOUT: + g_value_set_int (value, priv->auth_timeout); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) @@ -3647,150 +3730,76 @@ set_property (GObject *object, guint prop_id, } } -static void -get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - NMSetting8021x *setting = NM_SETTING_802_1X (object); - NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (setting); +/*****************************************************************************/ - switch (prop_id) { - case PROP_EAP: - g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->eap, TRUE)); - break; - case PROP_IDENTITY: - g_value_set_string (value, priv->identity); - break; - case PROP_ANONYMOUS_IDENTITY: - g_value_set_string (value, priv->anonymous_identity); - break; - case PROP_PAC_FILE: - g_value_set_string (value, priv->pac_file); - break; - case PROP_CA_CERT: - g_value_set_boxed (value, priv->ca_cert); - break; - case PROP_CA_CERT_PASSWORD: - g_value_set_string (value, priv->ca_cert_password); - break; - case PROP_CA_CERT_PASSWORD_FLAGS: - g_value_set_flags (value, priv->ca_cert_password_flags); - break; - case PROP_CA_PATH: - g_value_set_string (value, priv->ca_path); - break; - case PROP_SUBJECT_MATCH: - g_value_set_string (value, priv->subject_match); - break; - case PROP_ALTSUBJECT_MATCHES: - g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->altsubject_matches, TRUE)); - break; - case PROP_DOMAIN_SUFFIX_MATCH: - g_value_set_string (value, priv->domain_suffix_match); - break; - case PROP_CLIENT_CERT: - g_value_set_boxed (value, priv->client_cert); - break; - case PROP_CLIENT_CERT_PASSWORD: - g_value_set_string (value, priv->client_cert_password); - break; - case PROP_CLIENT_CERT_PASSWORD_FLAGS: - g_value_set_flags (value, priv->client_cert_password_flags); - break; - case PROP_PHASE1_PEAPVER: - g_value_set_string (value, priv->phase1_peapver); - break; - case PROP_PHASE1_PEAPLABEL: - g_value_set_string (value, priv->phase1_peaplabel); - break; - case PROP_PHASE1_FAST_PROVISIONING: - g_value_set_string (value, priv->phase1_fast_provisioning); - break; - case PROP_PHASE1_AUTH_FLAGS: - g_value_set_uint (value, priv->phase1_auth_flags); - break; - case PROP_PHASE2_AUTH: - g_value_set_string (value, priv->phase2_auth); - break; - case PROP_PHASE2_AUTHEAP: - g_value_set_string (value, priv->phase2_autheap); - break; - case PROP_PHASE2_CA_CERT: - g_value_set_boxed (value, priv->phase2_ca_cert); - break; - case PROP_PHASE2_CA_CERT_PASSWORD: - g_value_set_string (value, priv->phase2_ca_cert_password); - break; - case PROP_PHASE2_CA_CERT_PASSWORD_FLAGS: - g_value_set_flags (value, priv->phase2_ca_cert_password_flags); - break; - case PROP_PHASE2_CA_PATH: - g_value_set_string (value, priv->phase2_ca_path); - break; - case PROP_PHASE2_SUBJECT_MATCH: - g_value_set_string (value, priv->phase2_subject_match); - break; - case PROP_PHASE2_ALTSUBJECT_MATCHES: - g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->phase2_altsubject_matches, TRUE)); - break; - case PROP_PHASE2_DOMAIN_SUFFIX_MATCH: - g_value_set_string (value, priv->phase2_domain_suffix_match); - break; - case PROP_PHASE2_CLIENT_CERT: - g_value_set_boxed (value, priv->phase2_client_cert); - break; - case PROP_PHASE2_CLIENT_CERT_PASSWORD: - g_value_set_string (value, priv->phase2_client_cert_password); - break; - case PROP_PHASE2_CLIENT_CERT_PASSWORD_FLAGS: - g_value_set_flags (value, priv->phase2_client_cert_password_flags); - break; - case PROP_PASSWORD: - g_value_set_string (value, priv->password); - break; - case PROP_PASSWORD_FLAGS: - g_value_set_flags (value, priv->password_flags); - break; - case PROP_PASSWORD_RAW: - g_value_set_boxed (value, priv->password_raw); - break; - case PROP_PASSWORD_RAW_FLAGS: - g_value_set_flags (value, priv->password_raw_flags); - break; - case PROP_PRIVATE_KEY: - g_value_set_boxed (value, priv->private_key); - break; - case PROP_PRIVATE_KEY_PASSWORD: - g_value_set_string (value, priv->private_key_password); - break; - case PROP_PRIVATE_KEY_PASSWORD_FLAGS: - g_value_set_flags (value, priv->private_key_password_flags); - break; - case PROP_PHASE2_PRIVATE_KEY: - g_value_set_boxed (value, priv->phase2_private_key); - break; - case PROP_PHASE2_PRIVATE_KEY_PASSWORD: - g_value_set_string (value, priv->phase2_private_key_password); - break; - case PROP_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS: - g_value_set_flags (value, priv->phase2_private_key_password_flags); - break; - case PROP_PIN: - g_value_set_string (value, priv->pin); - break; - case PROP_PIN_FLAGS: - g_value_set_flags (value, priv->pin_flags); - break; - case PROP_SYSTEM_CA_CERTS: - g_value_set_boolean (value, priv->system_ca_certs); - break; - case PROP_AUTH_TIMEOUT: - g_value_set_int (value, priv->auth_timeout); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } +static void +nm_setting_802_1x_init (NMSetting8021x *setting) +{ +} + +/** + * nm_setting_802_1x_new: + * + * Creates a new #NMSetting8021x object with default values. + * + * Returns: the new empty #NMSetting8021x object + **/ +NMSetting * +nm_setting_802_1x_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_802_1X, NULL); +} + +static void +finalize (GObject *object) +{ + NMSetting8021x *self = NM_SETTING_802_1X (object); + NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self); + + /* Strings first. g_free() already checks for NULLs so we don't have to */ + + g_free (priv->identity); + g_free (priv->anonymous_identity); + g_free (priv->ca_path); + g_free (priv->subject_match); + g_free (priv->domain_suffix_match); + g_free (priv->phase1_peapver); + g_free (priv->phase1_peaplabel); + g_free (priv->phase1_fast_provisioning); + g_free (priv->phase2_auth); + g_free (priv->phase2_autheap); + g_free (priv->phase2_ca_path); + g_free (priv->phase2_subject_match); + g_free (priv->phase2_domain_suffix_match); + g_free (priv->password); + if (priv->password_raw) + g_bytes_unref (priv->password_raw); + g_free (priv->pin); + + g_slist_free_full (priv->eap, g_free); + g_slist_free_full (priv->altsubject_matches, g_free); + g_slist_free_full (priv->phase2_altsubject_matches, g_free); + + if (priv->ca_cert) + g_bytes_unref (priv->ca_cert); + g_free (priv->ca_cert_password); + if (priv->client_cert) + g_bytes_unref (priv->client_cert); + g_free (priv->client_cert_password); + if (priv->private_key) + g_bytes_unref (priv->private_key); + nm_free_secret (priv->private_key_password); + if (priv->phase2_ca_cert) + g_bytes_unref (priv->phase2_ca_cert); + g_free (priv->phase2_ca_cert_password); + if (priv->phase2_client_cert) + g_bytes_unref (priv->phase2_client_cert); + g_free (priv->phase2_client_cert_password); + if (priv->phase2_private_key) + g_bytes_unref (priv->phase2_private_key); + nm_free_secret (priv->phase2_private_key_password); + + G_OBJECT_CLASS (nm_setting_802_1x_parent_class)->finalize (object); } static void From 6d6016f83d69cbe3d951d76859077ec64461043b Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 31 Aug 2018 20:41:50 +0200 Subject: [PATCH 51/61] libnm/802-1x: call g_bytes_unref() directly without checking for NULL Since its inception, g_bytes_unref() is fine with NULL argument. --- libnm-core/nm-setting-8021x.c | 43 ++++++++++++----------------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/libnm-core/nm-setting-8021x.c b/libnm-core/nm-setting-8021x.c index 6923e50eac..d3e68a617f 100644 --- a/libnm-core/nm-setting-8021x.c +++ b/libnm-core/nm-setting-8021x.c @@ -3545,8 +3545,7 @@ set_property (GObject *object, guint prop_id, priv->pac_file = g_value_dup_string (value); break; case PROP_CA_CERT: - if (priv->ca_cert) - g_bytes_unref (priv->ca_cert); + g_bytes_unref (priv->ca_cert); priv->ca_cert = set_cert_prop_helper (value, NM_SETTING_802_1X_CA_CERT, &error); if (error) { g_warning ("Error setting certificate (invalid data): %s", error->message); @@ -3577,8 +3576,7 @@ set_property (GObject *object, guint prop_id, priv->domain_suffix_match = nm_strdup_not_empty (g_value_get_string (value)); break; case PROP_CLIENT_CERT: - if (priv->client_cert) - g_bytes_unref (priv->client_cert); + g_bytes_unref (priv->client_cert); priv->client_cert = set_cert_prop_helper (value, NM_SETTING_802_1X_CLIENT_CERT, &error); if (error) { g_warning ("Error setting certificate (invalid data): %s", error->message); @@ -3616,8 +3614,7 @@ set_property (GObject *object, guint prop_id, priv->phase2_autheap = g_value_dup_string (value); break; case PROP_PHASE2_CA_CERT: - if (priv->phase2_ca_cert) - g_bytes_unref (priv->phase2_ca_cert); + g_bytes_unref (priv->phase2_ca_cert); priv->phase2_ca_cert = set_cert_prop_helper (value, NM_SETTING_802_1X_PHASE2_CA_CERT, &error); if (error) { g_warning ("Error setting certificate (invalid data): %s", error->message); @@ -3648,9 +3645,7 @@ set_property (GObject *object, guint prop_id, priv->phase2_domain_suffix_match = nm_strdup_not_empty (g_value_get_string (value)); break; case PROP_PHASE2_CLIENT_CERT: - - if (priv->phase2_client_cert) - g_bytes_unref (priv->phase2_client_cert); + g_bytes_unref (priv->phase2_client_cert); priv->phase2_client_cert = set_cert_prop_helper (value, NM_SETTING_802_1X_PHASE2_CLIENT_CERT, &error); if (error) { g_warning ("Error setting certificate (invalid data): %s", error->message); @@ -3672,16 +3667,14 @@ set_property (GObject *object, guint prop_id, priv->password_flags = g_value_get_flags (value); break; case PROP_PASSWORD_RAW: - if (priv->password_raw) - g_bytes_unref (priv->password_raw); + g_bytes_unref (priv->password_raw); priv->password_raw = g_value_dup_boxed (value); break; case PROP_PASSWORD_RAW_FLAGS: priv->password_raw_flags = g_value_get_flags (value); break; case PROP_PRIVATE_KEY: - if (priv->private_key) - g_bytes_unref (priv->private_key); + g_bytes_unref (priv->private_key); priv->private_key = set_cert_prop_helper (value, NM_SETTING_802_1X_PRIVATE_KEY, &error); if (error) { g_warning ("Error setting private key (invalid data): %s", error->message); @@ -3696,8 +3689,7 @@ set_property (GObject *object, guint prop_id, priv->private_key_password_flags = g_value_get_flags (value); break; case PROP_PHASE2_PRIVATE_KEY: - if (priv->phase2_private_key) - g_bytes_unref (priv->phase2_private_key); + g_bytes_unref (priv->phase2_private_key); priv->phase2_private_key = set_cert_prop_helper (value, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, &error); if (error) { g_warning ("Error setting private key (invalid data): %s", error->message); @@ -3772,31 +3764,24 @@ finalize (GObject *object) g_free (priv->phase2_subject_match); g_free (priv->phase2_domain_suffix_match); g_free (priv->password); - if (priv->password_raw) - g_bytes_unref (priv->password_raw); + g_bytes_unref (priv->password_raw); g_free (priv->pin); g_slist_free_full (priv->eap, g_free); g_slist_free_full (priv->altsubject_matches, g_free); g_slist_free_full (priv->phase2_altsubject_matches, g_free); - if (priv->ca_cert) - g_bytes_unref (priv->ca_cert); + g_bytes_unref (priv->ca_cert); g_free (priv->ca_cert_password); - if (priv->client_cert) - g_bytes_unref (priv->client_cert); + g_bytes_unref (priv->client_cert); g_free (priv->client_cert_password); - if (priv->private_key) - g_bytes_unref (priv->private_key); + g_bytes_unref (priv->private_key); nm_free_secret (priv->private_key_password); - if (priv->phase2_ca_cert) - g_bytes_unref (priv->phase2_ca_cert); + g_bytes_unref (priv->phase2_ca_cert); g_free (priv->phase2_ca_cert_password); - if (priv->phase2_client_cert) - g_bytes_unref (priv->phase2_client_cert); + g_bytes_unref (priv->phase2_client_cert); g_free (priv->phase2_client_cert_password); - if (priv->phase2_private_key) - g_bytes_unref (priv->phase2_private_key); + g_bytes_unref (priv->phase2_private_key); nm_free_secret (priv->phase2_private_key_password); G_OBJECT_CLASS (nm_setting_802_1x_parent_class)->finalize (object); From 53ca365407bb0cffd8ba4c99e80143ea655928bf Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 31 Aug 2018 21:04:17 +0200 Subject: [PATCH 52/61] libnm/802-1x: refactor certificate handling in settings - all this code duplication. Add functions and macros to simplify the implementation of certificate properties. Overall, pretty trival. Replace code with a macro. --- libnm-core/nm-setting-8021x.c | 284 +++++++++++----------------------- 1 file changed, 94 insertions(+), 190 deletions(-) diff --git a/libnm-core/nm-setting-8021x.c b/libnm-core/nm-setting-8021x.c index d3e68a617f..b5cc751b1a 100644 --- a/libnm-core/nm-setting-8021x.c +++ b/libnm-core/nm-setting-8021x.c @@ -280,7 +280,7 @@ nm_setting_802_1x_check_cert_scheme (gconstpointer pdata, gsize length, GError * } static NMSetting8021xCKScheme -get_cert_scheme (GBytes *bytes, GError **error) +_cert_get_scheme (GBytes *bytes, GError **error) { const char *data; gsize length; @@ -297,6 +297,73 @@ get_cert_scheme (GBytes *bytes, GError **error) return nm_setting_802_1x_check_cert_scheme (data, length, error); } +#define _cert_assert_scheme(cert, check_scheme, ret_val) \ + G_STMT_START { \ + NMSetting8021xCKScheme scheme; \ + \ + scheme = _cert_get_scheme ((cert), NULL); \ + if (scheme != check_scheme) { \ + g_return_val_if_fail (scheme == check_scheme, ret_val); \ + return ret_val; \ + } \ + } G_STMT_END + +#define _cert_impl_get_scheme(setting, cert_field) \ + G_STMT_START { \ + NMSetting8021x *const _setting = (setting); \ + GBytes *_cert; \ + \ + g_return_val_if_fail (NM_IS_SETTING_802_1X (_setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN); \ + \ + _cert = NM_SETTING_802_1X_GET_PRIVATE (_setting)->cert_field; \ + \ + return _cert_get_scheme (_cert, NULL); \ + } G_STMT_END + +#define _cert_impl_get_blob(setting, cert_field) \ + G_STMT_START { \ + NMSetting8021x *const _setting = (setting); \ + GBytes *_cert; \ + \ + g_return_val_if_fail (NM_IS_SETTING_802_1X (_setting), NULL); \ + \ + _cert = NM_SETTING_802_1X_GET_PRIVATE (_setting)->cert_field; \ + \ + _cert_assert_scheme (_cert, NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL); \ + \ + return _cert; \ + } G_STMT_END + +#define _cert_impl_get_path(setting, cert_field) \ + G_STMT_START { \ + NMSetting8021x *const _setting = (setting); \ + GBytes *_cert; \ + const char *_data; \ + \ + g_return_val_if_fail (NM_IS_SETTING_802_1X (_setting), NULL); \ + \ + _cert = NM_SETTING_802_1X_GET_PRIVATE (_setting)->cert_field; \ + \ + _cert_assert_scheme (_cert, NM_SETTING_802_1X_CK_SCHEME_PATH, NULL); \ + \ + _data = g_bytes_get_data (_cert, NULL); \ + return &_data[NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)]; \ + } G_STMT_END + +#define _cert_impl_get_uri(setting, cert_field) \ + G_STMT_START { \ + NMSetting8021x *const _setting = (setting); \ + GBytes *_cert; \ + \ + g_return_val_if_fail (NM_IS_SETTING_802_1X (_setting), NULL); \ + \ + _cert = NM_SETTING_802_1X_GET_PRIVATE (_setting)->cert_field; \ + \ + _cert_assert_scheme (_cert, NM_SETTING_802_1X_CK_SCHEME_PKCS11, NULL); \ + \ + return g_bytes_get_data (_cert, NULL); \ + } G_STMT_END + static GBytes * load_and_verify_certificate (const char *cert_path, NMSetting8021xCKScheme scheme, @@ -359,11 +426,10 @@ verify_cert (GBytes *bytes, const char *prop_name, GError *local = NULL; NMSetting8021xCKScheme scheme; - if (bytes) - scheme = get_cert_scheme (bytes, &local); - else + if (!bytes) return TRUE; + scheme = _cert_get_scheme (bytes, &local); if (scheme == NM_SETTING_802_1X_CK_SCHEME_UNKNOWN) { g_set_error (error, NM_CONNECTION_ERROR, @@ -642,9 +708,7 @@ nm_setting_802_1x_get_system_ca_certs (NMSetting8021x *setting) NMSetting8021xCKScheme nm_setting_802_1x_get_ca_cert_scheme (NMSetting8021x *setting) { - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN); - - return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_cert, NULL); + _cert_impl_get_scheme (setting, ca_cert); } /** @@ -663,14 +727,7 @@ nm_setting_802_1x_get_ca_cert_scheme (NMSetting8021x *setting) GBytes * nm_setting_802_1x_get_ca_cert_blob (NMSetting8021x *setting) { - NMSetting8021xCKScheme scheme; - - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); - - scheme = nm_setting_802_1x_get_ca_cert_scheme (setting); - g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL); - - return NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_cert; + _cert_impl_get_blob (setting, ca_cert); } /** @@ -689,16 +746,7 @@ nm_setting_802_1x_get_ca_cert_blob (NMSetting8021x *setting) const char * nm_setting_802_1x_get_ca_cert_path (NMSetting8021x *setting) { - NMSetting8021xCKScheme scheme; - gconstpointer data; - - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); - - scheme = nm_setting_802_1x_get_ca_cert_scheme (setting); - g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL); - - data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_cert, NULL); - return (const char *)data + strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH); + _cert_impl_get_path (setting, ca_cert); } /** @@ -720,16 +768,7 @@ nm_setting_802_1x_get_ca_cert_path (NMSetting8021x *setting) const char * nm_setting_802_1x_get_ca_cert_uri (NMSetting8021x *setting) { - NMSetting8021xCKScheme scheme; - gconstpointer data; - - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); - - scheme = nm_setting_802_1x_get_ca_cert_scheme (setting); - g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11, NULL); - - data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_cert, NULL); - return (const char *)data; + _cert_impl_get_uri (setting, ca_cert); } /** @@ -1045,9 +1084,7 @@ nm_setting_802_1x_get_domain_suffix_match (NMSetting8021x *setting) NMSetting8021xCKScheme nm_setting_802_1x_get_client_cert_scheme (NMSetting8021x *setting) { - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN); - - return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->client_cert, NULL); + _cert_impl_get_scheme (setting, client_cert); } /** @@ -1063,14 +1100,7 @@ nm_setting_802_1x_get_client_cert_scheme (NMSetting8021x *setting) GBytes * nm_setting_802_1x_get_client_cert_blob (NMSetting8021x *setting) { - NMSetting8021xCKScheme scheme; - - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); - - scheme = nm_setting_802_1x_get_client_cert_scheme (setting); - g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL); - - return NM_SETTING_802_1X_GET_PRIVATE (setting)->client_cert; + _cert_impl_get_blob (setting, client_cert); } /** @@ -1086,16 +1116,7 @@ nm_setting_802_1x_get_client_cert_blob (NMSetting8021x *setting) const char * nm_setting_802_1x_get_client_cert_path (NMSetting8021x *setting) { - NMSetting8021xCKScheme scheme; - gconstpointer data; - - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); - - scheme = nm_setting_802_1x_get_client_cert_scheme (setting); - g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL); - - data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->client_cert, NULL); - return (const char *)data + strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH); + _cert_impl_get_path (setting, client_cert); } /** @@ -1117,16 +1138,7 @@ nm_setting_802_1x_get_client_cert_path (NMSetting8021x *setting) const char * nm_setting_802_1x_get_client_cert_uri (NMSetting8021x *setting) { - NMSetting8021xCKScheme scheme; - gconstpointer data; - - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); - - scheme = nm_setting_802_1x_get_client_cert_scheme (setting); - g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11, NULL); - - data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->client_cert, NULL); - return (const char *)data; + _cert_impl_get_uri (setting, client_cert); } /** @@ -1395,9 +1407,7 @@ nm_setting_802_1x_get_phase2_ca_path (NMSetting8021x *setting) NMSetting8021xCKScheme nm_setting_802_1x_get_phase2_ca_cert_scheme (NMSetting8021x *setting) { - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN); - - return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_ca_cert, NULL); + _cert_impl_get_scheme (setting, phase2_ca_cert); } /** @@ -1416,14 +1426,7 @@ nm_setting_802_1x_get_phase2_ca_cert_scheme (NMSetting8021x *setting) GBytes * nm_setting_802_1x_get_phase2_ca_cert_blob (NMSetting8021x *setting) { - NMSetting8021xCKScheme scheme; - - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); - - scheme = nm_setting_802_1x_get_phase2_ca_cert_scheme (setting); - g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL); - - return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_ca_cert; + _cert_impl_get_blob (setting, phase2_ca_cert); } /** @@ -1442,16 +1445,7 @@ nm_setting_802_1x_get_phase2_ca_cert_blob (NMSetting8021x *setting) const char * nm_setting_802_1x_get_phase2_ca_cert_path (NMSetting8021x *setting) { - NMSetting8021xCKScheme scheme; - gconstpointer data; - - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); - - scheme = nm_setting_802_1x_get_phase2_ca_cert_scheme (setting); - g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL); - - data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_ca_cert, NULL); - return (const char *)data + strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH); + _cert_impl_get_path (setting, phase2_ca_cert); } /** @@ -1473,16 +1467,7 @@ nm_setting_802_1x_get_phase2_ca_cert_path (NMSetting8021x *setting) const char * nm_setting_802_1x_get_phase2_ca_cert_uri (NMSetting8021x *setting) { - NMSetting8021xCKScheme scheme; - gconstpointer data; - - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); - - scheme = nm_setting_802_1x_get_phase2_ca_cert_scheme (setting); - g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11, NULL); - - data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_ca_cert, NULL); - return (const char *)data; + _cert_impl_get_uri (setting, phase2_ca_cert); } /** @@ -1802,9 +1787,7 @@ nm_setting_802_1x_clear_phase2_altsubject_matches (NMSetting8021x *setting) NMSetting8021xCKScheme nm_setting_802_1x_get_phase2_client_cert_scheme (NMSetting8021x *setting) { - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN); - - return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_client_cert, NULL); + _cert_impl_get_scheme (setting, phase2_client_cert); } /** @@ -1820,14 +1803,7 @@ nm_setting_802_1x_get_phase2_client_cert_scheme (NMSetting8021x *setting) GBytes * nm_setting_802_1x_get_phase2_client_cert_blob (NMSetting8021x *setting) { - NMSetting8021xCKScheme scheme; - - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); - - scheme = nm_setting_802_1x_get_phase2_client_cert_scheme (setting); - g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL); - - return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_client_cert; + _cert_impl_get_blob (setting, phase2_client_cert); } /** @@ -1843,16 +1819,7 @@ nm_setting_802_1x_get_phase2_client_cert_blob (NMSetting8021x *setting) const char * nm_setting_802_1x_get_phase2_client_cert_path (NMSetting8021x *setting) { - NMSetting8021xCKScheme scheme; - gconstpointer data; - - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); - - scheme = nm_setting_802_1x_get_phase2_client_cert_scheme (setting); - g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL); - - data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_client_cert, NULL); - return (const char *)data + strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH); + _cert_impl_get_path (setting, phase2_client_cert); } /** @@ -1874,16 +1841,7 @@ nm_setting_802_1x_get_phase2_client_cert_path (NMSetting8021x *setting) const char * nm_setting_802_1x_get_phase2_client_cert_uri (NMSetting8021x *setting) { - NMSetting8021xCKScheme scheme; - gconstpointer data; - - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); - - scheme = nm_setting_802_1x_get_phase2_client_cert_scheme (setting); - g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11, NULL); - - data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_client_cert, NULL); - return (const char *)data; + _cert_impl_get_uri (setting, phase2_client_cert); } /** @@ -2129,9 +2087,7 @@ nm_setting_802_1x_get_pin_flags (NMSetting8021x *setting) NMSetting8021xCKScheme nm_setting_802_1x_get_private_key_scheme (NMSetting8021x *setting) { - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN); - - return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key, NULL); + _cert_impl_get_scheme (setting, private_key); } /** @@ -2151,14 +2107,7 @@ nm_setting_802_1x_get_private_key_scheme (NMSetting8021x *setting) GBytes * nm_setting_802_1x_get_private_key_blob (NMSetting8021x *setting) { - NMSetting8021xCKScheme scheme; - - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); - - scheme = nm_setting_802_1x_get_private_key_scheme (setting); - g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL); - - return NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key; + _cert_impl_get_blob (setting, private_key); } /** @@ -2174,16 +2123,7 @@ nm_setting_802_1x_get_private_key_blob (NMSetting8021x *setting) const char * nm_setting_802_1x_get_private_key_path (NMSetting8021x *setting) { - NMSetting8021xCKScheme scheme; - gconstpointer data; - - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); - - scheme = nm_setting_802_1x_get_private_key_scheme (setting); - g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL); - - data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key, NULL); - return (const char *)data + strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH); + _cert_impl_get_path (setting, private_key); } /** @@ -2205,16 +2145,7 @@ nm_setting_802_1x_get_private_key_path (NMSetting8021x *setting) const char * nm_setting_802_1x_get_private_key_uri (NMSetting8021x *setting) { - NMSetting8021xCKScheme scheme; - gconstpointer data; - - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); - - scheme = nm_setting_802_1x_get_private_key_scheme (setting); - g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11, NULL); - - data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key, NULL); - return (const char *)data; + _cert_impl_get_uri (setting, private_key); } /** @@ -2495,9 +2426,7 @@ nm_setting_802_1x_get_phase2_private_key_password_flags (NMSetting8021x *setting NMSetting8021xCKScheme nm_setting_802_1x_get_phase2_private_key_scheme (NMSetting8021x *setting) { - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN); - - return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key, NULL); + _cert_impl_get_scheme (setting, phase2_private_key); } /** @@ -2517,14 +2446,7 @@ nm_setting_802_1x_get_phase2_private_key_scheme (NMSetting8021x *setting) GBytes * nm_setting_802_1x_get_phase2_private_key_blob (NMSetting8021x *setting) { - NMSetting8021xCKScheme scheme; - - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); - - scheme = nm_setting_802_1x_get_phase2_private_key_scheme (setting); - g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL); - - return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key; + _cert_impl_get_blob (setting, phase2_private_key); } /** @@ -2540,16 +2462,7 @@ nm_setting_802_1x_get_phase2_private_key_blob (NMSetting8021x *setting) const char * nm_setting_802_1x_get_phase2_private_key_path (NMSetting8021x *setting) { - NMSetting8021xCKScheme scheme; - gconstpointer data; - - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); - - scheme = nm_setting_802_1x_get_phase2_private_key_scheme (setting); - g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL); - - data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key, NULL); - return (const char *)data + strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH); + _cert_impl_get_path (setting, phase2_private_key); } /** @@ -2571,16 +2484,7 @@ nm_setting_802_1x_get_phase2_private_key_path (NMSetting8021x *setting) const char * nm_setting_802_1x_get_phase2_private_key_uri (NMSetting8021x *setting) { - NMSetting8021xCKScheme scheme; - gconstpointer data; - - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL); - - scheme = nm_setting_802_1x_get_phase2_private_key_scheme (setting); - g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11, NULL); - - data = g_bytes_get_data (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key, NULL); - return (const char *)data; + _cert_impl_get_uri (setting, phase2_private_key); } /** From 5ab6875d4e8af7a26eb5e067935670a1deb6308d Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 31 Aug 2018 21:04:17 +0200 Subject: [PATCH 53/61] libnm/802-1x: don't verify certificates in GObject property setter First of all, g_warning() is not a suitable error handling. In particular, note how this code is reached when obtaining a setting from D-Bus, that is, the user is not at fault. The proper way to handle this, is allowing the setter to set the invalid value. Only later, during verify() we will fail. This way, NetworkManager can extend the format and older libnm clients don't break. This is how forward-compatibility (with older libnm vs. newer daemon) is supposed to work. --- libnm-core/nm-setting-8021x.c | 55 ++++------------------------------- 1 file changed, 6 insertions(+), 49 deletions(-) diff --git a/libnm-core/nm-setting-8021x.c b/libnm-core/nm-setting-8021x.c index b5cc751b1a..8ddcc95671 100644 --- a/libnm-core/nm-setting-8021x.c +++ b/libnm-core/nm-setting-8021x.c @@ -3259,24 +3259,6 @@ need_secrets (NMSetting *setting) /*****************************************************************************/ -static GBytes * -set_cert_prop_helper (const GValue *value, const char *prop_name, GError **error) -{ - gboolean valid; - GBytes *bytes = NULL; - - bytes = g_value_dup_boxed (value); - /* Verify the new data */ - if (bytes) { - valid = verify_cert (bytes, prop_name, NULL, NULL, error); - if (!valid) - g_clear_pointer (&bytes, g_bytes_unref); - } - return bytes; -} - -/*****************************************************************************/ - static void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) @@ -3429,7 +3411,6 @@ set_property (GObject *object, guint prop_id, { NMSetting8021x *setting = NM_SETTING_802_1X (object); NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (setting); - GError *error = NULL; switch (prop_id) { case PROP_EAP: @@ -3450,11 +3431,7 @@ set_property (GObject *object, guint prop_id, break; case PROP_CA_CERT: g_bytes_unref (priv->ca_cert); - priv->ca_cert = set_cert_prop_helper (value, NM_SETTING_802_1X_CA_CERT, &error); - if (error) { - g_warning ("Error setting certificate (invalid data): %s", error->message); - g_error_free (error); - } + priv->ca_cert = g_value_dup_boxed (value); break; case PROP_CA_CERT_PASSWORD: g_free (priv->ca_cert_password); @@ -3481,11 +3458,7 @@ set_property (GObject *object, guint prop_id, break; case PROP_CLIENT_CERT: g_bytes_unref (priv->client_cert); - priv->client_cert = set_cert_prop_helper (value, NM_SETTING_802_1X_CLIENT_CERT, &error); - if (error) { - g_warning ("Error setting certificate (invalid data): %s", error->message); - g_error_free (error); - } + priv->client_cert = g_value_dup_boxed (value); break; case PROP_CLIENT_CERT_PASSWORD: g_free (priv->client_cert_password); @@ -3519,11 +3492,7 @@ set_property (GObject *object, guint prop_id, break; case PROP_PHASE2_CA_CERT: g_bytes_unref (priv->phase2_ca_cert); - priv->phase2_ca_cert = set_cert_prop_helper (value, NM_SETTING_802_1X_PHASE2_CA_CERT, &error); - if (error) { - g_warning ("Error setting certificate (invalid data): %s", error->message); - g_error_free (error); - } + priv->phase2_ca_cert = g_value_dup_boxed (value); break; case PROP_PHASE2_CA_CERT_PASSWORD: g_free (priv->phase2_ca_cert_password); @@ -3550,11 +3519,7 @@ set_property (GObject *object, guint prop_id, break; case PROP_PHASE2_CLIENT_CERT: g_bytes_unref (priv->phase2_client_cert); - priv->phase2_client_cert = set_cert_prop_helper (value, NM_SETTING_802_1X_PHASE2_CLIENT_CERT, &error); - if (error) { - g_warning ("Error setting certificate (invalid data): %s", error->message); - g_error_free (error); - } + priv->phase2_client_cert = g_value_dup_boxed (value); break; case PROP_PHASE2_CLIENT_CERT_PASSWORD: g_free (priv->phase2_client_cert_password); @@ -3579,11 +3544,7 @@ set_property (GObject *object, guint prop_id, break; case PROP_PRIVATE_KEY: g_bytes_unref (priv->private_key); - priv->private_key = set_cert_prop_helper (value, NM_SETTING_802_1X_PRIVATE_KEY, &error); - if (error) { - g_warning ("Error setting private key (invalid data): %s", error->message); - g_error_free (error); - } + priv->private_key = g_value_dup_boxed (value); break; case PROP_PRIVATE_KEY_PASSWORD: nm_free_secret (priv->private_key_password); @@ -3594,11 +3555,7 @@ set_property (GObject *object, guint prop_id, break; case PROP_PHASE2_PRIVATE_KEY: g_bytes_unref (priv->phase2_private_key); - priv->phase2_private_key = set_cert_prop_helper (value, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, &error); - if (error) { - g_warning ("Error setting private key (invalid data): %s", error->message); - g_error_free (error); - } + priv->phase2_private_key = g_value_dup_boxed (value); break; case PROP_PHASE2_PRIVATE_KEY_PASSWORD: nm_free_secret (priv->phase2_private_key_password); From f33dec306702d20be480c1b0ed055fb9304391d3 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 31 Aug 2018 21:53:35 +0200 Subject: [PATCH 54/61] libnm/802-1x: cleanup NMSetting8021x:verify() --- libnm-core/nm-setting-8021x.c | 87 +++++++++++++++++++++++++---------- 1 file changed, 62 insertions(+), 25 deletions(-) diff --git a/libnm-core/nm-setting-8021x.c b/libnm-core/nm-setting-8021x.c index 8ddcc95671..ab85a10db3 100644 --- a/libnm-core/nm-setting-8021x.c +++ b/libnm-core/nm-setting-8021x.c @@ -420,8 +420,11 @@ path_to_scheme_value (const char *path) } static gboolean -verify_cert (GBytes *bytes, const char *prop_name, - const char *password, const char *password_prop_name, GError **error) +_cert_verify_property (GBytes *bytes, + const char *prop_name, + const char *password, + const char *password_prop_name, + GError **error) { GError *local = NULL; NMSetting8021xCKScheme scheme; @@ -3088,11 +3091,6 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) NMSetting8021x *self = NM_SETTING_802_1X (setting); NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self); const char *valid_eap[] = { "leap", "md5", "tls", "peap", "ttls", "sim", "fast", "pwd", NULL }; - const char *valid_phase1_peapver[] = { "0", "1", NULL }; - const char *valid_phase1_peaplabel[] = { "0", "1", NULL }; - const char *valid_phase1_fast_pac[] = { "0", "1", "2", "3", NULL }; - const char *valid_phase2_auth[] = { "pap", "chap", "mschap", "mschapv2", "gtc", "otp", "md5", "tls", NULL }; - const char *valid_phase2_autheap[] = { "md5", "mschapv2", "otp", "gtc", "tls", NULL }; GSList *iter; if (error) @@ -3132,7 +3130,9 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) } } - if (priv->phase1_peapver && !g_strv_contains (valid_phase1_peapver, priv->phase1_peapver)) { + if (!NM_IN_STRSET (priv->phase1_peapver, NULL, + "0", + "1")) { g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -3142,7 +3142,9 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } - if (priv->phase1_peaplabel && !g_strv_contains (valid_phase1_peaplabel, priv->phase1_peaplabel)) { + if (!NM_IN_STRSET (priv->phase1_peaplabel, NULL, + "0", + "1")) { g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -3152,7 +3154,11 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } - if (priv->phase1_fast_provisioning && !g_strv_contains (valid_phase1_fast_pac, priv->phase1_fast_provisioning)) { + if (!NM_IN_STRSET (priv->phase1_fast_provisioning, NULL, + "0", + "1", + "2", + "3")) { g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -3171,7 +3177,15 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } - if (priv->phase2_auth && !g_strv_contains (valid_phase2_auth, priv->phase2_auth)) { + if (!NM_IN_STRSET (priv->phase2_auth, NULL, + "pap", + "chap", + "mschap", + "mschapv2", + "gtc", + "otp", + "md5", + "tls")) { g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -3181,7 +3195,12 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } - if (priv->phase2_autheap && !g_strv_contains (valid_phase2_autheap, priv->phase2_autheap)) { + if (!NM_IN_STRSET (priv->phase2_autheap, NULL, + "md5", + "mschapv2", + "otp", + "gtc", + "tls")) { g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -3191,27 +3210,45 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } - if (!verify_cert (priv->ca_cert, NM_SETTING_802_1X_CA_CERT, - priv->ca_cert_password, NM_SETTING_802_1X_CA_CERT_PASSWORD, error)) + if (!_cert_verify_property (priv->ca_cert, + NM_SETTING_802_1X_CA_CERT, + priv->ca_cert_password, + NM_SETTING_802_1X_CA_CERT_PASSWORD, + error)) return FALSE; - if (!verify_cert (priv->phase2_ca_cert, NM_SETTING_802_1X_PHASE2_CA_CERT, - priv->phase2_ca_cert_password, NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD, error)) + if (!_cert_verify_property (priv->phase2_ca_cert, + NM_SETTING_802_1X_PHASE2_CA_CERT, + priv->phase2_ca_cert_password, + NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD, + error)) return FALSE; - if (!verify_cert (priv->client_cert, NM_SETTING_802_1X_CLIENT_CERT, - priv->client_cert_password, NM_SETTING_802_1X_CLIENT_CERT_PASSWORD, error)) + if (!_cert_verify_property (priv->client_cert, + NM_SETTING_802_1X_CLIENT_CERT, + priv->client_cert_password, + NM_SETTING_802_1X_CLIENT_CERT_PASSWORD, + error)) return FALSE; - if (!verify_cert (priv->phase2_client_cert, NM_SETTING_802_1X_PHASE2_CLIENT_CERT, - priv->phase2_client_cert_password, NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD, error)) + if (!_cert_verify_property (priv->phase2_client_cert, + NM_SETTING_802_1X_PHASE2_CLIENT_CERT, + priv->phase2_client_cert_password, + NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD, + error)) return FALSE; - if (!verify_cert (priv->private_key, NM_SETTING_802_1X_PRIVATE_KEY, NULL, NULL, error)) + if (!_cert_verify_property (priv->private_key, + NM_SETTING_802_1X_PRIVATE_KEY, + NULL, + NULL, + error)) return FALSE; - if (!verify_cert (priv->phase2_private_key, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, NULL, NULL, error)) + if (!_cert_verify_property (priv->phase2_private_key, + NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, + NULL, + NULL, + error)) return FALSE; - /* FIXME: finish */ - return TRUE; } @@ -3251,7 +3288,7 @@ need_secrets (NMSetting *setting) if (secrets->len == 0) { g_ptr_array_free (secrets, TRUE); - secrets = NULL; + return NULL; } return secrets; From 068d3168220c2dcc0ee3ee2d0ff2151ad4930992 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sat, 1 Sep 2018 18:08:33 +0200 Subject: [PATCH 55/61] libnm/802-1x: refactor setting certificate from path NMSetting8021x has various utility functions to set the certificate: - nm_setting_802_1x_set_ca_cert() - nm_setting_802_1x_set_client_cert() - nm_setting_802_1x_set_private_key() - nm_setting_802_1x_set_phase2_ca_cert() - nm_setting_802_1x_set_phase2_client_cert() - nm_setting_802_1x_set_phase2_private_key() They support: - accepting a plain PKCS11 URI, with scheme set to NM_SETTING_802_1X_CK_SCHEME_PKCS11. - accepting a filename, with scheme set to NM_SETTING_802_1X_CK_SCHEME_BLOB or NM_SETTING_802_1X_CK_SCHEME_PATH. In the latter case, the function tries to load the file and verify it. In case of the private-key setters, this also involves accepting a password. Depending on whether the scheme is BLOB or PATH, the function will either set the certificate to a PATH blob, or take the blob that was read from file. The functions seem misdesigned to me, because their behavior is rather obscure. E.g. they behave fundamentally different, depending on whether scheme is PKCS11 or BLOB/PATH. Anyway, improve them: - refactor the common code into a function _cert_impl_set(). Previously, their non-trivial implementations were copy+pasted several times, now they all use the same implementation. - if the function is going to fail, don't touch the setting. Previously, the functions would first clear the certificate before trying to validate the input. It's more logical, that if a functions is going to fail to check for failure first and don't modify the settings. - not every blob can be represented. For example, if we have a blob which starts with "file://", then there is no way to set it, simply because we don't support a prefix for blobs (like "data:;base64,"). This means, if we try to set the certificate to a particular binary, we must check that the binary is interpreted with the expected scheme. Add this check. --- libnm-core/nm-crypto.c | 9 + libnm-core/nm-setting-8021x.c | 730 ++++++++++---------------------- libnm-core/tests/test-setting.c | 4 +- 3 files changed, 240 insertions(+), 503 deletions(-) diff --git a/libnm-core/nm-crypto.c b/libnm-core/nm-crypto.c index b582c880e3..7a6e8d18ce 100644 --- a/libnm-core/nm-crypto.c +++ b/libnm-core/nm-crypto.c @@ -840,6 +840,15 @@ nm_crypto_verify_private_key_data (const guint8 *data, } } + if ( format == NM_CRYPTO_FILE_FORMAT_UNKNOWN + && error + && !*error) { + g_set_error (error, + NM_CRYPTO_ERROR, + NM_CRYPTO_ERROR_INVALID_DATA, + _("not a valid private key")); + } + if (out_is_encrypted) *out_is_encrypted = is_encrypted; return format; diff --git a/libnm-core/nm-setting-8021x.c b/libnm-core/nm-setting-8021x.c index ab85a10db3..103665f987 100644 --- a/libnm-core/nm-setting-8021x.c +++ b/libnm-core/nm-setting-8021x.c @@ -297,6 +297,76 @@ _cert_get_scheme (GBytes *bytes, GError **error) return nm_setting_802_1x_check_cert_scheme (data, length, error); } +static gboolean +_cert_verify_scheme (NMSetting8021xCKScheme scheme, + GBytes *bytes, + GError **error) +{ + GError *local = NULL; + NMSetting8021xCKScheme scheme_detected; + + nm_assert (bytes); + + scheme_detected = _cert_get_scheme (bytes, &local); + if (scheme_detected == NM_SETTING_802_1X_CK_SCHEME_UNKNOWN) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("certificate is invalid: %s"), local->message); + return FALSE; + } + + if (scheme_detected != scheme) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("certificate detected as invalid scheme")); + return FALSE; + } + + return TRUE; +} + +static GBytes * +_cert_value_to_bytes (NMSetting8021xCKScheme scheme, + const guint8 *val_bin, + gssize val_len, + GError **error) +{ + gs_unref_bytes GBytes *bytes = NULL; + guint8 *mem; + gsize total_len; + + nm_assert (val_bin); + + switch (scheme) { + case NM_SETTING_802_1X_CK_SCHEME_PKCS11: + if (val_len < 0) + val_len = strlen ((char *) val_bin) + 1; + + bytes = g_bytes_new (val_bin, val_len); + break; + case NM_SETTING_802_1X_CK_SCHEME_PATH: + if (val_len < 0) + val_len = strlen ((char *) val_bin) + 1; + + total_len = NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH) + ((gsize) val_len); + + mem = g_new (guint8, total_len); + memcpy (mem, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH, NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)); + memcpy (&mem[NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)], val_bin, val_len); + bytes = g_bytes_new_take (mem, total_len); + break; + default: + g_return_val_if_reached (NULL); + } + + if (!_cert_verify_scheme (scheme, bytes, error)) + return NULL; + + return g_steal_pointer (&bytes); +} + #define _cert_assert_scheme(cert, check_scheme, ret_val) \ G_STMT_START { \ NMSetting8021xCKScheme scheme; \ @@ -364,59 +434,173 @@ _cert_get_scheme (GBytes *bytes, GError **error) return g_bytes_get_data (_cert, NULL); \ } G_STMT_END -static GBytes * -load_and_verify_certificate (const char *cert_path, - NMSetting8021xCKScheme scheme, - NMCryptoFileFormat *out_file_format, - GError **error) +static gboolean +_cert_impl_set (NMSetting8021x *setting, + _PropertyEnums property, + const char *value, + const char *password, + NMSetting8021xCKScheme scheme, + NMSetting8021xCKFormat *out_format, + GError **error) { + NMSetting8021xPrivate *priv; NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; gs_unref_bytes GBytes *cert = NULL; + GBytes **p_cert = NULL; + GBytes **p_client_cert = NULL; + char **p_password = NULL; + _PropertyEnums notify_cert = property; + _PropertyEnums notify_password = PROP_0; + _PropertyEnums notify_client_cert = PROP_0; - if (!nm_crypto_load_and_verify_certificate (cert_path, &format, &cert, error)) { - NM_SET_OUT (out_file_format, NM_CRYPTO_FILE_FORMAT_UNKNOWN); - return NULL; + g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE); + g_return_val_if_fail (!error || !*error, FALSE); + if (value) { + g_return_val_if_fail (g_utf8_validate (value, -1, NULL), FALSE); + g_return_val_if_fail (NM_IN_SET (scheme, NM_SETTING_802_1X_CK_SCHEME_BLOB, + NM_SETTING_802_1X_CK_SCHEME_PATH, + NM_SETTING_802_1X_CK_SCHEME_PKCS11), FALSE); } - nm_assert (format != NM_CRYPTO_FILE_FORMAT_UNKNOWN); - nm_assert (cert); + if (!value) { + /* coerce password to %NULL. It should be already. */ + password = NULL; + } - if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { - const guint8 *bin; - gsize len; + priv = NM_SETTING_802_1X_GET_PRIVATE (setting); - bin = g_bytes_get_data (cert, &len); - /* If we load the file as blob, we must ensure that the binary data does not - * start with file://. NMSetting8021x cannot represent blobs that start with - * file://. - * If that's the case, coerce the format to UNKNOWN. The callers will take care - * of that and not set the blob. */ - if (nm_setting_802_1x_check_cert_scheme (bin, len, NULL) != NM_SETTING_802_1X_CK_SCHEME_BLOB) { - NM_SET_OUT (out_file_format, NM_CRYPTO_FILE_FORMAT_UNKNOWN); - return NULL; + if (!value) { + /* pass. */ + } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) { + cert = _cert_value_to_bytes (scheme, (guint8 *) value, -1, error); + if (!cert) + goto err; + } else { + gs_unref_bytes GBytes *file = NULL; + + if (NM_IN_SET (property, PROP_PRIVATE_KEY, + PROP_PHASE2_PRIVATE_KEY)) { + file = nm_crypto_read_file (value, error); + if (!file) + goto err; + format = nm_crypto_verify_private_key_data (g_bytes_get_data (file, NULL), + g_bytes_get_size (file), + password, + NULL, + error); + if (format == NM_CRYPTO_FILE_FORMAT_UNKNOWN) + goto err; + } else { + if (!nm_crypto_load_and_verify_certificate (value, &format, &file, error)) + goto err; + } + + nm_assert (format != NM_CRYPTO_FILE_FORMAT_UNKNOWN); + nm_assert (file); + + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { + cert = g_steal_pointer (&file); + if (!_cert_verify_scheme (scheme, cert, error)) + goto err; + } else { + cert = _cert_value_to_bytes (scheme, (guint8 *) value, -1, error); + if (!cert) + goto err; } } - NM_SET_OUT (out_file_format, format); - return g_steal_pointer (&cert); -} + switch (property) { + case PROP_CA_CERT: + case PROP_PHASE2_CA_CERT: + if ( value + && scheme != NM_SETTING_802_1X_CK_SCHEME_PKCS11 + && format != NM_CRYPTO_FILE_FORMAT_X509) { + /* wpa_supplicant can only use raw x509 CA certs */ + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("CA certificate must be in X.509 format")); + goto err; + } + p_cert = (property == PROP_CA_CERT) + ? &priv->ca_cert + : &priv->phase2_ca_cert; + break; + case PROP_CLIENT_CERT: + case PROP_PHASE2_CLIENT_CERT: + if ( value + && scheme != NM_SETTING_802_1X_CK_SCHEME_PKCS11 + && !NM_IN_SET (format, NM_CRYPTO_FILE_FORMAT_X509, + NM_CRYPTO_FILE_FORMAT_PKCS12)) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("invalid certificate format")); + goto err; + } + p_cert = (property == PROP_CLIENT_CERT) + ? &priv->client_cert + : &priv->phase2_client_cert; + break; + case PROP_PRIVATE_KEY: + p_cert = &priv->private_key; + p_password = &priv->private_key_password; + p_client_cert = &priv->client_cert; + notify_password = PROP_PRIVATE_KEY_PASSWORD; + notify_client_cert = PROP_CLIENT_CERT; + break; + case PROP_PHASE2_PRIVATE_KEY: + p_cert = &priv->phase2_private_key; + p_password = &priv->phase2_private_key_password; + p_client_cert = &priv->phase2_client_cert; + notify_password = PROP_PHASE2_PRIVATE_KEY_PASSWORD; + notify_client_cert = PROP_PHASE2_CLIENT_CERT; + break; + default: + nm_assert_not_reached (); + break; + } -static GBytes * -path_to_scheme_value (const char *path) -{ - guint8 *mem; - gsize len, total_len; + /* As required by NM and wpa_supplicant, set the client-cert + * property to the same PKCS#12 data. + */ + if ( cert + && p_client_cert + && format == NM_CRYPTO_FILE_FORMAT_PKCS12 + && !nm_gbytes_equal0 (cert, *p_client_cert)) { + g_bytes_unref (*p_client_cert); + *p_client_cert = g_bytes_ref (cert); + } else + notify_client_cert = PROP_0; - g_return_val_if_fail (path != NULL && path[0], NULL); + if ( p_cert + && !nm_gbytes_equal0 (cert, *p_cert)) { + g_bytes_unref (*p_cert); + *p_cert = g_steal_pointer (&cert); + } else + notify_cert = PROP_0; - len = strlen (path); - total_len = (NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH) + 1) + len; + if ( p_password + && !nm_streq0 (password, *p_password)) { + nm_free_secret (*p_password); + *p_password = g_strdup (password); + } else + notify_password = PROP_0; - /* Add the path scheme tag to the front, then the filename */ - mem = g_new (guint8, total_len); - memcpy (mem, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH, NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)); - memcpy (&mem[NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)], path, len + 1); - return g_bytes_new_take (mem, total_len); + nm_gobject_notify_together (setting, notify_cert, + notify_password, + notify_client_cert); + + NM_SET_OUT (out_format, _crypto_format_to_ck (format)); + return TRUE; + +err: + g_prefix_error (error, + "%s.%s: ", + NM_SETTING_802_1X_SETTING_NAME, + obj_properties[property]->name); + NM_SET_OUT (out_format, NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); + return FALSE; } static gboolean @@ -800,62 +984,7 @@ nm_setting_802_1x_set_ca_cert (NMSetting8021x *setting, NMSetting8021xCKFormat *out_format, GError **error) { - NMSetting8021xPrivate *priv; - NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; - gs_unref_bytes GBytes *cert = NULL; - - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE); - - if (value) { - g_return_val_if_fail (g_utf8_validate (value, -1, NULL), FALSE); - g_return_val_if_fail ( scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB - || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH - || scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11, - FALSE); - } - - if (out_format) - g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE); - - priv = NM_SETTING_802_1X_GET_PRIVATE (setting); - - g_clear_pointer (&priv->ca_cert, g_bytes_unref); - - if (!value) { - _notify (setting, PROP_CA_CERT); - return TRUE; - } - - if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) { - priv->ca_cert = g_bytes_new (value, strlen (value) + 1); - _notify (setting, PROP_CA_CERT); - return TRUE; - } - - cert = load_and_verify_certificate (value, scheme, &format, error); - if (cert) { - /* wpa_supplicant can only use raw x509 CA certs */ - if (format == NM_CRYPTO_FILE_FORMAT_X509) { - if (out_format) - *out_format = NM_SETTING_802_1X_CK_FORMAT_X509; - - if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) - priv->ca_cert = g_bytes_ref (cert); - else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) - priv->ca_cert = path_to_scheme_value (value); - else - g_assert_not_reached (); - } else { - g_set_error_literal (error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("CA certificate must be in X.509 format")); - g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CA_CERT); - } - } - - _notify (setting, PROP_CA_CERT); - return priv->ca_cert != NULL; + return _cert_impl_set (setting, PROP_CA_CERT, value, NULL, scheme, out_format, error); } /** @@ -1174,74 +1303,7 @@ nm_setting_802_1x_set_client_cert (NMSetting8021x *setting, NMSetting8021xCKFormat *out_format, GError **error) { - NMSetting8021xPrivate *priv; - NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; - gs_unref_bytes GBytes *cert = NULL; - - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE); - - if (value) { - g_return_val_if_fail (g_utf8_validate (value, -1, NULL), FALSE); - g_return_val_if_fail ( scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB - || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH - || scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11, - FALSE); - } - - if (out_format) - g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE); - - priv = NM_SETTING_802_1X_GET_PRIVATE (setting); - - g_clear_pointer (&priv->client_cert, g_bytes_unref); - - if (!value) { - _notify (setting, PROP_CLIENT_CERT); - return TRUE; - } - - if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) { - priv->client_cert = g_bytes_new (value, strlen (value) + 1); - _notify (setting, PROP_CLIENT_CERT); - return TRUE; - } - - cert = load_and_verify_certificate (value, scheme, &format, error); - if (cert) { - gboolean valid = FALSE; - - switch (format) { - case NM_CRYPTO_FILE_FORMAT_X509: - if (out_format) - *out_format = NM_SETTING_802_1X_CK_FORMAT_X509; - valid = TRUE; - break; - case NM_CRYPTO_FILE_FORMAT_PKCS12: - if (out_format) - *out_format = NM_SETTING_802_1X_CK_FORMAT_PKCS12; - valid = TRUE; - break; - default: - g_set_error_literal (error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("invalid certificate format")); - g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CLIENT_CERT); - break; - } - - if (valid) { - if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) - priv->client_cert = g_bytes_ref (cert); - else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) - priv->client_cert = path_to_scheme_value (value); - else - g_assert_not_reached (); - } - } - - _notify (setting, PROP_CLIENT_CERT); - return priv->client_cert != NULL; + return _cert_impl_set (setting, PROP_CLIENT_CERT, value, NULL, scheme, out_format, error); } /** @@ -1499,62 +1561,7 @@ nm_setting_802_1x_set_phase2_ca_cert (NMSetting8021x *setting, NMSetting8021xCKFormat *out_format, GError **error) { - NMSetting8021xPrivate *priv; - NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; - gs_unref_bytes GBytes *cert = NULL; - - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE); - - if (value) { - g_return_val_if_fail (g_utf8_validate (value, -1, NULL), FALSE); - g_return_val_if_fail ( scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB - || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH - || scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11, - FALSE); - } - - if (out_format) - g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE); - - priv = NM_SETTING_802_1X_GET_PRIVATE (setting); - - g_clear_pointer (&priv->phase2_ca_cert, g_bytes_unref); - - if (!value) { - _notify (setting, PROP_PHASE2_CA_CERT); - return TRUE; - } - - if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) { - priv->phase2_ca_cert = g_bytes_new (value, strlen (value) + 1); - _notify (setting, PROP_PHASE2_CA_CERT); - return TRUE; - } - - cert = load_and_verify_certificate (value, scheme, &format, error); - if (cert) { - /* wpa_supplicant can only use raw x509 CA certs */ - if (format == NM_CRYPTO_FILE_FORMAT_X509) { - if (out_format) - *out_format = NM_SETTING_802_1X_CK_FORMAT_X509; - - if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) - priv->phase2_ca_cert = g_bytes_ref (cert); - else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) - priv->phase2_ca_cert = path_to_scheme_value (value); - else - g_assert_not_reached (); - } else { - g_set_error_literal (error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("invalid certificate format")); - g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_CA_CERT); - } - } - - _notify (setting, PROP_PHASE2_CA_CERT); - return priv->phase2_ca_cert != NULL; + return _cert_impl_set (setting, PROP_PHASE2_CA_CERT, value, NULL, scheme, out_format, error); } /** @@ -1877,75 +1884,7 @@ nm_setting_802_1x_set_phase2_client_cert (NMSetting8021x *setting, NMSetting8021xCKFormat *out_format, GError **error) { - NMSetting8021xPrivate *priv; - NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; - gs_unref_bytes GBytes *cert = NULL; - - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE); - - if (value) { - g_return_val_if_fail (g_utf8_validate (value, -1, NULL), FALSE); - g_return_val_if_fail ( scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB - || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH - || scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11, - FALSE); - } - - if (out_format) - g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE); - - priv = NM_SETTING_802_1X_GET_PRIVATE (setting); - - g_clear_pointer (&priv->phase2_client_cert, g_bytes_unref); - - if (!value) { - _notify (setting, PROP_PHASE2_CLIENT_CERT); - return TRUE; - } - - if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) { - priv->phase2_client_cert = g_bytes_new (value, strlen (value) + 1); - _notify (setting, PROP_PHASE2_CLIENT_CERT); - return TRUE; - } - - cert = load_and_verify_certificate (value, scheme, &format, error); - if (cert) { - gboolean valid = FALSE; - - /* wpa_supplicant can only use raw x509 CA certs */ - switch (format) { - case NM_CRYPTO_FILE_FORMAT_X509: - if (out_format) - *out_format = NM_SETTING_802_1X_CK_FORMAT_X509; - valid = TRUE; - break; - case NM_CRYPTO_FILE_FORMAT_PKCS12: - if (out_format) - *out_format = NM_SETTING_802_1X_CK_FORMAT_PKCS12; - valid = TRUE; - break; - default: - g_set_error_literal (error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("invalid certificate format")); - g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_CLIENT_CERT); - break; - } - - if (valid) { - if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) - priv->phase2_client_cert = g_bytes_ref (cert); - else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) - priv->phase2_client_cert = path_to_scheme_value (value); - else - g_assert_not_reached (); - } - } - - _notify (setting, PROP_PHASE2_CLIENT_CERT); - return priv->phase2_client_cert != NULL; + return _cert_impl_set (setting, PROP_PHASE2_CLIENT_CERT, value, NULL, scheme, out_format, error); } /** @@ -2198,112 +2137,7 @@ nm_setting_802_1x_set_private_key (NMSetting8021x *setting, NMSetting8021xCKFormat *out_format, GError **error) { - NMSetting8021xPrivate *priv; - NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; - gs_unref_bytes GBytes *file_content = NULL; - gs_unref_bytes GBytes *private_key_new = NULL; - gboolean private_key_changed = FALSE; - gboolean password_changed = FALSE; - - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE); - - if (value) { - g_return_val_if_fail (g_utf8_validate (value, -1, NULL), FALSE); - g_return_val_if_fail ( scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB - || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH - || scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11, - FALSE); - } - - NM_SET_OUT (out_format, NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); - - priv = NM_SETTING_802_1X_GET_PRIVATE (setting); - - if (!value) { - if (nm_clear_pointer (&priv->private_key, g_bytes_unref)) - _notify (setting, PROP_PRIVATE_KEY); - if (nm_clear_pointer (&priv->private_key_password, nm_free_secret)) - _notify (setting, PROP_PRIVATE_KEY_PASSWORD); - return TRUE; - } - - /* Ensure the private key is a recognized format and if the password was - * given, that it decrypts the private key. - */ - if (scheme != NM_SETTING_802_1X_CK_SCHEME_PKCS11) { - GError *local_err = NULL; - - file_content = nm_crypto_read_file (value, &local_err); - if (file_content) { - format = nm_crypto_verify_private_key_data (g_bytes_get_data (file_content, NULL), - g_bytes_get_size (file_content), - password, - NULL, - &local_err); - } - if (format == NM_CRYPTO_FILE_FORMAT_UNKNOWN) { - g_set_error_literal (error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - local_err ? local_err->message : _("invalid private key")); - g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PRIVATE_KEY); - g_clear_error (&local_err); - return FALSE; - } - } - - if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { - private_key_new = file_content - ? g_steal_pointer (&file_content) - : nm_crypto_read_file (value, NULL); - } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) - private_key_new = path_to_scheme_value (value); - else { - nm_assert (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11); - private_key_new = g_bytes_new (value, strlen (value) + 1); - } - - if ( !private_key_new - || nm_setting_802_1x_check_cert_scheme (g_bytes_get_data (private_key_new, NULL), - g_bytes_get_size (private_key_new), - NULL) != scheme) { - g_set_error_literal (error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("invalid private key")); - g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY); - return FALSE; - } - - if ( !priv->private_key - || !g_bytes_equal (priv->private_key, private_key_new)) { - g_bytes_unref (priv->private_key); - priv->private_key = g_steal_pointer (&private_key_new); - private_key_changed = TRUE; - } - - if (!nm_streq0 (priv->private_key_password, password)) { - nm_free_secret (priv->private_key_password); - priv->private_key_password = g_strdup (password); - password_changed = TRUE; - } - - /* As required by NM and wpa_supplicant, set the client-cert - * property to the same PKCS#12 data. - */ - if (format == NM_CRYPTO_FILE_FORMAT_PKCS12) { - g_bytes_unref (priv->client_cert); - priv->client_cert = g_bytes_ref (priv->private_key); - _notify (setting, PROP_CLIENT_CERT); - } - - if (private_key_changed) - _notify (setting, PROP_PRIVATE_KEY); - if (password_changed) - _notify (setting, PROP_PRIVATE_KEY_PASSWORD); - - NM_SET_OUT (out_format, _crypto_format_to_ck (format)); - return TRUE; + return _cert_impl_set (setting, PROP_PRIVATE_KEY, value, password, scheme, out_format, error); } /** @@ -2537,115 +2371,7 @@ nm_setting_802_1x_set_phase2_private_key (NMSetting8021x *setting, NMSetting8021xCKFormat *out_format, GError **error) { - NMSetting8021xPrivate *priv; - NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; - gs_unref_bytes GBytes *file_content = NULL; - gs_unref_bytes GBytes *private_key_new = NULL; - gboolean private_key_changed = FALSE; - gboolean password_changed = FALSE; - - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE); - - if (value) { - g_return_val_if_fail (g_utf8_validate (value, -1, NULL), FALSE); - g_return_val_if_fail ( scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB - || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH - || scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11, - FALSE); - } - - NM_SET_OUT (out_format, NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); - - priv = NM_SETTING_802_1X_GET_PRIVATE (setting); - - if (!value) { - if (nm_clear_pointer (&priv->phase2_private_key, g_bytes_unref)) - _notify (setting, PROP_PHASE2_PRIVATE_KEY); - if (nm_clear_pointer (&priv->phase2_private_key_password, nm_free_secret)) - _notify (setting, PROP_PHASE2_PRIVATE_KEY_PASSWORD); - return TRUE; - } - - /* Ensure the private key is a recognized format and if the password was - * given, that it decrypts the private key. - */ - if (scheme != NM_SETTING_802_1X_CK_SCHEME_PKCS11) { - GError *local_err = NULL; - - file_content = nm_crypto_read_file (value, &local_err); - if (file_content) { - format = nm_crypto_verify_private_key_data (g_bytes_get_data (file_content, NULL), - g_bytes_get_size (file_content), - password, - NULL, - &local_err); - } - - if (format == NM_CRYPTO_FILE_FORMAT_UNKNOWN) { - g_set_error_literal (error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - local_err ? local_err->message : _("invalid phase2 private key")); - g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY); - g_clear_error (&local_err); - return FALSE; - } - } - - if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { - private_key_new = file_content - ? g_steal_pointer (&file_content) - : nm_crypto_read_file (value, NULL); - } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) - private_key_new = path_to_scheme_value (value); - else { - nm_assert (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11); - private_key_new = g_bytes_new (value, strlen (value) + 1); - } - - if ( !private_key_new - || nm_setting_802_1x_check_cert_scheme (g_bytes_get_data (private_key_new, NULL), - g_bytes_get_size (private_key_new), - NULL) != scheme) { - g_set_error_literal (error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("invalid phase2 private key")); - g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY); - return FALSE; - } - - if ( !priv->phase2_private_key - || !g_bytes_equal (priv->phase2_private_key, private_key_new)) { - g_bytes_unref (priv->phase2_private_key); - priv->phase2_private_key = g_steal_pointer (&private_key_new); - private_key_changed = TRUE; - } - - if (!nm_streq0 (priv->phase2_private_key_password, password)) { - nm_free_secret (priv->phase2_private_key_password); - priv->phase2_private_key_password = g_strdup (password); - password_changed = TRUE; - } - - /* As required by NM and wpa_supplicant, set the client-cert - * property to the same PKCS#12 data. - */ - if ( format == NM_CRYPTO_FILE_FORMAT_PKCS12 - && ( !priv->phase2_client_cert - || g_bytes_equal (priv->phase2_client_cert, priv->phase2_private_key))) { - g_bytes_unref (priv->phase2_client_cert); - priv->phase2_client_cert = g_bytes_ref (priv->phase2_private_key); - _notify (setting, PROP_PHASE2_CLIENT_CERT); - } - - if (private_key_changed) - _notify (setting, PROP_PHASE2_PRIVATE_KEY); - if (password_changed) - _notify (setting, PROP_PHASE2_PRIVATE_KEY_PASSWORD); - - NM_SET_OUT (out_format, _crypto_format_to_ck (format)); - return TRUE; + return _cert_impl_set (setting, PROP_PHASE2_PRIVATE_KEY, value, password, scheme, out_format, error); } /** diff --git a/libnm-core/tests/test-setting.c b/libnm-core/tests/test-setting.c index fb431ed702..c8eda381ee 100644 --- a/libnm-core/tests/test-setting.c +++ b/libnm-core/tests/test-setting.c @@ -67,10 +67,12 @@ static void check_scheme_path (GBytes *value, const char *path) { const guint8 *p; + gsize l; g_assert (value); - p = g_bytes_get_data (value, NULL); + p = g_bytes_get_data (value, &l); + g_assert_cmpint (l, ==, strlen (path) + NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH) + 1); g_assert (memcmp (p, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH, strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)) == 0); p += strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH); g_assert (memcmp (p, path, strlen (path)) == 0); From 9e3ebfdebbc5e0fb3125f1383eb1b9085557008a Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 3 Sep 2018 16:33:28 +0200 Subject: [PATCH 56/61] libnm/802-1x: refactor getting private-key format Move similar code to a helper function/macro. --- libnm-core/nm-setting-8021x.c | 130 ++++++++++++++++------------------ 1 file changed, 63 insertions(+), 67 deletions(-) diff --git a/libnm-core/nm-setting-8021x.c b/libnm-core/nm-setting-8021x.c index 103665f987..087084f89d 100644 --- a/libnm-core/nm-setting-8021x.c +++ b/libnm-core/nm-setting-8021x.c @@ -367,6 +367,23 @@ _cert_value_to_bytes (NMSetting8021xCKScheme scheme, return g_steal_pointer (&bytes); } +static const char * +_cert_get_path (GBytes *bytes) +{ + const guint8 *bin; + + nm_assert (bytes); + nm_assert (g_bytes_get_size (bytes) >= NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)); + + bin = g_bytes_get_data (bytes, NULL); + + nm_assert (bin); + nm_assert (bin[g_bytes_get_size (bytes) - 1] == '\0'); + nm_assert (g_str_has_prefix ((const char *) bin, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)); + + return (const char *) &bin[NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)]; +} + #define _cert_assert_scheme(cert, check_scheme, ret_val) \ G_STMT_START { \ NMSetting8021xCKScheme scheme; \ @@ -408,7 +425,6 @@ _cert_value_to_bytes (NMSetting8021xCKScheme scheme, G_STMT_START { \ NMSetting8021x *const _setting = (setting); \ GBytes *_cert; \ - const char *_data; \ \ g_return_val_if_fail (NM_IS_SETTING_802_1X (_setting), NULL); \ \ @@ -416,8 +432,7 @@ _cert_value_to_bytes (NMSetting8021xCKScheme scheme, \ _cert_assert_scheme (_cert, NM_SETTING_802_1X_CK_SCHEME_PATH, NULL); \ \ - _data = g_bytes_get_data (_cert, NULL); \ - return &_data[NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)]; \ + return _cert_get_path (_cert); \ } G_STMT_END #define _cert_impl_get_uri(setting, cert_field) \ @@ -603,6 +618,49 @@ err: return FALSE; } +static NMSetting8021xCKFormat +_cert_impl_get_key_format_from_bytes (GBytes *private_key) +{ + const char *path; + GError *error = NULL; + + if (!private_key) + return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + + switch (_cert_get_scheme (private_key, NULL)) { + case NM_SETTING_802_1X_CK_SCHEME_BLOB: + if (nm_crypto_is_pkcs12_data (g_bytes_get_data (private_key, NULL), + g_bytes_get_size (private_key), + NULL)) + return NM_SETTING_802_1X_CK_FORMAT_PKCS12; + return NM_SETTING_802_1X_CK_FORMAT_RAW_KEY; + case NM_SETTING_802_1X_CK_SCHEME_PATH: + path = _cert_get_path (private_key); + if (nm_crypto_is_pkcs12_file (path, &error)) + return NM_SETTING_802_1X_CK_FORMAT_PKCS12; + if (error && error->domain == G_FILE_ERROR) { + g_error_free (error); + return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + } + g_error_free (error); + return NM_SETTING_802_1X_CK_FORMAT_RAW_KEY; + default: + break; + } + + return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; +} +#define _cert_impl_get_key_format(setting, private_key_field) \ + ({ \ + NMSetting8021x *_setting = (setting); \ + NMSetting8021xPrivate *_priv; \ + \ + g_return_val_if_fail (NM_IS_SETTING_802_1X (_setting), NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); \ + \ + _priv = NM_SETTING_802_1X_GET_PRIVATE (_setting); \ + _cert_impl_get_key_format_from_bytes (_priv->private_key_field); \ + }) + static gboolean _cert_verify_property (GBytes *bytes, const char *prop_name, @@ -2181,38 +2239,7 @@ nm_setting_802_1x_get_private_key_password_flags (NMSetting8021x *setting) NMSetting8021xCKFormat nm_setting_802_1x_get_private_key_format (NMSetting8021x *setting) { - NMSetting8021xPrivate *priv; - const char *path; - GError *error = NULL; - - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); - priv = NM_SETTING_802_1X_GET_PRIVATE (setting); - - if (!priv->private_key) - return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; - - switch (nm_setting_802_1x_get_private_key_scheme (setting)) { - case NM_SETTING_802_1X_CK_SCHEME_BLOB: - if (nm_crypto_is_pkcs12_data (g_bytes_get_data (priv->private_key, NULL), - g_bytes_get_size (priv->private_key), - NULL)) - return NM_SETTING_802_1X_CK_FORMAT_PKCS12; - return NM_SETTING_802_1X_CK_FORMAT_RAW_KEY; - case NM_SETTING_802_1X_CK_SCHEME_PATH: - path = nm_setting_802_1x_get_private_key_path (setting); - if (nm_crypto_is_pkcs12_file (path, &error)) - return NM_SETTING_802_1X_CK_FORMAT_PKCS12; - if (error && error->domain == G_FILE_ERROR) { - g_error_free (error); - return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; - } - g_error_free (error); - return NM_SETTING_802_1X_CK_FORMAT_RAW_KEY; - default: - break; - } - - return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + return _cert_impl_get_key_format (setting, private_key); } /** @@ -2384,38 +2411,7 @@ nm_setting_802_1x_set_phase2_private_key (NMSetting8021x *setting, NMSetting8021xCKFormat nm_setting_802_1x_get_phase2_private_key_format (NMSetting8021x *setting) { - NMSetting8021xPrivate *priv; - const char *path; - GError *error = NULL; - - g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); - priv = NM_SETTING_802_1X_GET_PRIVATE (setting); - - if (!priv->phase2_private_key) - return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; - - switch (nm_setting_802_1x_get_phase2_private_key_scheme (setting)) { - case NM_SETTING_802_1X_CK_SCHEME_BLOB: - if (nm_crypto_is_pkcs12_data (g_bytes_get_data (priv->phase2_private_key, NULL), - g_bytes_get_size (priv->phase2_private_key), - NULL)) - return NM_SETTING_802_1X_CK_FORMAT_PKCS12; - return NM_SETTING_802_1X_CK_FORMAT_RAW_KEY; - case NM_SETTING_802_1X_CK_SCHEME_PATH: - path = nm_setting_802_1x_get_phase2_private_key_path (setting); - if (nm_crypto_is_pkcs12_file (path, &error)) - return NM_SETTING_802_1X_CK_FORMAT_PKCS12; - if (error && error->domain == G_FILE_ERROR) { - g_error_free (error); - return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; - } - g_error_free (error); - return NM_SETTING_802_1X_CK_FORMAT_RAW_KEY; - default: - break; - } - - return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + return _cert_impl_get_key_format (setting, phase2_private_key); } /** From b8ea61d26e50a91647937be12647aee5d9c18917 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sun, 2 Sep 2018 14:54:11 +0200 Subject: [PATCH 57/61] libnm/keyfile: clear memory when reading certificates from keyfile Of course, there are countless places where we get it wrong to clear the memory. In particular, glib's GKeyFile implementation does not care about that. Anyway, the keyfile do contain private sensitive data. Adjust a few places to zero out such data from memory. --- libnm-core/nm-keyfile.c | 53 +++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/libnm-core/nm-keyfile.c b/libnm-core/nm-keyfile.c index d337854bda..c7d878691b 100644 --- a/libnm-core/nm-keyfile.c +++ b/libnm-core/nm-keyfile.c @@ -33,6 +33,7 @@ #include #include +#include "nm-utils/nm-secret-utils.h" #include "nm-common-macros.h" #include "nm-core-internal.h" #include "nm-keyfile-utils.h" @@ -939,7 +940,7 @@ unescape_semicolons (char *str) i++; str[j++] = str[i++];; } - str[j] = '\0'; + nm_explicit_bzero (&str[j], i - j); return j; } @@ -950,9 +951,10 @@ get_bytes (KeyfileReaderInfo *info, gboolean zero_terminate, gboolean unescape_semicolon) { - gs_free char *tmp_string = NULL; + nm_auto_free_secret char *tmp_string = NULL; gboolean may_be_int_list = TRUE; gsize length; + GBytes *result; /* New format: just a string * Old format: integer list; e.g. 11;25;38; @@ -986,12 +988,11 @@ get_bytes (KeyfileReaderInfo *info, /* Try to parse the string as a integer list. */ if (may_be_int_list && length > 0) { - gs_free guint8 *bin_data = NULL; + nm_auto_free_secret_buf NMSecretBuf *bin = NULL; const char *const s = tmp_string; gsize i, d; - const gsize BIN_DATA_LEN = (length / 2 + 3); - bin_data = g_malloc (BIN_DATA_LEN); + bin = nm_secret_buf_new (length / 2 + 3); #define DIGIT(c) ((c) - '0') i = 0; @@ -1024,8 +1025,8 @@ get_bytes (KeyfileReaderInfo *info, break; } - bin_data[d++] = n; - nm_assert (d < BIN_DATA_LEN); + nm_assert (d < bin->len); + bin->bin[d++] = n; /* allow whitespace after the digit. */ while (g_ascii_isspace (s[i])) @@ -1043,16 +1044,23 @@ get_bytes (KeyfileReaderInfo *info, * string format before. We expect that this conversion cannot fail. */ if (d > 0) { /* note that @zero_terminate does not add a terminating '\0' to - * binary data as an integer list. + * binary data as an integer list. If the bytes are expressed as + * an integer list, all potential NUL characters are supposed to + * be included there explicitly. * - * But we add a '\0' to the bin_data pointer, just to avoid somebody - * (erronously!) reading the binary data as C-string. - * - * @d itself does not entail the '\0'. */ - nm_assert (d + 1 <= BIN_DATA_LEN); - bin_data = g_realloc (bin_data, d + 1); - bin_data[d] = '\0'; - return g_bytes_new_take (g_steal_pointer (&bin_data), d); + * However, in the spirit of defensive programming, we do append a + * NUL character to the buffer, although this character is hidden + * and only a mitigation for bugs. */ + + if (d + 10 < bin->len) { + /* hm, too much unused memory. Copy the memory to a suitable + * sized buffer. */ + return nm_secret_copy_to_gbytes (bin->bin, d); + } + + nm_assert (d < bin->len); + bin->bin[d] = '\0'; + return nm_secret_buf_to_gbytes_take (g_steal_pointer (&bin), d); } } @@ -1063,8 +1071,13 @@ get_bytes (KeyfileReaderInfo *info, length++; if (length == 0) return NULL; - tmp_string = g_realloc (tmp_string, length + (zero_terminate ? 0 : 1)); - return g_bytes_new_take (g_steal_pointer (&tmp_string), length); + + result = g_bytes_new_with_free_func (tmp_string, + length, + (GDestroyNotify) nm_free_secret, + tmp_string); + tmp_string = NULL; + return result; } static void @@ -1108,12 +1121,12 @@ get_cert_path (const char *base_dir, const guint8 *cert_path, gsize cert_path_le g_return_val_if_fail (base_dir != NULL, NULL); g_return_val_if_fail (cert_path != NULL, NULL); - base = path = g_malloc0 (cert_path_len + 1); - memcpy (path, cert_path, cert_path_len); + path = g_strndup ((char *) cert_path, cert_path_len); if (path[0] == '/') return path; + base = path; p = strrchr (path, '/'); if (p) base = p + 1; From a9406ca4a7121e072f5ffd87f8d543c8b01f532b Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 3 Sep 2018 11:22:34 +0200 Subject: [PATCH 58/61] libnm-core: expose _nm_utils_str2bin_full() as internal API We only exposed wrappers around this function, but all of them have different behavior, and none exposes all possible features. For example, nm_utils_hexstr2bin() strips leading "0x", but it does not clear the data on failure (nm_explicit_bzero()). Instead of adding more wrappers, expose the underlying implementation, so that callers may use the function the way they want it. --- libnm-core/nm-core-internal.h | 7 +++++++ libnm-core/nm-utils.c | 20 ++++++++++---------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index 614e017490..f19709dfc6 100644 --- a/libnm-core/nm-core-internal.h +++ b/libnm-core/nm-core-internal.h @@ -215,6 +215,13 @@ const char *nm_utils_hwaddr_ntoa_buf (gconstpointer addr, gsize addr_len, gboole char *_nm_utils_bin2str (gconstpointer addr, gsize length, gboolean upper_case); void _nm_utils_bin2str_full (gconstpointer addr, gsize length, const char delimiter, gboolean upper_case, char *out); +guint8 *_nm_utils_str2bin_full (const char *asc, + gboolean delimiter_required, + const char *delimiter_candidates, + guint8 *buffer, + gsize buffer_length, + gsize *out_len); + GSList * _nm_utils_hash_values_to_slist (GHashTable *hash); GHashTable *_nm_utils_copy_strdict (GHashTable *strdict); diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 1ca78421c3..c9162e971e 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -3526,13 +3526,13 @@ nm_utils_hwaddr_len (int type) g_return_val_if_reached (0); } -static guint8 * -_str2bin (const char *asc, - gboolean delimiter_required, - const char *delimiter_candidates, - guint8 *buffer, - gsize buffer_length, - gsize *out_len) +guint8 * +_nm_utils_str2bin_full (const char *asc, + gboolean delimiter_required, + const char *delimiter_candidates, + guint8 *buffer, + gsize buffer_length, + gsize *out_len) { const char *in = asc; guint8 *out = buffer; @@ -3602,7 +3602,7 @@ _str2bin (const char *asc, return buffer; } -#define hwaddr_aton(asc, buffer, buffer_length, out_len) _str2bin ((asc), TRUE, ":-", (buffer), (buffer_length), (out_len)) +#define hwaddr_aton(asc, buffer, buffer_length, out_len) _nm_utils_str2bin_full ((asc), TRUE, ":-", (buffer), (buffer_length), (out_len)) /** * nm_utils_hexstr2bin: @@ -3628,7 +3628,7 @@ nm_utils_hexstr2bin (const char *hex) buffer_length = strlen (hex) / 2 + 3; buffer = g_malloc (buffer_length); - if (!_str2bin (hex, FALSE, ":", buffer, buffer_length, &len)) { + if (!_nm_utils_str2bin_full (hex, FALSE, ":", buffer, buffer_length, &len)) { g_free (buffer); return NULL; } @@ -4508,7 +4508,7 @@ _nm_utils_dhcp_duid_valid (const char *duid, GBytes **out_duid_bin) return TRUE; } - if (_str2bin (duid, FALSE, ":", duid_arr, sizeof (duid_arr), &duid_len)) { + if (_nm_utils_str2bin_full (duid, FALSE, ":", duid_arr, sizeof (duid_arr), &duid_len)) { /* MAX DUID length is 128 octects + the type code (2 octects). */ if ( duid_len > 2 && duid_len <= (128 + 2)) { From 4b6aa207b256e2567e4c41e227e8e85451934101 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 3 Sep 2018 09:03:03 +0200 Subject: [PATCH 59/61] ifcfg-rh/trivial: rename variable for ifcfg keys file The term "keys" is used ambigiously. Rename occurances which reference the "keys" ifcfg-rh file. While at it, rename the file "parsed" to "main_ifcfg". It follows the same pattern as the "keys_ifcfg" name. --- .../plugins/ifcfg-rh/nms-ifcfg-rh-reader.c | 118 +++++++++--------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c index 4d2a16bc84..0a6a5e0b44 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -2882,7 +2882,7 @@ read_8021x_password (shvarFile *ifcfg, shvarFile *keys_ifcfg, const char *name, static gboolean eap_simple_reader (const char *eap_method, shvarFile *ifcfg, - shvarFile *keys, + shvarFile *keys_ifcfg, NMSetting8021x *s_8021x, gboolean phase2, GError **error) @@ -2901,14 +2901,14 @@ eap_simple_reader (const char *eap_method, g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, value, NULL); nm_clear_g_free (&value); - read_8021x_password (ifcfg, keys, "IEEE_8021X_PASSWORD", &value, &flags); + read_8021x_password (ifcfg, keys_ifcfg, "IEEE_8021X_PASSWORD", &value, &flags); g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD_FLAGS, flags, NULL); if (value) { g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD, value, NULL); nm_clear_g_free (&value); } - read_8021x_password (ifcfg, keys, "IEEE_8021X_PASSWORD_RAW", &value, &flags); + read_8021x_password (ifcfg, keys_ifcfg, "IEEE_8021X_PASSWORD_RAW", &value, &flags); g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD_RAW_FLAGS, flags, NULL); if (value) { bytes = nm_utils_hexstr2bin (value); @@ -2965,7 +2965,7 @@ get_cert_value (const char *ifcfg_path, const char *value, static gboolean eap_tls_reader (const char *eap_method, shvarFile *ifcfg, - shvarFile *keys, + shvarFile *keys_ifcfg, NMSetting8021x *s_8021x, gboolean phase2, GError **error) @@ -3035,9 +3035,9 @@ eap_tls_reader (const char *eap_method, if (flags == NM_SETTING_SECRET_FLAG_NONE) { /* Private key password */ privkey_password = svGetValueStr_cp (ifcfg, pk_pw_key); - if (!privkey_password && keys) { + if (!privkey_password && keys_ifcfg) { /* Try the lookaside keys file */ - privkey_password = svGetValueStr_cp (keys, pk_pw_key); + privkey_password = svGetValueStr_cp (keys_ifcfg, pk_pw_key); } } @@ -3118,7 +3118,7 @@ eap_tls_reader (const char *eap_method, static gboolean eap_peap_reader (const char *eap_method, shvarFile *ifcfg, - shvarFile *keys, + shvarFile *keys_ifcfg, NMSetting8021x *s_8021x, gboolean phase2, GError **error) @@ -3179,10 +3179,10 @@ eap_peap_reader (const char *eap_method, if (NM_IN_STRSET (*iter, "MSCHAPV2", "MD5", "GTC")) { - if (!eap_simple_reader (*iter, ifcfg, keys, s_8021x, TRUE, error)) + if (!eap_simple_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error)) return FALSE; } else if (nm_streq (*iter, "TLS")) { - if (!eap_tls_reader (*iter, ifcfg, keys, s_8021x, TRUE, error)) + if (!eap_tls_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error)) return FALSE; } else { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, @@ -3211,7 +3211,7 @@ eap_peap_reader (const char *eap_method, static gboolean eap_ttls_reader (const char *eap_method, shvarFile *ifcfg, - shvarFile *keys, + shvarFile *keys_ifcfg, NMSetting8021x *s_8021x, gboolean phase2, GError **error) @@ -3258,17 +3258,17 @@ eap_ttls_reader (const char *eap_method, "mschap", "pap", "chap")) { - if (!eap_simple_reader (*iter, ifcfg, keys, s_8021x, TRUE, error)) + if (!eap_simple_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error)) return FALSE; g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, *iter, NULL); } else if (nm_streq (*iter, "eap-tls")) { - if (!eap_tls_reader (*iter, ifcfg, keys, s_8021x, TRUE, error)) + if (!eap_tls_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error)) return FALSE; g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTHEAP, "tls", NULL); } else if (NM_IN_STRSET (*iter, "eap-mschapv2", "eap-md5", "eap-gtc")) { - if (!eap_simple_reader (*iter, ifcfg, keys, s_8021x, TRUE, error)) + if (!eap_simple_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error)) return FALSE; g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTHEAP, (*iter + NM_STRLEN ("eap-")), NULL); } else { @@ -3285,7 +3285,7 @@ eap_ttls_reader (const char *eap_method, static gboolean eap_fast_reader (const char *eap_method, shvarFile *ifcfg, - shvarFile *keys, + shvarFile *keys_ifcfg, NMSetting8021x *s_8021x, gboolean phase2, GError **error) @@ -3351,7 +3351,7 @@ eap_fast_reader (const char *eap_method, if (iter) { if ( !strcmp (*iter, "MSCHAPV2") || !strcmp (*iter, "GTC")) { - if (!eap_simple_reader (*iter, ifcfg, keys, s_8021x, TRUE, error)) + if (!eap_simple_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error)) goto done; } else { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, @@ -3386,7 +3386,7 @@ typedef struct { const char *method; gboolean (*reader) (const char *eap_method, shvarFile *ifcfg, - shvarFile *keys, + shvarFile *keys_ifcfg, NMSetting8021x *s_8021x, gboolean phase2, GError **error); @@ -3438,7 +3438,7 @@ fill_8021x (shvarFile *ifcfg, gboolean wifi, GError **error) { - nm_auto_shvar_file_close shvarFile *keys = NULL; + nm_auto_shvar_file_close shvarFile *keys_ifcfg = NULL; gs_unref_object NMSetting8021x *s_8021x = NULL; gs_free char *value = NULL; const char *v; @@ -3459,8 +3459,8 @@ fill_8021x (shvarFile *ifcfg, s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); - /* Read in the lookaside keys file, if present */ - keys = utils_get_keys_ifcfg (file, FALSE); + /* Read in the lookaside keys_ifcfg file, if present */ + keys_ifcfg = utils_get_keys_ifcfg (file, FALSE); /* Validate and handle each EAP method */ for (iter = list; iter && *iter; iter++) { @@ -3484,7 +3484,7 @@ fill_8021x (shvarFile *ifcfg, } /* Parse EAP method specific options */ - if (!(*eap->reader)(lower, ifcfg, keys, s_8021x, FALSE, error)) + if (!(*eap->reader)(lower, ifcfg, keys_ifcfg, s_8021x, FALSE, error)) return NULL; nm_setting_802_1x_add_eap_method (s_8021x, lower); @@ -5459,7 +5459,7 @@ connection_from_file_full (const char *filename, GError **error, gboolean *out_ignore_error) { - nm_auto_shvar_file_close shvarFile *parsed = NULL; + nm_auto_shvar_file_close shvarFile *main_ifcfg = NULL; nm_auto_shvar_file_close shvarFile *network_ifcfg = NULL; gs_unref_object NMConnection *connection = NULL; gs_free char *type = NULL; @@ -5487,14 +5487,14 @@ connection_from_file_full (const char *filename, return NULL; } - parsed = svOpenFile (filename, error); - if (!parsed) + main_ifcfg = svOpenFile (filename, error); + if (!main_ifcfg) return NULL; network_ifcfg = svOpenFile (network_file, NULL); - if (!svGetValueBoolean (parsed, "NM_CONTROLLED", TRUE)) { - connection = create_unhandled_connection (filename, parsed, "unmanaged", out_unhandled); + if (!svGetValueBoolean (main_ifcfg, "NM_CONTROLLED", TRUE)) { + connection = create_unhandled_connection (filename, main_ifcfg, "unmanaged", out_unhandled); if (!connection) { NM_SET_OUT (out_ignore_error, TRUE); g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, @@ -5504,7 +5504,7 @@ connection_from_file_full (const char *filename, } /* iBFT is handled by the iBFT settings plugin */ - bootproto = svGetValueStr_cp (parsed, "BOOTPROTO"); + bootproto = svGetValueStr_cp (main_ifcfg, "BOOTPROTO"); if (bootproto && !g_ascii_strcasecmp (bootproto, "ibft")) { NM_SET_OUT (out_ignore_error, TRUE); g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, @@ -5514,19 +5514,19 @@ connection_from_file_full (const char *filename, } g_free (bootproto); - devtype = svGetValueStr_cp (parsed, "DEVICETYPE"); + devtype = svGetValueStr_cp (main_ifcfg, "DEVICETYPE"); if (devtype) { if (!strcasecmp (devtype, TYPE_TEAM)) type = g_strdup (TYPE_TEAM); else if (!strcasecmp (devtype, TYPE_TEAM_PORT)) { gs_free char *device = NULL; - type = svGetValueStr_cp (parsed, "TYPE"); - device = svGetValueStr_cp (parsed, "DEVICE"); + type = svGetValueStr_cp (main_ifcfg, "TYPE"); + device = svGetValueStr_cp (main_ifcfg, "DEVICE"); if (type) { /* nothing to do */ - } else if (device && is_vlan_device (device, parsed)) + } else if (device && is_vlan_device (device, main_ifcfg)) type = g_strdup (TYPE_VLAN); else type = g_strdup (TYPE_ETHERNET); @@ -5539,26 +5539,26 @@ connection_from_file_full (const char *filename, /* Team and TeamPort types are also accepted by the mere * presence of TEAM_CONFIG/TEAM_MASTER. They don't require * DEVICETYPE. */ - t = svGetValueStr_cp (parsed, "TEAM_CONFIG"); + t = svGetValueStr_cp (main_ifcfg, "TEAM_CONFIG"); if (t) type = g_strdup (TYPE_TEAM); } if (!type) - type = svGetValueStr_cp (parsed, "TYPE"); + type = svGetValueStr_cp (main_ifcfg, "TYPE"); if (!type) { gs_free char *tmp = NULL; char *device; - if ((tmp = svGetValueStr_cp (parsed, "IPV6TUNNELIPV4"))) { + if ((tmp = svGetValueStr_cp (main_ifcfg, "IPV6TUNNELIPV4"))) { NM_SET_OUT (out_ignore_error, TRUE); g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Ignoring unsupported connection due to IPV6TUNNELIPV4"); return NULL; } - device = svGetValueStr_cp (parsed, "DEVICE"); + device = svGetValueStr_cp (main_ifcfg, "DEVICE"); if (!device) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "File '%s' had neither TYPE nor DEVICE keys.", filename); @@ -5574,11 +5574,11 @@ connection_from_file_full (const char *filename, } if (!test_type) { - if (is_bond_device (device, parsed)) + if (is_bond_device (device, main_ifcfg)) type = g_strdup (TYPE_BOND); - else if (is_vlan_device (device, parsed)) + else if (is_vlan_device (device, main_ifcfg)) type = g_strdup (TYPE_VLAN); - else if (is_wifi_device (device, parsed)) + else if (is_wifi_device (device, main_ifcfg)) type = g_strdup (TYPE_WIRELESS); else { gs_free char *p_path = NULL; @@ -5644,14 +5644,14 @@ connection_from_file_full (const char *filename, if (nm_streq0 (type, TYPE_ETHERNET)) { gs_free char *bond_options = NULL; - if (svGetValueStr (parsed, "BONDING_OPTS", &bond_options)) { + if (svGetValueStr (main_ifcfg, "BONDING_OPTS", &bond_options)) { /* initscripts consider these as bond masters */ g_free (type); type = g_strdup (TYPE_BOND); } } - if (svGetValueBoolean (parsed, "BONDING_MASTER", FALSE) && + if (svGetValueBoolean (main_ifcfg, "BONDING_MASTER", FALSE) && strcasecmp (type, TYPE_BOND)) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "BONDING_MASTER=yes key only allowed in TYPE=bond connections"); @@ -5660,21 +5660,21 @@ connection_from_file_full (const char *filename, /* Construct the connection */ if (!strcasecmp (type, TYPE_ETHERNET)) - connection = wired_connection_from_ifcfg (filename, parsed, error); + connection = wired_connection_from_ifcfg (filename, main_ifcfg, error); else if (!strcasecmp (type, TYPE_WIRELESS)) - connection = wireless_connection_from_ifcfg (filename, parsed, error); + connection = wireless_connection_from_ifcfg (filename, main_ifcfg, error); else if (!strcasecmp (type, TYPE_INFINIBAND)) - connection = infiniband_connection_from_ifcfg (filename, parsed, error); + connection = infiniband_connection_from_ifcfg (filename, main_ifcfg, error); else if (!strcasecmp (type, TYPE_BOND)) - connection = bond_connection_from_ifcfg (filename, parsed, error); + connection = bond_connection_from_ifcfg (filename, main_ifcfg, error); else if (!strcasecmp (type, TYPE_TEAM)) - connection = team_connection_from_ifcfg (filename, parsed, error); + connection = team_connection_from_ifcfg (filename, main_ifcfg, error); else if (!strcasecmp (type, TYPE_VLAN)) - connection = vlan_connection_from_ifcfg (filename, parsed, error); + connection = vlan_connection_from_ifcfg (filename, main_ifcfg, error); else if (!strcasecmp (type, TYPE_BRIDGE)) - connection = bridge_connection_from_ifcfg (filename, parsed, error); + connection = bridge_connection_from_ifcfg (filename, main_ifcfg, error); else { - connection = create_unhandled_connection (filename, parsed, "unrecognized", out_unhandled); + connection = create_unhandled_connection (filename, main_ifcfg, "unrecognized", out_unhandled); if (!connection) { PARSE_WARNING ("connection type was unrecognized but device was not uniquely identified; device may be managed"); g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, @@ -5686,7 +5686,7 @@ connection_from_file_full (const char *filename, if (!connection) return NULL; - parse_ethtool_options (parsed, connection); + parse_ethtool_options (main_ifcfg, connection); has_complex_routes_v4 = utils_has_complex_routes (filename, AF_INET); has_complex_routes_v6 = utils_has_complex_routes (filename, AF_INET6); @@ -5700,7 +5700,7 @@ connection_from_file_full (const char *filename, PARSE_WARNING ("'rule-' and 'rule6-' files are present; you will need to use a dispatcher script to apply these routes"); } - s_ip6 = make_ip6_setting (parsed, + s_ip6 = make_ip6_setting (main_ifcfg, network_ifcfg, !has_complex_routes_v4 && !has_complex_routes_v6, error); @@ -5709,7 +5709,7 @@ connection_from_file_full (const char *filename, else nm_connection_add_setting (connection, s_ip6); - s_ip4 = make_ip4_setting (parsed, + s_ip4 = make_ip4_setting (main_ifcfg, network_ifcfg, !has_complex_routes_v4 && !has_complex_routes_v6, &has_ip4_defroute, @@ -5723,11 +5723,11 @@ connection_from_file_full (const char *filename, nm_connection_add_setting (connection, s_ip4); } - s_sriov = make_sriov_setting (parsed); + s_sriov = make_sriov_setting (main_ifcfg); if (s_sriov) nm_connection_add_setting (connection, s_sriov); - s_tc = make_tc_setting (parsed); + s_tc = make_tc_setting (main_ifcfg); if (s_tc) nm_connection_add_setting (connection, s_tc); @@ -5735,31 +5735,31 @@ connection_from_file_full (const char *filename, * config fails for some reason, we read DOMAIN and put the * values into IPv6 config instead of IPv4. */ - check_dns_search_domains (parsed, s_ip4, s_ip6); + check_dns_search_domains (main_ifcfg, s_ip4, s_ip6); - s_proxy = make_proxy_setting (parsed); + s_proxy = make_proxy_setting (main_ifcfg); if (s_proxy) nm_connection_add_setting (connection, s_proxy); - s_user = make_user_setting (parsed); + s_user = make_user_setting (main_ifcfg); if (s_user) nm_connection_add_setting (connection, s_user); - s_match = make_match_setting (parsed); + s_match = make_match_setting (main_ifcfg); if (s_match) nm_connection_add_setting (connection, s_match); /* Bridge port? */ - s_port = make_bridge_port_setting (parsed); + s_port = make_bridge_port_setting (main_ifcfg); if (s_port) nm_connection_add_setting (connection, s_port); /* Team port? */ - s_port = make_team_port_setting (parsed); + s_port = make_team_port_setting (main_ifcfg); if (s_port) nm_connection_add_setting (connection, s_port); - if (!make_dcb_setting (parsed, &s_dcb, error)) + if (!make_dcb_setting (main_ifcfg, &s_dcb, error)) return NULL; if (s_dcb) nm_connection_add_setting (connection, s_dcb); From 6b763af1b75d33b088bab44dce33f75a9e0ac515 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 3 Sep 2018 11:16:48 +0200 Subject: [PATCH 60/61] ifcfg-rh: rework parsing secrets - rename secret related functions to have a "_secret" prefix. Also, rename read_8021x_password() because it's not only useful for 802-1x. - In particular, this patch adds _secret_read_ifcfg() helper (formerly read_8021x_password()), which is smart enough to obtain secrets from the keys ifcfg file. We have other places where we don't get this right. - on a minor note, the patch also makes an effort to clear passwords and certifcate data from memory. Yes, there are countless places where we don't do that, but in this case, it's done and is as simple as replacing gs_free with nm_auto_free_secret, etc. --- .../plugins/ifcfg-rh/nms-ifcfg-rh-reader.c | 209 +++++++++++------- 1 file changed, 130 insertions(+), 79 deletions(-) diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c index 0a6a5e0b44..7be87b96f2 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -33,6 +33,7 @@ #include #include +#include "nm-utils/nm-secret-utils.h" #include "nm-connection.h" #include "nm-dbus-interface.h" #include "nm-setting-connection.h" @@ -79,6 +80,104 @@ /*****************************************************************************/ +static NMSettingSecretFlags +_secret_read_ifcfg_flags (shvarFile *ifcfg, const char *flags_key) +{ + NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE; + gs_free char *val_free = NULL; + const char *val; + + nm_assert (flags_key); + nm_assert (g_str_has_suffix (flags_key, "_FLAGS")); + + val = svGetValueStr (ifcfg, flags_key, &val_free); + if (val) { + if (strstr (val, SECRET_FLAG_AGENT)) + flags |= NM_SETTING_SECRET_FLAG_AGENT_OWNED; + if (strstr (val, SECRET_FLAG_NOT_SAVED)) + flags |= NM_SETTING_SECRET_FLAG_NOT_SAVED; + if (strstr (val, SECRET_FLAG_NOT_REQUIRED)) + flags |= NM_SETTING_SECRET_FLAG_NOT_REQUIRED; + } + return flags; +} + +static void +_secret_read_ifcfg (shvarFile *ifcfg, + shvarFile *keys_ifcfg, + const char *name, + char **value, + NMSettingSecretFlags *flags) +{ + char flags_key[250]; + + nm_sprintf_buf (flags_key, "%s_FLAGS", name); + + *flags = _secret_read_ifcfg_flags (ifcfg, flags_key); + + if (*flags != NM_SETTING_SECRET_FLAG_NONE) + *value = NULL; + else { + *value = svGetValue_cp (ifcfg, name); + if (!*value && keys_ifcfg) + *value = svGetValue_cp (keys_ifcfg, name); + } +} + +static void +_secret_set_from_ifcfg (gpointer setting, + shvarFile *ifcfg, + shvarFile *keys_ifcfg, + const char *ifcfg_key, + const char *property_name) +{ + nm_auto_free_secret char *secret = NULL; + NMSettingSecretFlags flags; + char flags_key[250]; + + nm_assert (NM_IS_SETTING (setting)); + + _secret_read_ifcfg (ifcfg, keys_ifcfg, ifcfg_key, &secret, &flags); + + g_object_set (setting, + property_name, + secret, + nm_sprintf_buf (flags_key, "%s-flags", property_name), + flags, + NULL); +} + +static gboolean +_secret_password_raw_to_bytes (const char *ifcfg_key, + const char *password_raw, + GBytes **out_bytes, + GError **error) +{ + nm_auto_free_secret_buf NMSecretBuf *secret = NULL; + gsize len; + + if (!password_raw) { + NM_SET_OUT (out_bytes, NULL); + return TRUE; + } + + if (password_raw[0] == '0' && password_raw[1] == 'x') + password_raw += 2; + + secret = nm_secret_buf_new (strlen (password_raw) / 2 + 3); + if (!_nm_utils_str2bin_full (password_raw, FALSE, ":", secret->bin, secret->len, &len)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid hex password in %s", + ifcfg_key); + return FALSE; + } + + NM_SET_OUT (out_bytes, nm_secret_buf_to_gbytes_take (g_steal_pointer (&secret), len)); + return TRUE; +} + +/*****************************************************************************/ + static void check_if_bond_slave (shvarFile *ifcfg, NMSettingConnection *s_con) @@ -2608,30 +2707,6 @@ read_wep_keys (shvarFile *ifcfg, return TRUE; } -static NMSettingSecretFlags -read_secret_flags (shvarFile *ifcfg, const char *flags_key) -{ - NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE; - char *val; - - g_return_val_if_fail (flags_key != NULL, NM_SETTING_SECRET_FLAG_NONE); - g_return_val_if_fail (flags_key[0] != '\0', NM_SETTING_SECRET_FLAG_NONE); - g_return_val_if_fail (g_str_has_suffix (flags_key, "_FLAGS"), NM_SETTING_SECRET_FLAG_NONE); - - val = svGetValueStr_cp (ifcfg, flags_key); - if (val) { - if (strstr (val, SECRET_FLAG_AGENT)) - flags |= NM_SETTING_SECRET_FLAG_AGENT_OWNED; - if (strstr (val, SECRET_FLAG_NOT_SAVED)) - flags |= NM_SETTING_SECRET_FLAG_NOT_SAVED; - if (strstr (val, SECRET_FLAG_NOT_REQUIRED)) - flags |= NM_SETTING_SECRET_FLAG_NOT_REQUIRED; - - g_free (val); - } - return flags; -} - static NMSetting * make_wep_setting (shvarFile *ifcfg, const char *file, @@ -2663,7 +2738,7 @@ make_wep_setting (shvarFile *ifcfg, } /* Read WEP key flags */ - key_flags = read_secret_flags (ifcfg, "WEP_KEY_FLAGS"); + key_flags = _secret_read_ifcfg_flags (ifcfg, "WEP_KEY_FLAGS"); g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS, key_flags, NULL); /* Read keys in the ifcfg file if they are system-owned */ @@ -2862,23 +2937,6 @@ parse_wpa_psk (shvarFile *ifcfg, return g_steal_pointer (&psk); } -static void -read_8021x_password (shvarFile *ifcfg, shvarFile *keys_ifcfg, const char *name, - char **value, NMSettingSecretFlags *flags) -{ - gs_free char *flags_key = NULL; - - *value = NULL; - flags_key = g_strdup_printf ("%s_FLAGS", name); - *flags = read_secret_flags (ifcfg, flags_key); - - if (*flags == NM_SETTING_SECRET_FLAG_NONE) { - *value = svGetValueStr_cp (ifcfg, name); - if (!*value && keys_ifcfg) - *value = svGetValueStr_cp (keys_ifcfg, name); - } -} - static gboolean eap_simple_reader (const char *eap_method, shvarFile *ifcfg, @@ -2888,41 +2946,34 @@ eap_simple_reader (const char *eap_method, GError **error) { NMSettingSecretFlags flags; - GBytes *bytes; - char *value; + gs_free char *identity_free = NULL; + nm_auto_free_secret char *password_raw_str = NULL; + gs_unref_bytes GBytes *password_raw_bytes = NULL; - value = svGetValueStr_cp (ifcfg, "IEEE_8021X_IDENTITY"); - if (!value) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing IEEE_8021X_IDENTITY for EAP method '%s'.", - eap_method); + g_object_set (s_8021x, + NM_SETTING_802_1X_IDENTITY, + svGetValueStr (ifcfg, "IEEE_8021X_IDENTITY", &identity_free), + NULL); + + _secret_set_from_ifcfg (s_8021x, + ifcfg, + keys_ifcfg, + "IEEE_8021X_PASSWORD", + NM_SETTING_802_1X_PASSWORD); + + _secret_read_ifcfg (ifcfg, keys_ifcfg, "IEEE_8021X_PASSWORD_RAW", &password_raw_str, &flags); + if (!_secret_password_raw_to_bytes ("IEEE_8021X_PASSWORD_RAW", + password_raw_str, + &password_raw_bytes, + error)) return FALSE; - } - g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, value, NULL); - nm_clear_g_free (&value); - read_8021x_password (ifcfg, keys_ifcfg, "IEEE_8021X_PASSWORD", &value, &flags); - g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD_FLAGS, flags, NULL); - if (value) { - g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD, value, NULL); - nm_clear_g_free (&value); - } - - read_8021x_password (ifcfg, keys_ifcfg, "IEEE_8021X_PASSWORD_RAW", &value, &flags); - g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD_RAW_FLAGS, flags, NULL); - if (value) { - bytes = nm_utils_hexstr2bin (value); - if (!bytes) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid hex string '%s' in IEEE_8021X_PASSWORD_RAW.", - value); - g_free (value); - return FALSE; - } - g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD_RAW, bytes, NULL); - g_bytes_unref (bytes); - nm_clear_g_free (&value); - } + g_object_set (s_8021x, + NM_SETTING_802_1X_PASSWORD_RAW_FLAGS, + flags, + NM_SETTING_802_1X_PASSWORD_RAW, + password_raw_bytes, + NULL); return TRUE; } @@ -3014,7 +3065,7 @@ eap_tls_reader (const char *eap_method, } if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) { - flags = read_secret_flags (ifcfg, ca_cert_pw_flags_key); + flags = _secret_read_ifcfg_flags (ifcfg, ca_cert_pw_flags_key); g_object_set (s_8021x, ca_cert_pw_flags_prop, flags, NULL); if (flags == NM_SETTING_SECRET_FLAG_NONE) { @@ -3028,7 +3079,7 @@ eap_tls_reader (const char *eap_method, } /* Read and set private key password flags */ - flags = read_secret_flags (ifcfg, pk_pw_flags_key); + flags = _secret_read_ifcfg_flags (ifcfg, pk_pw_flags_key); g_object_set (s_8021x, pk_pw_flags_prop, flags, NULL); /* Read the private key password if it's system-owned */ @@ -3102,7 +3153,7 @@ eap_tls_reader (const char *eap_method, } if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) { - flags = read_secret_flags (ifcfg, cli_cert_pw_flags_key); + flags = _secret_read_ifcfg_flags (ifcfg, cli_cert_pw_flags_key); g_object_set (s_8021x, cli_cert_pw_flags_prop, flags, NULL); if (flags == NM_SETTING_SECRET_FLAG_NONE) { @@ -3600,7 +3651,7 @@ make_wpa_setting (shvarFile *ifcfg, if (wpa_psk) { NMSettingSecretFlags psk_flags; - psk_flags = read_secret_flags (ifcfg, "WPA_PSK_FLAGS"); + psk_flags = _secret_read_ifcfg_flags (ifcfg, "WPA_PSK_FLAGS"); g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_PSK_FLAGS, psk_flags, NULL); /* Read PSK if it's system-owned */ @@ -3687,7 +3738,7 @@ make_leap_setting (shvarFile *ifcfg, return NULL; /* Not LEAP */ nm_clear_g_free (&value); - flags = read_secret_flags (ifcfg, "IEEE_8021X_PASSWORD_FLAGS"); + flags = _secret_read_ifcfg_flags (ifcfg, "IEEE_8021X_PASSWORD_FLAGS"); g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS, flags, NULL); /* Read LEAP password if it's system-owned */ From e3ac45c026104e05c6fda802131c92457720c119 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 3 Sep 2018 12:24:27 +0200 Subject: [PATCH 61/61] ifcfg-rh: don't use 802-1x certifcate setter functions The certificate setter function like nm_setting_802_1x_set_ca_cert() actually load the file from disk, and validate whether it is a valid certificate. That is very wrong to do. For one, the certificates are external files, which are not embedded into the NMConnection. That means, strongly validating the files while loading the ifcfg files, is wrong because: - if validation fails, loading the file fails in its entirety with a warning in the log. That is not helpful to the user, who now can no longer use nmcli to fix the path of the certificate (because the profile failed to load in the first place). - even if the certificate is valid at load-time, there is no guarantee that it is valid later on, when we actually try to use the file. What good does such a validation do? nm_setting_802_1x_set_ca_cert() might make sense during nmcli_connection_modify(). At the moment when we create or update the profile, we do want to validate the input and be helpful to the user. Validating the file later on, when reloading the profile from disk seems undesirable. - note how keyfile also does not perform such validations (for good reasons, I presume). Also, there is so much wrong with how ifcfg reader handles EAP files. There is a lot of duplication, and trying to be too smart. I find it wrong how the "eap_readers" are nested. E.g. both eap_peap_reader() and "tls" method call to eap_tls_reader(), making it look like that NMSetting8021x can handle multiple EAP profiles separately. But it cannot. The 802-1x profile is a flat set of properties like ca-cert and others. All EAP methods share these properties, so having this complex parsing is not only complicated, but also wrong. The reader should simply parse the shell variables, and let NMSetting8021x::verify() handle validation of the settings. Anyway, the patch does not address that. Also, the setting of the likes of NM_SETTING_802_1X_CLIENT_CERT_PASSWORD was awkwardly only done when privkey_format != NM_SETTING_802_1X_CK_FORMAT_PKCS12 && scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11 It is too smart. Just read it from file, if it contains invalid data, let verify() reject it. That is only partly addressed. Also note, how writer never actually writes the likes of IEEE_8021X_CLIENT_CERT_PASSWORD. That is another bug and not fixed either. --- libnm-core/nm-core-internal.h | 9 + libnm-core/nm-setting-8021x.c | 28 +- .../plugins/ifcfg-rh/nms-ifcfg-rh-reader.c | 372 +++++++++--------- .../plugins/ifcfg-rh/tests/test-ifcfg-rh.c | 6 - 4 files changed, 206 insertions(+), 209 deletions(-) diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index f19709dfc6..f0d4e40f93 100644 --- a/libnm-core/nm-core-internal.h +++ b/libnm-core/nm-core-internal.h @@ -588,4 +588,13 @@ const NMSettInfoProperty *_nm_sett_info_property_get (NMSettingClass *setting_cl /*****************************************************************************/ +NMSetting8021xCKScheme _nm_setting_802_1x_cert_get_scheme (GBytes *bytes, GError **error); + +GBytes *_nm_setting_802_1x_cert_value_to_bytes (NMSetting8021xCKScheme scheme, + const guint8 *val_bin, + gssize val_len, + GError **error); + +/*****************************************************************************/ + #endif diff --git a/libnm-core/nm-setting-8021x.c b/libnm-core/nm-setting-8021x.c index 087084f89d..529029de83 100644 --- a/libnm-core/nm-setting-8021x.c +++ b/libnm-core/nm-setting-8021x.c @@ -279,8 +279,8 @@ nm_setting_802_1x_check_cert_scheme (gconstpointer pdata, gsize length, GError * return scheme; } -static NMSetting8021xCKScheme -_cert_get_scheme (GBytes *bytes, GError **error) +NMSetting8021xCKScheme +_nm_setting_802_1x_cert_get_scheme (GBytes *bytes, GError **error) { const char *data; gsize length; @@ -307,7 +307,7 @@ _cert_verify_scheme (NMSetting8021xCKScheme scheme, nm_assert (bytes); - scheme_detected = _cert_get_scheme (bytes, &local); + scheme_detected = _nm_setting_802_1x_cert_get_scheme (bytes, &local); if (scheme_detected == NM_SETTING_802_1X_CK_SCHEME_UNKNOWN) { g_set_error (error, NM_CONNECTION_ERROR, @@ -327,11 +327,11 @@ _cert_verify_scheme (NMSetting8021xCKScheme scheme, return TRUE; } -static GBytes * -_cert_value_to_bytes (NMSetting8021xCKScheme scheme, - const guint8 *val_bin, - gssize val_len, - GError **error) +GBytes * +_nm_setting_802_1x_cert_value_to_bytes (NMSetting8021xCKScheme scheme, + const guint8 *val_bin, + gssize val_len, + GError **error) { gs_unref_bytes GBytes *bytes = NULL; guint8 *mem; @@ -388,7 +388,7 @@ _cert_get_path (GBytes *bytes) G_STMT_START { \ NMSetting8021xCKScheme scheme; \ \ - scheme = _cert_get_scheme ((cert), NULL); \ + scheme = _nm_setting_802_1x_cert_get_scheme ((cert), NULL); \ if (scheme != check_scheme) { \ g_return_val_if_fail (scheme == check_scheme, ret_val); \ return ret_val; \ @@ -404,7 +404,7 @@ _cert_get_path (GBytes *bytes) \ _cert = NM_SETTING_802_1X_GET_PRIVATE (_setting)->cert_field; \ \ - return _cert_get_scheme (_cert, NULL); \ + return _nm_setting_802_1x_cert_get_scheme (_cert, NULL); \ } G_STMT_END #define _cert_impl_get_blob(setting, cert_field) \ @@ -487,7 +487,7 @@ _cert_impl_set (NMSetting8021x *setting, if (!value) { /* pass. */ } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) { - cert = _cert_value_to_bytes (scheme, (guint8 *) value, -1, error); + cert = _nm_setting_802_1x_cert_value_to_bytes (scheme, (guint8 *) value, -1, error); if (!cert) goto err; } else { @@ -518,7 +518,7 @@ _cert_impl_set (NMSetting8021x *setting, if (!_cert_verify_scheme (scheme, cert, error)) goto err; } else { - cert = _cert_value_to_bytes (scheme, (guint8 *) value, -1, error); + cert = _nm_setting_802_1x_cert_value_to_bytes (scheme, (guint8 *) value, -1, error); if (!cert) goto err; } @@ -627,7 +627,7 @@ _cert_impl_get_key_format_from_bytes (GBytes *private_key) if (!private_key) return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; - switch (_cert_get_scheme (private_key, NULL)) { + switch (_nm_setting_802_1x_cert_get_scheme (private_key, NULL)) { case NM_SETTING_802_1X_CK_SCHEME_BLOB: if (nm_crypto_is_pkcs12_data (g_bytes_get_data (private_key, NULL), g_bytes_get_size (private_key), @@ -674,7 +674,7 @@ _cert_verify_property (GBytes *bytes, if (!bytes) return TRUE; - scheme = _cert_get_scheme (bytes, &local); + scheme = _nm_setting_802_1x_cert_get_scheme (bytes, &local); if (scheme == NM_SETTING_802_1X_CK_SCHEME_UNKNOWN) { g_set_error (error, NM_CONNECTION_ERROR, diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c index 7be87b96f2..2f38e16887 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -80,6 +80,30 @@ /*****************************************************************************/ +static char * +get_full_file_path (const char *ifcfg_path, const char *file_path) +{ + const char *base = file_path; + char *p, *ret, *dirname; + + g_return_val_if_fail (ifcfg_path != NULL, NULL); + g_return_val_if_fail (file_path != NULL, NULL); + + if (file_path[0] == '/') + return g_strdup (file_path); + + p = strrchr (file_path, '/'); + if (p) + base = p + 1; + + dirname = g_path_get_dirname (ifcfg_path); + ret = g_build_path ("/", dirname, base, NULL); + g_free (dirname); + return ret; +} + +/*****************************************************************************/ + static NMSettingSecretFlags _secret_read_ifcfg_flags (shvarFile *ifcfg, const char *flags_key) { @@ -178,6 +202,88 @@ _secret_password_raw_to_bytes (const char *ifcfg_key, /*****************************************************************************/ +static GBytes * +_cert_get_cert_bytes (const char *ifcfg_path, + const char *value, + GError **error) +{ + gs_free char *path = NULL; + + if (g_str_has_prefix (value, "pkcs11:")) + return _nm_setting_802_1x_cert_value_to_bytes (NM_SETTING_802_1X_CK_SCHEME_PKCS11, (guint8 *) value, -1, error); + + path = get_full_file_path (ifcfg_path, value); + return _nm_setting_802_1x_cert_value_to_bytes (NM_SETTING_802_1X_CK_SCHEME_PATH, (guint8 *) path, -1, error); +} + +static gboolean +_cert_get_cert (shvarFile *ifcfg, + const char *ifcfg_key, + GBytes **out_cert, + NMSetting8021xCKScheme *out_scheme, + GError **error) +{ + nm_auto_free_secret char *val_free = NULL; + const char *val; + gs_unref_bytes GBytes *cert = NULL; + GError *local = NULL; + NMSetting8021xCKScheme scheme; + + val = svGetValueStr (ifcfg, ifcfg_key, &val_free); + if (!val) { + NM_SET_OUT (out_cert, NULL); + NM_SET_OUT (out_scheme, NM_SETTING_802_1X_CK_SCHEME_UNKNOWN); + return TRUE; + } + + cert = _cert_get_cert_bytes (svFileGetName (ifcfg), val, &local); + if (!cert) + goto err; + + scheme = _nm_setting_802_1x_cert_get_scheme (cert, &local); + if (scheme == NM_SETTING_802_1X_CK_SCHEME_UNKNOWN) + goto err; + + NM_SET_OUT (out_cert, g_steal_pointer (&cert)); + NM_SET_OUT (out_scheme, scheme); + return TRUE; + +err: + g_set_error (error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "invalid certificate %s: %s", + ifcfg_key, + local->message); + g_error_free (local); + return FALSE; +} + +static gboolean +_cert_set_from_ifcfg (gpointer setting, + shvarFile *ifcfg, + const char *ifcfg_key, + const char *property_name, + GBytes **out_cert, + GError **error) +{ + gs_unref_bytes GBytes *cert = NULL; + + if (!_cert_get_cert (ifcfg, + ifcfg_key, + &cert, + NULL, + error)) + return FALSE; + + g_object_set (setting, property_name, cert, NULL); + + NM_SET_OUT (out_cert, g_steal_pointer (&cert)); + return TRUE; +} + +/*****************************************************************************/ + static void check_if_bond_slave (shvarFile *ifcfg, NMSettingConnection *s_con) @@ -2978,41 +3084,6 @@ eap_simple_reader (const char *eap_method, return TRUE; } -static char * -get_full_file_path (const char *ifcfg_path, const char *file_path) -{ - const char *base = file_path; - char *p, *ret, *dirname; - - g_return_val_if_fail (ifcfg_path != NULL, NULL); - g_return_val_if_fail (file_path != NULL, NULL); - - if (file_path[0] == '/') - return g_strdup (file_path); - - p = strrchr (file_path, '/'); - if (p) - base = p + 1; - - dirname = g_path_get_dirname (ifcfg_path); - ret = g_build_path ("/", dirname, base, NULL); - g_free (dirname); - return ret; -} - -static char * -get_cert_value (const char *ifcfg_path, const char *value, - NMSetting8021xCKScheme *out_scheme) -{ - if (strncmp (value, "pkcs11:", 7) == 0) { - *out_scheme = NM_SETTING_802_1X_CK_SCHEME_PKCS11; - return g_strdup (value); - } - - *out_scheme = NM_SETTING_802_1X_CK_SCHEME_PATH; - return get_full_file_path (ifcfg_path, value); -} - static gboolean eap_tls_reader (const char *eap_method, shvarFile *ifcfg, @@ -3021,146 +3092,69 @@ eap_tls_reader (const char *eap_method, gboolean phase2, GError **error) { - gs_free char *ca_cert = NULL; - gs_free char *privkey = NULL; - gs_free char *privkey_password = NULL; - char *value; - char *ca_cert_password = NULL; - char *client_cert_password = NULL; - NMSetting8021xCKFormat privkey_format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; - const char *ca_cert_key = phase2 ? "IEEE_8021X_INNER_CA_CERT" : "IEEE_8021X_CA_CERT"; - const char *ca_cert_pw_key = phase2 ? "IEEE_8021X_INNER_CA_CERT_PASSWORD" : "IEEE_8021X_CA_CERT_PASSWORD"; - const char *ca_cert_pw_prop = phase2 ? NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD : NM_SETTING_802_1X_CA_CERT_PASSWORD; - const char *ca_cert_pw_flags_key = phase2 ? "IEEE_8021X_INNER_CA_CERT_PASSWORD_FLAGS" : "IEEE_8021X_CA_CERT_PASSWORD_FLAGS"; - const char *ca_cert_pw_flags_prop = phase2 ? NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD_FLAGS : NM_SETTING_802_1X_CA_CERT_PASSWORD_FLAGS; - const char *cli_cert_key = phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : "IEEE_8021X_CLIENT_CERT"; - const char *cli_cert_pw_key = phase2 ? "IEEE_8021X_INNER_CLIENT_CERT_PASSWORD" : "IEEE_8021X_CLIENT_CERT_PASSWORD"; - const char *cli_cert_pw_prop = phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD : NM_SETTING_802_1X_CLIENT_CERT_PASSWORD; - const char *cli_cert_pw_flags_key = phase2 ? "IEEE_8021X_INNER_CLIENT_CERT_PASSWORD_FLAGS" : "IEEE_8021X_CLIENT_CERT_PASSWORD_FLAGS"; - const char *cli_cert_pw_flags_prop = phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD_FLAGS : NM_SETTING_802_1X_CLIENT_CERT_PASSWORD_FLAGS; - const char *pk_key = phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY" : "IEEE_8021X_PRIVATE_KEY"; - const char *pk_pw_key = phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD": "IEEE_8021X_PRIVATE_KEY_PASSWORD"; - const char *pk_pw_flags_key = phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD_FLAGS" : "IEEE_8021X_PRIVATE_KEY_PASSWORD_FLAGS"; - const char *pk_pw_flags_prop = phase2 ? NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS : NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS; - NMSettingSecretFlags flags; - NMSetting8021xCKScheme scheme; + gs_unref_bytes GBytes *privkey = NULL; + gs_unref_bytes GBytes *client_cert = NULL; + gs_free char *identity_free = NULL; - value = svGetValueStr_cp (ifcfg, "IEEE_8021X_IDENTITY"); - if (value) { - g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, value, NULL); - g_free (value); - } + g_object_set (s_8021x, + NM_SETTING_802_1X_IDENTITY, + svGetValueStr (ifcfg, "IEEE_8021X_IDENTITY", &identity_free), + NULL); - ca_cert = svGetValueStr_cp (ifcfg, ca_cert_key); - if (ca_cert) { - gs_free char *real_cert_value = NULL; + if (!_cert_set_from_ifcfg (s_8021x, + ifcfg, + phase2 ? "IEEE_8021X_INNER_CA_CERT" : "IEEE_8021X_CA_CERT", + phase2 ? NM_SETTING_802_1X_PHASE2_CA_CERT : NM_SETTING_802_1X_CA_CERT, + NULL, + error)) + return FALSE; + _secret_set_from_ifcfg (s_8021x, + ifcfg, + keys_ifcfg, + phase2 ? "IEEE_8021X_INNER_CA_CERT_PASSWORD" : "IEEE_8021X_CA_CERT_PASSWORD", + phase2 ? NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD : NM_SETTING_802_1X_CA_CERT_PASSWORD); - real_cert_value = get_cert_value (svFileGetName (ifcfg), ca_cert, &scheme); - if (phase2) { - if (!nm_setting_802_1x_set_phase2_ca_cert (s_8021x, real_cert_value, scheme, NULL, error)) - return FALSE; - } else { - if (!nm_setting_802_1x_set_ca_cert (s_8021x, real_cert_value, scheme, NULL, error)) - return FALSE; - } - - if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) { - flags = _secret_read_ifcfg_flags (ifcfg, ca_cert_pw_flags_key); - g_object_set (s_8021x, ca_cert_pw_flags_prop, flags, NULL); - - if (flags == NM_SETTING_SECRET_FLAG_NONE) { - ca_cert_password = svGetValueStr_cp (ifcfg, ca_cert_pw_key); - g_object_set (s_8021x, ca_cert_pw_prop, ca_cert_password, NULL); - } - } - } else { - PARSE_WARNING ("missing %s for EAP method '%s'; this is insecure!", - ca_cert_key, eap_method); - } - - /* Read and set private key password flags */ - flags = _secret_read_ifcfg_flags (ifcfg, pk_pw_flags_key); - g_object_set (s_8021x, pk_pw_flags_prop, flags, NULL); - - /* Read the private key password if it's system-owned */ - if (flags == NM_SETTING_SECRET_FLAG_NONE) { - /* Private key password */ - privkey_password = svGetValueStr_cp (ifcfg, pk_pw_key); - if (!privkey_password && keys_ifcfg) { - /* Try the lookaside keys file */ - privkey_password = svGetValueStr_cp (keys_ifcfg, pk_pw_key); - } - } - - /* The private key itself */ - privkey = svGetValueStr_cp (ifcfg, pk_key); + if (!_cert_set_from_ifcfg (s_8021x, + ifcfg, + phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY" : "IEEE_8021X_PRIVATE_KEY", + phase2 ? NM_SETTING_802_1X_PHASE2_PRIVATE_KEY : NM_SETTING_802_1X_PRIVATE_KEY, + &privkey, + error)) + return FALSE; + _secret_set_from_ifcfg (s_8021x, + ifcfg, + keys_ifcfg, + phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD" : "IEEE_8021X_PRIVATE_KEY_PASSWORD", + phase2 ? NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD : NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD); if (!privkey) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Missing %s for EAP method '%s'.", - pk_key, + phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY" : "IEEE_8021X_PRIVATE_KEY", eap_method); return FALSE; } - { - gs_free char *real_cert_value = NULL; - - real_cert_value = get_cert_value (svFileGetName (ifcfg), privkey, &scheme); - if (phase2) { - if (!nm_setting_802_1x_set_phase2_private_key (s_8021x, - real_cert_value, - privkey_password, - scheme, - &privkey_format, - error)) - return FALSE; - } else { - if (!nm_setting_802_1x_set_private_key (s_8021x, - real_cert_value, - privkey_password, - scheme, - &privkey_format, - error)) - return FALSE; - } - } - - /* Only set the client certificate if the private key is not PKCS#12 format, - * as NM (due to supplicant restrictions) requires. If the key was PKCS#12, - * then nm_setting_802_1x_set_private_key() already set the client certificate - * to the same value as the private key. - */ - if (privkey_format != NM_SETTING_802_1X_CK_FORMAT_PKCS12) { - gs_free char *real_cert_value = NULL; - gs_free char *client_cert = NULL; - - client_cert = svGetValueStr_cp (ifcfg, cli_cert_key); - if (!client_cert) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing %s for EAP method '%s'.", - cli_cert_key, - eap_method); - return FALSE; - } - - real_cert_value = get_cert_value (svFileGetName (ifcfg), client_cert, &scheme); - if (phase2) { - if (!nm_setting_802_1x_set_phase2_client_cert (s_8021x, real_cert_value, scheme, NULL, error)) - return FALSE; - } else { - if (!nm_setting_802_1x_set_client_cert (s_8021x, real_cert_value, scheme, NULL, error)) - return FALSE; - } - - if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) { - flags = _secret_read_ifcfg_flags (ifcfg, cli_cert_pw_flags_key); - g_object_set (s_8021x, cli_cert_pw_flags_prop, flags, NULL); - - if (flags == NM_SETTING_SECRET_FLAG_NONE) { - client_cert_password = svGetValueStr_cp (ifcfg, cli_cert_pw_key); - g_object_set (s_8021x, cli_cert_pw_prop, client_cert_password, NULL); - } - } + if (!_cert_set_from_ifcfg (s_8021x, + ifcfg, + phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : "IEEE_8021X_CLIENT_CERT", + phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT : NM_SETTING_802_1X_CLIENT_CERT, + &client_cert, + error)) + return FALSE; + /* FIXME: writer does not actually write IEEE_8021X_CLIENT_CERT_PASSWORD and other + * certificate related passwords. It should, because otherwise persisting such profiles + * to ifcfg looses information. As this currently only matters for PKCS11 URIs, it seems + * a seldomly used feature so that it is not fixed yet. */ + _secret_set_from_ifcfg (s_8021x, + ifcfg, + keys_ifcfg, + phase2 ? "IEEE_8021X_INNER_CLIENT_CERT_PASSWORD" : "IEEE_8021X_CLIENT_CERT_PASSWORD", + phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD : NM_SETTING_802_1X_CLIENT_CERT_PASSWORD); + if (!client_cert) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Missing certificate for EAP method '%s'.", + eap_method); + return FALSE; } return TRUE; @@ -3178,19 +3172,19 @@ eap_peap_reader (const char *eap_method, const char *v; gs_free const char **list = NULL; const char *const *iter; - NMSetting8021xCKScheme scheme; - v = svGetValueStr (ifcfg, "IEEE_8021X_CA_CERT", &value); - if (v) { - gs_free char *real_cert_value = NULL; - - real_cert_value = get_cert_value (svFileGetName (ifcfg), v, &scheme); - if (!nm_setting_802_1x_set_ca_cert (s_8021x, real_cert_value, scheme, NULL, error)) - return FALSE; - } else { - PARSE_WARNING ("missing IEEE_8021X_CA_CERT for EAP method '%s'; this is insecure!", - eap_method); - } + if (!_cert_set_from_ifcfg (s_8021x, + ifcfg, + "IEEE_8021X_CA_CERT", + NM_SETTING_802_1X_CA_CERT, + NULL, + error)) + return FALSE; + _secret_set_from_ifcfg (s_8021x, + ifcfg, + keys_ifcfg, + "IEEE_8021X_CA_CERT_PASSWORD", + NM_SETTING_802_1X_CA_CERT_PASSWORD); nm_clear_g_free (&value); v = svGetValueStr (ifcfg, "IEEE_8021X_PEAP_VERSION", &value); @@ -3272,19 +3266,19 @@ eap_ttls_reader (const char *eap_method, const char *v; gs_free const char **list = NULL; const char *const *iter; - NMSetting8021xCKScheme scheme; - v = svGetValueStr (ifcfg, "IEEE_8021X_CA_CERT", &value); - if (v) { - gs_free char *real_cert_value = NULL; - - real_cert_value = get_cert_value (svFileGetName (ifcfg), v, &scheme); - if (!nm_setting_802_1x_set_ca_cert (s_8021x, real_cert_value, scheme, NULL, error)) - return FALSE; - } else { - PARSE_WARNING ("missing IEEE_8021X_CA_CERT for EAP method '%s'; this is insecure!", - eap_method); - } + if (!_cert_set_from_ifcfg (s_8021x, + ifcfg, + "IEEE_8021X_CA_CERT", + NM_SETTING_802_1X_CA_CERT, + NULL, + error)) + return FALSE; + _secret_set_from_ifcfg (s_8021x, + ifcfg, + keys_ifcfg, + "IEEE_8021X_CA_CERT_PASSWORD", + NM_SETTING_802_1X_CA_CERT_PASSWORD); nm_clear_g_free (&value); v = svGetValueStr (ifcfg, "IEEE_8021X_ANON_IDENTITY", &value); diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index ab31fbe099..7069386103 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -1901,10 +1901,8 @@ test_read_write_802_1X_subj_matches (void) gs_unref_object NMConnection *reread = NULL; NMSetting8021x *s_8021x; - NMTST_EXPECT_NM_WARN ("*missing IEEE_8021X_CA_CERT*peap*"); connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-802-1X-subj-matches", NULL, TYPE_ETHERNET, NULL); - g_test_assert_expected_messages (); /* ===== 802.1x SETTING ===== */ s_8021x = nm_connection_get_setting_802_1x (connection); @@ -1922,16 +1920,12 @@ test_read_write_802_1X_subj_matches (void) g_assert_cmpstr (nm_setting_802_1x_get_phase2_altsubject_match (s_8021x, 0), ==, "x.yourdomain.tld"); g_assert_cmpstr (nm_setting_802_1x_get_phase2_altsubject_match (s_8021x, 1), ==, "y.yourdomain.tld"); - NMTST_EXPECT_NM_WARN ("*missing IEEE_8021X_CA_CERT for EAP method 'peap'; this is insecure!"); _writer_new_connec_exp (connection, TEST_SCRATCH_DIR, TEST_IFCFG_DIR"/ifcfg-System_test-wired-802-1X-subj-matches.cexpected", &testfile); - g_test_assert_expected_messages (); - NMTST_EXPECT_NM_WARN ("*missing IEEE_8021X_CA_CERT for EAP method 'peap'; this is insecure!"); reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); - g_test_assert_expected_messages (); nmtst_assert_connection_equals (connection, TRUE, reread, FALSE);