From 175655b771fd17b06dfb8cfb29eaadf0f3b6a8b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Sun, 11 Feb 2024 00:49:32 +0100 Subject: [PATCH 1/3] Fix case insensitive matching in isc_ht hash table implementation The case insensitive matching in isc_ht was basically completely broken as only the hashvalue computation was case insensitive, but the key comparison was always case sensitive. --- lib/isc/ht.c | 15 ++++++++++---- tests/isc/ht_test.c | 50 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/lib/isc/ht.c b/lib/isc/ht.c index f602e7195a..dd49b007da 100644 --- a/lib/isc/ht.c +++ b/lib/isc/ht.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -95,9 +96,11 @@ isc__ht_iter_next(isc_ht_iter_t *it); static bool isc__ht_node_match(isc_ht_node_t *node, const uint32_t hashval, - const uint8_t *key, uint32_t keysize) { + const uint8_t *key, uint32_t keysize, bool case_sensitive) { return (node->hashval == hashval && node->keysize == keysize && - memcmp(node->key, key, keysize) == 0); + (case_sensitive + ? (memcmp(node->key, key, keysize) == 0) + : (isc_ascii_lowerequal(node->key, key, keysize)))); } static uint32_t @@ -338,7 +341,9 @@ nexttable: for (isc_ht_node_t *node = ht->table[findex][hash]; node != NULL; node = node->next) { - if (isc__ht_node_match(node, hashval, key, keysize)) { + if (isc__ht_node_match(node, hashval, key, keysize, + ht->case_sensitive)) + { return (node); } } @@ -385,7 +390,9 @@ isc__ht_delete(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize, for (isc_ht_node_t *node = ht->table[idx][hash]; node != NULL; prev = node, node = node->next) { - if (isc__ht_node_match(node, hashval, key, keysize)) { + if (isc__ht_node_match(node, hashval, key, keysize, + ht->case_sensitive)) + { if (prev == NULL) { ht->table[idx][hash] = node->next; } else { diff --git a/tests/isc/ht_test.c b/tests/isc/ht_test.c index 0b90cbaa19..2303def008 100644 --- a/tests/isc/ht_test.c +++ b/tests/isc/ht_test.c @@ -330,7 +330,57 @@ ISC_RUN_TEST_IMPL(isc_ht_iterator) { test_ht_iterator(); } +ISC_RUN_TEST_IMPL(isc_ht_case) { + isc_ht_t *ht = NULL; + void *f = NULL; + isc_result_t result = ISC_R_UNSET; + + unsigned char lower[16] = { "test case" }; + unsigned char same[16] = { "test case" }; + unsigned char upper[16] = { "TEST CASE" }; + unsigned char mixed[16] = { "tEsT CaSe" }; + + isc_ht_init(&ht, mctx, 8, ISC_HT_CASE_SENSITIVE); + assert_non_null(ht); + + result = isc_ht_add(ht, lower, 16, (void *)lower); + assert_int_equal(result, ISC_R_SUCCESS); + + result = isc_ht_add(ht, same, 16, (void *)same); + assert_int_equal(result, ISC_R_EXISTS); + + result = isc_ht_add(ht, upper, 16, (void *)upper); + assert_int_equal(result, ISC_R_SUCCESS); + + result = isc_ht_find(ht, mixed, 16, &f); + assert_int_equal(result, ISC_R_NOTFOUND); + assert_null(f); + + isc_ht_destroy(&ht); + assert_null(ht); + + isc_ht_init(&ht, mctx, 8, ISC_HT_CASE_INSENSITIVE); + assert_non_null(ht); + + result = isc_ht_add(ht, lower, 16, (void *)lower); + assert_int_equal(result, ISC_R_SUCCESS); + + result = isc_ht_add(ht, same, 16, (void *)same); + assert_int_equal(result, ISC_R_EXISTS); + + result = isc_ht_add(ht, upper, 16, (void *)upper); + assert_int_equal(result, ISC_R_EXISTS); + + result = isc_ht_find(ht, mixed, 16, &f); + assert_int_equal(result, ISC_R_SUCCESS); + assert_ptr_equal(f, &lower); + + isc_ht_destroy(&ht); + assert_null(ht); +} + ISC_TEST_LIST_START +ISC_TEST_ENTRY(isc_ht_case) ISC_TEST_ENTRY(isc_ht_1_120) ISC_TEST_ENTRY(isc_ht_6_1000) ISC_TEST_ENTRY(isc_ht_24_200000) From a114042059ecbbc94ae0f604ca681323a75af480 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Sun, 11 Feb 2024 09:13:43 +0100 Subject: [PATCH 2/3] Add a system test for mixed-case data for the same owner We were missing a test where a single owner name would have multiple types with a different case. The generated RRSIGs and NSEC records will then have different case than the signed records and message parser have to cope with that and treat everything as the same owner. --- bin/tests/system/dnssec/ns3/secure.example.db.in | 5 +++++ bin/tests/system/dnssec/ns3/sign.sh | 4 +++- bin/tests/system/dnssec/tests.sh | 15 +++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/bin/tests/system/dnssec/ns3/secure.example.db.in b/bin/tests/system/dnssec/ns3/secure.example.db.in index ec39308e9a..883e06790b 100644 --- a/bin/tests/system/dnssec/ns3/secure.example.db.in +++ b/bin/tests/system/dnssec/ns3/secure.example.db.in @@ -47,3 +47,8 @@ rrsigonly A 10.0.0.29 cnameandkey CNAME @ cnamenokey CNAME @ dnameandkey DNAME @ + +mixedcase A 10.0.0.30 +mixedCASE TXT "mixed case" +MIXEDcase AAAA 2002:: +mIxEdCaSe LOC 37 52 56.788 N 121 54 55.02 W 1120m 10m 100m 10m diff --git a/bin/tests/system/dnssec/ns3/sign.sh b/bin/tests/system/dnssec/ns3/sign.sh index 77b63aee41..958c7e64c6 100644 --- a/bin/tests/system/dnssec/ns3/sign.sh +++ b/bin/tests/system/dnssec/ns3/sign.sh @@ -87,7 +87,9 @@ keyname=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -n zone "$zone cat "$infile" "$cnameandkey.key" "$dnameandkey.key" "$keyname.key" >"$zonefile" -"$SIGNER" -z -o "$zone" "$zonefile" >/dev/null +"$SIGNER" -z -D -o "$zone" "$zonefile" >/dev/null +cat "$zonefile" "$zonefile".signed >"$zonefile".tmp +mv "$zonefile".tmp "$zonefile".signed zone=bogus.example. infile=bogus.example.db.in diff --git a/bin/tests/system/dnssec/tests.sh b/bin/tests/system/dnssec/tests.sh index e98579b5b3..64a86d2cdf 100644 --- a/bin/tests/system/dnssec/tests.sh +++ b/bin/tests/system/dnssec/tests.sh @@ -766,6 +766,21 @@ n=$((n + 1)) test "$ret" -eq 0 || echo_i "failed" status=$((status + ret)) +echo_i "checking mixed-case positive validation ($n)" +ret=0 +for type in a txt aaaa loc; do + dig_with_opts +noauth mixedcase.secure.example. \ + @10.53.0.3 $type >dig.out.$type.ns3.test$n || ret=1 + dig_with_opts +noauth mixedcase.secure.example. \ + @10.53.0.4 $type >dig.out.$type.ns4.test$n || ret=1 + digcomp --lc dig.out.$type.ns3.test$n dig.out.$type.ns4.test$n || ret=1 + grep "status: NOERROR" dig.out.$type.ns4.test$n >/dev/null || ret=1 + grep "flags:.*ad.*QUERY" dig.out.$type.ns4.test$n >/dev/null || ret=1 +done +n=$((n + 1)) +test "$ret" -eq 0 || echo_i "failed" +status=$((status + ret)) + echo_i "checking multi-stage positive validation NSEC/NSEC3 ($n)" ret=0 dig_with_opts +noauth a.nsec3.example. \ From e91884553f8b53d8e7f595a36914d351c1a1789d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Sun, 11 Feb 2024 00:59:30 +0100 Subject: [PATCH 3/3] Add CHANGES note for [GL #4568] --- CHANGES | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index 3030de9f9e..78aaec3a30 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +6344. [bug] Fix case insensitive setting for isc_ht hashtable. + [GL #4568] + 6343. [placeholder] 6342. [placeholder]