diff options
author | Thomas Haller <thaller@redhat.com> | 2018-09-04 07:39:52 +0200 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2018-09-04 07:39:52 +0200 |
commit | 4b5680898f41947d26bccb2023010d7b05d1a240 (patch) | |
tree | 0cdd78512da4d61ef4ac9efe2b507ce85944b67a | |
parent | 5ef81dc0fbbe41b85abe7929563d41b03eaf6989 (diff) | |
parent | e3ac45c026104e05c6fda802131c92457720c119 (diff) | |
download | NetworkManager-4b5680898f41947d26bccb2023010d7b05d1a240.tar.gz |
crypto: merge branch 'th/crypto-secrets'
https://github.com/NetworkManager/NetworkManager/pull/191
42 files changed, 4274 insertions, 3911 deletions
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 && diff --git a/Makefile.am b/Makefile.am index 8176019594..c1615108a1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -501,12 +501,15 @@ 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 \ 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-crypto-impl.h \ libnm-core/nm-connection-private.h \ libnm-core/nm-core-internal.h \ libnm-core/nm-core-types-internal.h \ @@ -567,12 +570,14 @@ 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 \ 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 \ @@ -615,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 += \ @@ -668,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) \ @@ -678,19 +674,9 @@ libnm_core_libnm_core_la_LDFLAGS = \ $(CODE_COVERAGE_LDFLAGS) \ $(SANITIZER_LIB_LDFLAGS) -if WITH_GNUTLS -libnm_core_lib_c_real += libnm-core/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_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 @@ -713,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 \ @@ -759,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 = \ @@ -963,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) @@ -1588,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) \ @@ -4733,9 +4765,13 @@ 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 \ + 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/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 ea1c43f12f..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) @@ -668,21 +663,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 +1400,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/docs/libnm/Makefile.am b/docs/libnm/Makefile.am index c2bcff1be5..c5f2b836ba 100644 --- a/docs/libnm/Makefile.am +++ b/docs/libnm/Makefile.am @@ -32,7 +32,8 @@ 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-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 7baf09f823..77f7ed0f94 100644 --- a/docs/libnm/meson.build +++ b/docs/libnm/meson.build @@ -2,7 +2,8 @@ doc_module = libnm_name private_headers = [ 'common.h', - 'crypto.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/crypto.c b/libnm-core/crypto.c deleted file mode 100644 index 319f8055fe..0000000000 --- a/libnm-core/crypto.c +++ /dev/null @@ -1,826 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ - -/* - * Dan Williams <dcbw@redhat.com> - * - * 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 - 2011 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include <string.h> -#include <strings.h> -#include <unistd.h> -#include <stdlib.h> - -#include "crypto.h" -#include "nm-errors.h" - -#define PEM_RSA_KEY_BEGIN "-----BEGIN RSA PRIVATE KEY-----" -#define PEM_RSA_KEY_END "-----END RSA PRIVATE KEY-----" - -#define PEM_DSA_KEY_BEGIN "-----BEGIN DSA PRIVATE KEY-----" -#define PEM_DSA_KEY_END "-----END DSA PRIVATE KEY-----" - -#define PEM_CERT_BEGIN "-----BEGIN CERTIFICATE-----" -#define PEM_CERT_END "-----END CERTIFICATE-----" - -#define PEM_PKCS8_ENC_KEY_BEGIN "-----BEGIN ENCRYPTED PRIVATE KEY-----" -#define PEM_PKCS8_ENC_KEY_END "-----END ENCRYPTED PRIVATE KEY-----" - -#define PEM_PKCS8_DEC_KEY_BEGIN "-----BEGIN PRIVATE KEY-----" -#define PEM_PKCS8_DEC_KEY_END "-----END PRIVATE KEY-----" - -static gboolean -find_tag (const char *tag, - const guint8 *data, - gsize data_len, - gsize start_at, - gsize *out_pos) -{ - gsize i, taglen; - gsize len = data_len - start_at; - - g_return_val_if_fail (out_pos != NULL, FALSE); - - taglen = strlen (tag); - if (len >= taglen) { - for (i = 0; i < len - taglen + 1; i++) { - if (memcmp (data + start_at + i, tag, taglen) == 0) { - *out_pos = start_at + i; - return TRUE; - } - } - } - return FALSE; -} - -#define DEK_INFO_TAG "DEK-Info: " -#define PROC_TYPE_TAG "Proc-Type: " - -static GByteArray * -parse_old_openssl_key_file (const guint8 *data, - gsize data_len, - NMCryptoKeyType *out_key_type, - char **out_cipher, - char **out_iv, - GError **error) -{ - GByteArray *bindata = NULL; - char **lines = NULL; - char **ln = NULL; - gsize start = 0, end = 0; - GString *str = NULL; - int enc_tags = 0; - NMCryptoKeyType key_type; - char *iv = NULL; - char *cipher = NULL; - unsigned char *tmp = NULL; - gsize tmp_len = 0; - const char *start_tag; - const char *end_tag; - guint8 save_end = 0; - - *out_key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN; - *out_iv = NULL; - *out_cipher = NULL; - - if (find_tag (PEM_RSA_KEY_BEGIN, data, data_len, 0, &start)) { - key_type = NM_CRYPTO_KEY_TYPE_RSA; - start_tag = PEM_RSA_KEY_BEGIN; - end_tag = PEM_RSA_KEY_END; - } else if (find_tag (PEM_DSA_KEY_BEGIN, data, data_len, 0, &start)) { - key_type = NM_CRYPTO_KEY_TYPE_DSA; - start_tag = PEM_DSA_KEY_BEGIN; - end_tag = PEM_DSA_KEY_END; - } else - goto parse_error; - - start += strlen (start_tag); - if (!find_tag (end_tag, data, data_len, start, &end)) { - g_set_error (error, NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, - _("PEM key file had no end tag '%s'."), - end_tag); - goto parse_error; - } - - save_end = data[end]; - ((guint8 *)data)[end] = '\0'; - lines = g_strsplit ((const char *) (data + start), "\n", 0); - ((guint8 *)data)[end] = save_end; - - 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.")); - goto parse_error; - } - - str = g_string_new_len (NULL, end - start); - for (ln = lines; *ln; ln++) { - char *p = *ln; - - /* Chug leading spaces */ - p = g_strstrip (p); - if (!*p) - continue; - - if (!strncmp (p, PROC_TYPE_TAG, strlen (PROC_TYPE_TAG))) { - if (enc_tags++ != 0 || str->len != 0) { - g_set_error (error, NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, - _("Malformed PEM file: Proc-Type was not first tag.")); - goto parse_error; - } - - p += strlen (PROC_TYPE_TAG); - if (strcmp (p, "4,ENCRYPTED")) { - g_set_error (error, NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, - _("Malformed PEM file: unknown Proc-Type tag '%s'."), - p); - goto parse_error; - } - } 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 }; - char *comma; - guint i; - - if (enc_tags++ != 1 || str->len != 0) { - 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; - } - - p += strlen (DEK_INFO_TAG); - - /* Grab the IV first */ - comma = strchr (p, ','); - if (!comma || (*(comma + 1) == '\0')) { - 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; - } - *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; - } - 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]); - break; - } - } - if (i == G_N_ELEMENTS (known_ciphers)) { - g_set_error (error, NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, - _("Malformed PEM file: unknown private key cipher '%s'."), - p); - goto parse_error; - } - } 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; - } - g_string_append (str, p); - } - } - - tmp = g_base64_decode (str->str, &tmp_len); - if (tmp == NULL || !tmp_len) { - g_set_error (error, NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, - _("Could not decode private key.")); - goto parse_error; - } - 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_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 * -parse_pkcs8_key_file (const guint8 *data, - gsize data_len, - gboolean *out_encrypted, - GError **error) -{ - GByteArray *key = NULL; - gsize start = 0, end = 0; - unsigned char *der = NULL; - guint8 save_end; - gsize length = 0; - const char *start_tag = NULL, *end_tag = NULL; - gboolean encrypted = FALSE; - - /* Try encrypted first, decrypted next */ - if (find_tag (PEM_PKCS8_ENC_KEY_BEGIN, data, data_len, 0, &start)) { - start_tag = PEM_PKCS8_ENC_KEY_BEGIN; - end_tag = PEM_PKCS8_ENC_KEY_END; - encrypted = TRUE; - } else if (find_tag (PEM_PKCS8_DEC_KEY_BEGIN, data, data_len, 0, &start)) { - start_tag = PEM_PKCS8_DEC_KEY_BEGIN; - end_tag = PEM_PKCS8_DEC_KEY_END; - encrypted = FALSE; - } else { - g_set_error_literal (error, NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, - _("Failed to find expected PKCS#8 start tag.")); - return NULL; - } - - start += strlen (start_tag); - if (!find_tag (end_tag, data, data_len, start, &end)) { - g_set_error (error, NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, - _("Failed to find expected PKCS#8 end tag '%s'."), - end_tag); - return NULL; - } - - /* 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; - - 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 { - g_set_error_literal (error, NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, - _("Failed to decode PKCS#8 private key.")); - } - - g_free (der); - return key; -} - -static GByteArray * -file_to_g_byte_array (const char *filename, GError **error) -{ - char *contents; - 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); - } - return array; -} - -/* - * Convert a hex string into bytes. - */ -static char * -convert_iv (const char *src, - gsize *out_len, - GError **error) -{ - int num; - int i; - char conv[3]; - char *c; - - g_return_val_if_fail (src != NULL, NULL); - - num = strlen (src); - if (num % 2) { - g_set_error (error, NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, - _("IV must be an even number of bytes in length.")); - return NULL; - } - - num /= 2; - c = g_malloc0 (num + 1); - - 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])) { - g_set_error (error, NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, - _("IV contains non-hexadecimal digits.")); - goto error; - } - - c[i] = strtol(conv, NULL, 16); - } - *out_len = num; - return c; - -error: - g_free (c); - return NULL; -} - -char * -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; - - 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); - g_return_val_if_fail (out_len != NULL, NULL); - - 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; - } - - if (password[0] == '\0') - return NULL; - - key = g_malloc0 (digest_len + 1); - - crypto_md5_hash (salt, - 8, - password, - strlen (password), - key, - digest_len); - - *out_len = digest_len; - return key; -} - -static GByteArray * -decrypt_key (const char *cipher, - int key_type, - const guint8 *data, - gsize data_len, - const char *iv, - const char *password, - GError **error) -{ - char *bin_iv = NULL; - gsize bin_iv_len = 0; - char *key = NULL; - gsize key_len = 0; - char *output = NULL; - gsize decrypted_len = 0; - GByteArray *decrypted = NULL; - - g_return_val_if_fail (password != NULL, NULL); - - bin_iv = convert_iv (iv, &bin_iv_len, error); - if (!bin_iv) - 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; - - output = crypto_decrypt (cipher, key_type, - data, data_len, - bin_iv, bin_iv_len, - key, 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); - } - -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; -} - -GByteArray * -crypto_decrypt_openssl_private_key_data (const guint8 *data, - gsize data_len, - const char *password, - 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; - - 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); - - 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, - _("Unable to determine private key type.")); - return NULL; - } - - if (password) { - if (!cipher || !iv) { - 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); - } - } else if (!cipher && !iv) - decrypted = g_byte_array_ref (parsed); - - g_byte_array_unref (parsed); - g_free (cipher); - g_free (iv); - - return decrypted; -} - -GByteArray * -crypto_decrypt_openssl_private_key (const char *file, - const char *password, - NMCryptoKeyType *out_key_type, - GError **error) -{ - GByteArray *contents; - GByteArray *key = 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; -} - -static GByteArray * -extract_pem_cert_data (GByteArray *contents, GError **error) -{ - GByteArray *cert = NULL; - gsize start = 0, end = 0; - unsigned char *der = NULL; - guint8 save_end; - gsize length = 0; - - if (!find_tag (PEM_CERT_BEGIN, contents->data, 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); - goto done; - } - - start += strlen (PEM_CERT_BEGIN); - if (!find_tag (PEM_CERT_END, contents->data, 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); - goto done; - } - - /* 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; - - if (der && length) { - cert = g_byte_array_sized_new (length); - g_byte_array_append (cert, der, length); - g_assert (cert->len == length); - } else { - g_set_error (error, NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, - _("Failed to decode certificate.")); - } - -done: - g_free (der); - return cert; -} - -GByteArray * -crypto_load_and_verify_certificate (const char *file, - NMCryptoFileFormat *out_file_format, - GError **error) -{ - GByteArray *array, *contents; - - 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); - - if (!crypto_init (error)) - return NULL; - - contents = file_to_g_byte_array (file, error); - if (!contents) - return NULL; - - /* Check for PKCS#12 */ - if (crypto_is_pkcs12_data (contents->data, contents->len, NULL)) { - *out_file_format = NM_CRYPTO_FILE_FORMAT_PKCS12; - return 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 { - array = extract_pem_cert_data (contents, error); - if (!array) { - g_byte_array_free (contents, TRUE); - 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; - } - - return contents; -} - -gboolean -crypto_is_pkcs12_data (const guint8 *data, - gsize data_len, - GError **error) -{ - GError *local = NULL; - gboolean success; - - if (!data_len) - return FALSE; - - g_return_val_if_fail (data != NULL, FALSE); - - if (!crypto_init (error)) - return FALSE; - - success = 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) { - if (g_error_matches (local, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_DECRYPTION_FAILED)) { - success = TRUE; - g_error_free (local); - } else - g_propagate_error (error, local); - } - } - return success; -} - -gboolean -crypto_is_pkcs12_file (const char *file, GError **error) -{ - GByteArray *contents; - gboolean success = FALSE; - - g_return_val_if_fail (file != NULL, FALSE); - - if (!crypto_init (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; -} - -/* 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) -{ - GByteArray *tmp; - 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); - g_return_val_if_fail (out_is_encrypted == NULL || *out_is_encrypted == FALSE, NM_CRYPTO_FILE_FORMAT_UNKNOWN); - - if (!crypto_init (error)) - return NM_CRYPTO_FILE_FORMAT_UNKNOWN; - - /* Check for PKCS#12 first */ - if (crypto_is_pkcs12_data (data, data_len, NULL)) { - is_encrypted = TRUE; - if (!password || crypto_verify_pkcs12 (data, data_len, password, error)) - format = NM_CRYPTO_FILE_FORMAT_PKCS12; - } else { - /* 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; - - /* Or it's old-style OpenSSL */ - tmp = parse_old_openssl_key_file (data, data_len, &ktype, - &cipher, &iv, NULL); - 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); - } - } - - if (out_is_encrypted) - *out_is_encrypted = is_encrypted; - return format; -} - -NMCryptoFileFormat -crypto_verify_private_key (const char *filename, - const char *password, - gboolean *out_is_encrypted, - GError **error) -{ - GByteArray *contents; - NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; - - 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) { - format = crypto_verify_private_key_data (contents->data, contents->len, password, out_is_encrypted, error); - g_byte_array_free (contents, TRUE); - } - return format; -} - -void -crypto_md5_hash (const char *salt, - gssize salt_len, - const char *password, - gssize password_len, - char *buffer, - gsize buflen) -{ - GChecksum *ctx; - gsize digest_len; - char digest[16]; - gsize bufidx = 0; - int i; - - nm_assert (g_checksum_type_get_length (G_CHECKSUM_MD5) == sizeof (digest)); - - g_return_if_fail (password_len == 0 || password); - g_return_if_fail (buffer != NULL); - 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 (;;) { - 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)); - - for (i = 0; i < sizeof (digest); i++) { - if (bufidx >= buflen) - goto done; - buffer[bufidx++] = digest[i]; - } - - g_checksum_reset (ctx); - g_checksum_update (ctx, (const guchar *) digest, sizeof (digest)); - } - -done: - memset (digest, 0, sizeof (digest)); - g_checksum_free (ctx); -} diff --git a/libnm-core/crypto.h b/libnm-core/crypto.h deleted file mode 100644 index d20d6f3100..0000000000 --- a/libnm-core/crypto.h +++ /dev/null @@ -1,137 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ - -/* - * Dan Williams <dcbw@redhat.com> - * - * 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 - 2014 Red Hat, Inc. - */ - -#ifndef __CRYPTO_H__ -#define __CRYPTO_H__ - -#if !((NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_PRIVATE) -#error Cannot use this header. -#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" -#define CIPHER_AES_192_CBC "AES-192-CBC" -#define CIPHER_AES_256_CBC "AES-256-CBC" - -typedef enum { - NM_CRYPTO_KEY_TYPE_UNKNOWN = 0, - NM_CRYPTO_KEY_TYPE_RSA, - NM_CRYPTO_KEY_TYPE_DSA -} NMCryptoKeyType; - -typedef enum { - NM_CRYPTO_FILE_FORMAT_UNKNOWN = 0, - NM_CRYPTO_FILE_FORMAT_X509, - NM_CRYPTO_FILE_FORMAT_RAW_KEY, - NM_CRYPTO_FILE_FORMAT_PKCS12 -} NMCryptoFileFormat; - -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 *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, - 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, - GError **error); - -NMCryptoFileFormat crypto_verify_private_key (const char *file, - const char *password, - gboolean *out_is_encrypted, - GError **error); - -/* 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, - gsize buflen); - -char *crypto_make_des_aes_key (const char *cipher, - const char *salt, - const gsize salt_len, - const char *password, - 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, - const char *iv, - gsize iv_len, - const char *key, - gsize key_len, - gsize *out_len, - GError **error); - -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 /* __CRYPTO_H__ */ diff --git a/libnm-core/meson.build b/libnm-core/meson.build index ab570b6b47..eb6fcce94b 100644 --- a/libnm-core/meson.build +++ b/libnm-core/meson.build @@ -107,8 +107,7 @@ libnm_core_settings_sources = files( ) libnm_core_sources = libnm_core_settings_sources + files( - 'crypto.c', - 'crypto_' + crypto + '.c', + 'nm-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/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index 3dbbb7aa64..f0d4e40f93 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); @@ -287,12 +294,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, @@ -587,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/crypto_gnutls.c b/libnm-core/nm-crypto-gnutls.c index 49181ee722..6c897e6dd3 100644 --- a/libnm-core/crypto_gnutls.c +++ b/libnm-core/nm-crypto-gnutls.c @@ -23,26 +23,53 @@ #include "nm-default.h" +#include "nm-crypto-impl.h" + #include <gnutls/gnutls.h> #include <gnutls/crypto.h> #include <gnutls/x509.h> #include <gnutls/pkcs12.h> -#include "crypto.h" +#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; -static gboolean initialized = 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; +} + +/*****************************************************************************/ gboolean -crypto_init (GError **error) +_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.")); @@ -53,61 +80,47 @@ crypto_init (GError **error) return TRUE; } -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) +/*****************************************************************************/ + +guint8 * +_nmtst_crypto_decrypt (NMCryptoCipherType cipher, + const guint8 *data, + gsize data_len, + const guint8 *iv, + gsize iv_len, + const guint8 *key, + gsize key_len, + gsize *out_len, + GError **error) { 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, real_iv_len; - - if (!crypto_init (error)) - return NULL; + int cipher_mech; + nm_auto_clear_secret_ptr NMSecretPtr output = { 0 }; + guint8 pad_i, pad_len; + guint8 real_iv_len; - 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 { + if (!_get_cipher_info (cipher, &cipher_mech, &real_iv_len)) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_UNKNOWN_CIPHER, - _("Private key cipher '%s' was unknown."), - cipher); + _("Unsupported key cipher for decryption")); 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, - _("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; } - 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; @@ -120,107 +133,81 @@ crypto_decrypt (const char *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) { - /* Don't expose key material */ - memset (output, 0, data_len); - g_free (output); - output = NULL; - } - } - gnutls_cipher_deinit (ctx); - return output; + *out_len = output.len - pad_len; + return g_steal_pointer (&output.bin); } -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) +guint8 * +_nmtst_crypto_encrypt (NMCryptoCipherType cipher, + const guint8 *data, + gsize data_len, + const guint8 *iv, + gsize iv_len, + const guint8 *key, + gsize key_len, + gsize *out_len, + GError **error) { gnutls_cipher_hd_t ctx; 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; - if (!crypto_init (error)) - return NULL; + nm_assert (iv_len); - 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 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; key_dt.data = (unsigned char *) key; key_dt.size = key_len; @@ -233,51 +220,50 @@ crypto_encrypt (const char *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; - } - - *out_len = output_len; - success = TRUE; - -out: - if (padded_buf) { - memset (padded_buf, 0, padded_buf_len); - g_free (padded_buf); - padded_buf = NULL; + return NULL; } - if (!success) { - if (output) { - /* Don't expose key material */ - memset (output, 0, output_len); - g_free (output); - output = NULL; - } - } - gnutls_cipher_deinit (ctx); - return output; + *out_len = output.len; + return g_steal_pointer (&output.bin); } -NMCryptoFileFormat -crypto_verify_cert (const unsigned char *data, - gsize len, - GError **error) +gboolean +_nm_crypto_verify_x509 (const guint8 *data, + gsize len, + GError **error) { gnutls_x509_crt_t der; gnutls_datum_t dt; int err; - if (!crypto_init (error)) - return NM_CRYPTO_FILE_FORMAT_UNKNOWN; + if (!_nm_crypto_init (error)) + return FALSE; err = gnutls_x509_crt_init (&der); if (err < 0) { @@ -285,7 +271,7 @@ 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 */ @@ -294,36 +280,35 @@ 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 -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; - gboolean success = FALSE; int err; g_return_val_if_fail (data != NULL, FALSE); - if (!crypto_init (error)) + if (!_nm_crypto_init (error)) return FALSE; dt.data = (unsigned char *) data; @@ -348,31 +333,32 @@ 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 -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; @@ -380,12 +366,9 @@ 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; - dt.size = data_len; - err = gnutls_x509_privkey_init (&p8); if (err < 0) { g_set_error (error, NM_CRYPTO_ERROR, @@ -395,11 +378,15 @@ 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) { @@ -423,9 +410,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 new file mode 100644 index 0000000000..918651525b --- /dev/null +++ b/libnm-core/nm-crypto-impl.h @@ -0,0 +1,74 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * Dan Williams <dcbw@redhat.com> + * + * 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 _nm_crypto_init (GError **error); + +gboolean _nm_crypto_randomize (void *buffer, gsize buffer_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, + const char *password, + GError **error); + +gboolean _nm_crypto_verify_pkcs8 (const guint8 *data, + gsize data_len, + gboolean is_encrypted, + const char *password, + GError **error); + +/*****************************************************************************/ + +guint8 *_nmtst_crypto_encrypt (NMCryptoCipherType cipher, + const guint8 *data, + gsize data_len, + const guint8 *iv, + gsize iv_len, + const guint8 *key, + gsize key_len, + gsize *out_len, + GError **error); + +guint8 *_nmtst_crypto_decrypt (NMCryptoCipherType cipher, + 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/crypto_nss.c b/libnm-core/nm-crypto-nss.c index 3b457fbb30..711dde4baf 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-impl.h" + #include <prinit.h> #include <nss.h> #include <pk11pub.h> @@ -33,20 +35,46 @@ #include <ciferfam.h> #include <p12plcy.h> -#include "crypto.h" +#include "nm-utils/nm-secret-utils.h" #include "nm-errors.h" -static gboolean initialized = FALSE; +/*****************************************************************************/ + +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; +} + +/*****************************************************************************/ gboolean -crypto_init (GError **error) +_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, @@ -57,74 +85,61 @@ 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; } -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) +guint8 * +_nmtst_crypto_decrypt (NMCryptoCipherType 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 *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; - guint32 i, real_iv_len = 0; - - if (!crypto_init (error)) - return NULL; + int decrypted_len = 0; + unsigned extra = 0; + unsigned pad_len = 0; + guint32 i; + guint8 real_iv_len; - 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 { + if (!_get_cipher_info (cipher, &cipher_mech, &real_iv_len)) { g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_UNKNOWN_CIPHER, - _("Private key cipher '%s' was unknown."), - cipher); + _("Unsupported key cipher for decryption")); 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; } - output = g_malloc0 (data_len); + if (!_nm_crypto_init (error)) + return NULL; slot = PK11_GetBestSlot (cipher_mech, NULL); if (!slot) { @@ -162,10 +177,13 @@ crypto_decrypt (const char *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) { @@ -184,7 +202,7 @@ crypto_decrypt (const char *cipher, } s = PK11_DigestFinal (ctx, - (unsigned char *) (output + decrypted_len), + (unsigned char *) &output.bin[decrypted_len], &extra, data_len - decrypted_len); if (s != SECSuccess) { @@ -194,6 +212,7 @@ crypto_decrypt (const char *cipher, PORT_GetError ()); goto out; } + decrypted_len += extra; pad_len = data_len - decrypted_len; @@ -209,7 +228,7 @@ crypto_decrypt (const char *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.")); @@ -217,7 +236,6 @@ crypto_decrypt (const char *cipher, } } - *out_len = decrypted_len; success = TRUE; out: @@ -230,27 +248,25 @@ out: if (slot) PK11_FreeSlot (slot); - if (!success) { - if (output) { - /* Don't expose key material */ - memset (output, 0, data_len); - g_free (output); - output = NULL; - } - } - return 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); } -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) +guint8 * +_nmtst_crypto_encrypt (NMCryptoCipherType cipher, + const guint8 *data, + gsize data_len, + const guint8 *iv, + gsize iv_len, + const guint8 *key, + gsize key_len, + gsize *out_len, + GError **error) { SECStatus ret; CK_MECHANISM_TYPE cipher_mech = CKM_DES3_CBC_PAD; @@ -260,49 +276,29 @@ crypto_encrypt (const char *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 (!crypto_init (error)) - return NULL; + gsize pad_len; - 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; } - /* 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); @@ -329,7 +325,22 @@ crypto_encrypt (const char *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, @@ -338,46 +349,41 @@ crypto_encrypt (const char *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); - memset (padded_buf, 0, padded_buf_len); - g_free (padded_buf); + if (!success) + return NULL; - if (!success) { - memset (output, 0, output_len); - g_free (output); - output = NULL; - } - return (char *) output; + *out_len = output.len; + return g_steal_pointer (&output.bin); } -NMCryptoFileFormat -crypto_verify_cert (const unsigned char *data, - gsize len, - GError **error) +gboolean +_nm_crypto_verify_x509 (const guint8 *data, + gsize len, + GError **error) { CERTCertificate *cert; - if (!crypto_init (error)) - return NM_CRYPTO_FILE_FORMAT_UNKNOWN; + if (!_nm_crypto_init (error)) + return FALSE; /* Try DER/PEM first */ cert = CERT_DecodeCertFromPackage ((char *) data, len); @@ -385,76 +391,85 @@ crypto_verify_cert (const unsigned char *data, g_set_error (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_INVALID_DATA, _("Couldn't decode certificate: %d"), - PORT_GetError()); - return NM_CRYPTO_FILE_FORMAT_UNKNOWN; + PORT_GetError ()); + return FALSE; } CERT_DestroyCertificate (cert); - return NM_CRYPTO_FILE_FORMAT_X509; + return TRUE; } 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 }; PK11SlotInfo *slot = NULL; SECStatus s; - gunichar2 *ucs2_password; - long ucs2_chars = 0; -#ifndef WORDS_BIGENDIAN - guint16 *p; -#endif /* WORDS_BIGENDIAN */ + gboolean success = FALSE; - if (error) - g_return_val_if_fail (*error == NULL, FALSE); + g_return_val_if_fail (!error || !*error, 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 * 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 */ - - 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 */ - } else { - /* NULL password */ - pw.data = NULL; - pw.len = 0; + + 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, *p_end; + + 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 + } + + slot = PK11_GetInternalKeySlot (); + if (!slot) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERROR_FAILED, + _("Couldn't initialize slot")); + goto out; } - slot = PK11_GetInternalKeySlot(); 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); @@ -462,8 +477,8 @@ 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); @@ -471,35 +486,34 @@ 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); + + if (pw.data) + SECITEM_ZfreeItem (&pw, PR_FALSE); - SECITEM_ZfreeItem (&pw, PR_FALSE); - return FALSE; + return success; } 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 @@ -510,11 +524,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 new file mode 100644 index 0000000000..7a6e8d18ce --- /dev/null +++ b/libnm-core/nm-crypto.c @@ -0,0 +1,1035 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * Dan Williams <dcbw@redhat.com> + * + * 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. + */ + +#include "nm-default.h" + +#include "nm-crypto.h" + +#include <string.h> +#include <strings.h> +#include <unistd.h> +#include <stdlib.h> + +#include "nm-utils/nm-secret-utils.h" +#include "nm-utils/nm-io-utils.h" + +#include "nm-crypto-impl.h" +#include "nm-utils.h" +#include "nm-errors.h" + +#define PEM_RSA_KEY_BEGIN "-----BEGIN RSA PRIVATE KEY-----" +#define PEM_RSA_KEY_END "-----END RSA PRIVATE KEY-----" + +#define PEM_DSA_KEY_BEGIN "-----BEGIN DSA PRIVATE KEY-----" +#define PEM_DSA_KEY_END "-----END DSA PRIVATE KEY-----" + +#define PEM_CERT_BEGIN "-----BEGIN CERTIFICATE-----" +#define PEM_CERT_END "-----END CERTIFICATE-----" + +#define PEM_PKCS8_ENC_KEY_BEGIN "-----BEGIN ENCRYPTED PRIVATE KEY-----" +#define PEM_PKCS8_ENC_KEY_END "-----END ENCRYPTED PRIVATE KEY-----" + +#define PEM_PKCS8_DEC_KEY_BEGIN "-----BEGIN PRIVATE KEY-----" +#define PEM_PKCS8_DEC_KEY_END "-----END PRIVATE KEY-----" + +/*****************************************************************************/ + +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, + gsize data_len, + gsize start_at, + gsize *out_pos) +{ + gsize i, taglen; + gsize len = data_len - start_at; + + g_return_val_if_fail (out_pos != NULL, FALSE); + + taglen = strlen (tag); + if (len >= taglen) { + for (i = 0; i < len - taglen + 1; i++) { + if (memcmp (data + start_at + i, tag, taglen) == 0) { + *out_pos = start_at + i; + return TRUE; + } + } + } + return FALSE; +} + +#define DEK_INFO_TAG "DEK-Info: " +#define PROC_TYPE_TAG "Proc-Type: " + +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, + NMCryptoCipherType *out_cipher, + char **out_iv, + GError **error) +{ + gsize start = 0, end = 0; + nm_auto_free_secret char *str = NULL; + char *str_p; + gsize str_len; + int enc_tags = 0; + NMCryptoKeyType key_type; + nm_auto_clear_secret_ptr NMSecretPtr parsed = { 0 }; + nm_auto_clear_secret_ptr NMSecretPtr data_content = { 0 }; + nm_auto_free_secret char *iv = NULL; + NMCryptoCipherType cipher = NM_CRYPTO_CIPHER_UNKNOWN; + const char *start_tag; + const char *end_tag; + const guint8 *data_start, *data_end; + + 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); + 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; + start_tag = PEM_RSA_KEY_BEGIN; + end_tag = PEM_RSA_KEY_END; + } else if (find_tag (PEM_DSA_KEY_BEGIN, data, data_len, 0, &start)) { + key_type = NM_CRYPTO_KEY_TYPE_DSA; + start_tag = PEM_DSA_KEY_BEGIN; + end_tag = PEM_DSA_KEY_END; + } 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)) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERROR_INVALID_DATA, + _("PEM key file had no end tag '%s'."), + end_tag); + return FALSE; + } + + str_len = end - start + 1; + str = g_new (char, str_len); + str[0] = '\0'; + str_p = str; + + data_start = &data[start]; + data_end = &data[end]; + + while (data_start < data_end) { + nm_auto_free_secret char *line = NULL; + char *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_p != str) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERROR_INVALID_DATA, + _("Malformed PEM file: Proc-Type was not first tag.")); + return FALSE; + } + + p += strlen (PROC_TYPE_TAG); + if (strcmp (p, "4,ENCRYPTED")) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERROR_INVALID_DATA, + _("Malformed PEM file: unknown Proc-Type tag '%s'."), + p); + return FALSE; + } + } else if (!strncmp (p, DEK_INFO_TAG, strlen (DEK_INFO_TAG))) { + const NMCryptoCipherInfo *cipher_info; + char *comma; + gsize p_len; + + 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 FALSE; + } + + p += strlen (DEK_INFO_TAG); + + /* Grab the IV first */ + comma = strchr (p, ','); + if (!comma || (*(comma + 1) == '\0')) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERROR_INVALID_DATA, + _("Malformed PEM file: no IV found in DEK-Info tag.")); + return FALSE; + } + 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 FALSE; + } + nm_free_secret (iv); + iv = g_strdup (comma); + + /* Get the private key 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, + NM_CRYPTO_ERROR_INVALID_DATA, + "Malformed PEM file: both Proc-Type and DEK-Info tags are required."); + return FALSE; + } + nm_utils_strbuf_append_str (&str_p, &str_len, p); + nm_assert (str_len > 0); + } + } + + 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.")); + nm_secret_ptr_clear (&parsed); + return FALSE; + } + + NM_SET_OUT (out_key_type, key_type); + NM_SET_OUT (out_iv, g_steal_pointer (&iv)); + NM_SET_OUT (out_cipher, cipher); + nm_secret_ptr_move (out_parsed, &parsed); + return TRUE; +} + +static gboolean +parse_pkcs8_key_file (const guint8 *data, + gsize data_len, + NMSecretPtr *parsed, + gboolean *out_encrypted, + GError **error) +{ + gsize start = 0, end = 0; + gs_free guchar *der = NULL; + 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)) { + start_tag = PEM_PKCS8_ENC_KEY_BEGIN; + end_tag = PEM_PKCS8_ENC_KEY_END; + encrypted = TRUE; + } else if (find_tag (PEM_PKCS8_DEC_KEY_BEGIN, data, data_len, 0, &start)) { + start_tag = PEM_PKCS8_DEC_KEY_BEGIN; + end_tag = PEM_PKCS8_DEC_KEY_END; + encrypted = FALSE; + } else { + g_set_error_literal (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERROR_INVALID_DATA, + _("Failed to find expected PKCS#8 start tag.")); + return FALSE; + } + + start += strlen (start_tag); + if (!find_tag (end_tag, data, data_len, start, &end)) { + g_set_error (error, NM_CRYPTO_ERROR, + NM_CRYPTO_ERROR_INVALID_DATA, + _("Failed to find expected PKCS#8 end tag '%s'."), + end_tag); + return FALSE; + } + + /* g_base64_decode() wants a NULL-terminated string */ + der_base64 = g_strndup ((char *) &data[start], end - start); + + 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.")); + nm_secret_ptr_clear (parsed); + return FALSE; + } + + *out_encrypted = encrypted; + return TRUE; +} + +static gboolean +file_read_contents (const char *filename, + NMSecretPtr *out_contents, + GError **error) +{ + nm_assert (out_contents); + nm_assert (out_contents->len == 0); + nm_assert (!out_contents->str); + + 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; +} + +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. + */ +static guint8 * +_nmtst_convert_iv (const char *src, + gsize *out_len, + GError **error) +{ + gsize i, num; + gs_free guint8 *c = NULL; + int c0, c1; + + nm_assert (src); + + num = strlen (src); + 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.")); + return NULL; + } + + num /= 2; + c = g_malloc (num + 1); + + /* defensively add trailing NUL. This function returns binary data, + * do not assume it's NUL terminated. */ + c[num] = '\0'; + + for (i = 0; i < num; i++) { + 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] = (c0 << 4) + c1; + } + *out_len = num; + return g_steal_pointer (&c); +} + +guint8 * +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; + + 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); + g_return_val_if_fail (out_len != NULL, NULL); + + *out_len = 0; + + 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 (cipher_info->digest_len); + + nm_crypto_md5_hash (salt, + 8, + (guint8 *) password, + strlen (password), + key, + cipher_info->digest_len); + + *out_len = cipher_info->digest_len; + return key; +} + +static gboolean +_nmtst_decrypt_key (NMCryptoCipherType cipher, + 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; + + nm_assert (password); + nm_assert (cipher != NM_CRYPTO_CIPHER_UNKNOWN); + nm_assert (iv); + nm_assert (parsed); + nm_assert (!parsed->bin); + nm_assert (parsed->len == 0); + + bin_iv.bin = _nmtst_convert_iv (iv, &bin_iv.len, error); + if (!bin_iv.bin) + 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 FALSE; + } + + /* Convert the password and IV into a DES or AES key */ + 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; + + parsed->bin = _nmtst_crypto_decrypt (cipher, + data, + data_len, + bin_iv.bin, + bin_iv.len, + key.bin, + key.len, + &parsed->len, + error); + if (!parsed->bin || parsed->len == 0) { + nm_secret_ptr_clear (parsed); + return FALSE; + } + + return TRUE; +} + +GBytes * +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_clear_secret_ptr NMSecretPtr parsed = { 0 }; + nm_auto_free_secret char *iv = NULL; + NMCryptoCipherType cipher = NM_CRYPTO_CIPHER_UNKNOWN; + + g_return_val_if_fail (data != NULL, NULL); + + NM_SET_OUT (out_key_type, NM_CRYPTO_KEY_TYPE_UNKNOWN); + + if (!_nm_crypto_init (error)) + return NULL; + + 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.")); + return NULL; + } + + NM_SET_OUT (out_key_type, key_type); + + if (password) { + nm_auto_clear_secret_ptr NMSecretPtr parsed2 = { 0 }; + + 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.")); + return NULL; + } + + if (!_nmtst_decrypt_key (cipher, + parsed.bin, + parsed.len, + iv, + password, + &parsed2, + error)) + return NULL; + + return nm_secret_copy_to_gbytes (parsed2.bin, parsed2.len); + } + + if (cipher != NM_CRYPTO_CIPHER_UNKNOWN || iv) + return NULL; + + return nm_secret_copy_to_gbytes (parsed.bin, parsed.len); +} + +GBytes * +nmtst_crypto_decrypt_openssl_private_key (const char *file, + const char *password, + NMCryptoKeyType *out_key_type, + GError **error) +{ + nm_auto_clear_secret_ptr NMSecretPtr contents = { 0 }; + + if (!_nm_crypto_init (error)) + return NULL; + + if (!file_read_contents (file, &contents, error)) + return NULL; + + return nmtst_crypto_decrypt_openssl_private_key_data (contents.bin, + contents.len, + password, + out_key_type, + error); +} + +static gboolean +extract_pem_cert_data (const guint8 *contents, + gsize contents_len, + NMSecretPtr *out_cert, + GError **error) +{ + gsize start = 0; + gsize end = 0; + nm_auto_free_secret char *der_base64 = NULL; + + 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 FALSE; + } + + start += strlen (PEM_CERT_BEGIN); + 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 FALSE; + } + + /* g_base64_decode() wants a NULL-terminated string */ + der_base64 = g_strndup ((const char *) &contents[start], end - start); + + 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.")); + nm_secret_ptr_clear (out_cert); + return FALSE; + } + + return TRUE; +} + +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, FALSE); + nm_assert (!error || !*error); + + if (!_nm_crypto_init (error)) + goto out; + + if (!file_read_contents (file, &contents, error)) + 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)) { + 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)) { + 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)) { + 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; + } + } + } + + g_set_error (error, + NM_CRYPTO_ERROR, + NM_CRYPTO_ERROR_INVALID_DATA, + _("Failed to recognize certificate")); + +out: + NM_SET_OUT (out_file_format, NM_CRYPTO_FILE_FORMAT_UNKNOWN); + NM_SET_OUT (out_certificate, NULL); + return FALSE; +} + +gboolean +nm_crypto_is_pkcs12_data (const guint8 *data, + gsize data_len, + GError **error) +{ + GError *local = NULL; + gboolean success; + + 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); + + if (!_nm_crypto_init (error)) + return FALSE; + + 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) { + if (g_error_matches (local, NM_CRYPTO_ERROR, NM_CRYPTO_ERROR_DECRYPTION_FAILED)) { + success = TRUE; + g_error_free (local); + } else + g_propagate_error (error, local); + } + } + return success; +} + +gboolean +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 (!_nm_crypto_init (error)) + return FALSE; + + if (!file_read_contents (file, &contents, error)) + return FALSE; + + 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 +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; + + 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 (!_nm_crypto_init (error)) + return NM_CRYPTO_FILE_FORMAT_UNKNOWN; + + /* Check for PKCS#12 first */ + if (nm_crypto_is_pkcs12_data (data, data_len, NULL)) { + is_encrypted = TRUE; + 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 }; + + /* Maybe it's PKCS#8 */ + if (parse_pkcs8_key_file (data, data_len, &parsed, &is_encrypted, NULL)) { + if ( !password + || _nm_crypto_verify_pkcs8 (parsed.bin, parsed.len, is_encrypted, password, error)) + format = NM_CRYPTO_FILE_FORMAT_RAW_KEY; + } else { + 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 != NM_CRYPTO_CIPHER_UNKNOWN && iv); + } + } + } + + 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; +} + +NMCryptoFileFormat +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 (!_nm_crypto_init (error)) + return NM_CRYPTO_FILE_FORMAT_UNKNOWN; + + if (!file_read_contents (filename, &contents, error)) + return NM_CRYPTO_FILE_FORMAT_UNKNOWN; + + return nm_crypto_verify_private_key_data (contents.bin, contents.len, password, out_is_encrypted, error); +} + +void +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 + 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) == MD5_DIGEST_LEN); + + g_return_if_fail (password_len == 0 || password); + 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); + + 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 = MD5_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.bin[i]; + } + + g_checksum_reset (ctx); + g_checksum_update (ctx, digest.ptr, MD5_DIGEST_LEN); + } +} + +gboolean +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. + **/ +GBytes * +nmtst_crypto_rsa_key_encrypt (const guint8 *data, + gsize len, + const char *in_password, + char **out_password, + GError **error) +{ + 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; + nm_auto_free_secret char *tmp_password = NULL; + nm_auto_free_secret char *enc_base64 = NULL; + gsize enc_base64_len; + const char *p; + gsize ret_len, ret_idx; + guint i; + NMSecretBuf *ret; + + g_return_val_if_fail (data, NULL); + g_return_val_if_fail (len > 0, NULL); + g_return_val_if_fail (!out_password || !*out_password, NULL); + + /* Make the password if needed */ + if (!in_password) { + 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; + tmp_password = nm_utils_bin2hexstr (pw_buf.bin, pw_buf.len, -1); + in_password = tmp_password; + } + + if (!nm_crypto_randomize (salt, sizeof (salt), error)) + return 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); + + 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; + + /* 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 */ + 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")); + + /* Convert the encrypted key to a base64 string */ + 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)); + g_ptr_array_add (pem, g_strdup ("\n")); + } + + 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 new file mode 100644 index 0000000000..54fbbc5f57 --- /dev/null +++ b/libnm-core/nm-crypto.h @@ -0,0 +1,125 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * Dan Williams <dcbw@redhat.com> + * + * 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 - 2014 Red Hat, Inc. + */ + +#ifndef __NM_CRYPTO_H__ +#define __NM_CRYPTO_H__ + +#if !((NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_PRIVATE) +#error Cannot use this header. +#endif + +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; + +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, + NM_CRYPTO_KEY_TYPE_RSA, + NM_CRYPTO_KEY_TYPE_DSA +} NMCryptoKeyType; + +typedef enum { + NM_CRYPTO_FILE_FORMAT_UNKNOWN = 0, + NM_CRYPTO_FILE_FORMAT_X509, + NM_CRYPTO_FILE_FORMAT_RAW_KEY, + NM_CRYPTO_FILE_FORMAT_PKCS12 +} NMCryptoFileFormat; + +/*****************************************************************************/ + +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, + GError **error); + +gboolean nm_crypto_is_pkcs12_file (const char *file, GError **error); + +gboolean nm_crypto_is_pkcs12_data (const guint8 *data, gsize 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); + +NMCryptoFileFormat nm_crypto_verify_private_key (const char *file, + const char *password, + gboolean *out_is_encrypted, + GError **error); + +void nm_crypto_md5_hash (const guint8 *salt, + gsize salt_len, + const guint8 *password, + gsize password_len, + guint8 *buffer, + gsize buflen); + +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); + +GBytes *nmtst_crypto_rsa_key_encrypt (const guint8 *data, + gsize len, + const char *in_password, + 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__ */ diff --git a/libnm-core/nm-keyfile.c b/libnm-core/nm-keyfile.c index 3f9d60bbb7..c7d878691b 100644 --- a/libnm-core/nm-keyfile.c +++ b/libnm-core/nm-keyfile.c @@ -33,6 +33,7 @@ #include <string.h> #include <linux/pkt_sched.h> +#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; @@ -969,7 +971,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++) { @@ -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. - * - * But we add a '\0' to the bin_data pointer, just to avoid somebody - * (erronously!) reading the binary data as C-string. + * 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. * - * @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; @@ -1147,8 +1160,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 +1205,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) \ diff --git a/libnm-core/nm-setting-8021x.c b/libnm-core/nm-setting-8021x.c index 0d61f91c5d..529029de83 100644 --- a/libnm-core/nm-setting-8021x.c +++ b/libnm-core/nm-setting-8021x.c @@ -26,8 +26,9 @@ #include <string.h> +#include "nm-utils/nm-secret-utils.h" #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" @@ -60,14 +61,89 @@ * ISBN: 978-1587051548 **/ -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)) +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_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 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[]; + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE (NMSetting8021x, + PROP_EAP, + PROP_IDENTITY, + PROP_ANONYMOUS_IDENTITY, + PROP_PAC_FILE, + PROP_CA_CERT, + PROP_CA_CERT_PASSWORD, + PROP_CA_CERT_PASSWORD_FLAGS, + PROP_CA_PATH, + PROP_SUBJECT_MATCH, + PROP_ALTSUBJECT_MATCHES, + PROP_DOMAIN_SUFFIX_MATCH, + PROP_CLIENT_CERT, + PROP_CLIENT_CERT_PASSWORD, + PROP_CLIENT_CERT_PASSWORD_FLAGS, + PROP_PHASE1_PEAPVER, + PROP_PHASE1_PEAPLABEL, + PROP_PHASE1_FAST_PROVISIONING, + PROP_PHASE1_AUTH_FLAGS, + PROP_PHASE2_AUTH, + PROP_PHASE2_AUTHEAP, + PROP_PHASE2_CA_CERT, + PROP_PHASE2_CA_CERT_PASSWORD, + PROP_PHASE2_CA_CERT_PASSWORD_FLAGS, + PROP_PHASE2_CA_PATH, + PROP_PHASE2_SUBJECT_MATCH, + PROP_PHASE2_ALTSUBJECT_MATCHES, + PROP_PHASE2_DOMAIN_SUFFIX_MATCH, + PROP_PHASE2_CLIENT_CERT, + PROP_PHASE2_CLIENT_CERT_PASSWORD, + PROP_PHASE2_CLIENT_CERT_PASSWORD_FLAGS, + PROP_PASSWORD, + PROP_PASSWORD_FLAGS, + PROP_PASSWORD_RAW, + PROP_PASSWORD_RAW_FLAGS, + PROP_PRIVATE_KEY, + PROP_PRIVATE_KEY_PASSWORD, + PROP_PRIVATE_KEY_PASSWORD_FLAGS, + PROP_PHASE2_PRIVATE_KEY, + PROP_PHASE2_PRIVATE_KEY_PASSWORD, + PROP_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS, + PROP_PIN, + PROP_PIN_FLAGS, + PROP_SYSTEM_CA_CERTS, + PROP_AUTH_TIMEOUT, +); typedef struct { GSList *eap; /* GSList of strings */ @@ -116,67 +192,509 @@ typedef struct { int auth_timeout; } NMSetting8021xPrivate; -enum { - PROP_0, - PROP_EAP, - PROP_IDENTITY, - PROP_ANONYMOUS_IDENTITY, - PROP_PAC_FILE, - PROP_CA_CERT, - PROP_CA_CERT_PASSWORD, - PROP_CA_CERT_PASSWORD_FLAGS, - PROP_CA_PATH, - PROP_SUBJECT_MATCH, - PROP_ALTSUBJECT_MATCHES, - PROP_DOMAIN_SUFFIX_MATCH, - PROP_CLIENT_CERT, - PROP_CLIENT_CERT_PASSWORD, - PROP_CLIENT_CERT_PASSWORD_FLAGS, - PROP_PHASE1_PEAPVER, - PROP_PHASE1_PEAPLABEL, - PROP_PHASE1_FAST_PROVISIONING, - PROP_PHASE1_AUTH_FLAGS, - PROP_PHASE2_AUTH, - PROP_PHASE2_AUTHEAP, - PROP_PHASE2_CA_CERT, - PROP_PHASE2_CA_CERT_PASSWORD, - PROP_PHASE2_CA_CERT_PASSWORD_FLAGS, - PROP_PHASE2_CA_PATH, - PROP_PHASE2_SUBJECT_MATCH, - PROP_PHASE2_ALTSUBJECT_MATCHES, - PROP_PHASE2_DOMAIN_SUFFIX_MATCH, - PROP_PHASE2_CLIENT_CERT, - PROP_PHASE2_CLIENT_CERT_PASSWORD, - PROP_PHASE2_CLIENT_CERT_PASSWORD_FLAGS, - PROP_PASSWORD, - PROP_PASSWORD_FLAGS, - PROP_PASSWORD_RAW, - PROP_PASSWORD_RAW_FLAGS, - PROP_PRIVATE_KEY, - PROP_PRIVATE_KEY_PASSWORD, - PROP_PRIVATE_KEY_PASSWORD_FLAGS, - PROP_PHASE2_PRIVATE_KEY, - PROP_PHASE2_PRIVATE_KEY_PASSWORD, - PROP_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS, - PROP_PIN, - PROP_PIN_FLAGS, - PROP_SYSTEM_CA_CERTS, - PROP_AUTH_TIMEOUT, +G_DEFINE_TYPE (NMSetting8021x, nm_setting_802_1x, NM_TYPE_SETTING) - LAST_PROP -}; +#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; +} + +NMSetting8021xCKScheme +_nm_setting_802_1x_cert_get_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 gboolean +_cert_verify_scheme (NMSetting8021xCKScheme scheme, + GBytes *bytes, + GError **error) +{ + GError *local = NULL; + NMSetting8021xCKScheme scheme_detected; + + nm_assert (bytes); + + 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, + 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; +} + +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; + 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); +} + +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; \ + \ + 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; \ + } \ + } 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 _nm_setting_802_1x_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; \ + \ + 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); \ + \ + return _cert_get_path (_cert); \ + } 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 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; + + 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); + } + + if (!value) { + /* coerce password to %NULL. It should be already. */ + password = NULL; + } + + priv = NM_SETTING_802_1X_GET_PRIVATE (setting); + + if (!value) { + /* pass. */ + } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) { + cert = _nm_setting_802_1x_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 = _nm_setting_802_1x_cert_value_to_bytes (scheme, (guint8 *) value, -1, error); + if (!cert) + goto err; + } + } + + 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; + } + + /* 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; + + 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; + + if ( p_password + && !nm_streq0 (password, *p_password)) { + nm_free_secret (*p_password); + *p_password = g_strdup (password); + } else + notify_password = PROP_0; + + 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 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 (_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), + 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, + const char *password, + const char *password_prop_name, + GError **error) +{ + GError *local = NULL; + NMSetting8021xCKScheme scheme; + + if (!bytes) + return TRUE; + + 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, + 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; } /*****************************************************************************/ @@ -251,7 +769,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; } @@ -276,7 +794,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); } /** @@ -302,7 +820,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; } } @@ -325,7 +843,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); } /** @@ -421,134 +939,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 GByteArray * -load_and_verify_certificate (const char *cert_path, - NMSetting8021xCKScheme scheme, - NMCryptoFileFormat *out_file_format, - GError **error) -{ - NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; - GByteArray *array; - - array = crypto_load_and_verify_certificate (cert_path, &format, error); - - if (!array || !array->len || format == NM_CRYPTO_FILE_FORMAT_UNKNOWN) { - /* the array is empty or the format is already unknown. */ - format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; - } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { - /* If we load the file as blob, we must ensure that the binary data does not - * start with file://. NMSetting8021x cannot represent blobs that start with - * file://. - * If that's the case, coerce the format to UNKNOWN. The callers will take care - * of that and not set the blob. */ - if (nm_setting_802_1x_check_cert_scheme (array->data, array->len, NULL) != NM_SETTING_802_1X_CK_SCHEME_BLOB) - format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; - } - - if (out_file_format) - *out_file_format = format; - return array; -} - /** * nm_setting_802_1x_get_ca_cert_scheme: * @setting: the #NMSetting8021x @@ -563,9 +953,7 @@ load_and_verify_certificate (const char *cert_path, 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); } /** @@ -584,14 +972,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); } /** @@ -610,16 +991,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); } /** @@ -641,35 +1013,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; -} - -static GBytes * -path_to_scheme_value (const char *path) -{ - GByteArray *array; - gsize len; - - g_return_val_if_fail (path != NULL && path[0], NULL); - - len = strlen (path); - - /* Add the path scheme tag to the front, then the filename */ - array = g_byte_array_sized_new (len + strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH) + 1); - g_byte_array_append (array, (const guint8 *) NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH, strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)); - g_byte_array_append (array, (const guint8 *) path, len); - g_byte_array_append (array, (const guint8 *) "\0", 1); - - return g_byte_array_free_to_bytes (array); + _cert_impl_get_uri (setting, ca_cert); } /** @@ -698,65 +1042,7 @@ nm_setting_802_1x_set_ca_cert (NMSetting8021x *setting, NMSetting8021xCKFormat *out_format, GError **error) { - NMSetting8021xPrivate *priv; - NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; - GByteArray *data; - - 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) { - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_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); - return TRUE; - } - - data = load_and_verify_certificate (value, scheme, &format, error); - if (data) { - /* 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) - 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); - } - if (data) - g_byte_array_unref (data); - } - - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_CA_CERT); - return priv->ca_cert != NULL; + return _cert_impl_set (setting, PROP_CA_CERT, value, NULL, scheme, out_format, error); } /** @@ -880,7 +1166,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; } @@ -905,7 +1191,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); } /** @@ -932,7 +1218,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; } } @@ -955,7 +1241,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); } /** @@ -988,9 +1274,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); } /** @@ -1006,14 +1290,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); } /** @@ -1029,16 +1306,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); } /** @@ -1060,16 +1328,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); } /** @@ -1102,77 +1361,7 @@ nm_setting_802_1x_set_client_cert (NMSetting8021x *setting, NMSetting8021xCKFormat *out_format, GError **error) { - NMSetting8021xPrivate *priv; - NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; - GByteArray *data; - - 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) { - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_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); - return TRUE; - } - - data = load_and_verify_certificate (value, scheme, &format, error); - if (data) { - 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_byte_array_free_to_bytes (data); - data = NULL; - } 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); - return priv->client_cert != NULL; + return _cert_impl_set (setting, PROP_CLIENT_CERT, value, NULL, scheme, out_format, error); } /** @@ -1341,9 +1530,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); } /** @@ -1362,14 +1549,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); } /** @@ -1388,16 +1568,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); } /** @@ -1419,16 +1590,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); } /** @@ -1457,65 +1619,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; - GByteArray *data; - - 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) { - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_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); - return TRUE; - } - - data = load_and_verify_certificate (value, scheme, &format, error); - if (data) { - /* 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) - 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); - } - if (data) - g_byte_array_unref (data); - } - - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_CA_CERT); - return priv->phase2_ca_cert != NULL; + return _cert_impl_set (setting, PROP_PHASE2_CA_CERT, value, NULL, scheme, out_format, error); } /** @@ -1656,7 +1760,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; } @@ -1681,7 +1785,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); } /** @@ -1708,7 +1812,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; } } @@ -1731,7 +1835,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); } /** @@ -1751,9 +1855,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); } /** @@ -1769,14 +1871,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); } /** @@ -1792,16 +1887,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); } /** @@ -1823,16 +1909,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); } /** @@ -1865,78 +1942,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; - GByteArray *data; - - 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) { - g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_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); - return TRUE; - } - - data = load_and_verify_certificate (value, scheme, &format, error); - if (data) { - 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_byte_array_free_to_bytes (data); - data = NULL; - } 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); - return priv->phase2_client_cert != NULL; + return _cert_impl_set (setting, PROP_PHASE2_CLIENT_CERT, value, NULL, scheme, out_format, error); } /** @@ -2081,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); } /** @@ -2103,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); } /** @@ -2126,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); } /** @@ -2157,42 +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; -} - -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; + _cert_impl_get_uri (setting, private_key); } /** @@ -2242,88 +2195,7 @@ nm_setting_802_1x_set_private_key (NMSetting8021x *setting, NMSetting8021xCKFormat *out_format, GError **error) { - NMSetting8021xPrivate *priv; - NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; - gboolean password_changed = FALSE; - GError *local_err = 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); - - /* 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 = crypto_verify_private_key (value, 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; - } - } - - 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_g_free (&priv->private_key_password)) - 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); - 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 = file_to_secure_bytes (value); - 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); - 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 (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 _cert_impl_set (setting, PROP_PRIVATE_KEY, value, password, scheme, out_format, error); } /** @@ -2367,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 (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)) - 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); } /** @@ -2449,9 +2290,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); } /** @@ -2471,14 +2310,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); } /** @@ -2494,16 +2326,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); } /** @@ -2525,16 +2348,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); } /** @@ -2584,88 +2398,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; - gboolean password_changed = FALSE; - GError *local_err = 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); - - /* 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 = crypto_verify_private_key (value, 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; - } - } - - 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_g_free (&priv->phase2_private_key_password)) - 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); - priv->phase2_private_key_password = g_strdup (password); - password_changed = TRUE; - } - - 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); - 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); - - 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 (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 _cert_impl_set (setting, PROP_PHASE2_PRIVATE_KEY, value, password, scheme, out_format, error); } /** @@ -2678,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 (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)) - 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); } /** @@ -2731,6 +2433,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, @@ -2775,11 +2479,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; } @@ -2894,9 +2598,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, @@ -2942,9 +2646,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, @@ -3052,43 +2756,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, @@ -3122,79 +2789,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) @@ -3202,11 +2813,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) @@ -3246,7 +2852,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, @@ -3256,7 +2864,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, @@ -3266,7 +2876,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, @@ -3285,7 +2899,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, @@ -3295,7 +2917,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, @@ -3305,307 +2932,231 @@ 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; } -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 */ - - 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); + secrets = g_ptr_array_sized_new (4); - 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); + /* 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; - 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); - g_free (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); - g_free (priv->phase2_private_key_password); + 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); - G_OBJECT_CLASS (nm_setting_802_1x_parent_class)->finalize (object); -} + /* Only break out of the outer loop if this EAP method + * needed secrets. + */ + if (secrets->len > 0) + eap_method_found = TRUE; + break; + } + } + } -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); + if (secrets->len == 0) { + g_ptr_array_free (secrets, TRUE); + return NULL; } - return bytes; + + return secrets; } +/*****************************************************************************/ + static void -set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) +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); - GError *error = NULL; switch (prop_id) { case PROP_EAP: - g_slist_free_full (priv->eap, g_free); - priv->eap = _nm_utils_strv_to_slist (g_value_get_boxed (value), TRUE); + g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->eap, TRUE)); break; case PROP_IDENTITY: - g_free (priv->identity); - priv->identity = g_value_dup_string (value); + g_value_set_string (value, priv->identity); break; case PROP_ANONYMOUS_IDENTITY: - g_free (priv->anonymous_identity); - priv->anonymous_identity = g_value_dup_string (value); + g_value_set_string (value, priv->anonymous_identity); break; case PROP_PAC_FILE: - g_free (priv->pac_file); - priv->pac_file = g_value_dup_string (value); + g_value_set_string (value, priv->pac_file); break; case PROP_CA_CERT: - if (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); - g_error_free (error); - } + g_value_set_boxed (value, priv->ca_cert); break; case PROP_CA_CERT_PASSWORD: - g_free (priv->ca_cert_password); - priv->ca_cert_password = g_value_dup_string (value); + g_value_set_string (value, priv->ca_cert_password); break; case PROP_CA_CERT_PASSWORD_FLAGS: - priv->ca_cert_password_flags = g_value_get_flags (value); + g_value_set_flags (value, priv->ca_cert_password_flags); break; case PROP_CA_PATH: - g_free (priv->ca_path); - priv->ca_path = g_value_dup_string (value); + g_value_set_string (value, priv->ca_path); break; case PROP_SUBJECT_MATCH: - g_free (priv->subject_match); - priv->subject_match = nm_strdup_not_empty (g_value_get_string (value)); + g_value_set_string (value, priv->subject_match); break; case PROP_ALTSUBJECT_MATCHES: - g_slist_free_full (priv->altsubject_matches, g_free); - priv->altsubject_matches = _nm_utils_strv_to_slist (g_value_get_boxed (value), TRUE); + g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->altsubject_matches, TRUE)); break; case PROP_DOMAIN_SUFFIX_MATCH: - g_free (priv->domain_suffix_match); - priv->domain_suffix_match = nm_strdup_not_empty (g_value_get_string (value)); + g_value_set_string (value, priv->domain_suffix_match); break; case PROP_CLIENT_CERT: - if (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); - g_error_free (error); - } + g_value_set_boxed (value, priv->client_cert); break; case PROP_CLIENT_CERT_PASSWORD: - g_free (priv->client_cert_password); - priv->client_cert_password = g_value_dup_string (value); + g_value_set_string (value, priv->client_cert_password); break; case PROP_CLIENT_CERT_PASSWORD_FLAGS: - priv->client_cert_password_flags = g_value_get_flags (value); + g_value_set_flags (value, priv->client_cert_password_flags); break; case PROP_PHASE1_PEAPVER: - g_free (priv->phase1_peapver); - priv->phase1_peapver = g_value_dup_string (value); + g_value_set_string (value, priv->phase1_peapver); break; case PROP_PHASE1_PEAPLABEL: - g_free (priv->phase1_peaplabel); - priv->phase1_peaplabel = g_value_dup_string (value); + g_value_set_string (value, priv->phase1_peaplabel); break; case PROP_PHASE1_FAST_PROVISIONING: - g_free (priv->phase1_fast_provisioning); - priv->phase1_fast_provisioning = g_value_dup_string (value); + g_value_set_string (value, priv->phase1_fast_provisioning); break; case PROP_PHASE1_AUTH_FLAGS: - priv->phase1_auth_flags = g_value_get_uint (value); + g_value_set_uint (value, priv->phase1_auth_flags); break; case PROP_PHASE2_AUTH: - g_free (priv->phase2_auth); - priv->phase2_auth = g_value_dup_string (value); + g_value_set_string (value, priv->phase2_auth); break; case PROP_PHASE2_AUTHEAP: - g_free (priv->phase2_autheap); - priv->phase2_autheap = g_value_dup_string (value); + g_value_set_string (value, priv->phase2_autheap); break; case PROP_PHASE2_CA_CERT: - if (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); - g_error_free (error); - } + g_value_set_boxed (value, priv->phase2_ca_cert); break; case PROP_PHASE2_CA_CERT_PASSWORD: - g_free (priv->phase2_ca_cert_password); - priv->phase2_ca_cert_password = g_value_dup_string (value); + g_value_set_string (value, priv->phase2_ca_cert_password); break; case PROP_PHASE2_CA_CERT_PASSWORD_FLAGS: - priv->phase2_ca_cert_password_flags = g_value_get_flags (value); + g_value_set_flags (value, priv->phase2_ca_cert_password_flags); break; case PROP_PHASE2_CA_PATH: - g_free (priv->phase2_ca_path); - priv->phase2_ca_path = g_value_dup_string (value); + g_value_set_string (value, priv->phase2_ca_path); break; case PROP_PHASE2_SUBJECT_MATCH: - g_free (priv->phase2_subject_match); - priv->phase2_subject_match = nm_strdup_not_empty (g_value_get_string (value)); + g_value_set_string (value, priv->phase2_subject_match); break; case PROP_PHASE2_ALTSUBJECT_MATCHES: - g_slist_free_full (priv->phase2_altsubject_matches, g_free); - priv->phase2_altsubject_matches = _nm_utils_strv_to_slist (g_value_get_boxed (value), TRUE); + g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->phase2_altsubject_matches, TRUE)); break; case PROP_PHASE2_DOMAIN_SUFFIX_MATCH: - g_free (priv->phase2_domain_suffix_match); - priv->phase2_domain_suffix_match = nm_strdup_not_empty (g_value_get_string (value)); + g_value_set_string (value, priv->phase2_domain_suffix_match); break; case PROP_PHASE2_CLIENT_CERT: - - if (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); - g_error_free (error); - } + g_value_set_boxed (value, priv->phase2_client_cert); break; case PROP_PHASE2_CLIENT_CERT_PASSWORD: - g_free (priv->phase2_client_cert_password); - priv->phase2_client_cert_password = g_value_dup_string (value); + g_value_set_string (value, priv->phase2_client_cert_password); break; case PROP_PHASE2_CLIENT_CERT_PASSWORD_FLAGS: - priv->phase2_client_cert_password_flags = g_value_get_flags (value); + g_value_set_flags (value, priv->phase2_client_cert_password_flags); break; case PROP_PASSWORD: - g_free (priv->password); - priv->password = g_value_dup_string (value); + g_value_set_string (value, priv->password); break; case PROP_PASSWORD_FLAGS: - priv->password_flags = g_value_get_flags (value); + g_value_set_flags (value, priv->password_flags); break; case PROP_PASSWORD_RAW: - if (priv->password_raw) - g_bytes_unref (priv->password_raw); - priv->password_raw = g_value_dup_boxed (value); + g_value_set_boxed (value, priv->password_raw); break; case PROP_PASSWORD_RAW_FLAGS: - priv->password_raw_flags = g_value_get_flags (value); + g_value_set_flags (value, priv->password_raw_flags); break; case PROP_PRIVATE_KEY: - if (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); - g_error_free (error); - } + g_value_set_boxed (value, priv->private_key); break; case PROP_PRIVATE_KEY_PASSWORD: - g_free (priv->private_key_password); - priv->private_key_password = g_value_dup_string (value); + g_value_set_string (value, priv->private_key_password); break; case PROP_PRIVATE_KEY_PASSWORD_FLAGS: - priv->private_key_password_flags = g_value_get_flags (value); + g_value_set_flags (value, priv->private_key_password_flags); break; case PROP_PHASE2_PRIVATE_KEY: - if (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); - g_error_free (error); - } + g_value_set_boxed (value, priv->phase2_private_key); break; case PROP_PHASE2_PRIVATE_KEY_PASSWORD: - g_free (priv->phase2_private_key_password); - priv->phase2_private_key_password = g_value_dup_string (value); + g_value_set_string (value, priv->phase2_private_key_password); break; case PROP_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS: - priv->phase2_private_key_password_flags = g_value_get_flags (value); + g_value_set_flags (value, priv->phase2_private_key_password_flags); break; case PROP_PIN: - g_free (priv->pin); - priv->pin = g_value_dup_string (value); + g_value_set_string (value, priv->pin); break; case PROP_PIN_FLAGS: - priv->pin_flags = g_value_get_flags (value); + g_value_set_flags (value, priv->pin_flags); break; case PROP_SYSTEM_CA_CERTS: - priv->system_ca_certs = g_value_get_boolean (value); + g_value_set_boolean (value, priv->system_ca_certs); break; case PROP_AUTH_TIMEOUT: - priv->auth_timeout = g_value_get_int (value); + g_value_set_int (value, priv->auth_timeout); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -3614,144 +3165,176 @@ set_property (GObject *object, guint prop_id, } static void -get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) +set_property (GObject *object, guint prop_id, + const 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)); + g_slist_free_full (priv->eap, g_free); + priv->eap = _nm_utils_strv_to_slist (g_value_get_boxed (value), TRUE); break; case PROP_IDENTITY: - g_value_set_string (value, priv->identity); + g_free (priv->identity); + priv->identity = g_value_dup_string (value); break; case PROP_ANONYMOUS_IDENTITY: - g_value_set_string (value, priv->anonymous_identity); + g_free (priv->anonymous_identity); + priv->anonymous_identity = g_value_dup_string (value); break; case PROP_PAC_FILE: - g_value_set_string (value, priv->pac_file); + g_free (priv->pac_file); + priv->pac_file = g_value_dup_string (value); break; case PROP_CA_CERT: - g_value_set_boxed (value, priv->ca_cert); + g_bytes_unref (priv->ca_cert); + priv->ca_cert = g_value_dup_boxed (value); break; case PROP_CA_CERT_PASSWORD: - g_value_set_string (value, priv->ca_cert_password); + g_free (priv->ca_cert_password); + priv->ca_cert_password = g_value_dup_string (value); break; case PROP_CA_CERT_PASSWORD_FLAGS: - g_value_set_flags (value, priv->ca_cert_password_flags); + priv->ca_cert_password_flags = g_value_get_flags (value); break; case PROP_CA_PATH: - g_value_set_string (value, priv->ca_path); + g_free (priv->ca_path); + priv->ca_path = g_value_dup_string (value); break; case PROP_SUBJECT_MATCH: - g_value_set_string (value, priv->subject_match); + g_free (priv->subject_match); + priv->subject_match = nm_strdup_not_empty (g_value_get_string (value)); break; case PROP_ALTSUBJECT_MATCHES: - g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->altsubject_matches, TRUE)); + g_slist_free_full (priv->altsubject_matches, g_free); + priv->altsubject_matches = _nm_utils_strv_to_slist (g_value_get_boxed (value), TRUE); break; case PROP_DOMAIN_SUFFIX_MATCH: - g_value_set_string (value, priv->domain_suffix_match); + g_free (priv->domain_suffix_match); + priv->domain_suffix_match = nm_strdup_not_empty (g_value_get_string (value)); break; case PROP_CLIENT_CERT: - g_value_set_boxed (value, priv->client_cert); + g_bytes_unref (priv->client_cert); + priv->client_cert = g_value_dup_boxed (value); break; case PROP_CLIENT_CERT_PASSWORD: - g_value_set_string (value, priv->client_cert_password); + g_free (priv->client_cert_password); + priv->client_cert_password = g_value_dup_string (value); break; case PROP_CLIENT_CERT_PASSWORD_FLAGS: - g_value_set_flags (value, priv->client_cert_password_flags); + priv->client_cert_password_flags = g_value_get_flags (value); break; case PROP_PHASE1_PEAPVER: - g_value_set_string (value, priv->phase1_peapver); + g_free (priv->phase1_peapver); + priv->phase1_peapver = g_value_dup_string (value); break; case PROP_PHASE1_PEAPLABEL: - g_value_set_string (value, priv->phase1_peaplabel); + g_free (priv->phase1_peaplabel); + priv->phase1_peaplabel = g_value_dup_string (value); break; case PROP_PHASE1_FAST_PROVISIONING: - g_value_set_string (value, priv->phase1_fast_provisioning); + g_free (priv->phase1_fast_provisioning); + priv->phase1_fast_provisioning = g_value_dup_string (value); break; case PROP_PHASE1_AUTH_FLAGS: - g_value_set_uint (value, priv->phase1_auth_flags); + priv->phase1_auth_flags = g_value_get_uint (value); break; case PROP_PHASE2_AUTH: - g_value_set_string (value, priv->phase2_auth); + g_free (priv->phase2_auth); + priv->phase2_auth = g_value_dup_string (value); break; case PROP_PHASE2_AUTHEAP: - g_value_set_string (value, priv->phase2_autheap); + g_free (priv->phase2_autheap); + priv->phase2_autheap = g_value_dup_string (value); break; case PROP_PHASE2_CA_CERT: - g_value_set_boxed (value, priv->phase2_ca_cert); + g_bytes_unref (priv->phase2_ca_cert); + priv->phase2_ca_cert = g_value_dup_boxed (value); break; case PROP_PHASE2_CA_CERT_PASSWORD: - g_value_set_string (value, priv->phase2_ca_cert_password); + g_free (priv->phase2_ca_cert_password); + priv->phase2_ca_cert_password = g_value_dup_string (value); break; case PROP_PHASE2_CA_CERT_PASSWORD_FLAGS: - g_value_set_flags (value, priv->phase2_ca_cert_password_flags); + priv->phase2_ca_cert_password_flags = g_value_get_flags (value); break; case PROP_PHASE2_CA_PATH: - g_value_set_string (value, priv->phase2_ca_path); + g_free (priv->phase2_ca_path); + priv->phase2_ca_path = g_value_dup_string (value); break; case PROP_PHASE2_SUBJECT_MATCH: - g_value_set_string (value, priv->phase2_subject_match); + g_free (priv->phase2_subject_match); + priv->phase2_subject_match = nm_strdup_not_empty (g_value_get_string (value)); break; case PROP_PHASE2_ALTSUBJECT_MATCHES: - g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->phase2_altsubject_matches, TRUE)); + g_slist_free_full (priv->phase2_altsubject_matches, g_free); + priv->phase2_altsubject_matches = _nm_utils_strv_to_slist (g_value_get_boxed (value), TRUE); break; case PROP_PHASE2_DOMAIN_SUFFIX_MATCH: - g_value_set_string (value, priv->phase2_domain_suffix_match); + g_free (priv->phase2_domain_suffix_match); + priv->phase2_domain_suffix_match = nm_strdup_not_empty (g_value_get_string (value)); break; case PROP_PHASE2_CLIENT_CERT: - g_value_set_boxed (value, priv->phase2_client_cert); + g_bytes_unref (priv->phase2_client_cert); + priv->phase2_client_cert = g_value_dup_boxed (value); break; case PROP_PHASE2_CLIENT_CERT_PASSWORD: - g_value_set_string (value, priv->phase2_client_cert_password); + g_free (priv->phase2_client_cert_password); + priv->phase2_client_cert_password = g_value_dup_string (value); break; case PROP_PHASE2_CLIENT_CERT_PASSWORD_FLAGS: - g_value_set_flags (value, priv->phase2_client_cert_password_flags); + priv->phase2_client_cert_password_flags = g_value_get_flags (value); break; case PROP_PASSWORD: - g_value_set_string (value, priv->password); + g_free (priv->password); + priv->password = g_value_dup_string (value); break; case PROP_PASSWORD_FLAGS: - g_value_set_flags (value, priv->password_flags); + priv->password_flags = g_value_get_flags (value); break; case PROP_PASSWORD_RAW: - g_value_set_boxed (value, priv->password_raw); + g_bytes_unref (priv->password_raw); + priv->password_raw = g_value_dup_boxed (value); break; case PROP_PASSWORD_RAW_FLAGS: - g_value_set_flags (value, priv->password_raw_flags); + priv->password_raw_flags = g_value_get_flags (value); break; case PROP_PRIVATE_KEY: - g_value_set_boxed (value, priv->private_key); + g_bytes_unref (priv->private_key); + priv->private_key = g_value_dup_boxed (value); break; case PROP_PRIVATE_KEY_PASSWORD: - g_value_set_string (value, 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: - g_value_set_flags (value, priv->private_key_password_flags); + priv->private_key_password_flags = g_value_get_flags (value); break; case PROP_PHASE2_PRIVATE_KEY: - g_value_set_boxed (value, priv->phase2_private_key); + g_bytes_unref (priv->phase2_private_key); + priv->phase2_private_key = g_value_dup_boxed (value); break; case PROP_PHASE2_PRIVATE_KEY_PASSWORD: - g_value_set_string (value, 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: - g_value_set_flags (value, priv->phase2_private_key_password_flags); + priv->phase2_private_key_password_flags = g_value_get_flags (value); break; case PROP_PIN: - g_value_set_string (value, priv->pin); + g_free (priv->pin); + priv->pin = g_value_dup_string (value); break; case PROP_PIN_FLAGS: - g_value_set_flags (value, priv->pin_flags); + priv->pin_flags = g_value_get_flags (value); break; case PROP_SYSTEM_CA_CERTS: - g_value_set_boolean (value, priv->system_ca_certs); + priv->system_ca_certs = g_value_get_boolean (value); break; case PROP_AUTH_TIMEOUT: - g_value_set_int (value, priv->auth_timeout); + priv->auth_timeout = g_value_get_int (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -3759,6 +3342,71 @@ get_property (GObject *object, guint prop_id, } } +/*****************************************************************************/ + +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); + 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); + + g_bytes_unref (priv->ca_cert); + g_free (priv->ca_cert_password); + g_bytes_unref (priv->client_cert); + g_free (priv->client_cert_password); + g_bytes_unref (priv->private_key); + nm_free_secret (priv->private_key_password); + g_bytes_unref (priv->phase2_ca_cert); + g_free (priv->phase2_ca_cert_password); + g_bytes_unref (priv->phase2_client_cert); + g_free (priv->phase2_client_cert_password); + 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 nm_setting_802_1x_class_init (NMSetting8021xClass *klass) { @@ -3791,12 +3439,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: @@ -3811,12 +3458,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: @@ -3831,12 +3477,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: @@ -3850,12 +3495,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: @@ -3882,12 +3526,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: @@ -3901,13 +3544,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: @@ -3919,13 +3561,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: @@ -3940,12 +3581,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: @@ -3963,12 +3603,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: @@ -3984,12 +3623,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: @@ -4008,12 +3646,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: @@ -4038,12 +3675,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: @@ -4057,13 +3693,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: @@ -4075,13 +3710,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: @@ -4100,12 +3734,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: @@ -4123,12 +3756,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: @@ -4148,12 +3780,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: @@ -4175,13 +3806,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: @@ -4202,12 +3832,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: @@ -4228,12 +3857,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: @@ -4254,12 +3882,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: @@ -4273,13 +3900,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: @@ -4291,13 +3917,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: @@ -4306,12 +3931,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: @@ -4330,12 +3954,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: @@ -4350,12 +3973,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: @@ -4375,12 +3997,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: @@ -4408,12 +4029,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: @@ -4427,13 +4047,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: @@ -4445,13 +4064,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: @@ -4467,13 +4085,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: @@ -4487,13 +4104,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: @@ -4511,13 +4127,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: @@ -4530,13 +4145,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: @@ -4576,12 +4190,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: @@ -4600,13 +4213,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: @@ -4621,13 +4233,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: @@ -4661,12 +4272,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: @@ -4686,13 +4296,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: @@ -4707,13 +4316,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: @@ -4726,13 +4334,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: @@ -4745,13 +4352,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: @@ -4772,13 +4378,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: @@ -4795,13 +4400,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); } 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 <errno.h> #include <stdlib.h> +#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-core/nm-utils.c b/libnm-core/nm-utils.c index cb71d6e77d..c9162e971e 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" @@ -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)); + 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 }; @@ -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)); + 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; @@ -2911,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 (!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)) - goto out; - - key = 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); - 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[]) { @@ -3047,18 +2956,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 = 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; } @@ -3084,7 +2990,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; } /** @@ -3100,7 +3006,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); } /*****************************************************************************/ @@ -3620,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; @@ -3696,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: @@ -3722,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; } @@ -4602,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)) { diff --git a/libnm-core/tests/test-crypto.c b/libnm-core/tests/test-crypto.c index 5fb26c1fcc..6b63e6fdd4 100644 --- a/libnm-core/tests/test-crypto.c +++ b/libnm-core/tests/test-crypto.c @@ -28,7 +28,7 @@ #include <stdio.h> #include <string.h> -#include "crypto.h" +#include "nm-crypto-impl.h" #include "nm-utils.h" #include "nm-errors.h" #include "nm-core-internal.h" @@ -99,37 +99,20 @@ 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 = 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)); } -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, @@ -138,13 +121,13 @@ 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)); 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); @@ -164,16 +147,14 @@ test_load_private_key (const char *path, g_assert (array != NULL); if (decrypted_path) { - /* 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); + gs_free char *contents = NULL; + gsize length; - g_byte_array_free (decrypted, TRUE); + /* Compare the crypto decrypted key against a known-good decryption */ + 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 @@ -187,7 +168,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 +190,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 +202,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 +225,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); @@ -261,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; + gs_unref_bytes GBytes *encrypted = NULL; + gs_unref_bytes GBytes *re_decrypted = NULL; GError *error = NULL; - array = crypto_decrypt_openssl_private_key (path, password, &key_type, &error); - g_assert_no_error (error); - g_assert (array != NULL); + array = nmtst_crypto_decrypt_openssl_private_key (path, password, &key_type, &error); + 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 = 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 */ key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN; - re_decrypted = 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 (g_bytes_get_data (encrypted, NULL), + g_bytes_get_size (encrypted), + 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 @@ -417,15 +399,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() 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, - strlen (md5_tests[i].password), - 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); @@ -444,7 +427,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); 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); 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; 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 <signal.h> #include <stdlib.h> +#include "nm-utils/nm-secret-utils.h" #include "nm-enum-types.h" #include "nm-utils.h" #include "nm-connection.h" 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/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 diff --git a/shared/meson.build b/shared/meson.build index 8faec8765b..e1cf620bfd 100644 --- a/shared/meson.build +++ b/shared/meson.build @@ -50,7 +50,9 @@ 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 nm-utils/nm-udev-utils.c '''.split()) diff --git a/shared/nm-utils/nm-io-utils.c b/shared/nm-utils/nm-io-utils.c new file mode 100644 index 0000000000..88cb13ff18 --- /dev/null +++ b/shared/nm-utils/nm-io-utils.c @@ -0,0 +1,430 @@ +/* -*- 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" + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#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 new file mode 100644 index 0000000000..dc72a2a6c5 --- /dev/null +++ b/shared/nm-utils/nm-io-utils.h @@ -0,0 +1,63 @@ +/* -*- 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" + +/*****************************************************************************/ + +/** + * 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/shared/nm-utils/nm-macros-internal.h b/shared/nm-utils/nm-macros-internal.h index 3d2520d131..084219b4bb 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); \ -} - /*****************************************************************************/ /** @@ -213,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: @@ -244,22 +237,14 @@ 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) - -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) +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) { @@ -834,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) { @@ -924,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; \ @@ -945,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); \ 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__ */ diff --git a/shared/nm-utils/nm-shared-utils.h b/shared/nm-utils/nm-shared-utils.h index 26875534d9..171544feab 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) { @@ -202,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); @@ -210,6 +235,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); diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index 9aab919ebf..ca8c952671 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -38,6 +38,7 @@ #include <net/ethernet.h> #include "nm-utils/nm-random-utils.h" +#include "nm-utils/nm-io-utils.h" #include "nm-utils.h" #include "nm-core-internal.h" #include "nm-setting-connection.h" @@ -2560,262 +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; -} - -/** - * 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. - * - * @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, - 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; - - 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) - 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) - 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 { - 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)) - return _get_contents_error (error, errsv, "error during fread"); - - if ( n_have > G_MAXSIZE - 1 - n_read - || n_have + n_read + 1 > max_length) { - 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; - - if (str) { - if (n_alloc >= max_length / 2) - n_alloc = max_length; - else - n_alloc *= 2; - } else - n_alloc = NM_MIN (n_read + 1, sizeof (buf)); - - tmp = g_try_realloc (str, n_alloc); - if (!tmp) - 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 (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) - return _get_contents_error (error, ENOMEM, "failure to truncate buffer to %zu bytes", n_have + 1); - str = tmp; - } - } - - 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. - * @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, - 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, - contents, - length, - error); -} - -/*****************************************************************************/ - static gboolean _secret_key_read (guint8 **out_secret_key, gsize *out_key_len) @@ -2942,6 +2687,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); @@ -3987,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 53c72e16f9..30d1360a1e 100644 --- a/src/nm-core-utils.h +++ b/src/nm-core-utils.h @@ -264,26 +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); -int nm_utils_fd_get_contents (int fd, - gboolean close_fd, - gsize max_length, - char **contents, - gsize *length, - GError **error); - -int nm_utils_file_get_contents (int dirfd, - const char *filename, - gsize max_length, - 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 9ff976a259..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" /*****************************************************************************/ @@ -853,7 +854,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 +3518,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 +3737,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..057490c406 100644 --- a/src/platform/tests/test-link.c +++ b/src/platform/tests/test-link.c @@ -25,6 +25,7 @@ #include <sys/types.h> #include <linux/if_tun.h> +#include "nm-utils/nm-io-utils.h" #include "platform/nmp-object.h" #include "platform/nmp-netns.h" #include "platform/nm-platform-utils.h" @@ -2622,7 +2623,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 +2689,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 +2703,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/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 ] 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..2f38e16887 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 <sys/ioctl.h> #include <unistd.h> +#include "nm-utils/nm-secret-utils.h" #include "nm-connection.h" #include "nm-dbus-interface.h" #include "nm-setting-connection.h" @@ -79,6 +80,210 @@ /*****************************************************************************/ +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) +{ + 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 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) @@ -2608,30 +2813,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 +2844,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,254 +3043,118 @@ 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, - shvarFile *keys, + shvarFile *keys_ifcfg, NMSetting8021x *s_8021x, gboolean phase2, 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); - return FALSE; - } - g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, value, NULL); - nm_clear_g_free (&value); + g_object_set (s_8021x, + NM_SETTING_802_1X_IDENTITY, + svGetValueStr (ifcfg, "IEEE_8021X_IDENTITY", &identity_free), + NULL); - read_8021x_password (ifcfg, keys, "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); - } + _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; - read_8021x_password (ifcfg, keys, "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; } -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, - shvarFile *keys, + shvarFile *keys_ifcfg, NMSetting8021x *s_8021x, 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); - } - - ca_cert = svGetValueStr_cp (ifcfg, ca_cert_key); - if (ca_cert) { - gs_free char *real_cert_value = NULL; - - 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 = read_secret_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 = read_secret_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) { - /* Try the lookaside keys file */ - privkey_password = svGetValueStr_cp (keys, pk_pw_key); - } - } + g_object_set (s_8021x, + NM_SETTING_802_1X_IDENTITY, + svGetValueStr (ifcfg, "IEEE_8021X_IDENTITY", &identity_free), + NULL); - /* The private key itself */ - privkey = svGetValueStr_cp (ifcfg, pk_key); + 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); + + 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 = read_secret_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; @@ -3118,7 +3163,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) @@ -3127,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); @@ -3179,10 +3224,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 +3256,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) @@ -3221,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); @@ -3258,17 +3303,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 +3330,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 +3396,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 +3431,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 +3483,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 +3504,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 +3529,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); @@ -3600,7 +3645,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 +3732,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 */ @@ -5459,7 +5504,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 +5532,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 +5549,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 +5559,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 +5584,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 +5619,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 +5689,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 +5705,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 +5731,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 +5745,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 +5754,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 +5768,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 +5780,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); 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 <stdio.h> #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 f09bc2472f..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" /*****************************************************************************/ @@ -821,6 +822,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) { 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); 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 { |