diff options
Diffstat (limited to 'src/libnm-core-impl')
25 files changed, 1244 insertions, 3135 deletions
diff --git a/src/libnm-core-impl/meson.build b/src/libnm-core-impl/meson.build index 2e1f1751..83c29085 100644 --- a/src/libnm-core-impl/meson.build +++ b/src/libnm-core-impl/meson.build @@ -2,35 +2,6 @@ libnm_core_impl_inc = include_directories('.') -if crypto_nss_dep.found() - libnm_crypto_nss = static_library( - 'nm-crypto-nss', - sources: 'nm-crypto-nss.c', - dependencies: [ - libnm_core_public_dep, - crypto_nss_dep, - ], - ) -endif - -if crypto_gnutls_dep.found() - libnm_crypto_gnutls = static_library( - 'nm-crypto-gnutls', - sources: 'nm-crypto-gnutls.c', - dependencies: [ - libnm_core_public_dep, - crypto_gnutls_dep, - ], - ) -endif - -if crypto == 'nss' - libnm_crypto = libnm_crypto_nss -else - assert(crypto == 'gnutls', 'Unexpected setting "crypto=' + crypto + '"') - libnm_crypto = libnm_crypto_gnutls -endif - libnm_core_settings_sources = files( 'nm-setting-6lowpan.c', 'nm-setting-8021x.c', @@ -89,7 +60,6 @@ libnm_core_settings_sources = files( libnm_core_impl_sources = files( 'nm-connection.c', - 'nm-crypto.c', 'nm-dbus-utils.c', 'nm-errors.c', 'nm-keyfile-utils.c', diff --git a/src/libnm-core-impl/nm-connection.c b/src/libnm-core-impl/nm-connection.c index a9b130a7..27cea2cb 100644 --- a/src/libnm-core-impl/nm-connection.c +++ b/src/libnm-core-impl/nm-connection.c @@ -1642,6 +1642,66 @@ _normalize_gsm_auto_config(NMConnection *self) } static gboolean +_normalize_802_1x_empty_strings(NMConnection *self) +{ + NMSetting8021x *s_8021x; + gboolean changed = FALSE; + + s_8021x = _connection_get_setting_by_meta_type(NM_CONNECTION_GET_PRIVATE(self), + NM_META_SETTING_TYPE_802_1X); + if (!s_8021x) + return FALSE; + +#define _norm_8021x(s_8021x, getter, prop_name, p_changed) \ + G_STMT_START \ + { \ + NMSetting8021x *_s_8021x = (s_8021x); \ + gboolean *_p_changed = (p_changed); \ + const char *_v; \ + \ + _v = getter(_s_8021x); \ + if (_v && _v[0] == '\0') { \ + g_object_set(_s_8021x, "" prop_name "", NULL, NULL); \ + *(_p_changed) = TRUE; \ + } \ + } \ + G_STMT_END + + _norm_8021x(s_8021x, nm_setting_802_1x_get_identity, NM_SETTING_802_1X_IDENTITY, &changed); + _norm_8021x(s_8021x, + nm_setting_802_1x_get_anonymous_identity, + NM_SETTING_802_1X_ANONYMOUS_IDENTITY, + &changed); + _norm_8021x(s_8021x, nm_setting_802_1x_get_pac_file, NM_SETTING_802_1X_PAC_FILE, &changed); + _norm_8021x(s_8021x, + nm_setting_802_1x_get_subject_match, + NM_SETTING_802_1X_SUBJECT_MATCH, + &changed); + _norm_8021x(s_8021x, + nm_setting_802_1x_get_phase2_subject_match, + NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH, + &changed); + _norm_8021x(s_8021x, + nm_setting_802_1x_get_domain_suffix_match, + NM_SETTING_802_1X_DOMAIN_SUFFIX_MATCH, + &changed); + _norm_8021x(s_8021x, + nm_setting_802_1x_get_phase2_domain_suffix_match, + NM_SETTING_802_1X_PHASE2_DOMAIN_SUFFIX_MATCH, + &changed); + _norm_8021x(s_8021x, + nm_setting_802_1x_get_domain_match, + NM_SETTING_802_1X_DOMAIN_MATCH, + &changed); + _norm_8021x(s_8021x, + nm_setting_802_1x_get_phase2_domain_match, + NM_SETTING_802_1X_PHASE2_DOMAIN_MATCH, + &changed); + + return changed; +} + +static gboolean _normalize_required_settings(NMConnection *self) { NMSettingBluetooth *s_bt = nm_connection_get_setting_bluetooth(self); @@ -1952,6 +2012,7 @@ _connection_normalize(NMConnection *connection, was_modified |= _normalize_bridge_vlan_order(connection); was_modified |= _normalize_bridge_port_vlan_order(connection); was_modified |= _normalize_gsm_auto_config(connection); + was_modified |= _normalize_802_1x_empty_strings(connection); was_modified = !!was_modified; diff --git a/src/libnm-core-impl/nm-crypto-gnutls.c b/src/libnm-core-impl/nm-crypto-gnutls.c deleted file mode 100644 index d9e59136..00000000 --- a/src/libnm-core-impl/nm-crypto-gnutls.c +++ /dev/null @@ -1,415 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Dan Williams <dcbw@redhat.com> - * Copyright (C) 2007 - 2015 Red Hat, Inc. - */ - -#include "libnm-glib-aux/nm-default-glib-i18n-lib.h" - -#include "nm-crypto-impl.h" - -#include <gnutls/gnutls.h> -#include <gnutls/crypto.h> -#include <gnutls/x509.h> -#include <gnutls/pkcs12.h> - -#include "libnm-glib-aux/nm-secret-utils.h" -#include "nm-errors.h" - -/*****************************************************************************/ - -static gboolean -_get_cipher_info(NMCryptoCipherType cipher, int *out_cipher_mech, guint8 *out_real_iv_len) -{ - static const int cipher_mechs[] = { - [NM_CRYPTO_CIPHER_DES_EDE3_CBC] = GNUTLS_CIPHER_3DES_CBC, - [NM_CRYPTO_CIPHER_DES_CBC] = GNUTLS_CIPHER_DES_CBC, - [NM_CRYPTO_CIPHER_AES_128_CBC] = GNUTLS_CIPHER_AES_128_CBC, - [NM_CRYPTO_CIPHER_AES_192_CBC] = GNUTLS_CIPHER_AES_192_CBC, - [NM_CRYPTO_CIPHER_AES_256_CBC] = GNUTLS_CIPHER_AES_256_CBC, - }; - - g_return_val_if_fail(_NM_INT_NOT_NEGATIVE(cipher) - && (gsize) cipher < G_N_ELEMENTS(cipher_mechs), - FALSE); - - if (cipher_mechs[cipher] == 0) - return FALSE; - - NM_SET_OUT(out_cipher_mech, cipher_mechs[cipher]); - NM_SET_OUT(out_real_iv_len, nm_crypto_cipher_get_info(cipher)->real_iv_len); - return TRUE; -} - -/*****************************************************************************/ - -gboolean -_nm_crypto_init(GError **error) -{ - static gboolean initialized = FALSE; - - if (initialized) - return TRUE; - - 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.")); - return FALSE; - } - - initialized = TRUE; - return TRUE; -} - -/*****************************************************************************/ - -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; - nm_auto_clear_secret_ptr NMSecretPtr output = {0}; - guint8 pad_i, pad_len; - guint8 real_iv_len; - - if (!_get_cipher_info(cipher, &cipher_mech, &real_iv_len)) { - g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_UNKNOWN_CIPHER, - _("Unsupported key cipher for decryption")); - return NULL; - } - - if (!_nm_crypto_init(error)) - return NULL; - - if (iv_len < real_iv_len) { - g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, - _("Invalid IV length (must be at least %u)."), - (guint) real_iv_len); - return NULL; - } - - output.len = data_len; - output.bin = g_malloc(data_len); - - key_dt.data = (unsigned char *) key; - key_dt.size = key_len; - iv_dt.data = (unsigned char *) iv; - iv_dt.size = iv_len; - - err = gnutls_cipher_init(&ctx, cipher_mech, &key_dt, &iv_dt); - if (err < 0) { - g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_DECRYPTION_FAILED, - _("Failed to initialize the decryption cipher context: %s (%s)"), - gnutls_strerror_name(err), - gnutls_strerror(err)); - return NULL; - } - - 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)); - return NULL; - } - - 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) { - g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_DECRYPTION_FAILED, - _("Failed to decrypt the private key: unexpected padding length.")); - return NULL; - } - - /* Validate tail padding; last byte is the padding size, and all pad bytes - * should contain the padding size. - */ - 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.")); - return NULL; - } - } - - *out_len = output.len - pad_len; - return g_steal_pointer(&output.bin); -} - -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; - nm_auto_clear_secret_ptr NMSecretPtr output = {0}; - nm_auto_clear_secret_ptr NMSecretPtr padded_buf = {0}; - gsize i, pad_len; - - nm_assert(iv_len); - - if (cipher == NM_CRYPTO_CIPHER_DES_CBC || !_get_cipher_info(cipher, &cipher_mech, NULL)) { - g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_UNKNOWN_CIPHER, - _("Unsupported key cipher for encryption")); - return NULL; - } - - if (!_nm_crypto_init(error)) - return NULL; - - key_dt.data = (unsigned char *) key; - key_dt.size = key_len; - iv_dt.data = (unsigned char *) iv; - iv_dt.size = iv_len; - - err = gnutls_cipher_init(&ctx, cipher_mech, &key_dt, &iv_dt); - if (err < 0) { - g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_ENCRYPTION_FAILED, - _("Failed to initialize the encryption cipher context: %s (%s)"), - gnutls_strerror_name(err), - gnutls_strerror(err)); - 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); - - 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)); - return NULL; - } - - *out_len = output.len; - return g_steal_pointer(&output.bin); -} - -gboolean -_nm_crypto_verify_x509(const guint8 *data, gsize len, GError **error) -{ - gnutls_x509_crt_t der; - gnutls_datum_t dt; - int err; - - if (!_nm_crypto_init(error)) - return FALSE; - - err = gnutls_x509_crt_init(&der); - if (err < 0) { - g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, - _("Error initializing certificate data: %s"), - gnutls_strerror(err)); - return FALSE; - } - - /* Try DER first */ - dt.data = (unsigned char *) data; - dt.size = len; - err = gnutls_x509_crt_import(der, &dt, GNUTLS_X509_FMT_DER); - if (err == GNUTLS_E_SUCCESS) { - gnutls_x509_crt_deinit(der); - 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 TRUE; - - g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, - _("Couldn't decode certificate: %s"), - gnutls_strerror(err)); - return FALSE; -} - -gboolean -_nm_crypto_verify_pkcs12(const guint8 *data, gsize data_len, const char *password, GError **error) -{ - gnutls_pkcs12_t p12; - gnutls_datum_t dt; - int err; - - g_return_val_if_fail(data != NULL, FALSE); - - if (!_nm_crypto_init(error)) - return FALSE; - - dt.data = (unsigned char *) data; - dt.size = data_len; - - err = gnutls_pkcs12_init(&p12); - if (err < 0) { - g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_FAILED, - _("Couldn't initialize PKCS#12 decoder: %s"), - gnutls_strerror(err)); - return FALSE; - } - - /* DER first */ - err = gnutls_pkcs12_import(p12, &dt, GNUTLS_X509_FMT_DER, 0); - if (err < 0) { - /* PEM next */ - err = gnutls_pkcs12_import(p12, &dt, GNUTLS_X509_FMT_PEM, 0); - if (err < 0) { - g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, - _("Couldn't decode PKCS#12 file: %s"), - gnutls_strerror(err)); - gnutls_pkcs12_deinit(p12); - return FALSE; - } - } - - err = gnutls_pkcs12_verify_mac(p12, password); - - 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; - } - - return TRUE; -} - -gboolean -_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; - int err; - - g_return_val_if_fail(data != NULL, FALSE); - - if (!_nm_crypto_init(error)) - return FALSE; - - err = gnutls_x509_privkey_init(&p8); - if (err < 0) { - g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_FAILED, - _("Couldn't initialize PKCS#8 decoder: %s"), - gnutls_strerror(err)); - 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) { - if (err == GNUTLS_E_UNKNOWN_CIPHER_TYPE) { - /* HACK: gnutls < 3.5.4 doesn't support all the cipher types that openssl - * can use with PKCS#8, so if we encounter one, we have to assume - * the given password works. gnutls needs to unsuckify, apparently. - * Specifically, by default openssl uses pbeWithMD5AndDES-CBC - * which gnutls does not support. - */ - } else { - g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, - _("Couldn't decode PKCS#8 file: %s"), - gnutls_strerror(err)); - return FALSE; - } - } - - return TRUE; -} - -gboolean -_nm_crypto_randomize(void *buffer, gsize buffer_len, GError **error) -{ - if (!_nm_crypto_init(error)) - return FALSE; - - gnutls_rnd(GNUTLS_RND_RANDOM, buffer, buffer_len); - return TRUE; -} diff --git a/src/libnm-core-impl/nm-crypto-impl.h b/src/libnm-core-impl/nm-crypto-impl.h deleted file mode 100644 index 61c3f7f8..00000000 --- a/src/libnm-core-impl/nm-crypto-impl.h +++ /dev/null @@ -1,49 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Dan Williams <dcbw@redhat.com> - * Copyright (C) 2007 - 2018 Red Hat, Inc. - */ - -#ifndef __NM_CRYPTO_IMPL_H__ -#define __NM_CRYPTO_IMPL_H__ - -#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/src/libnm-core-impl/nm-crypto-nss.c b/src/libnm-core-impl/nm-crypto-nss.c deleted file mode 100644 index c27bf09e..00000000 --- a/src/libnm-core-impl/nm-crypto-nss.c +++ /dev/null @@ -1,548 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Dan Williams <dcbw@redhat.com> - * Copyright (C) 2007 - 2009 Red Hat, Inc. - */ - -#include "libnm-glib-aux/nm-default-glib-i18n-lib.h" - -#include "nm-crypto-impl.h" - -NM_PRAGMA_WARNING_DISABLE("-Wstrict-prototypes") -#include <prinit.h> -#include <nss.h> -#include <pk11pub.h> -#include <pkcs11t.h> -#include <cert.h> -#include <prerror.h> -#include <p12.h> -#include <ciferfam.h> -#include <p12plcy.h> -NM_PRAGMA_WARNING_REENABLE - -#include "libnm-glib-aux/nm-secret-utils.h" -#include "nm-errors.h" - -/*****************************************************************************/ - -static gboolean -_get_cipher_info(NMCryptoCipherType cipher, - CK_MECHANISM_TYPE *out_cipher_mech, - guint8 *out_real_iv_len) -{ - static const CK_MECHANISM_TYPE cipher_mechs[] = { - [NM_CRYPTO_CIPHER_DES_EDE3_CBC] = CKM_DES3_CBC_PAD, - [NM_CRYPTO_CIPHER_DES_CBC] = CKM_DES_CBC_PAD, - [NM_CRYPTO_CIPHER_AES_128_CBC] = CKM_AES_CBC_PAD, - [NM_CRYPTO_CIPHER_AES_192_CBC] = CKM_AES_CBC_PAD, - [NM_CRYPTO_CIPHER_AES_256_CBC] = CKM_AES_CBC_PAD, - }; - - g_return_val_if_fail(_NM_INT_NOT_NEGATIVE(cipher) - && (gsize) cipher < G_N_ELEMENTS(cipher_mechs), - FALSE); - - if (!cipher_mechs[cipher]) - return FALSE; - - NM_SET_OUT(out_cipher_mech, cipher_mechs[cipher]); - NM_SET_OUT(out_real_iv_len, nm_crypto_cipher_get_info(cipher)->real_iv_len); - return TRUE; -} - -/*****************************************************************************/ - -gboolean -_nm_crypto_init(GError **error) -{ - static gboolean initialized = FALSE; - SECStatus ret; - - if (initialized) - return TRUE; - - PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 1); - ret = NSS_NoDB_Init(NULL); - if (ret != SECSuccess) { - g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_FAILED, - _("Failed to initialize the crypto engine: %d."), - PR_GetError()); - PR_Cleanup(); - 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); - - initialized = TRUE; - return TRUE; -} - -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) -{ - 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; - int decrypted_len = 0; - unsigned extra = 0; - unsigned pad_len = 0; - guint32 i; - guint8 real_iv_len; - - if (!_get_cipher_info(cipher, &cipher_mech, &real_iv_len)) { - g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_UNKNOWN_CIPHER, - _("Unsupported key cipher for decryption")); - return NULL; - } - - if (iv_len < real_iv_len) { - g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, - _("Invalid IV length (must be at least %u)."), - (guint) real_iv_len); - return NULL; - } - - 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 decryption cipher slot.")); - goto out; - } - - key_item.data = (unsigned char *) key; - key_item.len = key_len; - sym_key = PK11_ImportSymKey(slot, cipher_mech, PK11_OriginUnwrap, CKA_DECRYPT, &key_item, NULL); - if (!sym_key) { - g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_DECRYPTION_FAILED, - _("Failed to set symmetric key for decryption.")); - goto out; - } - - key_item.data = (unsigned char *) iv; - key_item.len = real_iv_len; - sec_param = PK11_ParamFromIV(cipher_mech, &key_item); - if (!sec_param) { - g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_DECRYPTION_FAILED, - _("Failed to set IV for decryption.")); - goto out; - } - - ctx = PK11_CreateContextBySymKey(cipher_mech, CKA_DECRYPT, sym_key, sec_param); - if (!ctx) { - g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_DECRYPTION_FAILED, - _("Failed to initialize the decryption context.")); - goto out; - } - - output.len = data_len; - output.bin = g_malloc(data_len); - - s = PK11_CipherOp(ctx, - (unsigned char *) output.bin, - &decrypted_len, - output.len, - data, - data_len); - if (s != SECSuccess) { - g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_DECRYPTION_FAILED, - _("Failed to decrypt the private key: %d."), - PORT_GetError()); - goto out; - } - - if (decrypted_len > data_len) { - g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_DECRYPTION_FAILED, - _("Failed to decrypt the private key: decrypted data too large.")); - goto out; - } - - s = PK11_DigestFinal(ctx, - (unsigned char *) &output.bin[decrypted_len], - &extra, - data_len - decrypted_len); - if (s != SECSuccess) { - g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_DECRYPTION_FAILED, - _("Failed to finalize decryption of the private key: %d."), - PORT_GetError()); - goto out; - } - - decrypted_len += extra; - pad_len = data_len - decrypted_len; - - /* Check if the padding at the end of the decrypted data is valid */ - 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; - } - - /* Validate tail padding; last byte is the padding size, and all pad bytes - * should contain the padding size. - */ - for (i = pad_len; i > 0; i--) { - 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.")); - goto out; - } - } - - 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 (slot) - PK11_FreeSlot(slot); - - if (!success) - return NULL; - - if (decrypted_len < output.len) - nm_explicit_bzero(&output.bin[decrypted_len], output.len - decrypted_len); - *out_len = decrypted_len; - return g_steal_pointer(&output.bin); -} - -guint8 * -_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; - PK11SlotInfo *slot = NULL; - SECItem key_item = {.data = (unsigned char *) key, .len = key_len}; - SECItem iv_item = {.data = (unsigned char *) iv, .len = iv_len}; - PK11SymKey *sym_key = NULL; - SECItem *sec_param = NULL; - PK11Context *ctx = NULL; - 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 pad_len; - - 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, - _("Unsupported key cipher for encryption")); - return NULL; - } - - 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.")); - return NULL; - } - - sym_key = PK11_ImportSymKey(slot, cipher_mech, PK11_OriginUnwrap, CKA_ENCRYPT, &key_item, NULL); - if (!sym_key) { - g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_ENCRYPTION_FAILED, - _("Failed to set symmetric key for encryption.")); - goto out; - } - - sec_param = PK11_ParamFromIV(cipher_mech, &iv_item); - if (!sec_param) { - g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_ENCRYPTION_FAILED, - _("Failed to set IV for encryption.")); - goto out; - } - - ctx = PK11_CreateContextBySymKey(cipher_mech, CKA_ENCRYPT, sym_key, sec_param); - if (!ctx) { - g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_ENCRYPTION_FAILED, - _("Failed to initialize the encryption context.")); - goto out; - } - - /* 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, - _("Failed to encrypt: %d."), - PORT_GetError()); - goto out; - } - - 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; - } - - success = TRUE; - -out: - if (ctx) - PK11_DestroyContext(ctx, PR_TRUE); - if (sec_param) - SECITEM_FreeItem(sec_param, PR_TRUE); - if (sym_key) - PK11_FreeSymKey(sym_key); - if (slot) - PK11_FreeSlot(slot); - - if (!success) - return NULL; - - *out_len = output.len; - return g_steal_pointer(&output.bin); -} - -gboolean -_nm_crypto_verify_x509(const guint8 *data, gsize len, GError **error) -{ - CERTCertificate *cert; - - if (!_nm_crypto_init(error)) - return FALSE; - - /* Try DER/PEM first */ - cert = CERT_DecodeCertFromPackage((char *) data, len); - if (!cert) { - g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, - _("Couldn't decode certificate: %d"), - PORT_GetError()); - return FALSE; - } - - CERT_DestroyCertificate(cert); - return TRUE; -} - -gboolean -_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; - gboolean success = FALSE; - - g_return_val_if_fail(!error || !*error, FALSE); - - 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) { - 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; - } - - 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; - } - - 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 out; - } - - s = SEC_PKCS12DecoderUpdate(p12ctx, (guint8 *) data, data_len); - if (s != SECSuccess) { - g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, - _("Couldn't decode PKCS#12 file: %d"), - PORT_GetError()); - goto out; - } - - s = SEC_PKCS12DecoderVerify(p12ctx); - if (s != SECSuccess) { - g_set_error(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_DECRYPTION_FAILED, - _("Couldn't verify PKCS#12 file: %d"), - PORT_GetError()); - goto out; - } - - success = TRUE; - -out: - if (p12ctx) - SEC_PKCS12DecoderFinish(p12ctx); - if (slot) - PK11_FreeSlot(slot); - - if (pw.data) - SECITEM_ZfreeItem(&pw, PR_FALSE); - - return success; -} - -gboolean -_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 (!_nm_crypto_init(error)) - return FALSE; - - /* NSS apparently doesn't do PKCS#8 natively, but you have to put the - * PKCS#8 key into a PKCS#12 file and import that?? So until we figure - * all that out, we can only assume the password is valid. - */ - return TRUE; -} - -gboolean -_nm_crypto_randomize(void *buffer, gsize buffer_len, GError **error) -{ - SECStatus s; - - if (!_nm_crypto_init(error)) - return FALSE; - - s = PK11_GenerateRandom(buffer, buffer_len); - if (s != SECSuccess) { - g_set_error_literal(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_FAILED, - _("Could not generate random data.")); - return FALSE; - } - return TRUE; -} diff --git a/src/libnm-core-impl/nm-crypto.c b/src/libnm-core-impl/nm-crypto.c deleted file mode 100644 index 78f793bd..00000000 --- a/src/libnm-core-impl/nm-crypto.c +++ /dev/null @@ -1,1044 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Dan Williams <dcbw@redhat.com> - * Copyright (C) 2007 - 2018 Red Hat, Inc. - */ - -#include "libnm-core-impl/nm-default-libnm-core.h" - -#include "nm-crypto.h" - -#include <strings.h> -#include <unistd.h> -#include <stdlib.h> - -#include "libnm-glib-aux/nm-secret-utils.h" -#include "libnm-glib-aux/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-----" - -#define PEM_TPM2_WRAPPED_KEY_BEGIN "-----BEGIN TSS2 PRIVATE KEY-----" -#define PEM_TPM2_WRAPPED_KEY_END "-----END TSS2 PRIVATE KEY-----" - -#define PEM_TPM2_OLD_WRAPPED_KEY_BEGIN "-----BEGIN TSS2 KEY BLOB-----" -#define PEM_TPM2_OLD_WRAPPED_KEY_END "-----END TSS2 KEY BLOB-----" - -/*****************************************************************************/ - -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) -{ - const guint8 *p; - gsize taglen; - - nm_assert(out_pos); - nm_assert(start_at <= data_len); - - taglen = strlen(tag); - - p = memmem(&data[start_at], data_len - start_at, tag, taglen); - if (!p) - return FALSE; - - *out_pos = p - data; - - nm_assert(memcmp(&data[*out_pos], tag, taglen) == 0); - - return TRUE; -} - -#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_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_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; - 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 -parse_tpm2_wrapped_key_file(const guint8 *data, - gsize data_len, - gboolean *out_encrypted, - GError **error) -{ - gsize start = 0, end = 0; - const char *start_tag = NULL, *end_tag = NULL; - - nm_assert(out_encrypted); - - if (find_tag(PEM_TPM2_WRAPPED_KEY_BEGIN, data, data_len, 0, &start)) { - start_tag = PEM_TPM2_WRAPPED_KEY_BEGIN; - end_tag = PEM_TPM2_WRAPPED_KEY_END; - } else if (find_tag(PEM_TPM2_OLD_WRAPPED_KEY_BEGIN, data, data_len, 0, &start)) { - start_tag = PEM_TPM2_OLD_WRAPPED_KEY_BEGIN; - end_tag = PEM_TPM2_OLD_WRAPPED_KEY_END; - } else { - g_set_error_literal(error, - NM_CRYPTO_ERROR, - NM_CRYPTO_ERROR_INVALID_DATA, - _("Failed to find expected TSS 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 TSS end tag '%s'."), - end_tag); - return FALSE; - } - - *out_encrypted = FALSE; - 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, - NULL, - error); -} - -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}; - - 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 if (parse_tpm2_wrapped_key_file(data, data_len, &is_encrypted, NULL)) { - 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); -} - -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/src/libnm-core-impl/nm-crypto.h b/src/libnm-core-impl/nm-crypto.h deleted file mode 100644 index a740c43c..00000000 --- a/src/libnm-core-impl/nm-crypto.h +++ /dev/null @@ -1,96 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Dan Williams <dcbw@redhat.com> - * Copyright (C) 2007 - 2014 Red Hat, Inc. - */ - -#ifndef __NM_CRYPTO_H__ -#define __NM_CRYPTO_H__ - -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); - -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/src/libnm-core-impl/nm-errors.c b/src/libnm-core-impl/nm-errors.c index ea4ca5e9..fad854bc 100644 --- a/src/libnm-core-impl/nm-errors.c +++ b/src/libnm-core-impl/nm-errors.c @@ -12,12 +12,24 @@ NM_CACHED_QUARK_FCN("nm-agent-manager-error-quark", nm_agent_manager_error_quark); NM_CACHED_QUARK_FCN("nm-connection-error-quark", nm_connection_error_quark); -NM_CACHED_QUARK_FCN("nm-crypto-error-quark", nm_crypto_error_quark); NM_CACHED_QUARK_FCN("nm-device-error-quark", nm_device_error_quark); NM_CACHED_QUARK_FCN("nm-secret-agent-error-quark", nm_secret_agent_error_quark); NM_CACHED_QUARK_FCN("nm-settings-error-quark", nm_settings_error_quark); NM_CACHED_QUARK_FCN("nm-vpn-plugin-error-quark", nm_vpn_plugin_error_quark); +GQuark +nm_crypto_error_quark(void) +{ + G_STATIC_ASSERT(NM_CRYPTO_ERROR_FAILED == _NM_CRYPTO_ERROR_FAILED); + G_STATIC_ASSERT(NM_CRYPTO_ERROR_INVALID_DATA == _NM_CRYPTO_ERROR_INVALID_DATA); + G_STATIC_ASSERT(NM_CRYPTO_ERROR_INVALID_PASSWORD == _NM_CRYPTO_ERROR_INVALID_PASSWORD); + G_STATIC_ASSERT(NM_CRYPTO_ERROR_UNKNOWN_CIPHER == _NM_CRYPTO_ERROR_UNKNOWN_CIPHER); + G_STATIC_ASSERT(NM_CRYPTO_ERROR_DECRYPTION_FAILED == _NM_CRYPTO_ERROR_DECRYPTION_FAILED); + G_STATIC_ASSERT(NM_CRYPTO_ERROR_ENCRYPTION_FAILED == _NM_CRYPTO_ERROR_ENCRYPTION_FAILED); + + return _nm_crypto_error_quark(); +} + static void register_error_domain(GQuark domain, const char *interface, GType enum_type) { diff --git a/src/libnm-core-impl/nm-keyfile.c b/src/libnm-core-impl/nm-keyfile.c index dce53ae2..00fb8a33 100644 --- a/src/libnm-core-impl/nm-keyfile.c +++ b/src/libnm-core-impl/nm-keyfile.c @@ -115,12 +115,14 @@ _key_file_handler_data_init_write(NMKeyfileHandlerData *handler_data, &info->error); } -_nm_printf(5, 6) static void _handle_warn(KeyfileReaderInfo *info, - const char *kf_key, - const char *cur_property, - NMKeyfileWarnSeverity severity, - const char *fmt, - ...) +/*****************************************************************************/ + +_nm_printf(5, 6) static void _read_handle_warn(KeyfileReaderInfo *info, + const char *kf_key, + const char *cur_property, + NMKeyfileWarnSeverity severity, + const char *fmt, + ...) { NMKeyfileHandlerData handler_data; @@ -148,16 +150,75 @@ _nm_printf(5, 6) static void _handle_warn(KeyfileReaderInfo *info, g_free(handler_data.warn.message); } -#define handle_warn(arg_info, arg_kf_key, arg_property_name, arg_severity, ...) \ - ({ \ - KeyfileReaderInfo *_info = (arg_info); \ - \ - nm_assert(!_info->error); \ - \ - if (_info->read_handler) { \ - _handle_warn(_info, (arg_kf_key), (arg_property_name), (arg_severity), __VA_ARGS__); \ - } \ - _info->error == NULL; \ +#define read_handle_warn(arg_info, arg_kf_key, arg_property_name, arg_severity, ...) \ + ({ \ + KeyfileReaderInfo *_info = (arg_info); \ + \ + nm_assert(!_info->error); \ + \ + if (_info->read_handler) { \ + _read_handle_warn(_info, \ + (arg_kf_key), \ + (arg_property_name), \ + (arg_severity), \ + __VA_ARGS__); \ + } \ + _info->error == NULL; \ + }) + +/*****************************************************************************/ + +_nm_printf(6, 7) static void _write_handle_warn(KeyfileWriterInfo *info, + NMSetting *setting, + const char *kf_key, + const char *cur_property, + NMKeyfileWarnSeverity severity, + const char *fmt, + ...) +{ + NMKeyfileHandlerData handler_data; + + _key_file_handler_data_init_write(&handler_data, + NM_KEYFILE_HANDLER_TYPE_WARN, + info, + nm_setting_get_name(setting), + cur_property, + setting, + kf_key); + handler_data.warn = (NMKeyfileHandlerDataWarn){ + .severity = severity, + .message = NULL, + .fmt = fmt, + }; + + va_start(handler_data.warn.ap, fmt); + + info->write_handler(info->connection, + info->keyfile, + NM_KEYFILE_HANDLER_TYPE_WARN, + &handler_data, + info->user_data); + + va_end(handler_data.warn.ap); + + g_free(handler_data.warn.message); +} + +#define write_handle_warn(arg_info, arg_setting, arg_kf_key, arg_property_name, arg_severity, ...) \ + ({ \ + KeyfileWriterInfo *_info = (arg_info); \ + \ + nm_assert(!_info->error); \ + \ + if (_info->write_handler) { \ + _write_handle_warn(_info, \ + (arg_setting), \ + (arg_kf_key), \ + (arg_property_name), \ + (arg_severity), \ + __VA_ARGS__); \ + } \ + _info->error == NULL; \ }) /*****************************************************************************/ @@ -264,11 +325,11 @@ get_one_int(KeyfileReaderInfo *info, if (!str || !str[0]) { if (info) { - handle_warn(info, - kf_key, - property_name, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("ignoring missing number")); + read_handle_warn(info, + kf_key, + property_name, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring missing number")); } return FALSE; } @@ -276,12 +337,12 @@ get_one_int(KeyfileReaderInfo *info, tmp = _nm_utils_ascii_str_to_int64(str, 10, 0, max_val, -1); if (tmp == -1) { if (info) { - handle_warn(info, - kf_key, - property_name, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("ignoring invalid number '%s'"), - str); + read_handle_warn(info, + kf_key, + property_name, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid number '%s'"), + str); } return FALSE; } @@ -305,13 +366,13 @@ build_address(KeyfileReaderInfo *info, addr = nm_ip_address_new(family, address_str, plen, &error); if (!addr) { - handle_warn(info, - kf_key, - property_name, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("ignoring invalid %s address: %s"), - family == AF_INET ? "IPv4" : "IPv6", - error->message); + read_handle_warn(info, + kf_key, + property_name, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid %s address: %s"), + family == AF_INET ? "IPv4" : "IPv6", + error->message); g_error_free(error); } @@ -351,13 +412,13 @@ build_route(KeyfileReaderInfo *info, metric = u32; gateway_str = NULL; } else { - handle_warn(info, - kf_key, - property_name, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("ignoring invalid gateway '%s' for %s route"), - gateway_str, - family == AF_INET ? "IPv4" : "IPv6"); + read_handle_warn(info, + kf_key, + property_name, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid gateway '%s' for %s route"), + gateway_str, + family == AF_INET ? "IPv4" : "IPv6"); return NULL; } } @@ -373,13 +434,13 @@ build_route(KeyfileReaderInfo *info, route = nm_ip_route_new(family, dest_str, plen, gateway_str, metric, &error); if (!route) { - handle_warn(info, - kf_key, - property_name, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("ignoring invalid %s route: %s"), - family == AF_INET ? "IPv4" : "IPv6", - error->message); + read_handle_warn(info, + kf_key, + property_name, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid %s route: %s"), + family == AF_INET ? "IPv4" : "IPv6", + error->message); g_error_free(error); } @@ -551,15 +612,15 @@ read_one_ip_address_or_route(KeyfileReaderInfo *info, /* get address field */ address_str = read_field(¤t, &err_str, IP_ADDRESS_CHARS, DELIMITERS); if (err_str) { - handle_warn(info, - kf_key, - property_name, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("unexpected character '%c' for address %s: '%s' (position %td)"), - *err_str, - kf_key, - VALUE_ORIG(), - err_str - current); + read_handle_warn(info, + kf_key, + property_name, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("unexpected character '%c' for address %s: '%s' (position %td)"), + *err_str, + kf_key, + VALUE_ORIG(), + err_str - current); return NULL; } /* get prefix length field (skippable) */ @@ -567,30 +628,31 @@ read_one_ip_address_or_route(KeyfileReaderInfo *info, /* get gateway field */ gateway_str = read_field(¤t, &err_str, IP_ADDRESS_CHARS, DELIMITERS); if (err_str) { - handle_warn(info, - kf_key, - property_name, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("unexpected character '%c' for %s: '%s' (position %td)"), - *err_str, - kf_key, - VALUE_ORIG(), - err_str - current); + read_handle_warn(info, + kf_key, + property_name, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("unexpected character '%c' for %s: '%s' (position %td)"), + *err_str, + kf_key, + VALUE_ORIG(), + err_str - current); return NULL; } /* for routes, get metric */ if (route) { metric_str = read_field(¤t, &err_str, DIGITS, DELIMITERS); if (err_str) { - handle_warn(info, - kf_key, - property_name, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("unexpected character '%c' in prefix length for %s: '%s' (position %td)"), - *err_str, - kf_key, - VALUE_ORIG(), - err_str - current); + read_handle_warn( + info, + kf_key, + property_name, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("unexpected character '%c' in prefix length for %s: '%s' (position %td)"), + *err_str, + kf_key, + VALUE_ORIG(), + err_str - current); return NULL; } } else @@ -599,23 +661,23 @@ read_one_ip_address_or_route(KeyfileReaderInfo *info, /* there is still some data */ if (*current) { /* another field follows */ - handle_warn(info, - kf_key, - property_name, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("garbage at the end of value %s: '%s'"), - kf_key, - VALUE_ORIG()); - return NULL; - } else { - /* semicolon at the end of input */ - if (!handle_warn(info, + read_handle_warn(info, kf_key, property_name, - NM_KEYFILE_WARN_SEVERITY_INFO, - _("deprecated semicolon at the end of value %s: '%s'"), + NM_KEYFILE_WARN_SEVERITY_WARN, + _("garbage at the end of value %s: '%s'"), kf_key, - VALUE_ORIG())) + VALUE_ORIG()); + return NULL; + } else { + /* semicolon at the end of input */ + if (!read_handle_warn(info, + kf_key, + property_name, + NM_KEYFILE_WARN_SEVERITY_INFO, + _("deprecated semicolon at the end of value %s: '%s'"), + kf_key, + VALUE_ORIG())) return NULL; } } @@ -628,26 +690,26 @@ read_one_ip_address_or_route(KeyfileReaderInfo *info, if (!get_one_int(info, kf_key, property_name, plen_str, ipv6 ? 128 : 32, &plen)) { plen = DEFAULT_PREFIX(route, ipv6); if (info->error - || !handle_warn(info, - kf_key, - property_name, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid prefix length for %s '%s', defaulting to %d"), - kf_key, - VALUE_ORIG(), - plen)) + || !read_handle_warn(info, + kf_key, + property_name, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid prefix length for %s '%s', defaulting to %d"), + kf_key, + VALUE_ORIG(), + plen)) return NULL; } } else { plen = DEFAULT_PREFIX(route, ipv6); - if (!handle_warn(info, - kf_key, - property_name, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("missing prefix length for %s '%s', defaulting to %d"), - kf_key, - VALUE_ORIG(), - plen)) + if (!read_handle_warn(info, + kf_key, + property_name, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("missing prefix length for %s '%s', defaulting to %d"), + kf_key, + VALUE_ORIG(), + plen)) return NULL; } @@ -983,13 +1045,13 @@ ip_routing_rule_parser_full(KeyfileReaderInfo *info, NULL, &local); if (!rule) { - if (!handle_warn(info, - build_list[i_build_list].s_key, - property_info->name, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid value for \"%s\": %s"), - build_list[i_build_list].s_key, - local->message)) + if (!read_handle_warn(info, + build_list[i_build_list].s_key, + property_info->name, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid value for \"%s\": %s"), + build_list[i_build_list].s_key, + local->message)) return; continue; } @@ -1061,13 +1123,13 @@ ip_dns_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) NMIPAddr addr; if (inet_pton(addr_family, list[i], &addr) <= 0) { - if (!handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("ignoring invalid DNS server IPv%c address '%s'"), - nm_utils_addr_family_to_char(addr_family), - list[i])) { + if (!read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid DNS server IPv%c address '%s'"), + nm_utils_addr_family_to_char(addr_family), + list[i])) { do { nm_clear_g_free(&list[i]); } while (++i < length); @@ -1098,13 +1160,13 @@ ip6_addr_gen_mode_parser(KeyfileReaderInfo *info, NMSetting *setting, const char s, (int *) &addr_gen_mode, NULL)) { - handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid option '%s', use one of [%s]"), - s, - "eui64,stable-privacy"); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid option '%s', use one of [%s]"), + s, + "eui64,stable-privacy"); return; } } else @@ -1160,7 +1222,11 @@ mac_address_parser(KeyfileReaderInfo *info, goto good_addr_bin; } - handle_warn(info, key, key, NM_KEYFILE_WARN_SEVERITY_WARN, _("ignoring invalid MAC address")); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid MAC address")); return; good_addr_bin: @@ -1232,14 +1298,14 @@ read_hash_of_string(KeyfileReaderInfo *info, gs_free_error GError *error = NULL; if (!_nm_setting_bond_validate_option(name, value, &error)) { - if (!handle_warn(info, - kf_key, - name, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("ignoring invalid bond option %s%s%s = %s%s%s: %s"), - NM_PRINT_FMT_QUOTE_STRING(name), - NM_PRINT_FMT_QUOTE_STRING(value), - error->message)) + if (!read_handle_warn(info, + kf_key, + name, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid bond option %s%s%s = %s%s%s: %s"), + NM_PRINT_FMT_QUOTE_STRING(name), + NM_PRINT_FMT_QUOTE_STRING(value), + error->message)) return; } else nm_setting_bond_add_option(NM_SETTING_BOND(setting), name, value); @@ -1426,7 +1492,7 @@ ssid_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) bytes = get_bytes(info, setting_name, key, FALSE, TRUE); if (!bytes) { - handle_warn(info, key, key, NM_KEYFILE_WARN_SEVERITY_WARN, _("ignoring invalid SSID")); + read_handle_warn(info, key, key, NM_KEYFILE_WARN_SEVERITY_WARN, _("ignoring invalid SSID")); return; } g_object_set(setting, key, bytes, NULL); @@ -1440,11 +1506,11 @@ password_raw_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key bytes = get_bytes(info, setting_name, key, FALSE, TRUE); if (!bytes) { - handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("ignoring invalid raw password")); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid raw password")); return; } g_object_set(setting, key, bytes, NULL); @@ -1585,7 +1651,11 @@ cert_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) bin = g_bytes_get_data(bytes, &bin_len); if (bin_len == 0) { if (!info->error) { - handle_warn(info, key, key, NM_KEYFILE_WARN_SEVERITY_WARN, _("invalid key/cert value")); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid key/cert value")); } return; } @@ -1596,12 +1666,12 @@ cert_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) if (nm_setting_802_1x_check_cert_scheme(bin, bin_len, NULL) != NM_SETTING_802_1X_CK_SCHEME_PATH) { - handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid key/cert value path \"%s\""), - bin); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid key/cert value path \"%s\""), + bin); return; } @@ -1621,12 +1691,12 @@ cert_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) * then by invoking a callback (and possibly keyfile settings plugin would * collect the file names to be checked and check them later). */ if (!g_file_test(path2, G_FILE_TEST_EXISTS)) { - handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_INFO_MISSING_FILE, - _("certificate or key file '%s' does not exist"), - path2); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_INFO_MISSING_FILE, + _("certificate or key file '%s' does not exist"), + path2); } return; } @@ -1634,12 +1704,12 @@ cert_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) if (HAS_SCHEME_PREFIX(bin, bin_len, NM_KEYFILE_CERT_SCHEME_PREFIX_PKCS11)) { if (nm_setting_802_1x_check_cert_scheme(bin, bin_len, NULL) != NM_SETTING_802_1X_CK_SCHEME_PKCS11) { - handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid PKCS#11 URI \"%s\""), - bin); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid PKCS#11 URI \"%s\""), + bin); return; } @@ -1680,11 +1750,11 @@ cert_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) bin_decoded = g_base64_decode(cdata, &bin_decoded_len); if (bin_decoded_len == 0) { - handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid key/cert value data:;base64, is not base64")); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid key/cert value data:;base64, is not base64")); return; } @@ -1693,11 +1763,11 @@ cert_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) /* The blob probably starts with "file://". Setting the cert data will confuse NMSetting8021x. * In fact this is a limitation of NMSetting8021x which does not support setting blobs that start * with file://. Just warn and return TRUE to signal that we ~handled~ the setting. */ - handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid key/cert value data:;base64,file://")); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid key/cert value data:;base64,file://")); return; } @@ -1718,12 +1788,12 @@ cert_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) /* Warn if the certificate didn't exist */ if (!path_exists) { - handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_INFO_MISSING_FILE, - _("certificate or key file '%s' does not exist"), - path); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_INFO_MISSING_FILE, + _("certificate or key file '%s' does not exist"), + path); } return; } @@ -1734,11 +1804,11 @@ cert_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) * Setting the cert data will confuse NMSetting8021x. * In fact, NMSetting8021x does not support setting such binary data, so just warn and * continue. */ - handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid key/cert value is not a valid blob")); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid key/cert value is not a valid blob")); return; } @@ -1836,12 +1906,12 @@ parity_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) goto parity_good; } - handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid parity value '%s'"), - tmp_str ?: ""); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid parity value '%s'"), + tmp_str ?: ""); return; parity_good: @@ -1858,12 +1928,12 @@ out_err: /* ignore such errors. The key is not present. */ return; } - handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid setting: %s"), - err->message); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid setting: %s"), + err->message); } static void @@ -1878,12 +1948,12 @@ team_config_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) g_object_set(G_OBJECT(setting), key, conf, NULL); if (conf && !nm_setting_verify(setting, NULL, &error)) { - handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("ignoring invalid team configuration: %s"), - error->message); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("ignoring invalid team configuration: %s"), + error->message); g_object_set(G_OBJECT(setting), key, NULL, NULL); } } @@ -1909,12 +1979,12 @@ bridge_vlan_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) for (iter = strv; *iter; iter++) { vlan = nm_bridge_vlan_from_str(*iter, &local); if (!vlan) { - handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - "invalid bridge VLAN: %s", - local->message); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + "invalid bridge VLAN: %s", + local->message); g_clear_error(&local); continue; } @@ -1961,12 +2031,12 @@ qdisc_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) qdisc = nm_utils_tc_qdisc_from_str(qdisc_str, &err); if (!qdisc) { - handle_warn(info, - keys[i], - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid qdisc: %s"), - err->message); + read_handle_warn(info, + keys[i], + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid qdisc: %s"), + err->message); } else { g_ptr_array_add(qdiscs, qdisc); } @@ -2011,12 +2081,12 @@ tfilter_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) tfilter = nm_utils_tc_tfilter_from_str(tfilter_str, &err); if (!tfilter) { - handle_warn(info, - keys[i], - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid tfilter: %s"), - err->message); + read_handle_warn(info, + keys[i], + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid tfilter: %s"), + err->message); } else { g_ptr_array_add(tfilters, tfilter); } @@ -2689,16 +2759,10 @@ cert_writer_default(NMConnection *connection, static void cert_writer(KeyfileWriterInfo *info, NMSetting *setting, const char *key, const GValue *value) { - const NMSetting8021xSchemeVtable *vtable = NULL; + const NMSetting8021xSchemeVtable *vtable; const char *setting_name; - guint i; - for (i = 0; nm_setting_8021x_scheme_vtable[i].setting_key; i++) { - if (nm_streq0(nm_setting_8021x_scheme_vtable[i].setting_key, key)) { - vtable = &nm_setting_8021x_scheme_vtable[i]; - break; - } - } + vtable = nm_setting_8021x_scheme_vtable_by_setting_key(key); if (!vtable) g_return_if_reached(); @@ -3181,12 +3245,12 @@ read_one_setting_value(KeyfileReaderInfo *info, && !nm_keyfile_plugin_kf_has_key(keyfile, setting_info->setting_name, key, &err)) { /* Key doesn't exist or an error occurred, thus nothing to do. */ if (err) { - if (!handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("error loading setting value: %s"), - err->message)) + if (!read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("error loading setting value: %s"), + err->message)) return; } return; @@ -3296,7 +3360,7 @@ read_one_setting_value(KeyfileReaderInfo *info, if (val > 255u) { if (!already_warned - && !handle_warn( + && !read_handle_warn( info, key, key, @@ -3356,12 +3420,12 @@ read_one_setting_value(KeyfileReaderInfo *info, if (nm_keyfile_error_is_not_found(err)) { /* ignore such errors. The key is not present. */ } else { - handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid setting: %s"), - err->message); + read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid setting: %s"), + err->message); } } } @@ -3381,12 +3445,12 @@ _read_setting(KeyfileReaderInfo *info) type = nm_setting_lookup_type(alias); if (!type) { - handle_warn(info, - NULL, - NULL, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid setting name '%s'"), - info->group); + read_handle_warn(info, + NULL, + NULL, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid setting name '%s'"), + info->group); return; } @@ -3428,13 +3492,13 @@ _read_setting(KeyfileReaderInfo *info) variant_type = sett_info->detail.gendata_info->get_variant_type(sett_info, key, &local); if (!variant_type) { - if (!handle_warn(info, - key, - NULL, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid key '%s.%s'"), - info->group, - key)) + if (!read_handle_warn(info, + key, + NULL, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid key '%s.%s'"), + info->group, + key)) break; continue; } @@ -3444,13 +3508,13 @@ _read_setting(KeyfileReaderInfo *info) v = g_key_file_get_boolean(info->keyfile, info->group, key, &local); if (local) { - if (!handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("key '%s.%s' is not boolean"), - info->group, - key)) + if (!read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("key '%s.%s' is not boolean"), + info->group, + key)) break; continue; } @@ -3461,13 +3525,13 @@ _read_setting(KeyfileReaderInfo *info) v = g_key_file_get_uint64(info->keyfile, info->group, key, &local); if (local) { - if (!handle_warn(info, - key, - key, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("key '%s.%s' is not a uint32"), - info->group, - key)) + if (!read_handle_warn(info, + key, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("key '%s.%s' is not a uint32"), + info->group, + key)) break; continue; } @@ -3518,12 +3582,12 @@ _read_setting_wireguard_peer(KeyfileReaderInfo *info) || !nm_streq0(str, cstr)) { /* the group name must be identical to the normalized(!) key, so that it * is uniquely identified. */ - handle_warn(info, - NULL, - NM_SETTING_WIREGUARD_PEERS, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("invalid peer public key in section '%s'"), - info->group); + read_handle_warn(info, + NULL, + NM_SETTING_WIREGUARD_PEERS, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid peer public key in section '%s'"), + info->group); return; } nm_wireguard_peer_set_public_key(peer, cstr, TRUE); @@ -3533,13 +3597,13 @@ _read_setting_wireguard_peer(KeyfileReaderInfo *info) str = nm_keyfile_plugin_kf_get_string(info->keyfile, info->group, key, NULL); if (str) { if (!nm_wireguard_peer_set_preshared_key(peer, str, FALSE)) { - if (!handle_warn(info, - key, - NM_SETTING_WIREGUARD_PEERS, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("key '%s.%s' is not a valid 256 bit key in base64 encoding"), - info->group, - key)) + if (!read_handle_warn(info, + key, + NM_SETTING_WIREGUARD_PEERS, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("key '%s.%s' is not a valid 256 bit key in base64 encoding"), + info->group, + key)) return; } nm_clear_g_free(&str); @@ -3556,13 +3620,13 @@ _read_setting_wireguard_peer(KeyfileReaderInfo *info) NULL); if (errno != ENODATA) { if (i64 == -1 || !_nm_setting_secret_flags_valid(i64)) { - if (!handle_warn(info, - key, - NM_SETTING_WIREGUARD_PEERS, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("key '%s.%s' is not a valid secret flag"), - info->group, - key)) + if (!read_handle_warn(info, + key, + NM_SETTING_WIREGUARD_PEERS, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("key '%s.%s' is not a valid secret flag"), + info->group, + key)) return; } else nm_wireguard_peer_set_preshared_key_flags(peer, i64); @@ -3579,13 +3643,13 @@ _read_setting_wireguard_peer(KeyfileReaderInfo *info) NULL); if (errno != ENODATA) { if (i64 == -1) { - if (!handle_warn(info, - key, - NM_SETTING_WIREGUARD_PEERS, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("key '%s.%s' is not a integer in range 0 to 2^32"), - info->group, - key)) + if (!read_handle_warn(info, + key, + NM_SETTING_WIREGUARD_PEERS, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("key '%s.%s' is not a integer in range 0 to 2^32"), + info->group, + key)) return; } else nm_wireguard_peer_set_persistent_keepalive(peer, i64); @@ -3595,13 +3659,13 @@ _read_setting_wireguard_peer(KeyfileReaderInfo *info) str = nm_keyfile_plugin_kf_get_string(info->keyfile, info->group, key, NULL); if (str && str[0]) { if (!nm_wireguard_peer_set_endpoint(peer, str, FALSE)) { - if (!handle_warn(info, - key, - NM_SETTING_WIREGUARD_PEERS, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("key '%s.%s' is not a valid endpoint"), - info->group, - key)) + if (!read_handle_warn(info, + key, + NM_SETTING_WIREGUARD_PEERS, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("key '%s.%s' is not a valid endpoint"), + info->group, + key)) return; } } @@ -3621,13 +3685,13 @@ _read_setting_wireguard_peer(KeyfileReaderInfo *info) nm_wireguard_peer_append_allowed_ip(peer, sa[i], TRUE); } if (has_error) { - if (!handle_warn(info, - key, - NM_SETTING_WIREGUARD_PEERS, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("key '%s.%s' has invalid allowed-ips"), - info->group, - key)) + if (!read_handle_warn(info, + key, + NM_SETTING_WIREGUARD_PEERS, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("key '%s.%s' has invalid allowed-ips"), + info->group, + key)) return; } } @@ -3636,13 +3700,13 @@ _read_setting_wireguard_peer(KeyfileReaderInfo *info) return; if (!nm_wireguard_peer_is_valid(peer, TRUE, TRUE, &error)) { - handle_warn(info, - NULL, - NM_SETTING_WIREGUARD_PEERS, - NM_KEYFILE_WARN_SEVERITY_WARN, - _("peer '%s' is invalid: %s"), - info->group, - error->message); + read_handle_warn(info, + NULL, + NM_SETTING_WIREGUARD_PEERS, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("peer '%s' is invalid: %s"), + info->group, + error->message); return; } @@ -3855,29 +3919,24 @@ write_setting_value(KeyfileWriterInfo *info, _parse_info_find(setting, key, &setting_info, NULL, &pip); + if (pip && pip->has_writer_full) { + pip->writer_full(info, setting_info, property_info, pip, setting); + return; + } + + if (pip && pip->writer_skip) + return; + if (!pip) { if (!setting_info) { - /* the setting type is unknown. That is highly unexpected - * (and as this is currently only called from NetworkManager - * daemon, not possible). - * - * Still, handle it gracefully, because later keyfile writer will become - * public API of libnm, where @setting is (untrusted) user input. - * - * Gracefully here just means: ignore the setting. */ + /* the setting type is unknown. Handle this gracefully by + * ignoring the setting. */ return; } if (!property_info->param_spec) return; if (nm_streq(key, NM_SETTING_NAME)) return; - } else { - if (pip->has_writer_full) { - pip->writer_full(info, setting_info, property_info, pip, setting); - return; - } - if (pip->writer_skip) - return; } nm_assert(property_info->param_spec); @@ -4089,8 +4148,10 @@ _write_setting_wireguard(NMSetting *setting, KeyfileWriterInfo *info) * @user_data: argument for @handler. * @error: the #GError in case writing fails. * - * @connection must verify as a valid profile according to - * nm_connection_verify(). + * @connection should verify as a valid profile according to + * nm_connection_verify(). If it does not verify, the keyfile may + * be incomplete and the parser may not be able to fully recreate + * the original profile. * * Returns: (transfer full): a new #GKeyFile or %NULL on error. * @@ -4104,7 +4165,6 @@ nm_keyfile_write(NMConnection *connection, GError **error) { nm_auto_unref_keyfile GKeyFile *keyfile = NULL; - GError *local = NULL; KeyfileWriterInfo info; NMSetting **settings; int i; @@ -4114,28 +4174,6 @@ nm_keyfile_write(NMConnection *connection, g_return_val_if_fail(!error || !*error, NULL); g_return_val_if_fail(handler_flags == NM_KEYFILE_HANDLER_FLAGS_NONE, NULL); - /* Technically, we might not require that a profile is valid in - * order to serialize it. Like also nm_keyfile_read() does not - * ensure that the read profile validates. - * - * However, if the profile does not validate, then there might be - * unexpected edge cases when we try to serialize it. Edge cases - * that might result in dangerous crash. - * - * So, for now we require valid profiles. */ - if (!nm_connection_verify(connection, error ? &local : NULL)) { - if (error) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_FAILED, - _("the profile is not valid: %s"), - local->message); - g_error_free(local); - } else - nm_assert(!local); - return NULL; - } - keyfile = g_key_file_new(); info = (KeyfileWriterInfo){ @@ -4189,11 +4227,16 @@ nm_keyfile_write(NMConnection *connection, key, (guint64) g_variant_get_uint32(v)); } else { - /* BUG: The variant type is not implemented. Since the connection - * verifies, this can only mean we either wrongly didn't reject - * the connection as invalid, or we didn't properly implement the - * variant type. */ - nm_assert_not_reached(); + if (!write_handle_warn(&info, + setting, + NULL, + key, + NM_KEYFILE_WARN_SEVERITY_WARN, + _("unsupported option \"%s.%s\" of variant type %s"), + setting_name, + key, + g_variant_get_type_string(v))) + goto out_with_info_error; continue; } } diff --git a/src/libnm-core-impl/nm-meta-setting-base-impl.c b/src/libnm-core-impl/nm-meta-setting-base-impl.c index cd55779f..69daa76c 100644 --- a/src/libnm-core-impl/nm-meta-setting-base-impl.c +++ b/src/libnm-core-impl/nm-meta-setting-base-impl.c @@ -153,6 +153,68 @@ const NMSetting8021xSchemeVtable nm_setting_8021x_scheme_vtable[] = { #undef _D }; +const NMSetting8021xSchemeVtable * +nm_setting_8021x_scheme_vtable_by_setting_key(const char *key) +{ + static const NMSetting8021xSchemeType sorted_index[] = { + NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT, + NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT, + NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT, + NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT, + NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY, + NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY, + }; + int imin, imax; + + nm_assert(key); + + if (NM_MORE_ASSERT_ONCE(5)) { + const NMSetting8021xSchemeVtable *vtable_prev = NULL; + int i, j; + + for (i = 0; i < (int) G_N_ELEMENTS(sorted_index); i++) { + const NMSetting8021xSchemeType t = sorted_index[i]; + const NMSetting8021xSchemeVtable *vtable; + + nm_assert(_NM_INT_NOT_NEGATIVE(t)); + nm_assert(t < G_N_ELEMENTS(nm_setting_8021x_scheme_vtable) - 1); + + for (j = 0; j < i; j++) + nm_assert(t != sorted_index[j]); + + vtable = &nm_setting_8021x_scheme_vtable[t]; + + nm_assert(vtable->scheme_type == t); + nm_assert(vtable->setting_key); + + if (vtable_prev) + nm_assert(strcmp(vtable_prev->setting_key, vtable->setting_key) < 0); + vtable_prev = vtable; + } + } + + imin = 0; + imax = G_N_ELEMENTS(sorted_index) - 1; + while (imin <= imax) { + const NMSetting8021xSchemeVtable *vtable; + const int imid = imin + (imax - imin) / 2; + int cmp; + + vtable = &nm_setting_8021x_scheme_vtable[sorted_index[imid]]; + + cmp = strcmp(vtable->setting_key, key); + if (cmp == 0) + return vtable; + + if (cmp < 0) + imin = imid + 1; + else + imax = imid - 1; + } + + return NULL; +} + /*****************************************************************************/ const NMMetaSettingInfo nm_meta_setting_infos[] = { diff --git a/src/libnm-core-impl/nm-setting-8021x.c b/src/libnm-core-impl/nm-setting-8021x.c index 884f8830..41feae57 100644 --- a/src/libnm-core-impl/nm-setting-8021x.c +++ b/src/libnm-core-impl/nm-setting-8021x.c @@ -9,8 +9,8 @@ #include "nm-setting-8021x.h" #include "libnm-glib-aux/nm-secret-utils.h" +#include "libnm-crypto/nm-crypto.h" #include "nm-utils.h" -#include "nm-crypto.h" #include "nm-utils-private.h" #include "nm-setting-private.h" #include "nm-core-enum-types.h" @@ -516,7 +516,7 @@ _cert_impl_set(NMSetting8021x *setting, 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); + file = nm_utils_read_crypto_file_to_bytes(value, error); if (!file) goto err; format = nm_crypto_verify_private_key_data(g_bytes_get_data(file, NULL), @@ -2520,230 +2520,172 @@ need_secrets_sim(NMSetting8021x *self, GPtrArray *secrets, gboolean phase2) g_ptr_array_add(secrets, NM_SETTING_802_1X_PIN); } -static gboolean -need_private_key_password(GBytes *blob, - NMSetting8021xCKScheme scheme, - const char *path, - const char *password, - NMSettingSecretFlags flags) -{ - NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; - - if (flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED) - return FALSE; - - /* Private key password is required */ - if (password) { - if (path) - format = nm_crypto_verify_private_key(path, password, NULL, NULL); - else if (blob) - format = nm_crypto_verify_private_key_data(g_bytes_get_data(blob, NULL), - g_bytes_get_size(blob), - password, - NULL, - NULL); - else - return FALSE; - } - - return (format == NM_CRYPTO_FILE_FORMAT_UNKNOWN); -} - static void need_secrets_tls(NMSetting8021x *self, GPtrArray *secrets, gboolean phase2) { NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self); NMSetting8021xCKScheme scheme; - GBytes *blob = NULL; - const char *path = NULL; - - if (phase2) { - scheme = nm_setting_802_1x_get_phase2_private_key_scheme(self); - if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) - path = nm_setting_802_1x_get_phase2_private_key_path(self); - else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) - blob = nm_setting_802_1x_get_phase2_private_key_blob(self); - else if (scheme != NM_SETTING_802_1X_CK_SCHEME_PKCS11) - g_warning("%s: unknown phase2 private key scheme %d", __func__, scheme); - - if (need_private_key_password(blob, - scheme, - path, - priv->phase2_private_key_password, - priv->phase2_private_key_password_flags)) - g_ptr_array_add(secrets, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD); - - scheme = nm_setting_802_1x_get_phase2_ca_cert_scheme(self); - if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11 - && !(priv->phase2_ca_cert_password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED) - && !priv->phase2_ca_cert_password) - g_ptr_array_add(secrets, NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD); - scheme = nm_setting_802_1x_get_phase2_client_cert_scheme(self); - if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11 - && !(priv->phase2_client_cert_password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED) - && !priv->phase2_client_cert_password) - g_ptr_array_add(secrets, NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD); - } else { - scheme = nm_setting_802_1x_get_private_key_scheme(self); - if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) - path = nm_setting_802_1x_get_private_key_path(self); - else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) - blob = nm_setting_802_1x_get_private_key_blob(self); - else if (scheme != NM_SETTING_802_1X_CK_SCHEME_PKCS11) - g_warning("%s: unknown private key scheme %d", __func__, scheme); - - if (need_private_key_password(blob, - scheme, - path, - priv->private_key_password, - priv->private_key_password_flags)) - g_ptr_array_add(secrets, NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD); - - scheme = nm_setting_802_1x_get_ca_cert_scheme(self); + if (!NM_FLAGS_HAS(phase2 ? priv->phase2_private_key_password_flags + : priv->private_key_password_flags, + NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) { + NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN; + gboolean has_password = FALSE; + const char *password; + + password = phase2 ? priv->phase2_private_key_password : priv->private_key_password; + + /* Check whether the password works. */ + if (password) { + scheme = phase2 ? nm_setting_802_1x_get_phase2_private_key_scheme(self) + : nm_setting_802_1x_get_private_key_scheme(self); + + if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) { + const char *path = phase2 ? nm_setting_802_1x_get_phase2_private_key_path(self) + : nm_setting_802_1x_get_private_key_path(self); + + if (path) + format = nm_crypto_verify_private_key(path, password, NULL, NULL); + } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { + GBytes *blob = phase2 ? nm_setting_802_1x_get_phase2_private_key_blob(self) + : nm_setting_802_1x_get_private_key_blob(self); + + if (blob) + format = nm_crypto_verify_private_key_data(g_bytes_get_data(blob, NULL), + g_bytes_get_size(blob), + password, + NULL, + NULL); + } else { + /* For PKCS#11 URLS, we assume the password is correct. */ + has_password = TRUE; + } + } + if (!has_password && format == NM_CRYPTO_FILE_FORMAT_UNKNOWN) { + g_ptr_array_add(secrets, + phase2 ? NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD + : NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD); + } + } + + if (!NM_FLAGS_HAS(phase2 ? priv->phase2_ca_cert_password_flags : priv->ca_cert_password_flags, + NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) { + scheme = phase2 ? nm_setting_802_1x_get_phase2_ca_cert_scheme(self) + : nm_setting_802_1x_get_ca_cert_scheme(self); if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11 - && !(priv->ca_cert_password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED) - && !priv->ca_cert_password) - g_ptr_array_add(secrets, NM_SETTING_802_1X_CA_CERT_PASSWORD); + && !(phase2 ? priv->phase2_ca_cert_password : priv->ca_cert_password)) { + g_ptr_array_add(secrets, + phase2 ? NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD + : NM_SETTING_802_1X_CA_CERT_PASSWORD); + } + } - scheme = nm_setting_802_1x_get_client_cert_scheme(self); + if (!NM_FLAGS_HAS(phase2 ? priv->phase2_client_cert_password_flags + : priv->client_cert_password_flags, + NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) { + scheme = phase2 ? nm_setting_802_1x_get_phase2_client_cert_scheme(self) + : nm_setting_802_1x_get_client_cert_scheme(self); if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11 - && !(priv->client_cert_password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED) - && !priv->client_cert_password) - g_ptr_array_add(secrets, NM_SETTING_802_1X_CLIENT_CERT_PASSWORD); + && !(phase2 ? priv->phase2_client_cert_password : priv->client_cert_password)) { + g_ptr_array_add(secrets, + phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD + : NM_SETTING_802_1X_CLIENT_CERT_PASSWORD); + } } } static gboolean -verify_tls(NMSetting8021x *self, gboolean phase2, GError **error) +verify_identity(NMSetting8021x *self, gboolean phase2, GError **error) { NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self); - if (phase2) { - if (!priv->phase2_client_cert) { + if (nm_str_is_empty(priv->identity)) { + if (!priv->identity) { g_set_error_literal(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY, _("property is missing")); - g_prefix_error(error, - "%s.%s: ", - NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_PHASE2_CLIENT_CERT); - return FALSE; - } else if (!g_bytes_get_size(priv->phase2_client_cert)) { + } else { g_set_error_literal(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, _("property is empty")); - g_prefix_error(error, - "%s.%s: ", - NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_PHASE2_CLIENT_CERT); - return FALSE; } + g_prefix_error(error, + "%s.%s: ", + NM_SETTING_802_1X_SETTING_NAME, + NM_SETTING_802_1X_IDENTITY); + return FALSE; + } - /* Private key is required for TLS */ - if (!priv->phase2_private_key) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_MISSING_PROPERTY, - _("property is missing")); - g_prefix_error(error, - "%s.%s: ", - NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_PHASE2_PRIVATE_KEY); - return FALSE; - } else if (!g_bytes_get_size(priv->phase2_private_key)) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("property is empty")); - g_prefix_error(error, - "%s.%s: ", - NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_PHASE2_PRIVATE_KEY); - return FALSE; - } + return TRUE; +} - /* If the private key is PKCS#12, check that it matches the client cert */ - 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, +static gboolean +verify_tls(NMSetting8021x *self, gboolean phase2, GError **error) +{ + NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self); + GBytes *client_cert; + GBytes *private_key; + const char *prop_client_cert; + const char *prop_private_key; + + client_cert = phase2 ? priv->phase2_client_cert : priv->client_cert; + private_key = phase2 ? priv->phase2_private_key : priv->private_key; + prop_client_cert = + phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT : NM_SETTING_802_1X_CLIENT_CERT; + prop_private_key = + phase2 ? NM_SETTING_802_1X_PHASE2_PRIVATE_KEY : NM_SETTING_802_1X_PRIVATE_KEY; + + if (!client_cert) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error(error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, prop_client_cert); + return FALSE; + } + if (g_bytes_get_size(client_cert) == 0) { + g_set_error_literal(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("has to match '%s' property for PKCS#12"), - NM_SETTING_802_1X_PHASE2_PRIVATE_KEY); - g_prefix_error(error, - "%s.%s: ", - NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_PHASE2_CLIENT_CERT); - return FALSE; - } - } - } else { - if (!priv->client_cert) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_MISSING_PROPERTY, - _("property is missing")); - g_prefix_error(error, - "%s.%s: ", - NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_CLIENT_CERT); - return FALSE; - } else if (!g_bytes_get_size(priv->client_cert)) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("property is empty")); - g_prefix_error(error, - "%s.%s: ", - NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_CLIENT_CERT); - return FALSE; - } + _("property is empty")); + g_prefix_error(error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, prop_client_cert); + return FALSE; + } - /* Private key is required for TLS */ - if (!priv->private_key) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_MISSING_PROPERTY, - _("property is missing")); - g_prefix_error(error, - "%s.%s: ", - NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_PRIVATE_KEY); - return FALSE; - } else if (!g_bytes_get_size(priv->private_key)) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("property is empty")); - g_prefix_error(error, - "%s.%s: ", - NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_PRIVATE_KEY); - return FALSE; - } + /* Private key is required for TLS */ + if (!private_key) { + g_set_error_literal(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_MISSING_PROPERTY, + _("property is missing")); + g_prefix_error(error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, prop_private_key); + return FALSE; + } - /* If the private key is PKCS#12, check that it matches the client cert */ - 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, + if (g_bytes_get_size(private_key) == 0) { + g_set_error_literal(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("has to match '%s' property for PKCS#12"), - NM_SETTING_802_1X_PRIVATE_KEY); - g_prefix_error(error, - "%s.%s: ", - NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_CLIENT_CERT); - return FALSE; - } + _("property is empty")); + g_prefix_error(error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, prop_private_key); + return FALSE; + } + + if (_nm_setting_802_1x_cert_get_scheme(private_key, NULL) == NM_SETTING_802_1X_CK_SCHEME_BLOB + && nm_crypto_is_pkcs12_data(g_bytes_get_data(private_key, NULL), + g_bytes_get_size(private_key), + NULL)) { + /* If the private key is PKCS#12, check that it matches the client cert */ + if (!g_bytes_equal(private_key, client_cert)) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("has to match '%s' property for PKCS#12"), + prop_private_key); + g_prefix_error(error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, prop_client_cert); + return FALSE; } } @@ -2755,24 +2697,8 @@ verify_ttls(NMSetting8021x *self, gboolean phase2, GError **error) { NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self); - if (nm_str_is_empty(priv->identity)) { - if (!priv->identity) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_MISSING_PROPERTY, - _("property is missing")); - } else { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("property is empty")); - } - g_prefix_error(error, - "%s.%s: ", - NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_IDENTITY); + if (!verify_identity(self, phase2, error)) return FALSE; - } if ((!priv->phase2_auth && !priv->phase2_autheap) || (priv->phase2_auth && priv->phase2_autheap)) { @@ -2792,33 +2718,6 @@ verify_ttls(NMSetting8021x *self, gboolean phase2, GError **error) return TRUE; } -static gboolean -verify_identity(NMSetting8021x *self, gboolean phase2, GError **error) -{ - NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self); - - if (nm_str_is_empty(priv->identity)) { - if (!priv->identity) { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_MISSING_PROPERTY, - _("property is missing")); - } else { - g_set_error_literal(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("property is empty")); - } - g_prefix_error(error, - "%s.%s: ", - NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_IDENTITY); - return FALSE; - } - - return TRUE; -} - static void need_secrets_phase2(NMSetting8021x *self, GPtrArray *secrets, gboolean phase2) { @@ -2833,17 +2732,14 @@ need_secrets_phase2(NMSetting8021x *self, GPtrArray *secrets, gboolean phase2) if (!method) method = priv->phase2_autheap; - if (!method) { - g_warning("Couldn't find EAP method."); - g_assert_not_reached(); - return; - } + if (!method) + g_return_if_reached(); /* Ask the configured phase2 method if it needs secrets */ for (i = 0; eap_methods_table[i].method; i++) { - if (eap_methods_table[i].ns_func == NULL) + if (!eap_methods_table[i].ns_func) continue; - if (!strcmp(eap_methods_table[i].method, method)) { + if (nm_streq(eap_methods_table[i].method, method)) { (*eap_methods_table[i].ns_func)(self, secrets, TRUE); break; } @@ -2929,11 +2825,18 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) } if (!NM_IN_STRSET(priv->phase1_peapver, NULL, "0", "1")) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("'%s' is not a valid value for the property"), - priv->phase1_peapver); + if (priv->phase1_peapver) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid value for the property"), + priv->phase1_peapver); + } else { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("property is empty")); + } g_prefix_error(error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, @@ -2942,11 +2845,18 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) } if (!NM_IN_STRSET(priv->phase1_peaplabel, NULL, "0", "1")) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("'%s' is not a valid value for the property"), - priv->phase1_peaplabel); + if (priv->phase1_peaplabel) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid value for the property"), + priv->phase1_peaplabel); + } else { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("property is empty")); + } g_prefix_error(error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, @@ -2955,11 +2865,18 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) } 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, - _("'%s' is not a valid value for the property"), - priv->phase1_fast_provisioning); + if (priv->phase1_fast_provisioning) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid value for the property"), + priv->phase1_fast_provisioning); + } else { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("property is empty")); + } g_prefix_error(error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, @@ -2989,11 +2906,18 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) "otp", "md5", "tls")) { - g_set_error(error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("'%s' is not a valid value for the property"), - priv->phase2_auth); + if (priv->phase2_auth) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid value for the property"), + priv->phase2_auth); + } else { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("property is empty")); + } g_prefix_error(error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, @@ -3002,11 +2926,18 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) } 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, - _("'%s' is not a valid value for the property"), - priv->phase2_autheap); + if (priv->phase2_autheap) { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid value for the property"), + priv->phase2_autheap); + } else { + g_set_error(error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("property is empty")); + } g_prefix_error(error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, @@ -3049,6 +2980,50 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) error)) return FALSE; + /* normalizable warnings from here on. */ + +#define _check_strempty_and_return(priv, prop_name, field, error) \ + G_STMT_START \ + { \ + NMSetting8021xPrivate *_priv = (priv); \ + GError **_error = (error); \ + \ + if (_priv->field && _priv->field[0] == '\0') { \ + g_set_error(_error, \ + NM_CONNECTION_ERROR, \ + NM_CONNECTION_ERROR_INVALID_PROPERTY, \ + _("property is empty")); \ + g_prefix_error(_error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, "" prop_name ""); \ + return NM_SETTING_VERIFY_NORMALIZABLE; \ + } \ + } \ + G_STMT_END + + _check_strempty_and_return(priv, NM_SETTING_802_1X_IDENTITY, identity, error); + _check_strempty_and_return(priv, + NM_SETTING_802_1X_ANONYMOUS_IDENTITY, + anonymous_identity, + error); + _check_strempty_and_return(priv, NM_SETTING_802_1X_PAC_FILE, pac_file, error); + _check_strempty_and_return(priv, NM_SETTING_802_1X_SUBJECT_MATCH, subject_match, error); + _check_strempty_and_return(priv, + NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH, + phase2_subject_match, + error); + _check_strempty_and_return(priv, + NM_SETTING_802_1X_DOMAIN_SUFFIX_MATCH, + domain_suffix_match, + error); + _check_strempty_and_return(priv, + NM_SETTING_802_1X_PHASE2_DOMAIN_SUFFIX_MATCH, + phase2_domain_suffix_match, + error); + _check_strempty_and_return(priv, NM_SETTING_802_1X_DOMAIN_MATCH, domain_match, error); + _check_strempty_and_return(priv, + NM_SETTING_802_1X_PHASE2_DOMAIN_MATCH, + phase2_domain_match, + error); + return TRUE; } diff --git a/src/libnm-core-impl/nm-setting-connection.c b/src/libnm-core-impl/nm-setting-connection.c index 73611004..0d7d7cd5 100644 --- a/src/libnm-core-impl/nm-setting-connection.c +++ b/src/libnm-core-impl/nm-setting-connection.c @@ -71,32 +71,32 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMSettingConnection, PROP_MUD_URL, ); typedef struct { - GArray *permissions; - GArray *secondaries; - char *id; - char *uuid; - char *stable_id; - char *interface_name; - char *type; - char *master; - char *slave_type; - char *zone; - char *mud_url; - guint64 timestamp; - int autoconnect_slaves; - int metered; - gint32 autoconnect_priority; - gint32 autoconnect_retries; - gint32 multi_connect; - gint32 auth_retries; - gint32 mdns; - gint32 llmnr; - gint32 dns_over_tls; - gint32 wait_device_timeout; - gint32 lldp; - guint32 gateway_ping_timeout; - bool autoconnect; - bool read_only; + GArray *permissions; + NMValueStrv secondaries; + char *id; + char *uuid; + char *stable_id; + char *interface_name; + char *type; + char *master; + char *slave_type; + char *zone; + char *mud_url; + guint64 timestamp; + int autoconnect_slaves; + int metered; + gint32 autoconnect_priority; + gint32 autoconnect_retries; + gint32 multi_connect; + gint32 auth_retries; + gint32 mdns; + gint32 llmnr; + gint32 dns_over_tls; + gint32 wait_device_timeout; + gint32 lldp; + guint32 gateway_ping_timeout; + bool autoconnect; + bool read_only; } NMSettingConnectionPrivate; /** @@ -757,7 +757,7 @@ nm_setting_connection_get_autoconnect_slaves(NMSettingConnection *setting) GArray * _nm_setting_connection_get_secondaries(NMSettingConnection *setting) { - return NM_SETTING_CONNECTION_GET_PRIVATE(setting)->secondaries; + return NM_SETTING_CONNECTION_GET_PRIVATE(setting)->secondaries.arr; } /** @@ -771,7 +771,7 @@ nm_setting_connection_get_num_secondaries(NMSettingConnection *setting) { g_return_val_if_fail(NM_IS_SETTING_CONNECTION(setting), 0); - return nm_g_array_len(NM_SETTING_CONNECTION_GET_PRIVATE(setting)->secondaries); + return nm_g_array_len(NM_SETTING_CONNECTION_GET_PRIVATE(setting)->secondaries.arr); } /** @@ -794,14 +794,14 @@ nm_setting_connection_get_secondary(NMSettingConnection *setting, guint32 idx) priv = NM_SETTING_CONNECTION_GET_PRIVATE(setting); - secondaries_len = nm_g_array_len(priv->secondaries); + secondaries_len = nm_g_array_len(priv->secondaries.arr); if (idx >= secondaries_len) { /* access one past the length is OK. */ g_return_val_if_fail(idx == secondaries_len, NULL); return NULL; } - return nm_strvarray_get_idx(priv->secondaries, idx); + return nm_strvarray_get_idx(priv->secondaries.arr, idx); } /** @@ -841,10 +841,10 @@ nm_setting_connection_add_secondary(NMSettingConnection *setting, const char *se priv = NM_SETTING_CONNECTION_GET_PRIVATE(setting); - if (nm_strvarray_find_first(priv->secondaries, sec_uuid) >= 0) + if (nm_strvarray_find_first(priv->secondaries.arr, sec_uuid) >= 0) return FALSE; - nm_strvarray_add(nm_strvarray_ensure(&priv->secondaries), sec_uuid); + nm_strvarray_add(nm_strvarray_ensure(&priv->secondaries.arr), sec_uuid); _notify(setting, PROP_SECONDARIES); return TRUE; } @@ -865,9 +865,9 @@ nm_setting_connection_remove_secondary(NMSettingConnection *setting, guint32 idx priv = NM_SETTING_CONNECTION_GET_PRIVATE(setting); - g_return_if_fail(idx < nm_g_array_len(priv->secondaries)); + g_return_if_fail(idx < nm_g_array_len(priv->secondaries.arr)); - g_array_remove_index(priv->secondaries, idx); + g_array_remove_index(priv->secondaries.arr, idx); _notify(setting, PROP_SECONDARIES); } @@ -890,7 +890,7 @@ nm_setting_connection_remove_secondary_by_value(NMSettingConnection *setting, co priv = NM_SETTING_CONNECTION_GET_PRIVATE(setting); - if (nm_strvarray_remove_first(priv->secondaries, sec_uuid)) { + if (nm_strvarray_remove_first(priv->secondaries.arr, sec_uuid)) { _notify(setting, PROP_SECONDARIES); return TRUE; } @@ -1524,7 +1524,7 @@ after_interface_name: return NM_SETTING_VERIFY_NORMALIZABLE; } - if (!_nm_setting_connection_verify_secondaries(priv->secondaries, error)) + if (!_nm_setting_connection_verify_secondaries(priv->secondaries.arr, error)) return NM_SETTING_VERIFY_NORMALIZABLE; return TRUE; @@ -1637,9 +1637,6 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) case PROP_TIMESTAMP: g_value_set_uint64(value, nm_setting_connection_get_timestamp(setting)); break; - case PROP_SECONDARIES: - g_value_take_boxed(value, nm_strvarray_get_strv_non_empty_dup(priv->secondaries, NULL)); - break; default: _nm_setting_property_get_property_direct(object, prop_id, value, pspec); break; @@ -1675,9 +1672,6 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps case PROP_TIMESTAMP: priv->timestamp = g_value_get_uint64(value); break; - case PROP_SECONDARIES: - nm_strvarray_set_strv(&priv->secondaries, g_value_get_boxed(value)); - break; default: _nm_setting_property_set_property_direct(object, prop_id, value, pspec); break; @@ -1709,7 +1703,7 @@ finalize(GObject *object) NMSettingConnectionPrivate *priv = NM_SETTING_CONNECTION_GET_PRIVATE(object); nm_clear_pointer(&priv->permissions, g_array_unref); - nm_clear_pointer(&priv->secondaries, g_array_unref); + nm_clear_pointer(&priv->secondaries.arr, g_array_unref); G_OBJECT_CLASS(nm_setting_connection_parent_class)->finalize(object); } @@ -1808,7 +1802,8 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass) * is commonly also included, so that different systems end up generating * different IDs. Or with ipv6.addr-gen-mode=stable-privacy, also the device's * name is included, so that different interfaces yield different addresses. - * The per-host key is the identity of your machine and stored in /var/lib/NetworkManager/secret-key. + * The per-host key is the identity of your machine and stored in /var/lib/NetworkManager/secret_key. + * See NetworkManager(8) manual about the secret-key and the host identity. * * The '$' character is treated special to perform dynamic substitutions * at runtime. Currently, supported are "${CONNECTION}", "${DEVICE}", "${MAC}", @@ -1958,6 +1953,15 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass) * Note that autoconnect is not implemented for VPN profiles. See * #NMSettingConnection:secondaries as an alternative to automatically * connect VPN profiles. + * + * If multiple profiles are ready to autoconnect on the same device, + * the one with the better "connection.autoconnect-priority" is chosen. If + * the priorities are equal, then the most recently connected profile is activated. + * If the profiles were not connected earlier or their + * "connection.timestamp" is identical, the choice is undefined. + * + * Depending on "connection.multi-connect", a profile can (auto)connect only + * once at a time or multiple times. **/ /* ---ifcfg-rh--- * property: autoconnect @@ -2230,12 +2234,13 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass) * together with this connection. * ---end--- */ - obj_properties[PROP_SECONDARIES] = g_param_spec_boxed( - NM_SETTING_CONNECTION_SECONDARIES, - "", - "", - G_TYPE_STRV, - G_PARAM_READWRITE | NM_SETTING_PARAM_FUZZY_IGNORE | G_PARAM_STATIC_STRINGS); + _nm_setting_property_define_direct_strv(properties_override, + obj_properties, + NM_SETTING_CONNECTION_SECONDARIES, + PROP_SECONDARIES, + NM_SETTING_PARAM_FUZZY_IGNORE, + NMSettingConnectionPrivate, + secondaries); /** * NMSettingConnection:gateway-ping-timeout: diff --git a/src/libnm-core-impl/nm-setting-ip-config.c b/src/libnm-core-impl/nm-setting-ip-config.c index 52aa6515..cffd5b19 100644 --- a/src/libnm-core-impl/nm-setting-ip-config.c +++ b/src/libnm-core-impl/nm-setting-ip-config.c @@ -1390,7 +1390,8 @@ _ip_route_attribute_validate(const char *name, RTN_LOCAL, RTN_BLACKHOLE, RTN_UNREACHABLE, - RTN_PROHIBIT)) { + RTN_PROHIBIT, + RTN_THROW)) { g_set_error(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -1494,6 +1495,7 @@ _nm_ip_route_attribute_validate_all(const NMIPRoute *route, GError **error) case RTN_BLACKHOLE: case RTN_UNREACHABLE: case RTN_PROHIBIT: + case RTN_THROW: if (route->next_hop) { g_set_error(error, NM_CONNECTION_ERROR, @@ -2684,7 +2686,7 @@ nm_ip_routing_rule_validate(const NMIPRoutingRule *self, GError **error) g_set_error_literal(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("invalid priority")); + _("missing priority")); return FALSE; } diff --git a/src/libnm-core-impl/nm-setting-ip4-config.c b/src/libnm-core-impl/nm-setting-ip4-config.c index 5b06739c..bf555168 100644 --- a/src/libnm-core-impl/nm-setting-ip4-config.c +++ b/src/libnm-core-impl/nm-setting-ip4-config.c @@ -1006,7 +1006,8 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass) * </listitem> * <listitem> * <para><literal>"type"</literal> - one of <literal>unicast</literal>, <literal>local</literal>, <literal>blackhole</literal>, - * <literal>unavailable</literal>, <literal>prohibit</literal>. The default is <literal>unicast</literal>.</para> + * <literal>unavailable</literal>, <literal>prohibit</literal>, <literal>throw</literal>. + * The default is <literal>unicast</literal>.</para> * </listitem> * <listitem> * <para><literal>"window"</literal> - an unsigned 32 bit integer.</para> diff --git a/src/libnm-core-impl/nm-setting-ip6-config.c b/src/libnm-core-impl/nm-setting-ip6-config.c index f4623b28..f75c14ce 100644 --- a/src/libnm-core-impl/nm-setting-ip6-config.c +++ b/src/libnm-core-impl/nm-setting-ip6-config.c @@ -946,8 +946,9 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass) * format: a comma separated list of addresses * description: A list of IPv6 addresses and their prefix length. Multiple addresses * can be separated by comma. For example "2001:db8:85a3::8a2e:370:7334/64, 2001:db8:85a3::5/64". - * The addresses are listed in increasing priority, meaning the last address will - * be the primary address. + * The addresses are listed in decreasing priority, meaning the first address will + * be the primary address. This can make a difference with IPv6 source address selection + * (RFC 6724, section 5). * ---end--- */ _nm_properties_override_gobj( @@ -1042,7 +1043,8 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass) * </listitem> * <listitem> * <para><literal>"type"</literal> - one of <literal>unicast</literal>, <literal>local</literal>, <literal>blackhole</literal>, - * <literal>unavailable</literal>, <literal>prohibit</literal>. The default is <literal>unicast</literal>.</para> + * <literal>unavailable</literal>, <literal>prohibit</literal>, <literal>throw</literal>. + * The default is <literal>unicast</literal>.</para> * </listitem> * <listitem> * <para><literal>"window"</literal> - an unsigned 32 bit integer.</para> diff --git a/src/libnm-core-impl/nm-setting-match.c b/src/libnm-core-impl/nm-setting-match.c index 0ddffee5..f0e9fdf8 100644 --- a/src/libnm-core-impl/nm-setting-match.c +++ b/src/libnm-core-impl/nm-setting-match.c @@ -32,11 +32,11 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMSettingMatch, * Since: 1.14 */ struct _NMSettingMatch { - NMSetting parent; - GArray *interface_name; - GArray *kernel_command_line; - GArray *driver; - GArray *path; + NMSetting parent; + NMValueStrv interface_name; + NMValueStrv kernel_command_line; + NMValueStrv driver; + NMValueStrv path; }; struct _NMSettingMatchClass { @@ -60,7 +60,7 @@ nm_setting_match_get_num_interface_names(NMSettingMatch *setting) { g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), 0); - return nm_g_array_len(setting->interface_name); + return nm_g_array_len(setting->interface_name.arr); } /** @@ -77,10 +77,11 @@ nm_setting_match_get_interface_name(NMSettingMatch *setting, int idx) { g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), NULL); - g_return_val_if_fail(setting->interface_name && idx >= 0 && idx < setting->interface_name->len, + g_return_val_if_fail(setting->interface_name.arr && idx >= 0 + && idx < setting->interface_name.arr->len, NULL); - return nm_strvarray_get_idx(setting->interface_name, idx); + return nm_strvarray_get_idx(setting->interface_name.arr, idx); } /** @@ -98,7 +99,7 @@ nm_setting_match_add_interface_name(NMSettingMatch *setting, const char *interfa g_return_if_fail(NM_IS_SETTING_MATCH(setting)); g_return_if_fail(interface_name); - nm_strvarray_add(nm_strvarray_ensure(&setting->interface_name), interface_name); + nm_strvarray_add(nm_strvarray_ensure(&setting->interface_name.arr), interface_name); _notify(setting, PROP_INTERFACE_NAME); } @@ -116,9 +117,10 @@ nm_setting_match_remove_interface_name(NMSettingMatch *setting, int idx) { g_return_if_fail(NM_IS_SETTING_MATCH(setting)); - g_return_if_fail(setting->interface_name && idx >= 0 && idx < setting->interface_name->len); + g_return_if_fail(setting->interface_name.arr && idx >= 0 + && idx < setting->interface_name.arr->len); - g_array_remove_index(setting->interface_name, idx); + g_array_remove_index(setting->interface_name.arr, idx); _notify(setting, PROP_INTERFACE_NAME); } @@ -139,7 +141,7 @@ nm_setting_match_remove_interface_name_by_value(NMSettingMatch *setting, const c g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), FALSE); g_return_val_if_fail(interface_name, FALSE); - if (nm_strvarray_remove_first(setting->interface_name, interface_name)) { + if (nm_strvarray_remove_first(setting->interface_name.arr, interface_name)) { _notify(setting, PROP_INTERFACE_NAME); return TRUE; } @@ -160,8 +162,8 @@ nm_setting_match_clear_interface_names(NMSettingMatch *setting) { g_return_if_fail(NM_IS_SETTING_MATCH(setting)); - if (nm_g_array_len(setting->interface_name) != 0) { - nm_clear_pointer(&setting->interface_name, g_array_unref); + if (nm_g_array_len(setting->interface_name.arr) != 0) { + nm_clear_pointer(&setting->interface_name.arr, g_array_unref); _notify(setting, PROP_INTERFACE_NAME); } } @@ -185,7 +187,7 @@ nm_setting_match_get_interface_names(NMSettingMatch *setting, guint *length) { g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), NULL); - return nm_strvarray_get_strv(&setting->interface_name, length); + return nm_strvarray_get_strv(&setting->interface_name.arr, length); } /*****************************************************************************/ @@ -203,7 +205,7 @@ nm_setting_match_get_num_kernel_command_lines(NMSettingMatch *setting) { g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), 0); - return nm_g_array_len(setting->kernel_command_line); + return nm_g_array_len(setting->kernel_command_line.arr); } /** @@ -220,10 +222,11 @@ nm_setting_match_get_kernel_command_line(NMSettingMatch *setting, guint idx) { g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), NULL); - g_return_val_if_fail(setting->kernel_command_line && idx < setting->kernel_command_line->len, + g_return_val_if_fail(setting->kernel_command_line.arr + && idx < setting->kernel_command_line.arr->len, NULL); - return nm_strvarray_get_idx(setting->kernel_command_line, idx); + return nm_strvarray_get_idx(setting->kernel_command_line.arr, idx); } /** @@ -241,7 +244,7 @@ nm_setting_match_add_kernel_command_line(NMSettingMatch *setting, const char *ke g_return_if_fail(NM_IS_SETTING_MATCH(setting)); g_return_if_fail(kernel_command_line); - nm_strvarray_add(nm_strvarray_ensure(&setting->kernel_command_line), kernel_command_line); + nm_strvarray_add(nm_strvarray_ensure(&setting->kernel_command_line.arr), kernel_command_line); _notify(setting, PROP_KERNEL_COMMAND_LINE); } @@ -259,9 +262,10 @@ nm_setting_match_remove_kernel_command_line(NMSettingMatch *setting, guint idx) { g_return_if_fail(NM_IS_SETTING_MATCH(setting)); - g_return_if_fail(setting->kernel_command_line && idx < setting->kernel_command_line->len); + g_return_if_fail(setting->kernel_command_line.arr + && idx < setting->kernel_command_line.arr->len); - g_array_remove_index(setting->kernel_command_line, idx); + g_array_remove_index(setting->kernel_command_line.arr, idx); _notify(setting, PROP_KERNEL_COMMAND_LINE); } @@ -283,7 +287,7 @@ nm_setting_match_remove_kernel_command_line_by_value(NMSettingMatch *setting, g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), FALSE); g_return_val_if_fail(kernel_command_line, FALSE); - if (nm_strvarray_remove_first(setting->kernel_command_line, kernel_command_line)) { + if (nm_strvarray_remove_first(setting->kernel_command_line.arr, kernel_command_line)) { _notify(setting, PROP_KERNEL_COMMAND_LINE); return TRUE; } @@ -304,8 +308,8 @@ nm_setting_match_clear_kernel_command_lines(NMSettingMatch *setting) { g_return_if_fail(NM_IS_SETTING_MATCH(setting)); - if (nm_g_array_len(setting->kernel_command_line) != 0) { - nm_clear_pointer(&setting->kernel_command_line, g_array_unref); + if (nm_g_array_len(setting->kernel_command_line.arr) != 0) { + nm_clear_pointer(&setting->kernel_command_line.arr, g_array_unref); _notify(setting, PROP_KERNEL_COMMAND_LINE); } } @@ -326,7 +330,7 @@ nm_setting_match_get_kernel_command_lines(NMSettingMatch *setting, guint *length { g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), NULL); - return nm_strvarray_get_strv(&setting->kernel_command_line, length); + return nm_strvarray_get_strv(&setting->kernel_command_line.arr, length); } /*****************************************************************************/ @@ -344,7 +348,7 @@ nm_setting_match_get_num_drivers(NMSettingMatch *setting) { g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), 0); - return nm_g_array_len(setting->driver); + return nm_g_array_len(setting->driver.arr); } /** @@ -361,9 +365,9 @@ nm_setting_match_get_driver(NMSettingMatch *setting, guint idx) { g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), NULL); - g_return_val_if_fail(setting->driver && idx < setting->driver->len, NULL); + g_return_val_if_fail(setting->driver.arr && idx < setting->driver.arr->len, NULL); - return nm_strvarray_get_idx(setting->driver, idx); + return nm_strvarray_get_idx(setting->driver.arr, idx); } /** @@ -381,7 +385,7 @@ nm_setting_match_add_driver(NMSettingMatch *setting, const char *driver) g_return_if_fail(NM_IS_SETTING_MATCH(setting)); g_return_if_fail(driver); - nm_strvarray_add(nm_strvarray_ensure(&setting->driver), driver); + nm_strvarray_add(nm_strvarray_ensure(&setting->driver.arr), driver); _notify(setting, PROP_DRIVER); } @@ -399,9 +403,9 @@ nm_setting_match_remove_driver(NMSettingMatch *setting, guint idx) { g_return_if_fail(NM_IS_SETTING_MATCH(setting)); - g_return_if_fail(setting->driver && idx < setting->driver->len); + g_return_if_fail(setting->driver.arr && idx < setting->driver.arr->len); - g_array_remove_index(setting->driver, idx); + g_array_remove_index(setting->driver.arr, idx); _notify(setting, PROP_DRIVER); } @@ -422,7 +426,7 @@ nm_setting_match_remove_driver_by_value(NMSettingMatch *setting, const char *dri g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), FALSE); g_return_val_if_fail(driver, FALSE); - if (nm_strvarray_remove_first(setting->driver, driver)) { + if (nm_strvarray_remove_first(setting->driver.arr, driver)) { _notify(setting, PROP_DRIVER); return TRUE; } @@ -443,8 +447,8 @@ nm_setting_match_clear_drivers(NMSettingMatch *setting) { g_return_if_fail(NM_IS_SETTING_MATCH(setting)); - if (nm_g_array_len(setting->driver) != 0) { - nm_clear_pointer(&setting->driver, g_array_unref); + if (nm_g_array_len(setting->driver.arr) != 0) { + nm_clear_pointer(&setting->driver.arr, g_array_unref); _notify(setting, PROP_DRIVER); } } @@ -465,7 +469,7 @@ nm_setting_match_get_drivers(NMSettingMatch *setting, guint *length) { g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), NULL); - return nm_strvarray_get_strv(&setting->driver, length); + return nm_strvarray_get_strv(&setting->driver.arr, length); } /*****************************************************************************/ @@ -483,7 +487,7 @@ nm_setting_match_get_num_paths(NMSettingMatch *setting) { g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), 0); - return nm_g_array_len(setting->path); + return nm_g_array_len(setting->path.arr); } /** @@ -500,9 +504,9 @@ nm_setting_match_get_path(NMSettingMatch *setting, guint idx) { g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), NULL); - g_return_val_if_fail(setting->path && idx < setting->path->len, NULL); + g_return_val_if_fail(setting->path.arr && idx < setting->path.arr->len, NULL); - return nm_strvarray_get_idx(setting->path, idx); + return nm_strvarray_get_idx(setting->path.arr, idx); } /** @@ -520,7 +524,7 @@ nm_setting_match_add_path(NMSettingMatch *setting, const char *path) g_return_if_fail(NM_IS_SETTING_MATCH(setting)); g_return_if_fail(path); - nm_strvarray_add(nm_strvarray_ensure(&setting->path), path); + nm_strvarray_add(nm_strvarray_ensure(&setting->path.arr), path); _notify(setting, PROP_PATH); } @@ -538,9 +542,9 @@ nm_setting_match_remove_path(NMSettingMatch *setting, guint idx) { g_return_if_fail(NM_IS_SETTING_MATCH(setting)); - g_return_if_fail(setting->path && idx < setting->path->len); + g_return_if_fail(setting->path.arr && idx < setting->path.arr->len); - g_array_remove_index(setting->path, idx); + g_array_remove_index(setting->path.arr, idx); _notify(setting, PROP_PATH); } @@ -561,7 +565,7 @@ nm_setting_match_remove_path_by_value(NMSettingMatch *setting, const char *path) g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), FALSE); g_return_val_if_fail(path, FALSE); - if (nm_strvarray_remove_first(setting->path, path)) { + if (nm_strvarray_remove_first(setting->path.arr, path)) { _notify(setting, PROP_PATH); return TRUE; } @@ -582,8 +586,8 @@ nm_setting_match_clear_paths(NMSettingMatch *setting) { g_return_if_fail(NM_IS_SETTING_MATCH(setting)); - if (nm_g_array_len(setting->path) != 0) { - nm_clear_pointer(&setting->path, g_array_unref); + if (nm_g_array_len(setting->path.arr) != 0) { + nm_clear_pointer(&setting->path.arr, g_array_unref); _notify(setting, PROP_PATH); } } @@ -604,58 +608,7 @@ nm_setting_match_get_paths(NMSettingMatch *setting, guint *length) { g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), NULL); - return nm_strvarray_get_strv(&setting->path, length); -} - -/*****************************************************************************/ - -static void -get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) -{ - NMSettingMatch *self = NM_SETTING_MATCH(object); - - switch (prop_id) { - case PROP_INTERFACE_NAME: - g_value_take_boxed(value, nm_strvarray_get_strv_non_empty_dup(self->interface_name, NULL)); - break; - case PROP_KERNEL_COMMAND_LINE: - g_value_take_boxed(value, - nm_strvarray_get_strv_non_empty_dup(self->kernel_command_line, NULL)); - break; - case PROP_DRIVER: - g_value_take_boxed(value, nm_strvarray_get_strv_non_empty_dup(self->driver, NULL)); - break; - case PROP_PATH: - g_value_take_boxed(value, nm_strvarray_get_strv_non_empty_dup(self->path, NULL)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} - -static void -set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) -{ - NMSettingMatch *self = NM_SETTING_MATCH(object); - - switch (prop_id) { - case PROP_INTERFACE_NAME: - nm_strvarray_set_strv(&self->interface_name, g_value_get_boxed(value)); - break; - case PROP_KERNEL_COMMAND_LINE: - nm_strvarray_set_strv(&self->kernel_command_line, g_value_get_boxed(value)); - break; - case PROP_DRIVER: - nm_strvarray_set_strv(&self->driver, g_value_get_boxed(value)); - break; - case PROP_PATH: - nm_strvarray_set_strv(&self->path, g_value_get_boxed(value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } + return nm_strvarray_get_strv(&setting->path.arr, length); } /*****************************************************************************/ @@ -698,9 +651,9 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) NMSettingMatch *self = NM_SETTING_MATCH(setting); guint i; - if (self->interface_name) { - for (i = 0; i < self->interface_name->len; i++) { - if (nm_str_is_empty(nm_strvarray_get_idx(self->interface_name, i))) { + if (self->interface_name.arr) { + for (i = 0; i < self->interface_name.arr->len; i++) { + if (nm_str_is_empty(nm_strvarray_get_idx(self->interface_name.arr, i))) { g_set_error(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -714,9 +667,9 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) } } - if (self->kernel_command_line) { - for (i = 0; i < self->kernel_command_line->len; i++) { - if (nm_str_is_empty(nm_strvarray_get_idx(self->kernel_command_line, i))) { + if (self->kernel_command_line.arr) { + for (i = 0; i < self->kernel_command_line.arr->len; i++) { + if (nm_str_is_empty(nm_strvarray_get_idx(self->kernel_command_line.arr, i))) { g_set_error(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -730,9 +683,9 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) } } - if (self->driver) { - for (i = 0; i < self->driver->len; i++) { - if (nm_str_is_empty(nm_strvarray_get_idx(self->driver, i))) { + if (self->driver.arr) { + for (i = 0; i < self->driver.arr->len; i++) { + if (nm_str_is_empty(nm_strvarray_get_idx(self->driver.arr, i))) { g_set_error(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -746,9 +699,9 @@ verify(NMSetting *setting, NMConnection *connection, GError **error) } } - if (self->path) { - for (i = 0; i < self->path->len; i++) { - if (nm_str_is_empty(nm_strvarray_get_idx(self->path, i))) { + if (self->path.arr) { + for (i = 0; i < self->path.arr->len; i++) { + if (nm_str_is_empty(nm_strvarray_get_idx(self->path.arr, i))) { g_set_error(error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -770,10 +723,10 @@ finalize(GObject *object) { NMSettingMatch *self = NM_SETTING_MATCH(object); - nm_clear_pointer(&self->interface_name, g_array_unref); - nm_clear_pointer(&self->kernel_command_line, g_array_unref); - nm_clear_pointer(&self->driver, g_array_unref); - nm_clear_pointer(&self->path, g_array_unref); + nm_clear_pointer(&self->interface_name.arr, g_array_unref); + nm_clear_pointer(&self->kernel_command_line.arr, g_array_unref); + nm_clear_pointer(&self->driver.arr, g_array_unref); + nm_clear_pointer(&self->path.arr, g_array_unref); G_OBJECT_CLASS(nm_setting_match_parent_class)->finalize(object); } @@ -781,11 +734,12 @@ finalize(GObject *object) static void nm_setting_match_class_init(NMSettingMatchClass *klass) { - GObjectClass *object_class = G_OBJECT_CLASS(klass); - NMSettingClass *setting_class = NM_SETTING_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + NMSettingClass *setting_class = NM_SETTING_CLASS(klass); + GArray *properties_override = _nm_sett_info_property_override_create_array(); - object_class->get_property = get_property; - object_class->set_property = set_property; + object_class->get_property = _nm_setting_property_get_property_direct; + object_class->set_property = _nm_setting_property_set_property_direct; object_class->finalize = finalize; setting_class->verify = verify; @@ -810,12 +764,13 @@ nm_setting_match_class_init(NMSettingMatchClass *klass) * * Since: 1.14 **/ - obj_properties[PROP_INTERFACE_NAME] = g_param_spec_boxed( - NM_SETTING_MATCH_INTERFACE_NAME, - "", - "", - G_TYPE_STRV, - NM_SETTING_PARAM_FUZZY_IGNORE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + _nm_setting_property_define_direct_strv(properties_override, + obj_properties, + NM_SETTING_MATCH_INTERFACE_NAME, + PROP_INTERFACE_NAME, + NM_SETTING_PARAM_FUZZY_IGNORE, + NMSettingMatch, + interface_name); /** * NMSettingMatch:kernel-command-line @@ -834,12 +789,13 @@ nm_setting_match_class_init(NMSettingMatchClass *klass) * * Since: 1.26 **/ - obj_properties[PROP_KERNEL_COMMAND_LINE] = g_param_spec_boxed( - NM_SETTING_MATCH_KERNEL_COMMAND_LINE, - "", - "", - G_TYPE_STRV, - NM_SETTING_PARAM_FUZZY_IGNORE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + _nm_setting_property_define_direct_strv(properties_override, + obj_properties, + NM_SETTING_MATCH_KERNEL_COMMAND_LINE, + PROP_KERNEL_COMMAND_LINE, + NM_SETTING_PARAM_FUZZY_IGNORE, + NMSettingMatch, + kernel_command_line); /** * NMSettingMatch:driver @@ -852,12 +808,13 @@ nm_setting_match_class_init(NMSettingMatchClass *klass) * * Since: 1.26 **/ - obj_properties[PROP_DRIVER] = g_param_spec_boxed( - NM_SETTING_MATCH_DRIVER, - "", - "", - G_TYPE_STRV, - NM_SETTING_PARAM_FUZZY_IGNORE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + _nm_setting_property_define_direct_strv(properties_override, + obj_properties, + NM_SETTING_MATCH_DRIVER, + PROP_DRIVER, + NM_SETTING_PARAM_FUZZY_IGNORE, + NMSettingMatch, + driver); /** * NMSettingMatch:path @@ -892,14 +849,19 @@ nm_setting_match_class_init(NMSettingMatchClass *klass) * example: MATCH_PATH="pci-0000:01:00.0 pci-0000:0c:00.0" * ---end--- */ - obj_properties[PROP_PATH] = g_param_spec_boxed(NM_SETTING_MATCH_PATH, - "", - "", - G_TYPE_STRV, - NM_SETTING_PARAM_FUZZY_IGNORE | G_PARAM_READWRITE - | G_PARAM_STATIC_STRINGS); + _nm_setting_property_define_direct_strv(properties_override, + obj_properties, + NM_SETTING_MATCH_PATH, + PROP_PATH, + NM_SETTING_PARAM_FUZZY_IGNORE, + NMSettingMatch, + path); g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); - _nm_setting_class_commit(setting_class, NM_META_SETTING_TYPE_MATCH, NULL, NULL, 0); + _nm_setting_class_commit(setting_class, + NM_META_SETTING_TYPE_MATCH, + NULL, + properties_override, + 0); } diff --git a/src/libnm-core-impl/nm-setting-private.h b/src/libnm-core-impl/nm-setting-private.h index 7483eaed..38b233e9 100644 --- a/src/libnm-core-impl/nm-setting-private.h +++ b/src/libnm-core-impl/nm-setting-private.h @@ -232,6 +232,14 @@ gboolean _nm_setting_clear_secrets(NMSetting *setting, /*****************************************************************************/ +/* This holds a property of type NM_VALUE_TYPE_STRV. You probably want + * to use nm_strvarray_*() API with this. */ +typedef struct { + GArray *arr; +} NMValueStrv; + +/*****************************************************************************/ + #define NM_SETTING_PARAM_NONE 0 /* The property of the #NMSetting should be considered during comparisons that @@ -277,6 +285,7 @@ extern const NMSettInfoPropertType nm_sett_info_propert_type_direct_int64; extern const NMSettInfoPropertType nm_sett_info_propert_type_direct_uint64; extern const NMSettInfoPropertType nm_sett_info_propert_type_direct_string; extern const NMSettInfoPropertType nm_sett_info_propert_type_direct_bytes; +extern const NMSettInfoPropertType nm_sett_info_propert_type_direct_strv; extern const NMSettInfoPropertType nm_sett_info_propert_type_direct_enum; extern const NMSettInfoPropertType nm_sett_info_propert_type_direct_flags; extern const NMSettInfoPropertType nm_sett_info_propert_type_direct_mac_address; @@ -774,6 +783,42 @@ _nm_properties_override(GArray *properties_override, const NMSettInfoProperty *p /*****************************************************************************/ +#define _nm_setting_property_define_direct_strv(properties_override, \ + obj_properties, \ + prop_name, \ + prop_id, \ + param_flags, \ + private_struct_type, \ + private_struct_field, \ + ... /* extra NMSettInfoProperty fields */) \ + G_STMT_START \ + { \ + GParamSpec *_param_spec; \ + \ + G_STATIC_ASSERT(!NM_FLAGS_ANY((param_flags), ~(NM_SETTING_PARAM_FUZZY_IGNORE))); \ + \ + _param_spec = \ + g_param_spec_boxed("" prop_name "", \ + "", \ + "", \ + G_TYPE_STRV, \ + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | (param_flags)); \ + \ + (obj_properties)[(prop_id)] = _param_spec; \ + \ + _nm_properties_override_gobj((properties_override), \ + _param_spec, \ + &nm_sett_info_propert_type_direct_strv, \ + .direct_offset = \ + NM_STRUCT_OFFSET_ENSURE_TYPE(NMValueStrv, \ + private_struct_type, \ + private_struct_field), \ + __VA_ARGS__); \ + } \ + G_STMT_END + +/*****************************************************************************/ + #define _nm_setting_property_define_direct_enum(properties_override, \ obj_properties, \ prop_name, \ diff --git a/src/libnm-core-impl/nm-setting-proxy.c b/src/libnm-core-impl/nm-setting-proxy.c index 4b9a2970..8b91210b 100644 --- a/src/libnm-core-impl/nm-setting-proxy.c +++ b/src/libnm-core-impl/nm-setting-proxy.c @@ -114,7 +114,7 @@ nm_setting_proxy_get_pac_url(NMSettingProxy *setting) * nm_setting_proxy_get_pac_script: * @setting: the #NMSettingProxy * - * Returns: the PAC script + * Returns: the PAC script. * * Since: 1.6 **/ @@ -315,15 +315,25 @@ nm_setting_proxy_class_init(NMSettingProxyClass *klass) /** * NMSettingProxy:pac-script: * - * PAC script for the connection. + * PAC script for the connection. This is an UTF-8 encoded javascript code + * that defines a FindProxyForURL() function. * * Since: 1.6 **/ + /* ---nmcli--- + * property: pac-script + * description: The PAC script. In the profile this must be an UTF-8 encoded javascript code that defines + * a FindProxyForURL() function. + * When setting the property in nmcli, a filename is accepted too. In that case, + * nmcli will read the content of the file and set the script. The prefixes "file://" and "js://" are + * supported to explicitly differentiate between the two. + * ---end--- + */ /* ---ifcfg-rh--- * property: pac-script * variable: PAC_SCRIPT(+) - * description: Path of the PAC script. - * example: PAC_SCRIPT=/home/joe/proxy.pac + * description: The PAC script. This is an UTF-8 encoded javascript code that defines a FindProxyForURL() function. + * example: PAC_SCRIPT="function FindProxyForURL (url, host) { return 'PROXY proxy.example.com:8080; DIRECT'; }" * ---end--- */ _nm_setting_property_define_direct_string(properties_override, diff --git a/src/libnm-core-impl/nm-setting.c b/src/libnm-core-impl/nm-setting.c index be88effb..35070bae 100644 --- a/src/libnm-core-impl/nm-setting.c +++ b/src/libnm-core-impl/nm-setting.c @@ -774,6 +774,13 @@ _nm_setting_property_get_property_direct(GObject *object, g_value_set_boxed(value, *p_val); return; } + case NM_VALUE_TYPE_STRV: + { + const NMValueStrv *p_val = _nm_setting_get_private_field(setting, sett_info, property_info); + + g_value_take_boxed(value, nm_strvarray_get_strv_non_empty_dup(p_val->arr, NULL)); + return; + } default: goto out_fail; } @@ -909,6 +916,18 @@ _nm_setting_property_set_property_direct(GObject *object, *p_val = v ? g_bytes_ref(v) : NULL; goto out_notify; } + case NM_VALUE_TYPE_STRV: + { + NMValueStrv *p_val = _nm_setting_get_private_field(setting, sett_info, property_info); + const char *const *v; + + v = g_value_get_boxed(value); + if (nm_strvarray_equal_strv(p_val->arr, v, -1)) + return; + + nm_strvarray_set_strv(&p_val->arr, v); + goto out_notify; + } default: goto out_fail; } @@ -1026,6 +1045,11 @@ _init_direct(NMSetting *setting) nm_assert(!(*((const GBytes *const *) _nm_setting_get_private_field(setting, sett_info, property_info)))); break; + case NM_VALUE_TYPE_STRV: + nm_assert(!((const NMValueStrv *) + _nm_setting_get_private_field(setting, sett_info, property_info)) + ->arr); + break; default: nm_assert_not_reached(); break; @@ -1081,6 +1105,13 @@ _finalize_direct(NMSetting *setting) nm_clear_pointer(p_val, g_bytes_unref); break; } + case NM_VALUE_TYPE_STRV: + { + NMValueStrv *p_val = _nm_setting_get_private_field(setting, sett_info, property_info); + + nm_clear_pointer(&p_val->arr, g_array_unref); + break; + } default: nm_assert_not_reached(); break; @@ -1199,6 +1230,20 @@ _nm_setting_property_to_dbus_fcn_direct(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_ return NULL; return nm_g_bytes_to_variant_ay(val); } + case NM_VALUE_TYPE_STRV: + { + const NMValueStrv *val; + + /* Strv properties have always NULL as default. Setting "including_default" has no defined meaning + * (but it could have). */ + nm_assert(!property_info->to_dbus_including_default); + + val = + (const NMValueStrv *) _nm_setting_get_private_field(setting, sett_info, property_info); + if (!val->arr) + return NULL; + return g_variant_new_strv((const char *const *) val->arr->data, val->arr->len); + } default: return nm_assert_unreachable_val(NULL); } @@ -1545,6 +1590,8 @@ _nm_setting_property_from_dbus_fcn_direct(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS gs_unref_bytes GBytes *v = NULL; GBytes **p_val; + nm_assert(!property_info->property_type->from_dbus_direct_allow_transform); + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_BYTESTRING)) goto out_error_wrong_dbus_type; @@ -1557,6 +1604,28 @@ _nm_setting_property_from_dbus_fcn_direct(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS NM_SWAP(p_val, &v); goto out_notify; } + case NM_VALUE_TYPE_STRV: + { + NMValueStrv *p_val; + gs_free const char **ss = NULL; + gsize ss_len; + + nm_assert(!property_info->property_type->from_dbus_direct_allow_transform); + + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_STRING_ARRAY)) + goto out_error_wrong_dbus_type; + + ss = g_variant_get_strv(value, &ss_len); + nm_assert(ss_len <= G_MAXUINT); + + p_val = _nm_setting_get_private_field(setting, sett_info, property_info); + + if (nm_strvarray_equal_strv(p_val->arr, ss, ss_len)) + goto out_unchanged; + + nm_strvarray_set_strv(&p_val->arr, ss); + goto out_notify; + } default: break; } @@ -2465,6 +2534,9 @@ _nm_setting_property_compare_fcn_direct(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_ return nm_streq0(*((const char *const *) p_a), *((const char *const *) p_b)); case NM_VALUE_TYPE_BYTES: return nm_g_bytes_equal0(*((const GBytes *const *) p_a), *((const GBytes *const *) p_b)); + case NM_VALUE_TYPE_STRV: + return nm_strvarray_equal(((const NMValueStrv *) p_a)->arr, + ((const NMValueStrv *) p_b)->arr); default: return nm_assert_unreachable_val(TRUE); } @@ -3529,8 +3601,15 @@ const NMSettInfoPropertType nm_sett_info_propert_type_direct_bytes = .compare_fcn = _nm_setting_property_compare_fcn_direct, .to_dbus_fcn = _nm_setting_property_to_dbus_fcn_direct, .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_direct, - .from_dbus_is_full = TRUE, - .from_dbus_direct_allow_transform = TRUE); + .from_dbus_is_full = TRUE); + +const NMSettInfoPropertType nm_sett_info_propert_type_direct_strv = + NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_STRING_ARRAY, + .direct_type = NM_VALUE_TYPE_STRV, + .compare_fcn = _nm_setting_property_compare_fcn_direct, + .to_dbus_fcn = _nm_setting_property_to_dbus_fcn_direct, + .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_direct, + .from_dbus_is_full = TRUE); const NMSettInfoPropertType nm_sett_info_propert_type_direct_enum = NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_INT32, @@ -3896,7 +3975,7 @@ nm_setting_option_set(NMSetting *setting, const char *opt_name, GVariant *varian g_hash_table_insert(hash, g_strdup(opt_name), g_variant_ref_sink(variant)); if (changed_value) - _nm_setting_option_notify(setting, !changed_name); + _nm_setting_option_notify(setting, changed_name); } /** @@ -3933,7 +4012,7 @@ nm_setting_option_set_boolean(NMSetting *setting, const char *opt_name, gboolean g_hash_table_insert(hash, g_strdup(opt_name), g_variant_ref_sink(g_variant_new_boolean(value))); if (changed_value) - _nm_setting_option_notify(setting, !changed_name); + _nm_setting_option_notify(setting, changed_name); } /** @@ -3968,7 +4047,7 @@ nm_setting_option_set_uint32(NMSetting *setting, const char *opt_name, guint32 v g_hash_table_insert(hash, g_strdup(opt_name), g_variant_ref_sink(g_variant_new_uint32(value))); if (changed_value) - _nm_setting_option_notify(setting, !changed_name); + _nm_setting_option_notify(setting, changed_name); } /*****************************************************************************/ diff --git a/src/libnm-core-impl/nm-utils.c b/src/libnm-core-impl/nm-utils.c index 55b01c15..d5d884f2 100644 --- a/src/libnm-core-impl/nm-utils.c +++ b/src/libnm-core-impl/nm-utils.c @@ -27,7 +27,6 @@ #include "libnm-core-aux-intern/nm-common-macros.h" #include "nm-utils-private.h" #include "nm-setting-private.h" -#include "nm-crypto.h" #include "nm-setting-bond.h" #include "nm-setting-bond-port.h" #include "nm-setting-bridge.h" @@ -3083,94 +3082,6 @@ nm_utils_uuid_generate(void) /*****************************************************************************/ -static gboolean -file_has_extension(const char *filename, const char *extensions[]) -{ - const char *ext; - gsize i; - - ext = strrchr(filename, '.'); - if (!ext) - return FALSE; - - for (i = 0; extensions[i]; i++) { - if (!g_ascii_strcasecmp(ext, extensions[i])) - return TRUE; - } - - return FALSE; -} - -/** - * nm_utils_file_is_certificate: - * @filename: name of the file to test - * - * Tests if @filename has a valid extension for an X.509 certificate file - * (".cer", ".crt", ".der", or ".pem"), and contains a certificate in a format - * recognized by NetworkManager. - * - * Returns: %TRUE if the file is a certificate, %FALSE if it is not - **/ -gboolean -nm_utils_file_is_certificate(const char *filename) -{ - const char *extensions[] = {".der", ".pem", ".crt", ".cer", NULL}; - NMCryptoFileFormat file_format; - - g_return_val_if_fail(filename != NULL, FALSE); - - if (!file_has_extension(filename, extensions)) - return FALSE; - - if (!nm_crypto_load_and_verify_certificate(filename, &file_format, NULL, NULL)) - return FALSE; - return file_format = NM_CRYPTO_FILE_FORMAT_X509; -} - -/** - * nm_utils_file_is_private_key: - * @filename: name of the file to test - * @out_encrypted: (out): on return, whether the file is encrypted - * - * Tests if @filename has a valid extension for an X.509 private key file - * (".der", ".key", ".pem", or ".p12"), and contains a private key in a format - * recognized by NetworkManager. - * - * Returns: %TRUE if the file is a private key, %FALSE if it is not - **/ -gboolean -nm_utils_file_is_private_key(const char *filename, gboolean *out_encrypted) -{ - const char *extensions[] = {".der", ".pem", ".p12", ".key", NULL}; - - g_return_val_if_fail(filename != NULL, FALSE); - - NM_SET_OUT(out_encrypted, FALSE); - if (!file_has_extension(filename, extensions)) - return FALSE; - - return nm_crypto_verify_private_key(filename, NULL, out_encrypted, NULL) - != NM_CRYPTO_FILE_FORMAT_UNKNOWN; -} - -/** - * nm_utils_file_is_pkcs12: - * @filename: name of the file to test - * - * Tests if @filename is a PKCS#<!-- -->12 file. - * - * Returns: %TRUE if the file is PKCS#<!-- -->12, %FALSE if it is not - **/ -gboolean -nm_utils_file_is_pkcs12(const char *filename) -{ - g_return_val_if_fail(filename != NULL, FALSE); - - return nm_crypto_is_pkcs12_file(filename, NULL); -} - -/*****************************************************************************/ - gboolean _nm_utils_check_file(const char *filename, gint64 check_owner, @@ -3795,22 +3706,13 @@ nm_utils_hwaddr_aton(const char *asc, gpointer buffer, gsize length) char * nm_utils_bin2hexstr(gconstpointer src, gsize len, int final_len) { - char *result; gsize buflen = (len * 2) + 1; g_return_val_if_fail(src != NULL, NULL); g_return_val_if_fail(len > 0 && (buflen - 1) / 2 == len, NULL); g_return_val_if_fail(final_len < 0 || (gsize) final_len < buflen, NULL); - result = g_malloc(buflen); - - nm_utils_bin2hexstr_full(src, len, '\0', FALSE, result); - - /* Cut converted key off at the correct length for this cipher type */ - if (final_len >= 0 && (gsize) final_len < buflen) - result[final_len] = '\0'; - - return result; + return _nm_utils_bin2hexstr(src, len, final_len); } /** diff --git a/src/libnm-core-impl/tests/test-crypto.c b/src/libnm-core-impl/tests/test-crypto.c index 6a6e7fbc..896c3c2e 100644 --- a/src/libnm-core-impl/tests/test-crypto.c +++ b/src/libnm-core-impl/tests/test-crypto.c @@ -10,7 +10,7 @@ #include <stdlib.h> #include <stdio.h> -#include "nm-crypto-impl.h" +#include "libnm-crypto/nm-crypto-impl.h" #include "nm-utils.h" #include "nm-errors.h" #include "libnm-core-intern/nm-core-internal.h" @@ -92,7 +92,7 @@ test_cert(gconstpointer test_data) nmtst_assert_success(success, error); g_assert_cmpint(format, ==, NM_CRYPTO_FILE_FORMAT_X509); - g_assert(nm_utils_file_is_certificate(path)); + g_assert(nm_crypto_utils_file_is_certificate(path)); } static void @@ -106,7 +106,7 @@ test_load_private_key(const char *path, gs_unref_bytes GBytes *array = NULL; GError *error = NULL; - g_assert(nm_utils_file_is_private_key(path, &is_encrypted)); + g_assert(nm_crypto_utils_file_is_private_key(path, &is_encrypted)); g_assert(is_encrypted); array = nmtst_crypto_decrypt_openssl_private_key(path, password, &key_type, &error); @@ -146,7 +146,7 @@ test_load_pkcs12(const char *path, const char *password, int expected_error) gboolean is_encrypted = FALSE; GError *error = NULL; - g_assert(nm_utils_file_is_private_key(path, NULL)); + g_assert(nm_crypto_utils_file_is_private_key(path, NULL)); format = nm_crypto_verify_private_key(path, password, &is_encrypted, &error); if (expected_error != -1) { @@ -167,7 +167,7 @@ test_load_pkcs12_no_password(const char *path) gboolean is_encrypted = FALSE; GError *error = NULL; - g_assert(nm_utils_file_is_private_key(path, NULL)); + g_assert(nm_crypto_utils_file_is_private_key(path, NULL)); /* We should still get a valid returned crypto file format */ format = nm_crypto_verify_private_key(path, NULL, &is_encrypted, &error); @@ -201,7 +201,7 @@ test_load_pkcs8(const char *path, const char *password, int expected_error) gboolean is_encrypted = FALSE; GError *error = NULL; - g_assert(nm_utils_file_is_private_key(path, NULL)); + g_assert(nm_crypto_utils_file_is_private_key(path, NULL)); format = nm_crypto_verify_private_key(path, password, &is_encrypted, &error); if (expected_error != -1) { @@ -285,7 +285,7 @@ test_key_decrypted(gconstpointer test_data) path = g_build_filename(TEST_CERT_DIR, file, NULL); - g_assert(nm_utils_file_is_private_key(path, &is_encrypted)); + g_assert(nm_crypto_utils_file_is_private_key(path, &is_encrypted)); g_assert(!is_encrypted); g_free(path); @@ -399,6 +399,23 @@ test_md5(void) } } +/*****************************************************************************/ + +static void +test_crypto_error(void) +{ + G_STATIC_ASSERT(NM_CRYPTO_ERROR_FAILED == _NM_CRYPTO_ERROR_FAILED); + G_STATIC_ASSERT(NM_CRYPTO_ERROR_INVALID_DATA == _NM_CRYPTO_ERROR_INVALID_DATA); + G_STATIC_ASSERT(NM_CRYPTO_ERROR_INVALID_PASSWORD == _NM_CRYPTO_ERROR_INVALID_PASSWORD); + G_STATIC_ASSERT(NM_CRYPTO_ERROR_UNKNOWN_CIPHER == _NM_CRYPTO_ERROR_UNKNOWN_CIPHER); + G_STATIC_ASSERT(NM_CRYPTO_ERROR_DECRYPTION_FAILED == _NM_CRYPTO_ERROR_DECRYPTION_FAILED); + G_STATIC_ASSERT(NM_CRYPTO_ERROR_ENCRYPTION_FAILED == _NM_CRYPTO_ERROR_ENCRYPTION_FAILED); + + g_assert_cmpint(NM_CRYPTO_ERROR, ==, _NM_CRYPTO_ERROR); +} + +/*****************************************************************************/ + NMTST_DEFINE(); int @@ -448,6 +465,7 @@ main(int argc, char **argv) g_test_add_data_func("/libnm/crypto/PKCS#8", "pkcs8-enc-key.pem, 1234567890", test_pkcs8); g_test_add_func("/libnm/crypto/md5", test_md5); + g_test_add_func("/libnm/crypto/error", test_crypto_error); ret = g_test_run(); diff --git a/src/libnm-core-impl/tests/test-general-enums.h b/src/libnm-core-impl/tests/test-general-enums.h index b3163e23..2ac8cf2b 100644 --- a/src/libnm-core-impl/tests/test-general-enums.h +++ b/src/libnm-core-impl/tests/test-general-enums.h @@ -25,11 +25,11 @@ typedef enum { NM_TEST_GENERAL_META_FLAGS_0x4 = (1 << 4), } NMTestGeneralMetaFlags; -typedef enum { /*< flags >*/ - NM_TEST_GENERAL_COLOR_FLAGS_WHITE = 1, /*< skip >*/ - NM_TEST_GENERAL_COLOR_FLAGS_BLUE = 2, - NM_TEST_GENERAL_COLOR_FLAGS_RED = 4, - NM_TEST_GENERAL_COLOR_FLAGS_GREEN = 8, +typedef enum /*< flags >*/ { + NM_TEST_GENERAL_COLOR_FLAGS_WHITE = 1, /*< skip >*/ + NM_TEST_GENERAL_COLOR_FLAGS_BLUE = 2, + NM_TEST_GENERAL_COLOR_FLAGS_RED = 4, + NM_TEST_GENERAL_COLOR_FLAGS_GREEN = 8, } NMTestGeneralColorFlags; #endif /* _NM_TEST_GENERAL_ENUMS_H_ */ diff --git a/src/libnm-core-impl/tests/test-general.c b/src/libnm-core-impl/tests/test-general.c index d0f85a9f..1856f6ad 100644 --- a/src/libnm-core-impl/tests/test-general.c +++ b/src/libnm-core-impl/tests/test-general.c @@ -5060,7 +5060,7 @@ test_setting_connection_changed_signal(void) ASSERT_CHANGED(nm_setting_connection_add_secondary(s_con, uuid)); ASSERT_CHANGED(nm_setting_connection_remove_secondary(s_con, 0)); - NMTST_EXPECT_LIBNM_CRITICAL(NMTST_G_RETURN_MSG(idx < nm_g_array_len(priv->secondaries))); + NMTST_EXPECT_LIBNM_CRITICAL(NMTST_G_RETURN_MSG(idx < nm_g_array_len(priv->secondaries.arr))); ASSERT_UNCHANGED(nm_setting_connection_remove_secondary(s_con, 1)); g_test_assert_expected_messages(); diff --git a/src/libnm-core-impl/tests/test-keyfile.c b/src/libnm-core-impl/tests/test-keyfile.c index 9bd13ffc..c163c429 100644 --- a/src/libnm-core-impl/tests/test-keyfile.c +++ b/src/libnm-core-impl/tests/test-keyfile.c @@ -5,16 +5,18 @@ #include "libnm-core-impl/nm-default-libnm-core.h" -#include "libnm-glib-aux/nm-json-aux.h" -#include "libnm-core-intern/nm-keyfile-utils.h" +#include "libnm-base/nm-ethtool-utils-base.h" #include "libnm-core-intern/nm-keyfile-internal.h" -#include "nm-simple-connection.h" -#include "nm-setting-connection.h" -#include "nm-setting-wired.h" +#include "libnm-core-intern/nm-keyfile-utils.h" +#include "libnm-glib-aux/nm-json-aux.h" #include "nm-setting-8021x.h" +#include "nm-setting-connection.h" +#include "nm-setting-ethtool.h" +#include "nm-setting-proxy.h" #include "nm-setting-team.h" #include "nm-setting-user.h" -#include "nm-setting-proxy.h" +#include "nm-setting-wired.h" +#include "nm-simple-connection.h" #include "libnm-glib-aux/nm-test-utils.h" @@ -887,6 +889,100 @@ test_bridge_port_vlans(void) /*****************************************************************************/ +typedef struct { + bool expect; + guint n_calls; +} InvalidOptionWriteData; + +static gboolean +_invalid_option_write_handler(NMConnection *connection, + GKeyFile *keyfile, + NMKeyfileHandlerType handler_type, + NMKeyfileHandlerData *handler_data, + void *user_data) +{ + InvalidOptionWriteData *data = user_data; + const char *message; + NMKeyfileWarnSeverity severity; + + g_assert(data); + g_assert(data->expect); + + g_assert(data->n_calls == 0); + data->n_calls++; + + switch (handler_type) { + case NM_KEYFILE_HANDLER_TYPE_WARN: + nm_keyfile_handler_data_warn_get(handler_data, &message, &severity); + g_assert(message && strstr(message, "ethtool.bogus")); + break; + default: + g_assert_not_reached(); + } + + return TRUE; +} + +static void +test_invalid_option(void) +{ + gs_unref_object NMConnection *con = NULL; + NMSetting *s_ethtool; + nm_auto_unref_keyfile GKeyFile *kf = NULL; + gs_free_error GError *error = NULL; + InvalidOptionWriteData data; + + con = nmtst_create_minimal_connection("test invalid option", + NULL, + NM_SETTING_WIRED_SETTING_NAME, + NULL); + + s_ethtool = nm_setting_ethtool_new(); + + nm_connection_add_setting(con, s_ethtool); + + nm_setting_option_set_boolean(s_ethtool, NM_ETHTOOL_OPTNAME_PAUSE_RX, TRUE); + + data = (InvalidOptionWriteData){}; + kf = nm_keyfile_write(con, + NM_KEYFILE_HANDLER_FLAGS_NONE, + _invalid_option_write_handler, + &data, + nmtst_get_rand_bool() ? &error : NULL); + nmtst_assert_success(kf, error); + nm_clear_pointer(&kf, g_key_file_unref); + + nmtst_connection_normalize(con); + + nmtst_assert_connection_verifies_without_normalization(con); + + data = (InvalidOptionWriteData){}; + kf = nm_keyfile_write(con, + NM_KEYFILE_HANDLER_FLAGS_NONE, + _invalid_option_write_handler, + &data, + nmtst_get_rand_bool() ? &error : NULL); + nmtst_assert_success(kf, error); + nm_clear_pointer(&kf, g_key_file_unref); + + nm_setting_option_set(s_ethtool, "bogus", g_variant_new_int64(0)); + + data = (InvalidOptionWriteData){ + .expect = TRUE, + }; + kf = nm_keyfile_write(con, + NM_KEYFILE_HANDLER_FLAGS_NONE, + _invalid_option_write_handler, + &data, + nmtst_get_rand_bool() ? &error : NULL); + nmtst_assert_success(kf, error); + nm_clear_pointer(&kf, g_key_file_unref); + + g_assert_cmpint(data.n_calls, ==, 1); +} + +/*****************************************************************************/ + NMTST_DEFINE(); int @@ -904,6 +1000,7 @@ main(int argc, char **argv) g_test_add_func("/core/keyfile/test_vpn/1", test_vpn_1); g_test_add_func("/core/keyfile/bridge/vlans", test_bridge_vlans); g_test_add_func("/core/keyfile/bridge-port/vlans", test_bridge_port_vlans); + g_test_add_func("/core/keyfile/invalid-option", test_invalid_option); return g_test_run(); } diff --git a/src/libnm-core-impl/tests/test-setting.c b/src/libnm-core-impl/tests/test-setting.c index b260ac37..788f218d 100644 --- a/src/libnm-core-impl/tests/test-setting.c +++ b/src/libnm-core-impl/tests/test-setting.c @@ -4565,6 +4565,12 @@ test_setting_metadata(void) == _nm_setting_property_to_dbus_fcn_direct); g_assert(sip->param_spec); g_assert(sip->param_spec->value_type == G_TYPE_BYTES); + } else if (sip->property_type->direct_type == NM_VALUE_TYPE_STRV) { + g_assert(g_variant_type_equal(sip->property_type->dbus_type, "as")); + g_assert(sip->property_type->to_dbus_fcn + == _nm_setting_property_to_dbus_fcn_direct); + g_assert(sip->param_spec); + g_assert(sip->param_spec->value_type == G_TYPE_STRV); } else g_assert_not_reached(); @@ -4653,7 +4659,12 @@ check_done:; } if (sip->property_type->from_dbus_fcn == _nm_setting_property_from_dbus_fcn_direct) { /* for the moment, all direct properties allow transformation. */ - g_assert(sip->property_type->from_dbus_direct_allow_transform); + if (NM_IN_SET(sip->property_type->direct_type, + NM_VALUE_TYPE_BYTES, + NM_VALUE_TYPE_STRV)) + g_assert(!sip->property_type->from_dbus_direct_allow_transform); + else + g_assert(sip->property_type->from_dbus_direct_allow_transform); } if (sip->property_type->from_dbus_fcn == _nm_setting_property_from_dbus_fcn_gprop) @@ -4763,10 +4774,12 @@ check_done:; g_assert(NM_IS_SETTING_VPN(setting)); g_assert_cmpstr(sip->name, ==, NM_SETTING_VPN_SECRETS); } else { + NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER g_error("secret %s.%s is of unexpected property type %s", nm_setting_get_name(setting), sip->name, g_type_name(sip->param_spec->value_type)); + NM_PRAGMA_WARNING_REENABLE } } } @@ -4889,12 +4902,14 @@ check_done:; /* the property-types with same content should all be shared. Here we have two that * are the same content, but different instances. Bug. */ + NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER g_error("The identical property type for D-Bus type \"%s\" is used by: %s and %s. " "If a NMSettInfoPropertType is identical, it should be shared by creating " "a common instance of the property type", (const char *) pt->dbus_type, _PROP_IDX_OWNER(h_property_types, pt), _PROP_IDX_OWNER(h_property_types, pt_2)); + NM_PRAGMA_WARNING_REENABLE } } } |