diff --git a/SOURCES/gnutls-3.8.3-deterministic-ecdsa-fixes.patch b/SOURCES/gnutls-3.8.3-deterministic-ecdsa-fixes.patch new file mode 100644 index 0000000..635cad8 --- /dev/null +++ b/SOURCES/gnutls-3.8.3-deterministic-ecdsa-fixes.patch @@ -0,0 +1,418 @@ +From 1c4701ffc342259fc5965d5a0de90d87f780e3e5 Mon Sep 17 00:00:00 2001 +From: Daiki Ueno +Date: Fri, 12 Jan 2024 17:56:58 +0900 +Subject: [PATCH] nettle: avoid normalization of mpz_t in deterministic ECDSA + +This removes function calls that potentially leak bit-length of a +private key used to calculate a nonce in deterministic ECDSA. Namely: + +- _gnutls_dsa_compute_k has been rewritten to work on always + zero-padded mp_limb_t arrays instead of mpz_t +- rnd_mpz_func has been replaced with rnd_datum_func, which is backed + by a byte array instead of an mpz_t value + +Signed-off-by: Daiki Ueno +--- + lib/nettle/int/dsa-compute-k.c | 70 +++++++++++++++++++++---------- + lib/nettle/int/dsa-compute-k.h | 23 +++++++++- + lib/nettle/int/ecdsa-compute-k.c | 28 +++---------- + lib/nettle/int/ecdsa-compute-k.h | 4 +- + lib/nettle/pk.c | 65 +++++++++++++++++++++------- + tests/sign-verify-deterministic.c | 2 +- + 6 files changed, 127 insertions(+), 65 deletions(-) + +diff --git a/lib/nettle/int/dsa-compute-k.c b/lib/nettle/int/dsa-compute-k.c +index 8ff5739c2b..2fcb2bb80e 100644 +--- a/lib/nettle/int/dsa-compute-k.c ++++ b/lib/nettle/int/dsa-compute-k.c +@@ -31,19 +31,30 @@ + #include "mpn-base256.h" + #include + +-#define BITS_TO_LIMBS(bits) (((bits) + GMP_NUMB_BITS - 1) / GMP_NUMB_BITS) ++/* For mini-gmp */ ++#ifndef GMP_LIMB_BITS ++#define GMP_LIMB_BITS GMP_NUMB_BITS ++#endif + +-/* The maximum size of q, chosen from the fact that we support +- * 521-bit elliptic curve generator and 512-bit DSA subgroup at +- * maximum. */ +-#define MAX_Q_BITS 521 +-#define MAX_Q_SIZE ((MAX_Q_BITS + 7) / 8) +-#define MAX_Q_LIMBS BITS_TO_LIMBS(MAX_Q_BITS) ++static inline int is_zero_limb(mp_limb_t x) ++{ ++ x |= (x << 1); ++ return ((x >> 1) - 1) >> (GMP_LIMB_BITS - 1); ++} ++ ++static int sec_zero_p(const mp_limb_t *ap, mp_size_t n) ++{ ++ volatile mp_limb_t w; ++ mp_size_t i; + +-#define MAX_HASH_BITS (MAX_HASH_SIZE * 8) +-#define MAX_HASH_LIMBS BITS_TO_LIMBS(MAX_HASH_BITS) ++ for (i = 0, w = 0; i < n; i++) ++ w |= ap[i]; + +-int _gnutls_dsa_compute_k(mpz_t k, const mpz_t q, const mpz_t x, ++ return is_zero_limb(w); ++} ++ ++int _gnutls_dsa_compute_k(mp_limb_t *h, const mp_limb_t *q, const mp_limb_t *x, ++ mp_size_t qn, mp_bitcnt_t q_bits, + gnutls_mac_algorithm_t mac, const uint8_t *digest, + size_t length) + { +@@ -51,9 +62,6 @@ int _gnutls_dsa_compute_k(mpz_t k, const mpz_t q, const mpz_t x, + uint8_t K[MAX_HASH_SIZE]; + uint8_t xp[MAX_Q_SIZE]; + uint8_t tp[MAX_Q_SIZE]; +- mp_limb_t h[MAX(MAX_Q_LIMBS, MAX_HASH_LIMBS)]; +- mp_bitcnt_t q_bits = mpz_sizeinbase(q, 2); +- mp_size_t qn = mpz_size(q); + mp_bitcnt_t h_bits = length * 8; + mp_size_t hn = BITS_TO_LIMBS(h_bits); + size_t nbytes = (q_bits + 7) / 8; +@@ -62,6 +70,7 @@ int _gnutls_dsa_compute_k(mpz_t k, const mpz_t q, const mpz_t x, + mp_limb_t cy; + gnutls_hmac_hd_t hd; + int ret = 0; ++ mp_limb_t scratch[MAX_Q_LIMBS]; + + if (unlikely(q_bits > MAX_Q_BITS)) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); +@@ -69,7 +78,7 @@ int _gnutls_dsa_compute_k(mpz_t k, const mpz_t q, const mpz_t x, + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + /* int2octets(x) */ +- mpn_get_base256(xp, nbytes, mpz_limbs_read(x), qn); ++ mpn_get_base256(xp, nbytes, x, qn); + + /* bits2octets(h) */ + mpn_set_base256(h, hn, digest, length); +@@ -93,12 +102,12 @@ int _gnutls_dsa_compute_k(mpz_t k, const mpz_t q, const mpz_t x, + mpn_rshift(h, h, hn, shift % GMP_NUMB_BITS); + } + +- cy = mpn_sub_n(h, h, mpz_limbs_read(q), qn); ++ cy = mpn_sub_n(h, h, q, qn); + /* Fall back to addmul_1, if nettle is linked with mini-gmp. */ + #ifdef mpn_cnd_add_n +- mpn_cnd_add_n(cy, h, h, mpz_limbs_read(q), qn); ++ mpn_cnd_add_n(cy, h, h, q, qn); + #else +- mpn_addmul_1(h, mpz_limbs_read(q), qn, cy != 0); ++ mpn_addmul_1(h, q, qn, cy != 0); + #endif + mpn_get_base256(tp, nbytes, h, qn); + +@@ -174,12 +183,8 @@ int _gnutls_dsa_compute_k(mpz_t k, const mpz_t q, const mpz_t x, + if (tlen * 8 > q_bits) + mpn_rshift(h, h, qn, tlen * 8 - q_bits); + /* Check if k is in [1,q-1] */ +- if (!mpn_zero_p(h, qn) && +- mpn_cmp(h, mpz_limbs_read(q), qn) < 0) { +- mpn_copyi(mpz_limbs_write(k, qn), h, qn); +- mpz_limbs_finish(k, qn); ++ if (!sec_zero_p(h, qn) && mpn_sub_n(scratch, h, q, qn)) + break; +- } + + ret = gnutls_hmac_init(&hd, mac, K, length); + if (ret < 0) +@@ -203,3 +208,24 @@ out: + + return ret; + } ++ ++/* cancel-out dsa_sign's addition of 1 to random data */ ++void _gnutls_dsa_compute_k_finish(uint8_t *k, size_t nbytes, mp_limb_t *h, ++ mp_size_t n) ++{ ++ /* Fall back to sub_1, if nettle is linked with mini-gmp. */ ++#ifdef mpn_sec_sub_1 ++ mp_limb_t t[MAX_Q_LIMBS]; ++ ++ mpn_sec_sub_1(h, h, n, 1, t); ++#else ++ mpn_sub_1(h, h, n, 1); ++#endif ++ mpn_get_base256(k, nbytes, h, n); ++} ++ ++void _gnutls_ecdsa_compute_k_finish(uint8_t *k, size_t nbytes, mp_limb_t *h, ++ mp_size_t n) ++{ ++ mpn_get_base256(k, nbytes, h, n); ++} +diff --git a/lib/nettle/int/dsa-compute-k.h b/lib/nettle/int/dsa-compute-k.h +index 49d243acb4..2f0667a01e 100644 +--- a/lib/nettle/int/dsa-compute-k.h ++++ b/lib/nettle/int/dsa-compute-k.h +@@ -26,8 +26,29 @@ + #include + #include /* includes gmp.h */ + +-int _gnutls_dsa_compute_k(mpz_t k, const mpz_t q, const mpz_t x, ++#define BITS_TO_LIMBS(bits) (((bits) + GMP_NUMB_BITS - 1) / GMP_NUMB_BITS) ++ ++/* The maximum size of q, chosen from the fact that we support ++ * 521-bit elliptic curve generator and 512-bit DSA subgroup at ++ * maximum. */ ++#define MAX_Q_BITS 521 ++#define MAX_Q_SIZE ((MAX_Q_BITS + 7) / 8) ++#define MAX_Q_LIMBS BITS_TO_LIMBS(MAX_Q_BITS) ++ ++#define MAX_HASH_BITS (MAX_HASH_SIZE * 8) ++#define MAX_HASH_LIMBS BITS_TO_LIMBS(MAX_HASH_BITS) ++ ++#define DSA_COMPUTE_K_ITCH MAX(MAX_Q_LIMBS, MAX_HASH_LIMBS) ++ ++int _gnutls_dsa_compute_k(mp_limb_t *h, const mp_limb_t *q, const mp_limb_t *x, ++ mp_size_t qn, mp_bitcnt_t q_bits, + gnutls_mac_algorithm_t mac, const uint8_t *digest, + size_t length); + ++void _gnutls_dsa_compute_k_finish(uint8_t *k, size_t nbytes, mp_limb_t *h, ++ mp_size_t n); ++ ++void _gnutls_ecdsa_compute_k_finish(uint8_t *k, size_t nbytes, mp_limb_t *h, ++ mp_size_t n); ++ + #endif /* GNUTLS_LIB_NETTLE_INT_DSA_COMPUTE_K_H */ +diff --git a/lib/nettle/int/ecdsa-compute-k.c b/lib/nettle/int/ecdsa-compute-k.c +index 3b7f886160..4e25235c40 100644 +--- a/lib/nettle/int/ecdsa-compute-k.c ++++ b/lib/nettle/int/ecdsa-compute-k.c +@@ -29,38 +29,38 @@ + #include "dsa-compute-k.h" + #include "gnutls_int.h" + +-static inline int _gnutls_ecc_curve_to_dsa_q(mpz_t *q, gnutls_ecc_curve_t curve) ++int _gnutls_ecc_curve_to_dsa_q(mpz_t q, gnutls_ecc_curve_t curve) + { + switch (curve) { + #ifdef ENABLE_NON_SUITEB_CURVES + case GNUTLS_ECC_CURVE_SECP192R1: +- mpz_init_set_str(*q, ++ mpz_init_set_str(q, + "FFFFFFFFFFFFFFFFFFFFFFFF99DEF836" + "146BC9B1B4D22831", + 16); + return 0; + case GNUTLS_ECC_CURVE_SECP224R1: +- mpz_init_set_str(*q, ++ mpz_init_set_str(q, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2" + "E0B8F03E13DD29455C5C2A3D", + 16); + return 0; + #endif + case GNUTLS_ECC_CURVE_SECP256R1: +- mpz_init_set_str(*q, ++ mpz_init_set_str(q, + "FFFFFFFF00000000FFFFFFFFFFFFFFFF" + "BCE6FAADA7179E84F3B9CAC2FC632551", + 16); + return 0; + case GNUTLS_ECC_CURVE_SECP384R1: +- mpz_init_set_str(*q, ++ mpz_init_set_str(q, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFC7634D81F4372DDF" + "581A0DB248B0A77AECEC196ACCC52973", + 16); + return 0; + case GNUTLS_ECC_CURVE_SECP521R1: +- mpz_init_set_str(*q, ++ mpz_init_set_str(q, + "1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFA51868783BF2F966B7FCC0148F709A" +@@ -73,19 +73,3 @@ static inline int _gnutls_ecc_curve_to_dsa_q(mpz_t *q, gnutls_ecc_curve_t curve) + GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM); + } + } +- +-int _gnutls_ecdsa_compute_k(mpz_t k, gnutls_ecc_curve_t curve, const mpz_t x, +- gnutls_mac_algorithm_t mac, const uint8_t *digest, +- size_t length) +-{ +- mpz_t q; +- int ret; +- +- ret = _gnutls_ecc_curve_to_dsa_q(&q, curve); +- if (ret < 0) +- return gnutls_assert_val(ret); +- +- ret = _gnutls_dsa_compute_k(k, q, x, mac, digest, length); +- mpz_clear(q); +- return ret; +-} +diff --git a/lib/nettle/int/ecdsa-compute-k.h b/lib/nettle/int/ecdsa-compute-k.h +index be8beddb5d..207685763f 100644 +--- a/lib/nettle/int/ecdsa-compute-k.h ++++ b/lib/nettle/int/ecdsa-compute-k.h +@@ -26,8 +26,6 @@ + #include + #include /* includes gmp.h */ + +-int _gnutls_ecdsa_compute_k(mpz_t k, gnutls_ecc_curve_t curve, const mpz_t x, +- gnutls_mac_algorithm_t mac, const uint8_t *digest, +- size_t length); ++int _gnutls_ecc_curve_to_dsa_q(mpz_t q, gnutls_ecc_curve_t curve); + + #endif /* GNUTLS_LIB_NETTLE_INT_ECDSA_COMPUTE_K_H */ +diff --git a/lib/nettle/pk.c b/lib/nettle/pk.c +index 305548f4d1..dd6b9936a8 100644 +--- a/lib/nettle/pk.c ++++ b/lib/nettle/pk.c +@@ -103,10 +103,16 @@ static void rnd_nonce_func(void *_ctx, size_t length, uint8_t *data) + } + } + +-static void rnd_mpz_func(void *_ctx, size_t length, uint8_t *data) ++static void rnd_datum_func(void *ctx, size_t length, uint8_t *data) + { +- mpz_t *k = _ctx; +- nettle_mpz_get_str_256(length, data, *k); ++ gnutls_datum_t *d = ctx; ++ ++ if (length > d->size) { ++ memset(data, 0, length - d->size); ++ memcpy(data + (length - d->size), d->data, d->size); ++ } else { ++ memcpy(data, d->data, length); ++ } + } + + static void rnd_nonce_func_fallback(void *_ctx, size_t length, uint8_t *data) +@@ -1403,7 +1409,10 @@ static int _wrap_nettle_pk_sign(gnutls_pk_algorithm_t algo, + struct dsa_signature sig; + int curve_id = pk_params->curve; + const struct ecc_curve *curve; +- mpz_t k; ++ mpz_t q; ++ /* 521-bit elliptic curve generator at maximum */ ++ uint8_t buf[(521 + 7) / 8]; ++ gnutls_datum_t k = { NULL, 0 }; + void *random_ctx; + nettle_random_func *random_func; + +@@ -1447,17 +1456,32 @@ static int _wrap_nettle_pk_sign(gnutls_pk_algorithm_t algo, + not_approved = true; + } + +- mpz_init(k); ++ mpz_init(q); ++ + if (_gnutls_get_lib_state() == LIB_STATE_SELFTEST || + (sign_params->flags & GNUTLS_PK_FLAG_REPRODUCIBLE)) { +- ret = _gnutls_ecdsa_compute_k( +- k, curve_id, pk_params->params[ECC_K], ++ mp_limb_t h[DSA_COMPUTE_K_ITCH]; ++ ++ ret = _gnutls_ecc_curve_to_dsa_q(q, curve_id); ++ if (ret < 0) ++ goto ecdsa_cleanup; ++ ++ ret = _gnutls_dsa_compute_k( ++ h, mpz_limbs_read(q), priv.p, ++ ecc_size(priv.ecc), ecc_bit_size(priv.ecc), + DIG_TO_MAC(sign_params->dsa_dig), vdata->data, + vdata->size); + if (ret < 0) + goto ecdsa_cleanup; ++ ++ k.data = buf; ++ k.size = (ecc_bit_size(priv.ecc) + 7) / 8; ++ ++ _gnutls_ecdsa_compute_k_finish(k.data, k.size, h, ++ ecc_size(priv.ecc)); ++ + random_ctx = &k; +- random_func = rnd_mpz_func; ++ random_func = rnd_datum_func; + } else { + random_ctx = NULL; + random_func = rnd_nonce_func; +@@ -1476,7 +1500,7 @@ static int _wrap_nettle_pk_sign(gnutls_pk_algorithm_t algo, + ecdsa_cleanup: + dsa_signature_clear(&sig); + ecc_scalar_zclear(&priv); +- mpz_clear(k); ++ mpz_clear(q); + + if (ret < 0) { + gnutls_assert(); +@@ -1488,7 +1512,9 @@ static int _wrap_nettle_pk_sign(gnutls_pk_algorithm_t algo, + struct dsa_params pub; + bigint_t priv; + struct dsa_signature sig; +- mpz_t k; ++ /* 512-bit DSA subgroup at maximum */ ++ uint8_t buf[(512 + 7) / 8]; ++ gnutls_datum_t k = { NULL, 0 }; + void *random_ctx; + nettle_random_func *random_func; + +@@ -1515,19 +1541,27 @@ static int _wrap_nettle_pk_sign(gnutls_pk_algorithm_t algo, + hash_len = vdata->size; + } + +- mpz_init(k); + if (_gnutls_get_lib_state() == LIB_STATE_SELFTEST || + (sign_params->flags & GNUTLS_PK_FLAG_REPRODUCIBLE)) { ++ mp_limb_t h[DSA_COMPUTE_K_ITCH]; ++ + ret = _gnutls_dsa_compute_k( +- k, pub.q, TOMPZ(priv), ++ h, mpz_limbs_read(pub.q), ++ mpz_limbs_read(TOMPZ(priv)), mpz_size(pub.q), ++ mpz_sizeinbase(pub.q, 2), + DIG_TO_MAC(sign_params->dsa_dig), vdata->data, + vdata->size); + if (ret < 0) + goto dsa_fail; +- /* cancel-out dsa_sign's addition of 1 to random data */ +- mpz_sub_ui(k, k, 1); ++ ++ k.data = buf; ++ k.size = (mpz_sizeinbase(pub.q, 2) + 7) / 8; ++ ++ _gnutls_dsa_compute_k_finish(k.data, k.size, h, ++ mpz_size(pub.q)); ++ + random_ctx = &k; +- random_func = rnd_mpz_func; ++ random_func = rnd_datum_func; + } else { + random_ctx = NULL; + random_func = rnd_nonce_func; +@@ -1544,7 +1578,6 @@ static int _wrap_nettle_pk_sign(gnutls_pk_algorithm_t algo, + + dsa_fail: + dsa_signature_clear(&sig); +- mpz_clear(k); + + if (ret < 0) { + gnutls_assert(); +diff --git a/tests/sign-verify-deterministic.c b/tests/sign-verify-deterministic.c +index 6969b57a11..bdd5a49c7d 100644 +--- a/tests/sign-verify-deterministic.c ++++ b/tests/sign-verify-deterministic.c +@@ -198,7 +198,7 @@ void doit(void) + &tests[i].msg, &signature); + if (ret < 0) + testfail("gnutls_pubkey_verify_data2\n"); +- success(" - pass"); ++ success(" - pass\n"); + + next: + gnutls_free(signature.data); +-- +2.44.0 + diff --git a/SOURCES/gnutls-3.8.3-verify-chain.patch b/SOURCES/gnutls-3.8.3-verify-chain.patch new file mode 100644 index 0000000..96fbfc4 --- /dev/null +++ b/SOURCES/gnutls-3.8.3-verify-chain.patch @@ -0,0 +1,410 @@ +From e369e67a62f44561d417cb233acc566cc696d82d Mon Sep 17 00:00:00 2001 +From: Daiki Ueno +Date: Mon, 29 Jan 2024 13:52:46 +0900 +Subject: [PATCH] gnutls_x509_trust_list_verify_crt2: remove length limit of + input + +Previously, if cert_list_size exceeded DEFAULT_MAX_VERIFY_DEPTH, the +chain verification logic crashed with assertion failure. This patch +removes the restriction while keeping the maximum number of +retrieved certificates being DEFAULT_MAX_VERIFY_DEPTH. + +Signed-off-by: Daiki Ueno +--- + lib/gnutls_int.h | 5 +- + lib/x509/common.c | 10 +- + lib/x509/verify-high.c | 51 ++++++---- + tests/test-chains.h | 211 ++++++++++++++++++++++++++++++++++++++++- + 4 files changed, 258 insertions(+), 19 deletions(-) + +diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h +index d8561ebe3a..8cf9a87157 100644 +--- a/lib/gnutls_int.h ++++ b/lib/gnutls_int.h +@@ -232,7 +232,10 @@ typedef enum record_send_state_t { + + #define MAX_PK_PARAM_SIZE 2048 + +-/* defaults for verification functions ++/* Defaults for verification functions. ++ * ++ * update many_icas in tests/test-chains.h when increasing ++ * DEFAULT_MAX_VERIFY_DEPTH. + */ + #define DEFAULT_MAX_VERIFY_DEPTH 16 + #define DEFAULT_MAX_VERIFY_BITS (MAX_PK_PARAM_SIZE * 8) +diff --git a/lib/x509/common.c b/lib/x509/common.c +index 2cc83c9155..705aa868bc 100644 +--- a/lib/x509/common.c ++++ b/lib/x509/common.c +@@ -1725,7 +1725,15 @@ unsigned int _gnutls_sort_clist(gnutls_x509_crt_t *clist, + bool insorted[DEFAULT_MAX_VERIFY_DEPTH]; /* non zero if clist[i] used in sorted list */ + gnutls_x509_crt_t sorted[DEFAULT_MAX_VERIFY_DEPTH]; + +- assert(clist_size <= DEFAULT_MAX_VERIFY_DEPTH); ++ /* Limit the number of certificates in the chain, to avoid DoS ++ * because of the O(n^2) sorting below. FIXME: Switch to a ++ * topological sort algorithm which should be linear to the ++ * number of certificates and subject-issuer relationships. ++ */ ++ if (clist_size > DEFAULT_MAX_VERIFY_DEPTH) { ++ _gnutls_debug_log("too many certificates; skipping sorting\n"); ++ return 1; ++ } + + for (i = 0; i < DEFAULT_MAX_VERIFY_DEPTH; i++) { + issuer[i] = -1; +diff --git a/lib/x509/verify-high.c b/lib/x509/verify-high.c +index 4e7361eb63..aacc24a7d8 100644 +--- a/lib/x509/verify-high.c ++++ b/lib/x509/verify-high.c +@@ -25,7 +25,7 @@ + #include "errors.h" + #include + #include "global.h" +-#include "num.h" /* MAX */ ++#include "num.h" /* MIN */ + #include "tls-sig.h" + #include "str.h" + #include "datum.h" +@@ -1361,7 +1361,8 @@ int gnutls_x509_trust_list_verify_crt2( + int ret = 0; + unsigned int i; + size_t hash; +- gnutls_x509_crt_t sorted[DEFAULT_MAX_VERIFY_DEPTH]; ++ gnutls_x509_crt_t *cert_list_copy = NULL; ++ unsigned int cert_list_max_size = 0; + gnutls_x509_crt_t retrieved[DEFAULT_MAX_VERIFY_DEPTH]; + unsigned int retrieved_size = 0; + const char *hostname = NULL, *purpose = NULL, *email = NULL; +@@ -1421,16 +1422,28 @@ int gnutls_x509_trust_list_verify_crt2( + } + } + +- memcpy(sorted, cert_list, cert_list_size * sizeof(gnutls_x509_crt_t)); +- cert_list = sorted; ++ /* Allocate extra for retrieved certificates. */ ++ if (!INT_ADD_OK(cert_list_size, DEFAULT_MAX_VERIFY_DEPTH, ++ &cert_list_max_size)) ++ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); ++ ++ cert_list_copy = _gnutls_reallocarray(NULL, cert_list_max_size, ++ sizeof(gnutls_x509_crt_t)); ++ if (!cert_list_copy) ++ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); ++ ++ memcpy(cert_list_copy, cert_list, ++ cert_list_size * sizeof(gnutls_x509_crt_t)); ++ cert_list = cert_list_copy; + + records = gl_list_nx_create_empty(GL_LINKEDHASH_LIST, cert_eq, + cert_hashcode, NULL, false); +- if (records == NULL) +- return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); ++ if (records == NULL) { ++ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); ++ goto cleanup; ++ } + +- for (i = 0; i < cert_list_size && +- cert_list_size <= DEFAULT_MAX_VERIFY_DEPTH;) { ++ for (i = 0; i < cert_list_size;) { + unsigned int sorted_size = 1; + unsigned int j, k; + gnutls_x509_crt_t issuer; +@@ -1442,8 +1455,7 @@ int gnutls_x509_trust_list_verify_crt2( + + assert(sorted_size > 0); + +- /* Remove duplicates. Start with index 1, as the first element +- * may be re-checked after issuer retrieval. */ ++ /* Remove duplicates. */ + for (j = 0; j < sorted_size; j++) { + if (gl_list_search(records, cert_list[i + j])) { + if (i + j < cert_list_size - 1) { +@@ -1495,13 +1507,15 @@ int gnutls_x509_trust_list_verify_crt2( + + ret = retrieve_issuers( + list, cert_list[i - 1], &retrieved[retrieved_size], +- DEFAULT_MAX_VERIFY_DEPTH - +- MAX(retrieved_size, cert_list_size)); ++ MIN(DEFAULT_MAX_VERIFY_DEPTH - retrieved_size, ++ cert_list_max_size - cert_list_size)); + if (ret < 0) { + break; + } else if (ret > 0) { + assert((unsigned int)ret <= +- DEFAULT_MAX_VERIFY_DEPTH - cert_list_size); ++ DEFAULT_MAX_VERIFY_DEPTH - retrieved_size); ++ assert((unsigned int)ret <= ++ cert_list_max_size - cert_list_size); + memmove(&cert_list[i + ret], &cert_list[i], + (cert_list_size - i) * + sizeof(gnutls_x509_crt_t)); +@@ -1517,8 +1531,10 @@ int gnutls_x509_trust_list_verify_crt2( + } + + cert_list_size = shorten_clist(list, cert_list, cert_list_size); +- if (cert_list_size <= 0) +- return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); ++ if (cert_list_size <= 0) { ++ ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); ++ goto cleanup; ++ } + + hash = hash_pjw_bare(cert_list[cert_list_size - 1]->raw_issuer_dn.data, + cert_list[cert_list_size - 1]->raw_issuer_dn.size); +@@ -1661,10 +1677,13 @@ int gnutls_x509_trust_list_verify_crt2( + } + + cleanup: ++ gnutls_free(cert_list_copy); + for (i = 0; i < retrieved_size; i++) { + gnutls_x509_crt_deinit(retrieved[i]); + } +- gl_list_free(records); ++ if (records) { ++ gl_list_free(records); ++ } + return ret; + } + +diff --git a/tests/test-chains.h b/tests/test-chains.h +index 3e559fecd5..a7fe1cdecc 100644 +--- a/tests/test-chains.h ++++ b/tests/test-chains.h +@@ -23,7 +23,7 @@ + #ifndef GNUTLS_TESTS_TEST_CHAINS_H + #define GNUTLS_TESTS_TEST_CHAINS_H + +-#define MAX_CHAIN 10 ++#define MAX_CHAIN 17 + + static const char *chain_with_no_subject_id_in_ca_ok[] = { + "-----BEGIN CERTIFICATE-----\n" +@@ -4383,6 +4383,213 @@ static const char *cross_signed_ca[] = { + NULL + }; + ++/* This assumes DEFAULT_MAX_VERIFY_DEPTH to be 16 */ ++static const char *many_icas[] = { ++ /* Server */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBqzCCAV2gAwIBAgIUIK3+SD3GmqJlRLZ/ESyhTzkSDL8wBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowNzEbMBkGA1UEChMSR251VExTIHRlc3Qgc2VydmVyMRgwFgYD\n" ++ "VQQDEw90ZXN0LmdudXRscy5vcmcwKjAFBgMrZXADIQAWGjx45NIJiKFsNBxxRRjm\n" ++ "NxUT5KYK7xXr5HPVywwgLaOBkjCBjzAMBgNVHRMBAf8EAjAAMBoGA1UdEQQTMBGC\n" ++ "D3Rlc3QuZ251dGxzLm9yZzATBgNVHSUEDDAKBggrBgEFBQcDATAOBgNVHQ8BAf8E\n" ++ "BAMCB4AwHQYDVR0OBBYEFKgNAQWZPx76/vXqQOdIi5mTftsaMB8GA1UdIwQYMBaA\n" ++ "FDaPsY6WAGuRtrhYJE6Gk/bg5qbdMAUGAytlcANBAMIDh8aGcIIFDTUrzfV7tnkX\n" ++ "hHrxyFKBH/cApf6xcJQTfDXm23po627Ibp+WgLaWMY08Fn9Y2V6Ev8ADfqXNbQ8=\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA16 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBYTCCAROgAwIBAgIUSnE0PKdm/dsnZSWBh5Ct4pS6DcwwBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowHTEbMBkGA1UEAwwSR251VExTIHRlc3QgSUNBICRpMCowBQYD\n" ++ "K2VwAyEAxq9SI8vp0QH1dDBBuZW+t+bLLROppQbjSQ4O1BEonDOjYzBhMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBQ2j7GOlgBrkba4\n" ++ "WCROhpP24Oam3TAfBgNVHSMEGDAWgBRvdUKX0aw3nfUIdvivXGSfRO7zyjAFBgMr\n" ++ "ZXADQQBsI2Hc7X5hXoHTvk01qMc5a1I27QHAFRARJnvIQ15wxNS2LVLzGk+AUmwr\n" ++ "sOhBKAcVfS55uWtYdjoWQ80h238H\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA15 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBYTCCAROgAwIBAgIUQk4XkgQVImnp6OPZas7ctwgBza4wBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowHTEbMBkGA1UEAwwSR251VExTIHRlc3QgSUNBICRpMCowBQYD\n" ++ "K2VwAyEAs3yVKLJd3sKbNVmj6Bxy2j1x025rksyQpZZWnCx5a+CjYzBhMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBRvdUKX0aw3nfUI\n" ++ "dvivXGSfRO7zyjAfBgNVHSMEGDAWgBRhGfUXYPh4YQsdtTWYUozLphGgfzAFBgMr\n" ++ "ZXADQQBXTtm56x6/pHXdW8dTvZLc/8RufNQrMlc23TCgX0apUnrZdTsNAb7OE4Uu\n" ++ "9PBuxK+CC9NL/BL2hXsKvAT+NWME\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA14 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBYTCCAROgAwIBAgIUKfwz7UUYRvYlvqwmnLJlTOS9o1AwBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowHTEbMBkGA1UEAwwSR251VExTIHRlc3QgSUNBICRpMCowBQYD\n" ++ "K2VwAyEAXbUetQ08t+F4+IcKL++HpeclqTxXZ7cG4mwqvHmTUEWjYzBhMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBRhGfUXYPh4YQsd\n" ++ "tTWYUozLphGgfzAfBgNVHSMEGDAWgBQYRQqO+V1kefF7QvNnFU1fX5H9+jAFBgMr\n" ++ "ZXADQQAiSHNMTLPFP3oa6q13Dj8jSxF9trQDJGM1ArWffFcPZUt2U4/ODHdcMTHx\n" ++ "kGwhIj+ghBlu6ykgu6J2wewCUooC\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA13 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBYTCCAROgAwIBAgIUUKOs59gyCPAZzoC7zMZQSh6AnQgwBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowHTEbMBkGA1UEAwwSR251VExTIHRlc3QgSUNBICRpMCowBQYD\n" ++ "K2VwAyEAmvqhj5GYqsXIpsr1BXBfD+2mTP/m/TEpKIYSZHM62dijYzBhMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBQYRQqO+V1kefF7\n" ++ "QvNnFU1fX5H9+jAfBgNVHSMEGDAWgBQ27HzvP5hl2xR+LOzRcPfmY5ndXjAFBgMr\n" ++ "ZXADQQBrB3NkrYC7EQ74qgeesVOE71rW012dPOOKPAV0laR+JLEgsv9sfus+AdBF\n" ++ "WBNwR3KeYBTi/MFDuecxBHU2m5gD\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA12 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBYTCCAROgAwIBAgIUUQooGfH21+sR7/pSgCWm13gg2H4wBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowHTEbMBkGA1UEAwwSR251VExTIHRlc3QgSUNBICRpMCowBQYD\n" ++ "K2VwAyEAK2of/B4wMpk6k/KdugC5dMS+jo2fseUM7/PvXkE6HASjYzBhMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBQ27HzvP5hl2xR+\n" ++ "LOzRcPfmY5ndXjAfBgNVHSMEGDAWgBSJDHU0Mj1Xr0e8ErCnRK24w7XwTTAFBgMr\n" ++ "ZXADQQDY8d2bAZpj7oGhdl2dBsCE48jEWj49da0PbgN12koAj3gf4hjMPd8G7p5z\n" ++ "8RsURAwQmCkE8ShvdNw/Qr2tDL0E\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA11 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBYTCCAROgAwIBAgIUW9Dw0hU2pfjXhb5Stip+mk9SndIwBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowHTEbMBkGA1UEAwwSR251VExTIHRlc3QgSUNBICRpMCowBQYD\n" ++ "K2VwAyEAn5ISjLVV6RBWsnxDWHDicpye7SjFwGOTwzF01/psiJ2jYzBhMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBSJDHU0Mj1Xr0e8\n" ++ "ErCnRK24w7XwTTAfBgNVHSMEGDAWgBSR9UU27RI0XohiEgHDxNo/9HP4djAFBgMr\n" ++ "ZXADQQCfQg6MDHk71vhyrEo4/5PcLb2Li5F/FKURyux7snv2TbkSdInloAqca9UR\n" ++ "DtqHSLCNLXCNdSPr5QwIt5p29rsE\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA10 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBYTCCAROgAwIBAgIUR4uTedG8e6MibKViQ3eX7QzXG1swBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowHTEbMBkGA1UEAwwSR251VExTIHRlc3QgSUNBICRpMCowBQYD\n" ++ "K2VwAyEAnslX04kSVOL5LAf1e+Ze3ggNnDJcEAxLDk8I/IhyjTyjYzBhMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBSR9UU27RI0Xohi\n" ++ "EgHDxNo/9HP4djAfBgNVHSMEGDAWgBRC7US5gJYnvd5F7EN+C4anMgd2NzAFBgMr\n" ++ "ZXADQQDo+jHt07Tvz3T5Lbz6apBrSln8xKYfJk2W1wP85XAnf7sZT9apM1bS4EyD\n" ++ "Kckw+KG+9x7myOZz6AXJgZB5OGAO\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA9 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBYTCCAROgAwIBAgIUSIIIRjrNpE+kEPkiJMOqaNAazvQwBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowHTEbMBkGA1UEAwwSR251VExTIHRlc3QgSUNBICRpMCowBQYD\n" ++ "K2VwAyEAZKy7p1Gn4W/reRxKJN99+QkHt2q9aELktCKe5PqrX5ejYzBhMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBRC7US5gJYnvd5F\n" ++ "7EN+C4anMgd2NzAfBgNVHSMEGDAWgBSOhR7Ornis2x8g0J+bvTTwMnW60zAFBgMr\n" ++ "ZXADQQA0MEcC4FgKZEAfalVpApU2to0G158MVz/WTNcSc7fnl8ifJ/g56dVHL1jr\n" ++ "REvC/S28dn/CGAlbVXUAgxnHAbgE\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA8 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBYTCCAROgAwIBAgIUGGFSgD95vOTSj7iFxfXA5vq6vsYwBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowHTEbMBkGA1UEAwwSR251VExTIHRlc3QgSUNBICRpMCowBQYD\n" ++ "K2VwAyEAg3W/bTdW0fR32NeZEVMXICpa30d7rSdddLOYDvqqUO+jYzBhMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBSOhR7Ornis2x8g\n" ++ "0J+bvTTwMnW60zAfBgNVHSMEGDAWgBT3zK8Hbn9aVTAOOFY6RSxJ2o5x2jAFBgMr\n" ++ "ZXADQQBl4gnzE463iMFg57gPvjHdVzA39sJBpiu0kUGfRcLnoRI/VOaLcx7WnJ9+\n" ++ "c3KxPZBec76EdIoQDkTmI6m2FIAM\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA7 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBYTCCAROgAwIBAgIUGktMGXhNuaMhKyAlecymmLD+/GIwBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowHTEbMBkGA1UEAwwSR251VExTIHRlc3QgSUNBICRpMCowBQYD\n" ++ "K2VwAyEA/Z1oc76hOQ0Hi+2hePaGIntnMIDqBlb7RDMjRpYONP2jYzBhMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBT3zK8Hbn9aVTAO\n" ++ "OFY6RSxJ2o5x2jAfBgNVHSMEGDAWgBSPae3JUN3jP0NgUJqDV3eYxcaM3DAFBgMr\n" ++ "ZXADQQBMkwKaUZlvG/hax8rv3nnDv8kJOr6KVHBnxSx3hZ+8HIBT7GFm1+YDeYOB\n" ++ "jhNg66kyeFPGXXBCe+mvNQFFjCEE\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA6 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBYTCCAROgAwIBAgIUKn3gz5lAUpKqWlHKLKYDbOJ4rygwBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowHTEbMBkGA1UEAwwSR251VExTIHRlc3QgSUNBICRpMCowBQYD\n" ++ "K2VwAyEAZ/eD4eTe91ddvHusm7YlLPxU4ByGFc6suAmlP1CxXkWjYzBhMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBSPae3JUN3jP0Ng\n" ++ "UJqDV3eYxcaM3DAfBgNVHSMEGDAWgBT9f/qSI/jhxvGI7aMtkpraDcjBnjAFBgMr\n" ++ "ZXADQQAMRnkmRhnLGdmJaY8B42gfyaAsqCMyds/Tw4OHYy+N48XuAxRjKkhf3szC\n" ++ "0lY71oU043mNP1yx/dzAuCTrVSgI\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA5 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBYTCCAROgAwIBAgIUEgEYbBXXEyGv3vOq10JQv1SBiUUwBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowHTEbMBkGA1UEAwwSR251VExTIHRlc3QgSUNBICRpMCowBQYD\n" ++ "K2VwAyEAs2xEDPw8RVal53nX9GVwUd1blq1wjtVFC8S1V7up7MWjYzBhMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBT9f/qSI/jhxvGI\n" ++ "7aMtkpraDcjBnjAfBgNVHSMEGDAWgBRBVkLu9BmCKz7HNI8md4vPpoE/7jAFBgMr\n" ++ "ZXADQQCCufAyLijtzzmeCuO3K50rBSbGvB3FQfep7g6kVsQKM3bw/olWK5/Ji0dD\n" ++ "ubJ0cFl1FmfAda7aVxLBtJOvO6MI\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA4 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBYTCCAROgAwIBAgIULj8GkaHw+92HuOTnXnXlxCy3VrEwBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowHTEbMBkGA1UEAwwSR251VExTIHRlc3QgSUNBICRpMCowBQYD\n" ++ "K2VwAyEAiedxh4dvtwDellMAHc/pZH0MAOXobRenTUgF1yj5l12jYzBhMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBRBVkLu9BmCKz7H\n" ++ "NI8md4vPpoE/7jAfBgNVHSMEGDAWgBSDtNRgQ36KwW/ASaMyr6WeDt0STDAFBgMr\n" ++ "ZXADQQDL8U2ckzur7CktdrVUNvfLhVCOz33d/62F28vQFHUa8h/4h+Mi1MMbXOKT\n" ++ "1bL2TvpFpU7Fx/vcIPXDielVqr4C\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA3 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBYTCCAROgAwIBAgIUQXl74TDDw6MQRMbQUSPa6Qrvba8wBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowHTEbMBkGA1UEAwwSR251VExTIHRlc3QgSUNBICRpMCowBQYD\n" ++ "K2VwAyEA7l0jQ0f4fJRw7Qja/Hz2qn8y91SI7CokxhSf+FT+9M6jYzBhMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBSDtNRgQ36KwW/A\n" ++ "SaMyr6WeDt0STDAfBgNVHSMEGDAWgBQ2inEK4KH6ATftmybxKE1dZUzOozAFBgMr\n" ++ "ZXADQQCnP7Oqx1epGnFnO7TrTJwcUukXDEYsINve2GeUsi8HEIeKKlMcLZ2Cnaj7\n" ++ "5v9NGuWh3QJpmmSGpEemiv8dJc4A\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA2 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBYTCCAROgAwIBAgIUP7Nmof8H2F1LyDkjqlYIUpGdXE8wBQYDK2VwMB0xGzAZ\n" ++ "BgNVBAMMEkdudVRMUyB0ZXN0IElDQSAkaTAgFw0yNDAzMTIyMjUzMzlaGA85OTk5\n" ++ "MTIzMTIzNTk1OVowHTEbMBkGA1UEAwwSR251VExTIHRlc3QgSUNBICRpMCowBQYD\n" ++ "K2VwAyEAkW9Rod3CXAnha6nlaHkDbCOegq94lgmjqclA9sOIt3yjYzBhMA8GA1Ud\n" ++ "EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBQ2inEK4KH6ATft\n" ++ "mybxKE1dZUzOozAfBgNVHSMEGDAWgBRPq/CQlK/zuXkjZvTCibu+vejD+jAFBgMr\n" ++ "ZXADQQBU+A+uF0yrtO/yv9cRUdCoL3Y1NKM35INg8BQDnkv724cW9zk1x0q9Fuou\n" ++ "zvfSVb8S3vT8fF5ZDOxarQs6ZH0C\n" ++ "-----END CERTIFICATE-----\n", ++ /* ICA1 */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBXTCCAQ+gAwIBAgIUfUWP+AQHpdFTRKTf21mMzjaJsp0wBQYDK2VwMBkxFzAV\n" ++ "BgNVBAMTDkdudVRMUyB0ZXN0IENBMCAXDTI0MDMxMjIyNTMzOVoYDzk5OTkxMjMx\n" ++ "MjM1OTU5WjAdMRswGQYDVQQDDBJHbnVUTFMgdGVzdCBJQ0EgJGkwKjAFBgMrZXAD\n" ++ "IQAVmfBAvLbT+pTD24pQrr6S0jEIFIV/qOv93yYvAUzpzKNjMGEwDwYDVR0TAQH/\n" ++ "BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAgQwHQYDVR0OBBYEFE+r8JCUr/O5eSNm9MKJ\n" ++ "u7696MP6MB8GA1UdIwQYMBaAFAFpt5wrFsqCtHc4PpluPDvwcxQLMAUGAytlcANB\n" ++ "AC6+XZnthjlUD0TbBKRF3qT5if3Pp29Bgvutw8859unzUZW8FkHg5KeDBj9ncgJc\n" ++ "O2tFnNH2hV6LDPJzU0rtLQc=\n" ++ "-----END CERTIFICATE-----\n", ++ NULL ++}; ++ ++static const char *many_icas_ca[] = { ++ /* CA (self-signed) */ ++ "-----BEGIN CERTIFICATE-----\n" ++ "MIIBNzCB6qADAgECAhRjaokcQwcrtW8tjuVFz3A33F8POjAFBgMrZXAwGTEXMBUG\n" ++ "A1UEAxMOR251VExTIHRlc3QgQ0EwIBcNMjQwMzEyMjI1MzM5WhgPOTk5OTEyMzEy\n" ++ "MzU5NTlaMBkxFzAVBgNVBAMTDkdudVRMUyB0ZXN0IENBMCowBQYDK2VwAyEAvoxP\n" ++ "TNdbWktxA8qQNNH+25Cx9rzP+DxLGeI/7ODwrQGjQjBAMA8GA1UdEwEB/wQFMAMB\n" ++ "Af8wDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBQBabecKxbKgrR3OD6Zbjw78HMU\n" ++ "CzAFBgMrZXADQQCP5IUD74M7WrUx20uqzrzuj+s2jnBVmLQfWf/Ucetx+oTRFeq4\n" ++ "xZB/adWhycSeJUAB1zKqYUV9hgT8FWHbnHII\n" ++ "-----END CERTIFICATE-----\n", ++ NULL ++}; ++ + #if defined __clang__ || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wunused-variable" +@@ -4696,6 +4903,8 @@ static struct { + 1620118136, 1 }, + { "cross signed - ok", cross_signed, cross_signed_ca, 0, 0, 0, + 1704955300 }, ++ { "many intermediates - ok", many_icas, many_icas_ca, 0, 0, 0, ++ 1710284400 }, + { NULL, NULL, NULL, 0, 0 } + }; + +-- +2.44.0 + diff --git a/SOURCES/gnutls-3.8.9-CVE-2024-12243.patch b/SOURCES/gnutls-3.8.9-CVE-2024-12243.patch new file mode 100644 index 0000000..096f95d --- /dev/null +++ b/SOURCES/gnutls-3.8.9-CVE-2024-12243.patch @@ -0,0 +1,1148 @@ +From 4760bc63531e3f5039e70ede91a20e1194410892 Mon Sep 17 00:00:00 2001 +From: Daiki Ueno +Date: Mon, 18 Nov 2024 17:23:46 +0900 +Subject: [PATCH] x509: optimize name constraints processing + +This switches the representation name constraints from linked lists to +array lists to optimize the lookup performance from O(n) to O(1), also +enforces a limit of name constraint checks against subject alternative +names. + +Signed-off-by: Daiki Ueno +--- + lib/datum.c | 7 +- + lib/x509/name_constraints.c | 595 +++++++++++++++++++++--------------- + lib/x509/x509_ext.c | 80 +++-- + lib/x509/x509_ext_int.h | 5 + + lib/x509/x509_int.h | 21 +- + 5 files changed, 399 insertions(+), 309 deletions(-) + +diff --git a/lib/datum.c b/lib/datum.c +index 66e016965d..5577c2b4ab 100644 +--- a/lib/datum.c ++++ b/lib/datum.c +@@ -29,6 +29,7 @@ + #include "num.h" + #include "datum.h" + #include "errors.h" ++#include "intprops.h" + + /* On error, @dat is not changed. */ + int _gnutls_set_datum(gnutls_datum_t *dat, const void *data, size_t data_size) +@@ -60,7 +61,11 @@ int _gnutls_set_strdatum(gnutls_datum_t *dat, const void *data, + if (data == NULL) + return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER); + +- unsigned char *m = gnutls_malloc(data_size + 1); ++ size_t capacity; ++ if (!INT_ADD_OK(data_size, 1, &capacity)) ++ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); ++ ++ unsigned char *m = gnutls_malloc(capacity); + if (!m) + return GNUTLS_E_MEMORY_ERROR; + +diff --git a/lib/x509/name_constraints.c b/lib/x509/name_constraints.c +index 8327a9d94e..3c6e306303 100644 +--- a/lib/x509/name_constraints.c ++++ b/lib/x509/name_constraints.c +@@ -33,51 +33,98 @@ + #include + #include "x509_b64.h" + #include "x509_int.h" ++#include "x509_ext_int.h" + #include + + #include "ip.h" + #include "ip-in-cidr.h" ++#include "intprops.h" ++ ++#define MAX_NC_CHECKS (1 << 20) ++ ++struct name_constraints_node_st { ++ unsigned type; ++ gnutls_datum_t name; ++}; ++ ++struct name_constraints_node_list_st { ++ struct name_constraints_node_st **data; ++ size_t size; ++ size_t capacity; ++}; ++ ++struct gnutls_name_constraints_st { ++ struct name_constraints_node_list_st nodes; /* owns elements */ ++ struct name_constraints_node_list_st permitted; /* borrows elements */ ++ struct name_constraints_node_list_st excluded; /* borrows elements */ ++}; ++ ++static struct name_constraints_node_st * ++name_constraints_node_new(gnutls_x509_name_constraints_t nc, unsigned type, ++ unsigned char *data, unsigned int size); + +-// for documentation see the implementation + static int +-name_constraints_intersect_nodes(name_constraints_node_st *nc1, +- name_constraints_node_st *nc2, +- name_constraints_node_st **intersection); ++name_constraints_node_list_add(struct name_constraints_node_list_st *list, ++ struct name_constraints_node_st *node) ++{ ++ if (!list->capacity || list->size == list->capacity) { ++ size_t new_capacity = list->capacity; ++ struct name_constraints_node_st **new_data; ++ ++ if (!INT_MULTIPLY_OK(new_capacity, 2, &new_capacity) || ++ !INT_ADD_OK(new_capacity, 1, &new_capacity)) ++ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); ++ new_data = _gnutls_reallocarray( ++ list->data, new_capacity, ++ sizeof(struct name_constraints_node_st *)); ++ if (!new_data) ++ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); ++ list->capacity = new_capacity; ++ list->data = new_data; ++ } ++ list->data[list->size++] = node; ++ return 0; ++} ++ ++// for documentation see the implementation ++static int name_constraints_intersect_nodes( ++ gnutls_x509_name_constraints_t nc, ++ const struct name_constraints_node_st *node1, ++ const struct name_constraints_node_st *node2, ++ struct name_constraints_node_st **intersection); + + /*- +- * is_nc_empty: ++ * _gnutls_x509_name_constraints_is_empty: + * @nc: name constraints structure +- * @type: type (gnutls_x509_subject_alt_name_t) ++ * @type: type (gnutls_x509_subject_alt_name_t or 0) + * + * Test whether given name constraints structure has any constraints (permitted + * or excluded) of a given type. @nc must be allocated (not NULL) before the call. ++ * If @type is 0, type checking will be skipped. + * +- * Returns: 0 if @nc contains constraints of type @type, 1 otherwise ++ * Returns: false if @nc contains constraints of type @type, true otherwise + -*/ +-static unsigned is_nc_empty(struct gnutls_name_constraints_st *nc, +- unsigned type) ++bool _gnutls_x509_name_constraints_is_empty(gnutls_x509_name_constraints_t nc, ++ unsigned type) + { +- name_constraints_node_st *t; ++ if (nc->permitted.size == 0 && nc->excluded.size == 0) ++ return true; + +- if (nc->permitted == NULL && nc->excluded == NULL) +- return 1; ++ if (type == 0) ++ return false; + +- t = nc->permitted; +- while (t != NULL) { +- if (t->type == type) +- return 0; +- t = t->next; ++ for (size_t i = 0; i < nc->permitted.size; i++) { ++ if (nc->permitted.data[i]->type == type) ++ return false; + } + +- t = nc->excluded; +- while (t != NULL) { +- if (t->type == type) +- return 0; +- t = t->next; ++ for (size_t i = 0; i < nc->excluded.size; i++) { ++ if (nc->excluded.data[i]->type == type) ++ return false; + } + + /* no constraint for that type exists */ +- return 1; ++ return true; + } + + /*- +@@ -115,21 +162,16 @@ static int validate_name_constraints_node(gnutls_x509_subject_alt_name_t type, + return GNUTLS_E_SUCCESS; + } + +-int _gnutls_extract_name_constraints(asn1_node c2, const char *vstr, +- name_constraints_node_st **_nc) ++static int extract_name_constraints(gnutls_x509_name_constraints_t nc, ++ asn1_node c2, const char *vstr, ++ struct name_constraints_node_list_st *nodes) + { + int ret; + char tmpstr[128]; + unsigned indx; + gnutls_datum_t tmp = { NULL, 0 }; + unsigned int type; +- struct name_constraints_node_st *nc, *prev; +- +- prev = *_nc; +- if (prev != NULL) { +- while (prev->next != NULL) +- prev = prev->next; +- } ++ struct name_constraints_node_st *node; + + for (indx = 1;; indx++) { + snprintf(tmpstr, sizeof(tmpstr), "%s.?%u.base", vstr, indx); +@@ -172,25 +214,19 @@ int _gnutls_extract_name_constraints(asn1_node c2, const char *vstr, + goto cleanup; + } + +- nc = gnutls_malloc(sizeof(struct name_constraints_node_st)); +- if (nc == NULL) { ++ node = name_constraints_node_new(nc, type, tmp.data, tmp.size); ++ _gnutls_free_datum(&tmp); ++ if (node == NULL) { + gnutls_assert(); + ret = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + +- memcpy(&nc->name, &tmp, sizeof(gnutls_datum_t)); +- nc->type = type; +- nc->next = NULL; +- +- if (prev == NULL) { +- *_nc = prev = nc; +- } else { +- prev->next = nc; +- prev = nc; ++ ret = name_constraints_node_list_add(nodes, node); ++ if (ret < 0) { ++ gnutls_assert(); ++ goto cleanup; + } +- +- tmp.data = NULL; + } + + assert(ret < 0); +@@ -205,84 +241,104 @@ cleanup: + return ret; + } + ++int _gnutls_x509_name_constraints_extract(asn1_node c2, ++ const char *permitted_name, ++ const char *excluded_name, ++ gnutls_x509_name_constraints_t nc) ++{ ++ int ret; ++ ++ ret = extract_name_constraints(nc, c2, permitted_name, &nc->permitted); ++ if (ret < 0) ++ return gnutls_assert_val(ret); ++ ret = extract_name_constraints(nc, c2, excluded_name, &nc->excluded); ++ if (ret < 0) ++ return gnutls_assert_val(ret); ++ ++ return ret; ++} ++ + /*- +- * _gnutls_name_constraints_node_free: ++ * name_constraints_node_free: + * @node: name constraints node + * +- * Deallocate a list of name constraints nodes starting at the given node. ++ * Deallocate a name constraints node. + -*/ +-void _gnutls_name_constraints_node_free(name_constraints_node_st *node) ++static void name_constraints_node_free(struct name_constraints_node_st *node) + { +- name_constraints_node_st *next, *t; +- +- t = node; +- while (t != NULL) { +- next = t->next; +- gnutls_free(t->name.data); +- gnutls_free(t); +- t = next; ++ if (node) { ++ gnutls_free(node->name.data); ++ gnutls_free(node); + } + } + + /*- + * name_constraints_node_new: + * @type: name constraints type to set (gnutls_x509_subject_alt_name_t) ++ * @nc: a %gnutls_x509_name_constraints_t + * @data: name.data to set or NULL + * @size: name.size to set + * + * Allocate a new name constraints node and set its type, name size and name data. +- * If @data is set to NULL, name data will be an array of \x00 (the length of @size). +- * The .next pointer is set to NULL. + * + * Returns: Pointer to newly allocated node or NULL in case of memory error. + -*/ +-static name_constraints_node_st * +-name_constraints_node_new(unsigned type, unsigned char *data, unsigned int size) ++static struct name_constraints_node_st * ++name_constraints_node_new(gnutls_x509_name_constraints_t nc, unsigned type, ++ unsigned char *data, unsigned int size) + { +- name_constraints_node_st *tmp = +- gnutls_malloc(sizeof(struct name_constraints_node_st)); ++ struct name_constraints_node_st *tmp; ++ int ret; ++ ++ tmp = gnutls_calloc(1, sizeof(struct name_constraints_node_st)); + if (tmp == NULL) + return NULL; + tmp->type = type; +- tmp->next = NULL; +- tmp->name.size = size; +- tmp->name.data = NULL; +- if (tmp->name.size > 0) { +- tmp->name.data = gnutls_malloc(tmp->name.size); +- if (tmp->name.data == NULL) { ++ ++ if (data) { ++ ret = _gnutls_set_strdatum(&tmp->name, data, size); ++ if (ret < 0) { ++ gnutls_assert(); + gnutls_free(tmp); + return NULL; + } +- if (data != NULL) { +- memcpy(tmp->name.data, data, size); +- } else { +- memset(tmp->name.data, 0, size); +- } + } ++ ++ ret = name_constraints_node_list_add(&nc->nodes, tmp); ++ if (ret < 0) { ++ gnutls_assert(); ++ name_constraints_node_free(tmp); ++ return NULL; ++ } ++ + return tmp; + } + + /*- +- * @brief _gnutls_name_constraints_intersect: +- * @_nc: first name constraints list (permitted) +- * @_nc2: name constraints list to merge with (permitted) +- * @_nc_excluded: Corresponding excluded name constraints list ++ * @brief name_constraints_node_list_intersect: ++ * @nc: %gnutls_x509_name_constraints_t ++ * @permitted: first name constraints list (permitted) ++ * @permitted2: name constraints list to merge with (permitted) ++ * @excluded: Corresponding excluded name constraints list + * +- * This function finds the intersection of @_nc and @_nc2. The result is placed in @_nc, +- * the original @_nc is deallocated. @_nc2 is not changed. If necessary, a universal ++ * This function finds the intersection of @permitted and @permitted2. The result is placed in @permitted, ++ * the original @permitted is modified. @permitted2 is not changed. If necessary, a universal + * excluded name constraint node of the right type is added to the list provided +- * in @_nc_excluded. ++ * in @excluded. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + -*/ +-static int +-_gnutls_name_constraints_intersect(name_constraints_node_st **_nc, +- name_constraints_node_st *_nc2, +- name_constraints_node_st **_nc_excluded) ++static int name_constraints_node_list_intersect( ++ gnutls_x509_name_constraints_t nc, ++ struct name_constraints_node_list_st *permitted, ++ const struct name_constraints_node_list_st *permitted2, ++ struct name_constraints_node_list_st *excluded) + { +- name_constraints_node_st *nc, *nc2, *t, *tmp, *dest = NULL, +- *prev = NULL; ++ struct name_constraints_node_st *tmp; + int ret, type, used; ++ struct name_constraints_node_list_st removed = { .data = NULL, ++ .size = 0, ++ .capacity = 0 }; + + /* temporary array to see, if we need to add universal excluded constraints + * (see phase 3 for details) +@@ -291,61 +347,73 @@ _gnutls_name_constraints_intersect(name_constraints_node_st **_nc, + memset(types_with_empty_intersection, 0, + sizeof(types_with_empty_intersection)); + +- if (*_nc == NULL || _nc2 == NULL) ++ if (permitted->size == 0 || permitted2->size == 0) + return 0; + + /* Phase 1 +- * For each name in _NC, if a _NC2 does not contain a name +- * with the same type, preserve the original name. +- * Do this also for node of unknown type (not DNS, email, IP */ +- t = nc = *_nc; +- while (t != NULL) { +- name_constraints_node_st *next = t->next; +- nc2 = _nc2; +- while (nc2 != NULL) { +- if (t->type == nc2->type) { ++ * For each name in PERMITTED, if a PERMITTED2 does not contain a name ++ * with the same type, move the original name to REMOVED. ++ * Do this also for node of unknown type (not DNS, email, IP) */ ++ for (size_t i = 0; i < permitted->size;) { ++ struct name_constraints_node_st *t = permitted->data[i]; ++ const struct name_constraints_node_st *found = NULL; ++ ++ for (size_t j = 0; j < permitted2->size; j++) { ++ const struct name_constraints_node_st *t2 = ++ permitted2->data[j]; ++ if (t->type == t2->type) { + // check bounds (we will use 't->type' as index) +- if (t->type > GNUTLS_SAN_MAX || t->type == 0) +- return gnutls_assert_val( +- GNUTLS_E_INTERNAL_ERROR); ++ if (t->type > GNUTLS_SAN_MAX || t->type == 0) { ++ gnutls_assert(); ++ ret = GNUTLS_E_INTERNAL_ERROR; ++ goto cleanup; ++ } + // note the possibility of empty intersection for this type + // if we add something to the intersection in phase 2, + // we will reset this flag back to 0 then + types_with_empty_intersection[t->type - 1] = 1; ++ found = t2; + break; + } +- nc2 = nc2->next; + } +- if (nc2 == NULL || (t->type != GNUTLS_SAN_DNSNAME && +- t->type != GNUTLS_SAN_RFC822NAME && +- t->type != GNUTLS_SAN_IPADDRESS)) { +- /* move node from NC to DEST */ +- if (prev != NULL) +- prev->next = next; +- else +- prev = nc = next; +- t->next = dest; +- dest = t; +- } else { +- prev = t; ++ ++ if (found != NULL && (t->type == GNUTLS_SAN_DNSNAME || ++ t->type == GNUTLS_SAN_RFC822NAME || ++ t->type == GNUTLS_SAN_IPADDRESS)) { ++ /* move node from PERMITTED to REMOVED */ ++ ret = name_constraints_node_list_add(&removed, t); ++ if (ret < 0) { ++ gnutls_assert(); ++ goto cleanup; ++ } ++ /* remove node by swapping */ ++ if (i < permitted->size - 1) ++ permitted->data[i] = ++ permitted->data[permitted->size - 1]; ++ permitted->size--; ++ continue; + } +- t = next; ++ i++; + } + + /* Phase 2 +- * iterate through all combinations from nc2 and nc1 ++ * iterate through all combinations from PERMITTED2 and PERMITTED + * and create intersections of nodes with same type */ +- nc2 = _nc2; +- while (nc2 != NULL) { +- // current nc2 node has not yet been used for any intersection +- // (and is not in DEST either) ++ for (size_t i = 0; i < permitted2->size; i++) { ++ const struct name_constraints_node_st *t2 = permitted2->data[i]; ++ ++ // current PERMITTED2 node has not yet been used for any intersection ++ // (and is not in REMOVED either) + used = 0; +- t = nc; +- while (t != NULL) { ++ for (size_t j = 0; j < removed.size; j++) { ++ const struct name_constraints_node_st *t = ++ removed.data[j]; + // save intersection of name constraints into tmp +- ret = name_constraints_intersect_nodes(t, nc2, &tmp); +- if (ret < 0) +- return gnutls_assert_val(ret); ++ ret = name_constraints_intersect_nodes(nc, t, t2, &tmp); ++ if (ret < 0) { ++ gnutls_assert(); ++ goto cleanup; ++ } + used = 1; + // if intersection is not empty + if (tmp != +@@ -360,32 +428,34 @@ _gnutls_name_constraints_intersect(name_constraints_node_st **_nc, + // we will not add universal excluded constraint for this type + types_with_empty_intersection[tmp->type - 1] = + 0; +- // add intersection node to DEST +- tmp->next = dest; +- dest = tmp; ++ // add intersection node to PERMITTED ++ ret = name_constraints_node_list_add(permitted, ++ tmp); ++ if (ret < 0) { ++ gnutls_assert(); ++ goto cleanup; ++ } + } +- t = t->next; + } +- // if the node from nc2 was not used for intersection, copy it to DEST ++ // if the node from PERMITTED2 was not used for intersection, copy it to DEST + // Beware: also copies nodes other than DNS, email, IP, + // since their counterpart may have been moved in phase 1. + if (!used) { + tmp = name_constraints_node_new( +- nc2->type, nc2->name.data, nc2->name.size); ++ nc, t2->type, t2->name.data, t2->name.size); + if (tmp == NULL) { +- _gnutls_name_constraints_node_free(dest); +- return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); ++ gnutls_assert(); ++ ret = GNUTLS_E_MEMORY_ERROR; ++ goto cleanup; ++ } ++ ret = name_constraints_node_list_add(permitted, tmp); ++ if (ret < 0) { ++ gnutls_assert(); ++ goto cleanup; + } +- tmp->next = dest; +- dest = tmp; + } +- nc2 = nc2->next; + } + +- /* replace the original with the new */ +- _gnutls_name_constraints_node_free(nc); +- *_nc = dest; +- + /* Phase 3 + * For each type: If we have empty permitted name constraints now + * and we didn't have at the beginning, we have to add a new +@@ -400,63 +470,77 @@ _gnutls_name_constraints_intersect(name_constraints_node_st **_nc, + switch (type) { + case GNUTLS_SAN_IPADDRESS: + // add universal restricted range for IPv4 +- tmp = name_constraints_node_new(GNUTLS_SAN_IPADDRESS, +- NULL, 8); ++ tmp = name_constraints_node_new( ++ nc, GNUTLS_SAN_IPADDRESS, NULL, 8); + if (tmp == NULL) { +- _gnutls_name_constraints_node_free(dest); +- return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); ++ gnutls_assert(); ++ ret = GNUTLS_E_MEMORY_ERROR; ++ goto cleanup; ++ } ++ ret = name_constraints_node_list_add(excluded, tmp); ++ if (ret < 0) { ++ gnutls_assert(); ++ goto cleanup; + } +- tmp->next = *_nc_excluded; +- *_nc_excluded = tmp; + // add universal restricted range for IPv6 +- tmp = name_constraints_node_new(GNUTLS_SAN_IPADDRESS, +- NULL, 32); ++ tmp = name_constraints_node_new( ++ nc, GNUTLS_SAN_IPADDRESS, NULL, 32); + if (tmp == NULL) { +- _gnutls_name_constraints_node_free(dest); +- return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); ++ gnutls_assert(); ++ ret = GNUTLS_E_MEMORY_ERROR; ++ goto cleanup; ++ } ++ ret = name_constraints_node_list_add(excluded, tmp); ++ if (ret < 0) { ++ gnutls_assert(); ++ goto cleanup; + } +- tmp->next = *_nc_excluded; +- *_nc_excluded = tmp; + break; + case GNUTLS_SAN_DNSNAME: + case GNUTLS_SAN_RFC822NAME: +- tmp = name_constraints_node_new(type, NULL, 0); ++ tmp = name_constraints_node_new(nc, type, NULL, 0); + if (tmp == NULL) { +- _gnutls_name_constraints_node_free(dest); +- return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); ++ gnutls_assert(); ++ ret = GNUTLS_E_MEMORY_ERROR; ++ goto cleanup; ++ } ++ ret = name_constraints_node_list_add(excluded, tmp); ++ if (ret < 0) { ++ gnutls_assert(); ++ goto cleanup; + } +- tmp->next = *_nc_excluded; +- *_nc_excluded = tmp; + break; + default: // do nothing, at least one node was already moved in phase 1 + break; + } + } +- return GNUTLS_E_SUCCESS; ++ ret = GNUTLS_E_SUCCESS; ++ ++cleanup: ++ gnutls_free(removed.data); ++ return ret; + } + +-static int _gnutls_name_constraints_append(name_constraints_node_st **_nc, +- name_constraints_node_st *_nc2) ++static int name_constraints_node_list_concat( ++ gnutls_x509_name_constraints_t nc, ++ struct name_constraints_node_list_st *nodes, ++ const struct name_constraints_node_list_st *nodes2) + { +- name_constraints_node_st *nc, *nc2; +- struct name_constraints_node_st *tmp; +- +- if (_nc2 == NULL) +- return 0; +- +- nc2 = _nc2; +- while (nc2) { +- nc = *_nc; +- +- tmp = name_constraints_node_new(nc2->type, nc2->name.data, +- nc2->name.size); +- if (tmp == NULL) ++ for (size_t i = 0; i < nodes2->size; i++) { ++ const struct name_constraints_node_st *node = nodes2->data[i]; ++ struct name_constraints_node_st *tmp; ++ int ret; ++ ++ tmp = name_constraints_node_new(nc, node->type, node->name.data, ++ node->name.size); ++ if (tmp == NULL) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); +- +- tmp->next = nc; +- *_nc = tmp; +- +- nc2 = nc2->next; ++ } ++ ret = name_constraints_node_list_add(nodes, tmp); ++ if (ret < 0) { ++ name_constraints_node_free(tmp); ++ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); ++ } + } + + return 0; +@@ -524,6 +608,25 @@ cleanup: + return ret; + } + ++void _gnutls_x509_name_constraints_clear(gnutls_x509_name_constraints_t nc) ++{ ++ for (size_t i = 0; i < nc->nodes.size; i++) { ++ struct name_constraints_node_st *node = nc->nodes.data[i]; ++ name_constraints_node_free(node); ++ } ++ gnutls_free(nc->nodes.data); ++ nc->nodes.capacity = 0; ++ nc->nodes.size = 0; ++ ++ gnutls_free(nc->permitted.data); ++ nc->permitted.capacity = 0; ++ nc->permitted.size = 0; ++ ++ gnutls_free(nc->excluded.data); ++ nc->excluded.capacity = 0; ++ nc->excluded.size = 0; ++} ++ + /** + * gnutls_x509_name_constraints_deinit: + * @nc: The nameconstraints +@@ -534,9 +637,7 @@ cleanup: + **/ + void gnutls_x509_name_constraints_deinit(gnutls_x509_name_constraints_t nc) + { +- _gnutls_name_constraints_node_free(nc->permitted); +- _gnutls_name_constraints_node_free(nc->excluded); +- ++ _gnutls_x509_name_constraints_clear(nc); + gnutls_free(nc); + } + +@@ -552,12 +653,15 @@ void gnutls_x509_name_constraints_deinit(gnutls_x509_name_constraints_t nc) + **/ + int gnutls_x509_name_constraints_init(gnutls_x509_name_constraints_t *nc) + { +- *nc = gnutls_calloc(1, sizeof(struct gnutls_name_constraints_st)); +- if (*nc == NULL) { ++ struct gnutls_name_constraints_st *tmp; ++ ++ tmp = gnutls_calloc(1, sizeof(struct gnutls_name_constraints_st)); ++ if (tmp == NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + ++ *nc = tmp; + return 0; + } + +@@ -565,36 +669,25 @@ static int name_constraints_add(gnutls_x509_name_constraints_t nc, + gnutls_x509_subject_alt_name_t type, + const gnutls_datum_t *name, unsigned permitted) + { +- struct name_constraints_node_st *tmp, *prev = NULL; ++ struct name_constraints_node_st *tmp; ++ struct name_constraints_node_list_st *nodes; + int ret; + + ret = validate_name_constraints_node(type, name); + if (ret < 0) + return gnutls_assert_val(ret); + +- if (permitted != 0) +- prev = tmp = nc->permitted; +- else +- prev = tmp = nc->excluded; +- +- while (tmp != NULL) { +- tmp = tmp->next; +- if (tmp != NULL) +- prev = tmp; +- } ++ nodes = permitted ? &nc->permitted : &nc->excluded; + +- tmp = name_constraints_node_new(type, name->data, name->size); ++ tmp = name_constraints_node_new(nc, type, name->data, name->size); + if (tmp == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); +- tmp->next = NULL; + +- if (prev == NULL) { +- if (permitted != 0) +- nc->permitted = tmp; +- else +- nc->excluded = tmp; +- } else +- prev->next = tmp; ++ ret = name_constraints_node_list_add(nodes, tmp); ++ if (ret < 0) { ++ name_constraints_node_free(tmp); ++ return gnutls_assert_val(ret); ++ } + + return 0; + } +@@ -620,14 +713,15 @@ int _gnutls_x509_name_constraints_merge(gnutls_x509_name_constraints_t nc, + { + int ret; + +- ret = _gnutls_name_constraints_intersect(&nc->permitted, nc2->permitted, +- &nc->excluded); ++ ret = name_constraints_node_list_intersect( ++ nc, &nc->permitted, &nc2->permitted, &nc->excluded); + if (ret < 0) { + gnutls_assert(); + return ret; + } + +- ret = _gnutls_name_constraints_append(&nc->excluded, nc2->excluded); ++ ret = name_constraints_node_list_concat(nc, &nc->excluded, ++ &nc2->excluded); + if (ret < 0) { + gnutls_assert(); + return ret; +@@ -804,50 +898,51 @@ static unsigned email_matches(const gnutls_datum_t *name, + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + -*/ +-static int +-name_constraints_intersect_nodes(name_constraints_node_st *nc1, +- name_constraints_node_st *nc2, +- name_constraints_node_st **_intersection) ++static int name_constraints_intersect_nodes( ++ gnutls_x509_name_constraints_t nc, ++ const struct name_constraints_node_st *node1, ++ const struct name_constraints_node_st *node2, ++ struct name_constraints_node_st **_intersection) + { + // presume empty intersection +- name_constraints_node_st *intersection = NULL; +- name_constraints_node_st *to_copy = NULL; ++ struct name_constraints_node_st *intersection = NULL; ++ const struct name_constraints_node_st *to_copy = NULL; + unsigned iplength = 0; + unsigned byte; + + *_intersection = NULL; + +- if (nc1->type != nc2->type) { ++ if (node1->type != node2->type) { + return GNUTLS_E_SUCCESS; + } +- switch (nc1->type) { ++ switch (node1->type) { + case GNUTLS_SAN_DNSNAME: +- if (!dnsname_matches(&nc2->name, &nc1->name)) ++ if (!dnsname_matches(&node2->name, &node1->name)) + return GNUTLS_E_SUCCESS; +- to_copy = nc2; ++ to_copy = node2; + break; + case GNUTLS_SAN_RFC822NAME: +- if (!email_matches(&nc2->name, &nc1->name)) ++ if (!email_matches(&node2->name, &node1->name)) + return GNUTLS_E_SUCCESS; +- to_copy = nc2; ++ to_copy = node2; + break; + case GNUTLS_SAN_IPADDRESS: +- if (nc1->name.size != nc2->name.size) ++ if (node1->name.size != node2->name.size) + return GNUTLS_E_SUCCESS; +- iplength = nc1->name.size / 2; ++ iplength = node1->name.size / 2; + for (byte = 0; byte < iplength; byte++) { +- if (((nc1->name.data[byte] ^ +- nc2->name.data[byte]) // XOR of addresses +- & +- nc1->name.data[byte + iplength] // AND mask from nc1 +- & +- nc2->name.data[byte + iplength]) // AND mask from nc2 ++ if (((node1->name.data[byte] ^ ++ node2->name.data[byte]) // XOR of addresses ++ & node1->name.data[byte + ++ iplength] // AND mask from nc1 ++ & node2->name.data[byte + ++ iplength]) // AND mask from nc2 + != 0) { + // CIDRS do not intersect + return GNUTLS_E_SUCCESS; + } + } +- to_copy = nc2; ++ to_copy = node2; + break; + default: + // for other types, we don't know how to do the intersection, assume empty +@@ -856,8 +951,9 @@ name_constraints_intersect_nodes(name_constraints_node_st *nc1, + + // copy existing node if applicable + if (to_copy != NULL) { +- *_intersection = name_constraints_node_new( +- to_copy->type, to_copy->name.data, to_copy->name.size); ++ *_intersection = name_constraints_node_new(nc, to_copy->type, ++ to_copy->name.data, ++ to_copy->name.size); + if (*_intersection == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + intersection = *_intersection; +@@ -869,12 +965,12 @@ name_constraints_intersect_nodes(name_constraints_node_st *nc1, + _gnutls_mask_ip(intersection->name.data, + intersection->name.data + iplength, + iplength); +- _gnutls_mask_ip(nc1->name.data, +- nc1->name.data + iplength, iplength); ++ _gnutls_mask_ip(node1->name.data, ++ node1->name.data + iplength, iplength); + // update intersection, if necessary (we already know one is subset of other) + for (byte = 0; byte < 2 * iplength; byte++) { + intersection->name.data[byte] |= +- nc1->name.data[byte]; ++ node1->name.data[byte]; + } + } + } +@@ -1177,10 +1273,17 @@ gnutls_x509_name_constraints_check_crt(gnutls_x509_name_constraints_t nc, + unsigned idx, t, san_type; + gnutls_datum_t n; + unsigned found_one; ++ size_t checks; + +- if (is_nc_empty(nc, type) != 0) ++ if (_gnutls_x509_name_constraints_is_empty(nc, type) != 0) + return 1; /* shortcut; no constraints to check */ + ++ if (!INT_ADD_OK(nc->permitted.size, nc->excluded.size, &checks) || ++ !INT_MULTIPLY_OK(checks, cert->san->size, &checks) || ++ checks > MAX_NC_CHECKS) { ++ return gnutls_assert_val(0); ++ } ++ + if (type == GNUTLS_SAN_RFC822NAME) { + found_one = 0; + for (idx = 0;; idx++) { +@@ -1378,20 +1481,13 @@ int gnutls_x509_name_constraints_get_permitted(gnutls_x509_name_constraints_t nc + unsigned idx, unsigned *type, + gnutls_datum_t *name) + { +- unsigned int i; +- struct name_constraints_node_st *tmp = nc->permitted; ++ const struct name_constraints_node_st *tmp; + +- for (i = 0; i < idx; i++) { +- if (tmp == NULL) +- return gnutls_assert_val( +- GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); +- +- tmp = tmp->next; +- } +- +- if (tmp == NULL) ++ if (idx >= nc->permitted.size) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + ++ tmp = nc->permitted.data[idx]; ++ + *type = tmp->type; + *name = tmp->name; + +@@ -1421,20 +1517,13 @@ int gnutls_x509_name_constraints_get_excluded(gnutls_x509_name_constraints_t nc, + unsigned idx, unsigned *type, + gnutls_datum_t *name) + { +- unsigned int i; +- struct name_constraints_node_st *tmp = nc->excluded; +- +- for (i = 0; i < idx; i++) { +- if (tmp == NULL) +- return gnutls_assert_val( +- GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); ++ const struct name_constraints_node_st *tmp; + +- tmp = tmp->next; +- } +- +- if (tmp == NULL) ++ if (idx >= nc->excluded.size) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + ++ tmp = nc->excluded.data[idx]; ++ + *type = tmp->type; + *name = tmp->name; + +diff --git a/lib/x509/x509_ext.c b/lib/x509/x509_ext.c +index ae7216f23f..1714578de6 100644 +--- a/lib/x509/x509_ext.c ++++ b/lib/x509/x509_ext.c +@@ -34,10 +34,6 @@ + #include "intprops.h" + + #define MAX_ENTRIES 64 +-struct gnutls_subject_alt_names_st { +- struct name_st *names; +- unsigned int size; +-}; + + /** + * gnutls_subject_alt_names_init: +@@ -389,22 +385,15 @@ int gnutls_x509_ext_import_name_constraints(const gnutls_datum_t *ext, + } + + if (flags & GNUTLS_NAME_CONSTRAINTS_FLAG_APPEND && +- (nc->permitted != NULL || nc->excluded != NULL)) { ++ !_gnutls_x509_name_constraints_is_empty(nc, 0)) { + ret = gnutls_x509_name_constraints_init(&nc2); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + +- ret = _gnutls_extract_name_constraints(c2, "permittedSubtrees", +- &nc2->permitted); +- if (ret < 0) { +- gnutls_assert(); +- goto cleanup; +- } +- +- ret = _gnutls_extract_name_constraints(c2, "excludedSubtrees", +- &nc2->excluded); ++ ret = _gnutls_x509_name_constraints_extract( ++ c2, "permittedSubtrees", "excludedSubtrees", nc2); + if (ret < 0) { + gnutls_assert(); + goto cleanup; +@@ -416,18 +405,10 @@ int gnutls_x509_ext_import_name_constraints(const gnutls_datum_t *ext, + goto cleanup; + } + } else { +- _gnutls_name_constraints_node_free(nc->permitted); +- _gnutls_name_constraints_node_free(nc->excluded); +- +- ret = _gnutls_extract_name_constraints(c2, "permittedSubtrees", +- &nc->permitted); +- if (ret < 0) { +- gnutls_assert(); +- goto cleanup; +- } ++ _gnutls_x509_name_constraints_clear(nc); + +- ret = _gnutls_extract_name_constraints(c2, "excludedSubtrees", +- &nc->excluded); ++ ret = _gnutls_x509_name_constraints_extract( ++ c2, "permittedSubtrees", "excludedSubtrees", nc); + if (ret < 0) { + gnutls_assert(); + goto cleanup; +@@ -463,9 +444,10 @@ int gnutls_x509_ext_export_name_constraints(gnutls_x509_name_constraints_t nc, + int ret, result; + uint8_t null = 0; + asn1_node c2 = NULL; +- struct name_constraints_node_st *tmp; ++ unsigned rtype; ++ gnutls_datum_t rname; + +- if (nc->permitted == NULL && nc->excluded == NULL) ++ if (_gnutls_x509_name_constraints_is_empty(nc, 0)) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + result = asn1_create_element(_gnutls_get_pkix(), +@@ -475,11 +457,20 @@ int gnutls_x509_ext_export_name_constraints(gnutls_x509_name_constraints_t nc, + return _gnutls_asn2err(result); + } + +- if (nc->permitted == NULL) { ++ ret = gnutls_x509_name_constraints_get_permitted(nc, 0, &rtype, &rname); ++ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + (void)asn1_write_value(c2, "permittedSubtrees", NULL, 0); + } else { +- tmp = nc->permitted; +- do { ++ for (unsigned i = 0;; i++) { ++ ret = gnutls_x509_name_constraints_get_permitted( ++ nc, i, &rtype, &rname); ++ if (ret < 0) { ++ if (ret == ++ GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) ++ break; ++ gnutls_assert(); ++ goto cleanup; ++ } + result = asn1_write_value(c2, "permittedSubtrees", + "NEW", 1); + if (result != ASN1_SUCCESS) { +@@ -506,21 +497,29 @@ int gnutls_x509_ext_export_name_constraints(gnutls_x509_name_constraints_t nc, + } + + ret = _gnutls_write_general_name( +- c2, "permittedSubtrees.?LAST.base", tmp->type, +- tmp->name.data, tmp->name.size); ++ c2, "permittedSubtrees.?LAST.base", rtype, ++ rname.data, rname.size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } +- tmp = tmp->next; +- } while (tmp != NULL); ++ } + } + +- if (nc->excluded == NULL) { ++ ret = gnutls_x509_name_constraints_get_excluded(nc, 0, &rtype, &rname); ++ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + (void)asn1_write_value(c2, "excludedSubtrees", NULL, 0); + } else { +- tmp = nc->excluded; +- do { ++ for (unsigned i = 0;; i++) { ++ ret = gnutls_x509_name_constraints_get_excluded( ++ nc, i, &rtype, &rname); ++ if (ret < 0) { ++ if (ret == ++ GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) ++ break; ++ gnutls_assert(); ++ goto cleanup; ++ } + result = asn1_write_value(c2, "excludedSubtrees", "NEW", + 1); + if (result != ASN1_SUCCESS) { +@@ -546,14 +545,13 @@ int gnutls_x509_ext_export_name_constraints(gnutls_x509_name_constraints_t nc, + } + + ret = _gnutls_write_general_name( +- c2, "excludedSubtrees.?LAST.base", tmp->type, +- tmp->name.data, tmp->name.size); ++ c2, "excludedSubtrees.?LAST.base", rtype, ++ rname.data, rname.size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } +- tmp = tmp->next; +- } while (tmp != NULL); ++ } + } + + ret = _gnutls_x509_der_encode(c2, "", ext, 0); +diff --git a/lib/x509/x509_ext_int.h b/lib/x509/x509_ext_int.h +index 558d619565..b37d749976 100644 +--- a/lib/x509/x509_ext_int.h ++++ b/lib/x509/x509_ext_int.h +@@ -29,6 +29,11 @@ struct name_st { + gnutls_datum_t othername_oid; + }; + ++struct gnutls_subject_alt_names_st { ++ struct name_st *names; ++ unsigned int size; ++}; ++ + int _gnutls_alt_name_process(gnutls_datum_t *out, unsigned type, + const gnutls_datum_t *san, unsigned raw); + +diff --git a/lib/x509/x509_int.h b/lib/x509/x509_int.h +index 693a4dd924..30f8051a70 100644 +--- a/lib/x509/x509_int.h ++++ b/lib/x509/x509_int.h +@@ -503,20 +503,13 @@ int _gnutls_x509_crt_check_revocation(gnutls_x509_crt_t cert, + int crl_list_length, + gnutls_verify_output_function func); + +-typedef struct gnutls_name_constraints_st { +- struct name_constraints_node_st *permitted; +- struct name_constraints_node_st *excluded; +-} gnutls_name_constraints_st; +- +-typedef struct name_constraints_node_st { +- unsigned type; +- gnutls_datum_t name; +- struct name_constraints_node_st *next; +-} name_constraints_node_st; +- +-int _gnutls_extract_name_constraints(asn1_node c2, const char *vstr, +- name_constraints_node_st **_nc); +-void _gnutls_name_constraints_node_free(name_constraints_node_st *node); ++bool _gnutls_x509_name_constraints_is_empty(gnutls_x509_name_constraints_t nc, ++ unsigned type); ++int _gnutls_x509_name_constraints_extract(asn1_node c2, ++ const char *permitted_name, ++ const char *excluded_name, ++ gnutls_x509_name_constraints_t nc); ++void _gnutls_x509_name_constraints_clear(gnutls_x509_name_constraints_t nc); + int _gnutls_x509_name_constraints_merge(gnutls_x509_name_constraints_t nc, + gnutls_x509_name_constraints_t nc2); + +-- +GitLab + diff --git a/SPECS/gnutls.spec b/SPECS/gnutls.spec index f6303ad..7d9b29f 100644 --- a/SPECS/gnutls.spec +++ b/SPECS/gnutls.spec @@ -13,7 +13,7 @@ print(string.sub(hash, 0, 16)) } Version: 3.8.3 -Release: 1%{?dist} +Release: 6%{?dist} # not upstreamed Patch: gnutls-3.2.7-rpath.patch Patch: gnutls-3.7.2-enable-intel-cet.patch @@ -27,6 +27,9 @@ Patch: gnutls-3.7.8-ktls_skip_tls12_chachapoly_test.patch # upstreamed Patch: gnutls-3.8.3-ktls-utsname.patch +Patch: gnutls-3.8.3-deterministic-ecdsa-fixes.patch +Patch: gnutls-3.8.3-verify-chain.patch +Patch: gnutls-3.8.9-CVE-2024-12243.patch %bcond_without bootstrap %bcond_without dane @@ -58,7 +61,7 @@ BuildRequires: zlib-devel, brotli-devel, libzstd-devel BuildRequires: automake, autoconf, gperf, libtool %endif BuildRequires: texinfo -BuildRequires: nettle-devel >= 3.9.1 +BuildRequires: nettle-devel >= 3.10.1 %if %{with tpm12} BuildRequires: trousers-devel >= 0.3.11.2 %endif @@ -76,7 +79,7 @@ BuildRequires: p11-kit-trust, ca-certificates Requires: crypto-policies Requires: p11-kit-trust Requires: libtasn1 >= 4.3 -Requires: nettle >= 3.9.1 +Requires: nettle >= 3.10.1 %if %{with tpm12} Recommends: trousers >= 0.3.11.2 %endif @@ -413,6 +416,22 @@ make check %{?_smp_mflags} GNUTLS_SYSTEM_PRIORITY_FILE=/dev/null XFAIL_TESTS="$x %endif %changelog +* Mon Feb 17 2025 Daiki Ueno - 3.8.3-6 +- Bump nettle dependency to 3.10.1 (RHEL-52740) + +* Wed Feb 12 2025 Alexander Sosedkin - 3.8.3-5 +- Backport the fix for CVE-2024-12243 (RHEL-78580) + +* Fri Apr 5 2024 Daiki Ueno - 3.8.3-4 +- Bump release to ensure el9 package is greater than el9_* packages + +* Fri Mar 22 2024 Daiki Ueno - 3.8.3-3 +- Bump release to ensure el9 package is greater than el9_* packages + +* Thu Mar 21 2024 Daiki Ueno - 3.8.3-2 +- Fix timing side-channel in deterministic ECDSA (RHEL-28959) +- Fix potential crash during chain building/verification (RHEL-28954) + * Tue Jan 23 2024 Daiki Ueno - 3.8.3-1 - Update to gnutls 3.8.3 (RHEL-14891)