From 2c544401b922cb3a443c88d9c99bdae106930c65 Mon Sep 17 00:00:00 2001 From: chengyechun Date: Tue, 19 Mar 2024 11:13:54 +0800 Subject: [PATCH] fix CVE-2023-4408 CVE-2023-5517 CVE-2023-5679 CVE-2023-50387 CVE-2023-50868 (cherry picked from commit ab8deb433bf4516aeaf7fa5e386c6d91766c8cfe) --- backport-CVE-2023-4408.patch | 901 +++++++++++++++++++ backport-CVE-2023-50387-CVE-2023-50868.patch | 600 ++++++++++++ backport-CVE-2023-5517.patch | 108 +++ backport-CVE-2023-5679.patch | 38 + bind.spec | 12 +- 5 files changed, 1658 insertions(+), 1 deletion(-) create mode 100644 backport-CVE-2023-4408.patch create mode 100644 backport-CVE-2023-50387-CVE-2023-50868.patch create mode 100644 backport-CVE-2023-5517.patch create mode 100644 backport-CVE-2023-5679.patch diff --git a/backport-CVE-2023-4408.patch b/backport-CVE-2023-4408.patch new file mode 100644 index 0000000..d1d3971 --- /dev/null +++ b/backport-CVE-2023-4408.patch @@ -0,0 +1,901 @@ +From 608707b4f5b473e416563bfe0d43e26d6dc4a5c6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= +Date: Mon, 11 Sep 2023 10:35:28 +0200 +Subject: [PATCH] Use hashtable when parsing a message + +When parsing messages use a hashtable instead of a linear search to +reduce the amount of work done in findname when there's more than one +name in the section. + +There are two hashtables: + +1) hashtable for owner names - that's constructed for each section when +we hit the second name in the section and destroyed right after parsing +that section; + +2) per-name hashtable - for each name in the section, we construct a new +hashtable for that name if there are more than one rdataset for that +particular name. + +Conflict:NA +Reference:https://downloads.isc.org/isc/bind/9.18.24/patches/0001-CVE-2023-4408.patch + +(cherry picked from commit b8a96317544c7b310b4f74360825a87b6402ddc2) + +--- + lib/dns/include/dns/message.h | 38 ---- + lib/dns/include/dns/name.h | 37 ++-- + lib/dns/message.c | 374 ++++++++++++++++++++++------------ + lib/dns/name.c | 1 + + lib/isc/ht.c | 55 ++++- + 5 files changed, 309 insertions(+), 196 deletions(-) + +diff --git a/lib/dns/include/dns/message.h b/lib/dns/include/dns/message.h +index 940c9b1..f15884a 100644 +--- a/lib/dns/include/dns/message.h ++++ b/lib/dns/include/dns/message.h +@@ -856,44 +856,6 @@ dns_message_findtype(const dns_name_t *name, dns_rdatatype_t type, + *\li #ISC_R_NOTFOUND -- the desired type does not exist. + */ + +-isc_result_t +-dns_message_find(const dns_name_t *name, dns_rdataclass_t rdclass, +- dns_rdatatype_t type, dns_rdatatype_t covers, +- dns_rdataset_t **rdataset); +-/*%< +- * Search the name for the specified rdclass and type. If it is found, +- * *rdataset is filled in with a pointer to that rdataset. +- * +- * Requires: +- *\li if '**rdataset' is non-NULL, *rdataset needs to be NULL. +- * +- *\li 'type' be a valid type, and NOT dns_rdatatype_any. +- * +- *\li If 'type' is dns_rdatatype_rrsig, 'covers' must be a valid type. +- * Otherwise it should be 0. +- * +- * Returns: +- *\li #ISC_R_SUCCESS -- all is well. +- *\li #ISC_R_NOTFOUND -- the desired type does not exist. +- */ +- +-void +-dns_message_movename(dns_message_t *msg, dns_name_t *name, +- dns_section_t fromsection, dns_section_t tosection); +-/*%< +- * Move a name from one section to another. +- * +- * Requires: +- * +- *\li 'msg' be valid. +- * +- *\li 'name' must be a name already in 'fromsection'. +- * +- *\li 'fromsection' must be a valid section. +- * +- *\li 'tosection' must be a valid section. +- */ +- + void + dns_message_addname(dns_message_t *msg, dns_name_t *name, + dns_section_t section); +diff --git a/lib/dns/include/dns/name.h b/lib/dns/include/dns/name.h +index a758c4d..199856a 100644 +--- a/lib/dns/include/dns/name.h ++++ b/lib/dns/include/dns/name.h +@@ -68,6 +68,7 @@ + #include + #include + ++#include + #include + #include + #include /* Required for storage size of dns_label_t. */ +@@ -111,6 +112,7 @@ struct dns_name { + isc_buffer_t *buffer; + ISC_LINK(dns_name_t) link; + ISC_LIST(dns_rdataset_t) list; ++ isc_ht_t *ht; + }; + + #define DNS_NAME_MAGIC ISC_MAGIC('D', 'N', 'S', 'n') +@@ -166,30 +168,24 @@ extern const dns_name_t *dns_wildcardname; + * unsigned char offsets[] = { 0, 6 }; + * dns_name_t value = DNS_NAME_INITABSOLUTE(data, offsets); + */ +-#define DNS_NAME_INITNONABSOLUTE(A, B) \ +- { \ +- DNS_NAME_MAGIC, A, (sizeof(A) - 1), sizeof(B), \ +- DNS_NAMEATTR_READONLY, B, NULL, \ +- { (void *)-1, (void *)-1 }, { \ +- NULL, NULL \ +- } \ ++#define DNS_NAME_INITNONABSOLUTE(A, B) \ ++ { \ ++ DNS_NAME_MAGIC, A, (sizeof(A) - 1), sizeof(B), \ ++ DNS_NAMEATTR_READONLY, B, NULL, \ ++ { (void *)-1, (void *)-1 }, { NULL, NULL }, NULL \ + } + +-#define DNS_NAME_INITABSOLUTE(A, B) \ +- { \ +- DNS_NAME_MAGIC, A, sizeof(A), sizeof(B), \ +- DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE, B, \ +- NULL, { (void *)-1, (void *)-1 }, { \ +- NULL, NULL \ +- } \ ++#define DNS_NAME_INITABSOLUTE(A, B) \ ++ { \ ++ DNS_NAME_MAGIC, A, sizeof(A), sizeof(B), \ ++ DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE, B, \ ++ NULL, { (void *)-1, (void *)-1 }, { NULL, NULL }, NULL \ + } + +-#define DNS_NAME_INITEMPTY \ +- { \ +- DNS_NAME_MAGIC, NULL, 0, 0, 0, NULL, NULL, \ +- { (void *)-1, (void *)-1 }, { \ +- NULL, NULL \ +- } \ ++#define DNS_NAME_INITEMPTY \ ++ { \ ++ DNS_NAME_MAGIC, NULL, 0, 0, 0, NULL, NULL, \ ++ { (void *)-1, (void *)-1 }, { NULL, NULL }, NULL \ + } + + /*% +@@ -1330,6 +1326,7 @@ ISC_LANG_ENDDECLS + _n->buffer = NULL; \ + ISC_LINK_INIT(_n, link); \ + ISC_LIST_INIT(_n->list); \ ++ _n->ht = NULL; \ + } while (0) + + #define DNS_NAME_RESET(n) \ +diff --git a/lib/dns/message.c b/lib/dns/message.c +index 761a8e1..8654e92 100644 +--- a/lib/dns/message.c ++++ b/lib/dns/message.c +@@ -22,6 +22,8 @@ + #include + + #include ++#include ++#include + #include + #include + #include +@@ -493,9 +495,11 @@ msgresetsigs(dns_message_t *msg, bool replying) { + } else { + dns_rdataset_disassociate(msg->tsig); + isc_mempool_put(msg->rdspool, msg->tsig); ++ msg->tsig = NULL; + if (msg->querytsig != NULL) { + dns_rdataset_disassociate(msg->querytsig); + isc_mempool_put(msg->rdspool, msg->querytsig); ++ msg->querytsig = NULL; + } + } + dns_message_puttempname(msg, &msg->tsigname); +@@ -790,6 +794,18 @@ dns_message_detach(dns_message_t **messagep) { + } + } + ++static isc_result_t ++name_hash_add(isc_ht_t *ht, dns_name_t *name, dns_name_t **foundp) { ++ isc_result_t result = isc_ht_find(ht, name->ndata, name->length, ++ (void **)foundp); ++ if (result == ISC_R_SUCCESS) { ++ return (ISC_R_EXISTS); ++ } ++ result = isc_ht_add(ht, name->ndata, name->length, (void *)name); ++ INSIST(result == ISC_R_SUCCESS); ++ return (ISC_R_SUCCESS); ++} ++ + static isc_result_t + findname(dns_name_t **foundname, const dns_name_t *target, + dns_namelist_t *section) { +@@ -809,29 +825,26 @@ findname(dns_name_t **foundname, const dns_name_t *target, + return (ISC_R_NOTFOUND); + } + +-isc_result_t +-dns_message_find(const dns_name_t *name, dns_rdataclass_t rdclass, +- dns_rdatatype_t type, dns_rdatatype_t covers, +- dns_rdataset_t **rdataset) { +- dns_rdataset_t *curr; +- +- REQUIRE(name != NULL); +- REQUIRE(rdataset == NULL || *rdataset == NULL); +- +- for (curr = ISC_LIST_TAIL(name->list); curr != NULL; +- curr = ISC_LIST_PREV(curr, link)) +- { +- if (curr->rdclass == rdclass && curr->type == type && +- curr->covers == covers) +- { +- if (rdataset != NULL) { +- *rdataset = curr; +- } +- return (ISC_R_SUCCESS); +- } +- } ++typedef struct __attribute__((__packed__)) rds_key { ++ dns_rdataclass_t rdclass; ++ dns_rdatatype_t type; ++ dns_rdatatype_t covers; ++} rds_key_t; + +- return (ISC_R_NOTFOUND); ++static isc_result_t ++rds_hash_add(isc_ht_t *ht, dns_rdataset_t *rds, dns_rdataset_t **foundp) { ++ rds_key_t key = { .rdclass = rds->rdclass, ++ .type = rds->type, ++ .covers = rds->covers }; ++ isc_result_t result = isc_ht_find(ht, (const unsigned char *)&key, ++ sizeof(key), (void **)foundp); ++ if (result == ISC_R_SUCCESS) { ++ return (ISC_R_EXISTS); ++ } ++ result = isc_ht_add(ht, (const unsigned char *)&key, sizeof(key), ++ (void *)rds); ++ INSIST(result == ISC_R_SUCCESS); ++ return (ISC_R_SUCCESS); + } + + isc_result_t +@@ -958,6 +971,18 @@ getrdata(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + } \ + } while (0) + ++static void ++cleanup_name_hashmaps(dns_namelist_t *section) { ++ dns_name_t *name = NULL; ++ for (name = ISC_LIST_HEAD(*section); name != NULL; ++ name = ISC_LIST_NEXT(name, link)) ++ { ++ if (name->ht != NULL) { ++ isc_ht_destroy(&name->ht); ++ } ++ } ++} ++ + static isc_result_t + getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + unsigned int options) { +@@ -967,13 +992,19 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + dns_name_t *name2 = NULL; + dns_rdataset_t *rdataset = NULL; + dns_rdatalist_t *rdatalist = NULL; +- isc_result_t result; ++ isc_result_t result = ISC_R_SUCCESS; + dns_rdatatype_t rdtype; + dns_rdataclass_t rdclass; + dns_namelist_t *section = &msg->sections[DNS_SECTION_QUESTION]; + bool best_effort = ((options & DNS_MESSAGEPARSE_BESTEFFORT) != 0); + bool seen_problem = false; + bool free_name = false; ++ bool free_ht = false; ++ isc_ht_t *name_map = NULL; ++ ++ if (msg->counts[DNS_SECTION_QUESTION] > 1) { ++ isc_ht_init(&name_map, msg->mctx, 1, ISC_HT_CASE_INSENSITIVE); ++ } + + for (count = 0; count < msg->counts[DNS_SECTION_QUESTION]; count++) { + name = NULL; +@@ -994,13 +1025,19 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + goto cleanup; + } + ++ /* If there is only one QNAME, skip the duplicity checks */ ++ if (name_map == NULL) { ++ result = ISC_R_SUCCESS; ++ goto skip_name_check; ++ } ++ + /* + * Run through the section, looking to see if this name + * is already there. If it is found, put back the allocated + * name since we no longer need it, and set our name pointer + * to point to the name we found. + */ +- result = findname(&name2, name, section); ++ result = name_hash_add(name_map, name, &name2); + + /* + * If it is the first name in the section, accept it. +@@ -1012,19 +1049,25 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + * this should be legal or not. In either case we no longer + * need this name pointer. + */ +- if (result != ISC_R_SUCCESS) { ++ skip_name_check: ++ switch (result) { ++ case ISC_R_SUCCESS: + if (!ISC_LIST_EMPTY(*section)) { + DO_ERROR(DNS_R_FORMERR); + } + ISC_LIST_APPEND(*section, name, link); +- free_name = false; +- } else { ++ break; ++ case ISC_R_EXISTS: + dns_message_puttempname(msg, &name); + name = name2; + name2 = NULL; +- free_name = false; ++ break; ++ default: ++ UNREACHABLE(); + } + ++ free_name = false; ++ + /* + * Get type and class. + */ +@@ -1054,14 +1097,6 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + msg->tkey = 1; + } + +- /* +- * Can't ask the same question twice. +- */ +- result = dns_message_find(name, rdclass, rdtype, 0, NULL); +- if (result == ISC_R_SUCCESS) { +- DO_ERROR(DNS_R_FORMERR); +- } +- + /* + * Allocate a new rdatalist. + */ +@@ -1071,6 +1106,7 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + goto cleanup; + } + rdataset = isc_mempool_get(msg->rdspool); ++ dns_rdataset_init(rdataset); + + /* + * Convert rdatalist to rdataset, and attach the latter to +@@ -1078,8 +1114,6 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + */ + rdatalist->type = rdtype; + rdatalist->rdclass = rdclass; +- +- dns_rdataset_init(rdataset); + result = dns_rdatalist_tordataset(rdatalist, rdataset); + if (result != ISC_R_SUCCESS) { + goto cleanup; +@@ -1087,24 +1121,66 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + + rdataset->attributes |= DNS_RDATASETATTR_QUESTION; + ++ /* ++ * Skip the duplicity check for first rdataset ++ */ ++ if (ISC_LIST_EMPTY(name->list)) { ++ result = ISC_R_SUCCESS; ++ goto skip_rds_check; ++ } ++ ++ /* ++ * Can't ask the same question twice. ++ */ ++ if (name->ht == NULL) { ++ isc_ht_init(&name->ht, msg->mctx, 1, ++ ISC_HT_CASE_SENSITIVE); ++ free_ht = true; ++ ++ INSIST(ISC_LIST_HEAD(name->list) == ++ ISC_LIST_TAIL(name->list)); ++ ++ dns_rdataset_t *old_rdataset = ++ ISC_LIST_HEAD(name->list); ++ ++ result = rds_hash_add(name->ht, old_rdataset, NULL); ++ ++ INSIST(result == ISC_R_SUCCESS); ++ } ++ result = rds_hash_add(name->ht, rdataset, NULL); ++ if (result == ISC_R_EXISTS) { ++ DO_ERROR(DNS_R_FORMERR); ++ } ++ ++ skip_rds_check: + ISC_LIST_APPEND(name->list, rdataset, link); ++ + rdataset = NULL; + } + + if (seen_problem) { +- return (DNS_R_RECOVERABLE); ++ result = DNS_R_RECOVERABLE; + } +- return (ISC_R_SUCCESS); + + cleanup: + if (rdataset != NULL) { +- INSIST(!dns_rdataset_isassociated(rdataset)); ++ if (dns_rdataset_isassociated(rdataset)) { ++ dns_rdataset_disassociate(rdataset); ++ } + isc_mempool_put(msg->rdspool, rdataset); + } + if (free_name) { + dns_message_puttempname(msg, &name); + } + ++ if (free_ht) { ++ cleanup_name_hashmaps(section); ++ } ++ ++ if (name_map != NULL) { ++ isc_ht_destroy(&name_map); ++ } ++ + return (result); + } + +@@ -1184,17 +1260,24 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + dns_name_t *name = NULL; + dns_name_t *name2 = NULL; + dns_rdataset_t *rdataset = NULL; ++ dns_rdataset_t *found_rdataset = NULL; + dns_rdatalist_t *rdatalist = NULL; +- isc_result_t result; ++ isc_result_t result = ISC_R_SUCCESS; + dns_rdatatype_t rdtype, covers; + dns_rdataclass_t rdclass; + dns_rdata_t *rdata = NULL; + dns_ttl_t ttl; + dns_namelist_t *section = &msg->sections[sectionid]; +- bool free_name = false, free_rdataset = false, seen_problem = false; ++ bool free_name = false, seen_problem = false; ++ bool free_ht = false; + bool preserve_order = ((options & DNS_MESSAGEPARSE_PRESERVEORDER) != 0); + bool best_effort = ((options & DNS_MESSAGEPARSE_BESTEFFORT) != 0); + bool isedns, issigzero, istsig; ++ isc_ht_t *name_map = NULL; ++ ++ if (msg->counts[sectionid] > 1) { ++ isc_ht_init(&name_map, msg->mctx, 1, ISC_HT_CASE_INSENSITIVE); ++ } + + for (count = 0; count < msg->counts[sectionid]; count++) { + int recstart = source->current; +@@ -1202,10 +1285,10 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + + skip_name_search = false; + skip_type_search = false; +- free_rdataset = false; + isedns = false; + issigzero = false; + istsig = false; ++ found_rdataset = NULL; + + name = NULL; + result = dns_message_gettempname(msg, &name); +@@ -1245,8 +1328,8 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + if (msg->rdclass_set == 0 && + rdtype != dns_rdatatype_opt && /* class is UDP SIZE */ + rdtype != dns_rdatatype_tsig && /* class is ANY */ +- rdtype != dns_rdatatype_tkey) +- { /* class is undefined */ ++ rdtype != dns_rdatatype_tkey) /* class is undefined */ ++ { + msg->rdclass = rdclass; + msg->rdclass_set = 1; + } +@@ -1353,10 +1436,6 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + * Then put the meta-class back into the finished rdata. + */ + rdata = newrdata(msg); +- if (rdata == NULL) { +- result = ISC_R_NOMEMORY; +- goto cleanup; +- } + if (msg->opcode == dns_opcode_update && + update(sectionid, rdclass)) + { +@@ -1445,34 +1524,62 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + free_name = false; + } + } else { ++ if (name_map == NULL) { ++ result = ISC_R_SUCCESS; ++ goto skip_name_check; ++ } ++ + /* + * Run through the section, looking to see if this name + * is already there. If it is found, put back the + * allocated name since we no longer need it, and set + * our name pointer to point to the name we found. + */ +- result = findname(&name2, name, section); ++ result = name_hash_add(name_map, name, &name2); + + /* + * If it is a new name, append to the section. + */ +- if (result == ISC_R_SUCCESS) { ++ skip_name_check: ++ switch (result) { ++ case ISC_R_SUCCESS: ++ ISC_LIST_APPEND(*section, name, link); ++ break; ++ case ISC_R_EXISTS: + dns_message_puttempname(msg, &name); + name = name2; +- } else { +- ISC_LIST_APPEND(*section, name, link); ++ name2 = NULL; ++ break; ++ default: ++ UNREACHABLE(); + } + free_name = false; + } + ++ rdatalist = newrdatalist(msg); ++ rdatalist->type = rdtype; ++ rdatalist->covers = covers; ++ rdatalist->rdclass = rdclass; ++ rdatalist->ttl = ttl; ++ ++ dns_message_gettemprdataset(msg, &rdataset); ++ RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, rdataset) == ++ ISC_R_SUCCESS); ++ dns_rdataset_setownercase(rdataset, name); ++ rdatalist = NULL; ++ + /* + * Search name for the particular type and class. + * Skip this stage if in update mode or this is a meta-type. + */ +- if (preserve_order || msg->opcode == dns_opcode_update || +- skip_type_search) ++ if (isedns || istsig || issigzero) { ++ /* Skip adding the rdataset to the tables */ ++ } else if (preserve_order || msg->opcode == dns_opcode_update || ++ skip_type_search) + { +- result = ISC_R_NOTFOUND; ++ result = ISC_R_SUCCESS; ++ ++ ISC_LIST_APPEND(name->list, rdataset, link); + } else { + /* + * If this is a type that can only occur in +@@ -1482,59 +1589,71 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + DO_ERROR(DNS_R_FORMERR); + } + +- rdataset = NULL; +- result = dns_message_find(name, rdclass, rdtype, covers, +- &rdataset); +- } +- +- /* +- * If we found an rdataset that matches, we need to +- * append this rdata to that set. If we did not, we need +- * to create a new rdatalist, store the important bits there, +- * convert it to an rdataset, and link the latter to the name. +- * Yuck. When appending, make certain that the type isn't +- * a singleton type, such as SOA or CNAME. +- * +- * Note that this check will be bypassed when preserving order, +- * the opcode is an update, or the type search is skipped. +- */ +- if (result == ISC_R_SUCCESS) { +- if (dns_rdatatype_issingleton(rdtype)) { +- dns_rdata_t *first; +- dns_rdatalist_fromrdataset(rdataset, +- &rdatalist); +- first = ISC_LIST_HEAD(rdatalist->rdata); +- INSIST(first != NULL); +- if (dns_rdata_compare(rdata, first) != 0) { +- DO_ERROR(DNS_R_FORMERR); +- } ++ if (ISC_LIST_EMPTY(name->list)) { ++ result = ISC_R_SUCCESS; ++ goto skip_rds_check; + } +- } + +- if (result == ISC_R_NOTFOUND) { +- rdataset = isc_mempool_get(msg->rdspool); +- free_rdataset = true; ++ if (name->ht == NULL) { ++ isc_ht_init(&name->ht, msg->mctx, 1, ++ ISC_HT_CASE_SENSITIVE); ++ free_ht = true; + +- rdatalist = newrdatalist(msg); +- if (rdatalist == NULL) { +- result = ISC_R_NOMEMORY; +- goto cleanup; ++ INSIST(ISC_LIST_HEAD(name->list) == ++ ISC_LIST_TAIL(name->list)); ++ ++ dns_rdataset_t *old_rdataset = ++ ISC_LIST_HEAD(name->list); ++ ++ result = rds_hash_add(name->ht, old_rdataset, ++ NULL); ++ ++ INSIST(result == ISC_R_SUCCESS); + } ++ found_rdataset = NULL; ++ result = rds_hash_add(name->ht, rdataset, ++ &found_rdataset); + +- rdatalist->type = rdtype; +- rdatalist->covers = covers; +- rdatalist->rdclass = rdclass; +- rdatalist->ttl = ttl; ++ /* ++ * If we found an rdataset that matches, we need to ++ * append this rdata to that set. If we did not, we ++ * need to create a new rdatalist, store the important ++ * bits there, convert it to an rdataset, and link the ++ * latter to the name. Yuck. When appending, make ++ * certain that the type isn't a singleton type, such as ++ * SOA or CNAME. ++ * ++ * Note that this check will be bypassed when preserving ++ * order, the opcode is an update, or the type search is ++ * skipped. ++ */ ++ skip_rds_check: ++ switch (result) { ++ case ISC_R_EXISTS: ++ /* Free the rdataset we used as the key */ ++ dns_rdataset_disassociate(rdataset); ++ isc_mempool_put(msg->rdspool, rdataset); ++ result = ISC_R_SUCCESS; ++ rdataset = found_rdataset; + +- dns_rdataset_init(rdataset); +- RUNTIME_CHECK( +- dns_rdatalist_tordataset(rdatalist, rdataset) == +- ISC_R_SUCCESS); +- dns_rdataset_setownercase(rdataset, name); ++ if (!dns_rdatatype_issingleton(rdtype)) { ++ break; ++ } + +- if (!isedns && !istsig && !issigzero) { ++ dns_rdatalist_fromrdataset(rdataset, ++ &rdatalist); ++ dns_rdata_t *first = ++ ISC_LIST_HEAD(rdatalist->rdata); ++ INSIST(first != NULL); ++ if (dns_rdata_compare(rdata, first) != 0) { ++ DO_ERROR(DNS_R_FORMERR); ++ } ++ break; ++ case ISC_R_SUCCESS: + ISC_LIST_APPEND(name->list, rdataset, link); +- free_rdataset = false; ++ break; ++ default: ++ UNREACHABLE(); + } + } + +@@ -1569,8 +1688,6 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + dns_rcode_t ercode; + + msg->opt = rdataset; +- rdataset = NULL; +- free_rdataset = false; + ercode = (dns_rcode_t)((msg->opt->ttl & + DNS_MESSAGE_EDNSRCODE_MASK) >> + 20); +@@ -1581,8 +1698,6 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + msg->sig0 = rdataset; + msg->sig0name = name; + msg->sigstart = recstart; +- rdataset = NULL; +- free_rdataset = false; + free_name = false; + } else if (istsig) { + msg->tsig = rdataset; +@@ -1592,22 +1707,17 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + * Windows doesn't like TSIG names to be compressed. + */ + msg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS; +- rdataset = NULL; +- free_rdataset = false; + free_name = false; + } ++ rdataset = NULL; + + if (seen_problem) { + if (free_name) { + dns_message_puttempname(msg, &name); + } +- if (free_rdataset) { +- isc_mempool_put(msg->rdspool, rdataset); +- } +- free_name = free_rdataset = false; ++ free_name = false; + } + INSIST(!free_name); +- INSIST(!free_rdataset); + } + + /* +@@ -1625,16 +1735,24 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + } + + if (seen_problem) { +- return (DNS_R_RECOVERABLE); ++ result = DNS_R_RECOVERABLE; + } +- return (ISC_R_SUCCESS); + + cleanup: ++ if (rdataset != NULL && rdataset != found_rdataset) { ++ dns_rdataset_disassociate(rdataset); ++ isc_mempool_put(msg->rdspool, rdataset); ++ } + if (free_name) { + dns_message_puttempname(msg, &name); + } +- if (free_rdataset) { +- isc_mempool_put(msg->rdspool, rdataset); ++ ++ if (free_ht) { ++ cleanup_name_hashmaps(section); ++ } ++ ++ if (name_map != NULL) { ++ isc_ht_destroy(&name_map); + } + + return (result); +@@ -2452,7 +2570,7 @@ dns_message_findname(dns_message_t *msg, dns_section_t section, + const dns_name_t *target, dns_rdatatype_t type, + dns_rdatatype_t covers, dns_name_t **name, + dns_rdataset_t **rdataset) { +- dns_name_t *foundname; ++ dns_name_t *foundname = NULL; + isc_result_t result; + + /* +@@ -2499,22 +2617,6 @@ dns_message_findname(dns_message_t *msg, dns_section_t section, + return (result); + } + +-void +-dns_message_movename(dns_message_t *msg, dns_name_t *name, +- dns_section_t fromsection, dns_section_t tosection) { +- REQUIRE(msg != NULL); +- REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER); +- REQUIRE(name != NULL); +- REQUIRE(VALID_NAMED_SECTION(fromsection)); +- REQUIRE(VALID_NAMED_SECTION(tosection)); +- +- /* +- * Unlink the name from the old section +- */ +- ISC_LIST_UNLINK(msg->sections[fromsection], name, link); +- ISC_LIST_APPEND(msg->sections[tosection], name, link); +-} +- + void + dns_message_addname(dns_message_t *msg, dns_name_t *name, + dns_section_t section) { +@@ -2591,6 +2693,10 @@ dns_message_puttempname(dns_message_t *msg, dns_name_t **itemp) { + REQUIRE(!ISC_LINK_LINKED(item, link)); + REQUIRE(ISC_LIST_HEAD(item->list) == NULL); + ++ if (item->ht != NULL) { ++ isc_ht_destroy(&item->ht); ++ } ++ + /* + * we need to check this in case dns_name_dup() was used. + */ +diff --git a/lib/dns/name.c b/lib/dns/name.c +index 8a258a2..90044ba 100644 +--- a/lib/dns/name.c ++++ b/lib/dns/name.c +@@ -188,6 +188,7 @@ dns_name_invalidate(dns_name_t *name) { + name->offsets = NULL; + name->buffer = NULL; + ISC_LINK_INIT(name, link); ++ INSIST(name->ht == NULL); + } + + bool +diff --git a/lib/isc/ht.c b/lib/isc/ht.c +index eaf2b3c..e11050f 100644 +--- a/lib/isc/ht.c ++++ b/lib/isc/ht.c +@@ -93,11 +93,54 @@ maybe_rehash(isc_ht_t *ht, size_t newcount); + static isc_result_t + isc__ht_iter_next(isc_ht_iter_t *it); + ++static uint8_t maptolower[] = { ++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, ++ 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, ++ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, ++ 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, ++ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, ++ 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, ++ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, ++ 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, ++ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, ++ 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, ++ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, ++ 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, ++ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, ++ 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, ++ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, ++ 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, ++ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, ++ 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, ++ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, ++ 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, ++ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, ++ 0xfc, 0xfd, 0xfe, 0xff ++}; ++ ++static int ++memcasecmp(const void *vs1, const void *vs2, size_t len) { ++ uint8_t const *s1 = vs1; ++ uint8_t const *s2 = vs2; ++ for (size_t i = 0; i < len; i++) { ++ uint8_t u1 = s1[i]; ++ uint8_t u2 = s2[i]; ++ int U1 = maptolower[u1]; ++ int U2 = maptolower[u2]; ++ int diff = U1 - U2; ++ if (diff) { ++ return diff; ++ } ++ } ++ return 0; ++} ++ + 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) ++ : (memcasecmp(node->key, key, keysize) == 0))); + } + + static uint32_t +@@ -341,7 +384,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); + } + } +@@ -390,7 +435,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 { +-- +2.33.0 + diff --git a/backport-CVE-2023-50387-CVE-2023-50868.patch b/backport-CVE-2023-50387-CVE-2023-50868.patch new file mode 100644 index 0000000..08472b4 --- /dev/null +++ b/backport-CVE-2023-50387-CVE-2023-50868.patch @@ -0,0 +1,600 @@ +From c12608ca934c0433d280e65fe6c631013e200cfe Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= +Date: Thu, 11 Jan 2024 12:03:24 +0100 +Subject: [PATCH] Split fast and slow task queues + +Change the taskmgr (and thus netmgr) in a way that it supports fast and +slow task queues. The fast queue is used for incoming DNS traffic and +it will pass the processing to the slow queue for sending outgoing DNS +messages and processing resolver messages. + +In the future, more tasks might get moved to the slow queues, so the +cached and authoritative DNS traffic can be handled without being slowed +down by operations that take longer time to process. + +Conflict:NA +Reference:https://downloads.isc.org/isc/bind/9.18.24/patches/0004-CVE-2023-50387-CVE-2023-50868.patch + +(cherry picked from commit 1b3b0cef224e7a9e8279c5cfe2f7e188e3777cc7) +--- + lib/dns/dst_api.c | 27 +++++++++---- + lib/dns/include/dns/validator.h | 1 + + lib/dns/include/dst/dst.h | 4 ++ + lib/dns/resolver.c | 4 +- + lib/dns/validator.c | 67 +++++++++++++++------------------ + lib/isc/include/isc/netmgr.h | 3 ++ + lib/isc/netmgr/http.c | 18 ++++----- + lib/isc/netmgr/netmgr-int.h | 1 + + lib/isc/netmgr/netmgr.c | 38 ++++++++++++------- + lib/isc/netmgr/tcp.c | 6 +-- + lib/isc/netmgr/tcpdns.c | 4 +- + lib/isc/netmgr/tlsdns.c | 4 +- + lib/isc/netmgr/tlsstream.c | 12 +++--- + lib/isc/netmgr/udp.c | 6 +-- + 14 files changed, 109 insertions(+), 86 deletions(-) + +diff --git a/lib/dns/dst_api.c b/lib/dns/dst_api.c +index 4ffda8b..0658c69 100644 +--- a/lib/dns/dst_api.c ++++ b/lib/dns/dst_api.c +@@ -164,7 +164,8 @@ computeid(dst_key_t *key); + static isc_result_t + frombuffer(const dns_name_t *name, unsigned int alg, unsigned int flags, + unsigned int protocol, dns_rdataclass_t rdclass, +- isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp); ++ isc_buffer_t *source, isc_mem_t *mctx, bool no_rdata, ++ dst_key_t **keyp); + + static isc_result_t + algorithm_status(unsigned int alg); +@@ -753,6 +754,13 @@ dst_key_todns(const dst_key_t *key, isc_buffer_t *target) { + isc_result_t + dst_key_fromdns(const dns_name_t *name, dns_rdataclass_t rdclass, + isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp) { ++ return (dst_key_fromdns_ex(name, rdclass, source, mctx, false, keyp)); ++} ++ ++isc_result_t ++dst_key_fromdns_ex(const dns_name_t *name, dns_rdataclass_t rdclass, ++ isc_buffer_t *source, isc_mem_t *mctx, bool no_rdata, ++ dst_key_t **keyp) { + uint8_t alg, proto; + uint32_t flags, extflags; + dst_key_t *key = NULL; +@@ -783,7 +791,7 @@ dst_key_fromdns(const dns_name_t *name, dns_rdataclass_t rdclass, + } + + result = frombuffer(name, alg, flags, proto, rdclass, source, mctx, +- &key); ++ no_rdata, &key); + if (result != ISC_R_SUCCESS) { + return (result); + } +@@ -804,7 +812,7 @@ dst_key_frombuffer(const dns_name_t *name, unsigned int alg, unsigned int flags, + REQUIRE(dst_initialized); + + result = frombuffer(name, alg, flags, protocol, rdclass, source, mctx, +- &key); ++ false, &key); + if (result != ISC_R_SUCCESS) { + return (result); + } +@@ -2351,7 +2359,8 @@ computeid(dst_key_t *key) { + static isc_result_t + frombuffer(const dns_name_t *name, unsigned int alg, unsigned int flags, + unsigned int protocol, dns_rdataclass_t rdclass, +- isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp) { ++ isc_buffer_t *source, isc_mem_t *mctx, bool no_rdata, ++ dst_key_t **keyp) { + dst_key_t *key; + isc_result_t ret; + +@@ -2376,10 +2385,12 @@ frombuffer(const dns_name_t *name, unsigned int alg, unsigned int flags, + return (DST_R_UNSUPPORTEDALG); + } + +- ret = key->func->fromdns(key, source); +- if (ret != ISC_R_SUCCESS) { +- dst_key_free(&key); +- return (ret); ++ if (!no_rdata) { ++ ret = key->func->fromdns(key, source); ++ if (ret != ISC_R_SUCCESS) { ++ dst_key_free(&key); ++ return (ret); ++ } + } + } + +diff --git a/lib/dns/include/dns/validator.h b/lib/dns/include/dns/validator.h +index 383dcb4..352a60a 100644 +--- a/lib/dns/include/dns/validator.h ++++ b/lib/dns/include/dns/validator.h +@@ -148,6 +148,7 @@ struct dns_validator { + unsigned int depth; + unsigned int authcount; + unsigned int authfail; ++ bool failed; + isc_stdtime_t start; + }; + +diff --git a/lib/dns/include/dst/dst.h b/lib/dns/include/dst/dst.h +index ca292b0..f845e9b 100644 +--- a/lib/dns/include/dst/dst.h ++++ b/lib/dns/include/dst/dst.h +@@ -482,6 +482,10 @@ dst_key_tofile(const dst_key_t *key, int type, const char *directory); + */ + + isc_result_t ++dst_key_fromdns_ex(const dns_name_t *name, dns_rdataclass_t rdclass, ++ isc_buffer_t *source, isc_mem_t *mctx, bool no_rdata, ++ dst_key_t **keyp); ++isc_result_t + dst_key_fromdns(const dns_name_t *name, dns_rdataclass_t rdclass, + isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp); + /*%< +diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c +index 4b3d1c0..60cac29 100644 +--- a/lib/dns/resolver.c ++++ b/lib/dns/resolver.c +@@ -10408,8 +10408,8 @@ dns_resolver_create(dns_view_t *view, isc_taskmgr_t *taskmgr, + * Since we have a pool of tasks we bind them to task + * queues to spread the load evenly + */ +- result = isc_task_create_bound(taskmgr, 0, +- &res->buckets[i].task, i); ++ result = isc_task_create_bound( ++ taskmgr, 0, &res->buckets[i].task, ISC_NM_TASK_SLOW(i)); + if (result != ISC_R_SUCCESS) { + ntasks = i; + isc_mutex_destroy(&res->buckets[i].lock); +diff --git a/lib/dns/validator.c b/lib/dns/validator.c +index 56a0ced..47c4813 100644 +--- a/lib/dns/validator.c ++++ b/lib/dns/validator.c +@@ -1104,8 +1104,8 @@ create_validator(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type, + * 'rdataset'. If found, build a dst_key_t for it and point val->key at + * it. + * +- * If val->key is already non-NULL, locate it in the rdataset and then +- * search past it for the *next* key that could have signed 'siginfo', then ++ * If val->key is already non-NULL, start searching from the next position in ++ * 'rdataset' to find the *next* key that could have signed 'siginfo', then + * set val->key to that. + * + * Returns ISC_R_SUCCESS if a possible matching key has been found, +@@ -1118,59 +1118,59 @@ select_signing_key(dns_validator_t *val, dns_rdataset_t *rdataset) { + isc_buffer_t b; + dns_rdata_t rdata = DNS_RDATA_INIT; + dst_key_t *oldkey = val->key; +- bool foundold; ++ bool no_rdata = false; + + if (oldkey == NULL) { +- foundold = true; ++ result = dns_rdataset_first(rdataset); + } else { +- foundold = false; ++ dst_key_free(&oldkey); + val->key = NULL; ++ result = dns_rdataset_next(rdataset); + } +- +- result = dns_rdataset_first(rdataset); + if (result != ISC_R_SUCCESS) { +- goto failure; ++ goto done; + } ++ + do { + dns_rdataset_current(rdataset, &rdata); + + isc_buffer_init(&b, rdata.data, rdata.length); + isc_buffer_add(&b, rdata.length); + INSIST(val->key == NULL); +- result = dst_key_fromdns(&siginfo->signer, rdata.rdclass, &b, +- val->view->mctx, &val->key); ++ result = dst_key_fromdns_ex(&siginfo->signer, rdata.rdclass, &b, ++ val->view->mctx, no_rdata, ++ &val->key); + if (result == ISC_R_SUCCESS) { + if (siginfo->algorithm == + (dns_secalg_t)dst_key_alg(val->key) && + siginfo->keyid == + (dns_keytag_t)dst_key_id(val->key) && ++ (dst_key_flags(val->key) & DNS_KEYFLAG_REVOKE) == ++ 0 && + dst_key_iszonekey(val->key)) + { +- if (foundold) { +- /* +- * This is the key we're looking for. +- */ +- return (ISC_R_SUCCESS); +- } else if (dst_key_compare(oldkey, val->key)) { +- foundold = true; +- dst_key_free(&oldkey); ++ if (no_rdata) { ++ /* Retry with full key */ ++ dns_rdata_reset(&rdata); ++ dst_key_free(&val->key); ++ no_rdata = false; ++ continue; + } ++ /* This is the key we're looking for. */ ++ goto done; + } + dst_key_free(&val->key); + } + dns_rdata_reset(&rdata); + result = dns_rdataset_next(rdataset); ++ no_rdata = true; + } while (result == ISC_R_SUCCESS); + ++done: + if (result == ISC_R_NOMORE) { + result = ISC_R_NOTFOUND; + } + +-failure: +- if (oldkey != NULL) { +- dst_key_free(&oldkey); +- } +- + return (result); + } + +@@ -1589,20 +1589,9 @@ validate_answer(dns_validator_t *val, bool resume) { + continue; + } + +- do { +- isc_result_t tresult; +- vresult = verify(val, val->key, &rdata, +- val->siginfo->keyid); +- if (vresult == ISC_R_SUCCESS) { +- break; +- } +- +- tresult = select_signing_key(val, val->keyset); +- if (tresult != ISC_R_SUCCESS) { +- break; +- } +- } while (1); ++ vresult = verify(val, val->key, &rdata, val->siginfo->keyid); + if (vresult != ISC_R_SUCCESS) { ++ val->failed = true; + validator_log(val, ISC_LOG_DEBUG(3), + "failed to verify rdataset"); + } else { +@@ -1639,9 +1628,13 @@ validate_answer(dns_validator_t *val, bool resume) { + } else { + validator_log(val, ISC_LOG_DEBUG(3), + "verify failure: %s", +- isc_result_totext(result)); ++ isc_result_totext(vresult)); + resume = false; + } ++ if (val->failed) { ++ result = ISC_R_NOMORE; ++ break; ++ } + } + if (result != ISC_R_NOMORE) { + validator_log(val, ISC_LOG_DEBUG(3), +diff --git a/lib/isc/include/isc/netmgr.h b/lib/isc/include/isc/netmgr.h +index eff33f6..d42cfe9 100644 +--- a/lib/isc/include/isc/netmgr.h ++++ b/lib/isc/include/isc/netmgr.h +@@ -750,6 +750,9 @@ isc_nm_verify_tls_peer_result_string(const isc_nmhandle_t *handle); + * \li 'handle' is a valid netmgr handle object. + */ + ++#define ISC_NM_TASK_SLOW_OFFSET -2 ++#define ISC_NM_TASK_SLOW(i) (ISC_NM_TASK_SLOW_OFFSET - 1 - i) ++ + void + isc_nm_task_enqueue(isc_nm_t *mgr, isc_task_t *task, int threadid); + /*%< +diff --git a/lib/isc/netmgr/http.c b/lib/isc/netmgr/http.c +index d7a33d5..2220edf 100644 +--- a/lib/isc/netmgr/http.c ++++ b/lib/isc/netmgr/http.c +@@ -2969,7 +2969,7 @@ isc__nm_http_set_max_streams(isc_nmsocket_t *listener, + void + isc_nm_http_set_endpoints(isc_nmsocket_t *listener, + isc_nm_http_endpoints_t *eps) { +- size_t nworkers; ++ size_t nlisteners; + + REQUIRE(VALID_NMSOCK(listener)); + REQUIRE(listener->type == isc_nm_httplistener); +@@ -2977,8 +2977,8 @@ isc_nm_http_set_endpoints(isc_nmsocket_t *listener, + + atomic_store(&eps->in_use, true); + +- nworkers = (size_t)listener->mgr->nworkers; +- for (size_t i = 0; i < nworkers; i++) { ++ nlisteners = (size_t)listener->mgr->nlisteners; ++ for (size_t i = 0; i < nlisteners; i++) { + isc__netievent__http_eps_t *ievent = + isc__nm_get_netievent_httpendpoints(listener->mgr, + listener, eps); +@@ -3003,20 +3003,20 @@ isc__nm_async_httpendpoints(isc__networker_t *worker, isc__netievent_t *ev0) { + static void + http_init_listener_endpoints(isc_nmsocket_t *listener, + isc_nm_http_endpoints_t *epset) { +- size_t nworkers; ++ size_t nlisteners; + + REQUIRE(VALID_NMSOCK(listener)); + REQUIRE(VALID_NM(listener->mgr)); + REQUIRE(VALID_HTTP_ENDPOINTS(epset)); + +- nworkers = (size_t)listener->mgr->nworkers; +- INSIST(nworkers > 0); ++ nlisteners = (size_t)listener->mgr->nlisteners; ++ INSIST(nlisteners > 0); + + listener->h2.listener_endpoints = + isc_mem_get(listener->mgr->mctx, +- sizeof(isc_nm_http_endpoints_t *) * nworkers); +- listener->h2.n_listener_endpoints = nworkers; +- for (size_t i = 0; i < nworkers; i++) { ++ sizeof(isc_nm_http_endpoints_t *) * nlisteners); ++ listener->h2.n_listener_endpoints = nlisteners; ++ for (size_t i = 0; i < nlisteners; i++) { + listener->h2.listener_endpoints[i] = NULL; + isc_nm_http_endpoints_attach( + epset, &listener->h2.listener_endpoints[i]); +diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h +index 364a933..6aca9ab 100644 +--- a/lib/isc/netmgr/netmgr-int.h ++++ b/lib/isc/netmgr/netmgr-int.h +@@ -776,6 +776,7 @@ struct isc_nm { + isc_refcount_t references; + isc_mem_t *mctx; + int nworkers; ++ int nlisteners; + isc_mutex_t lock; + isc_condition_t wkstatecond; + isc_condition_t wkpausecond; +diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c +index b19d468..2310b4b 100644 +--- a/lib/isc/netmgr/netmgr.c ++++ b/lib/isc/netmgr/netmgr.c +@@ -189,12 +189,12 @@ isc__nm_force_tid(int tid) { + } + + static void +-isc__nm_threadpool_initialize(uint32_t workers) { ++isc__nm_threadpool_initialize(uint32_t nworkers) { + char buf[11]; + int r = uv_os_getenv("UV_THREADPOOL_SIZE", buf, + &(size_t){ sizeof(buf) }); + if (r == UV_ENOENT) { +- snprintf(buf, sizeof(buf), "%" PRIu32, workers); ++ snprintf(buf, sizeof(buf), "%" PRIu32, nworkers); + uv_os_setenv("UV_THREADPOOL_SIZE", buf); + } + } +@@ -212,11 +212,11 @@ isc__nm_threadpool_initialize(uint32_t workers) { + #endif + + void +-isc__netmgr_create(isc_mem_t *mctx, uint32_t workers, isc_nm_t **netmgrp) { ++isc__netmgr_create(isc_mem_t *mctx, uint32_t nworkers, isc_nm_t **netmgrp) { + isc_nm_t *mgr = NULL; + char name[32]; + +- REQUIRE(workers > 0); ++ REQUIRE(nworkers > 0); + + #ifdef MAXIMAL_UV_VERSION + if (uv_version() > MAXIMAL_UV_VERSION) { +@@ -234,10 +234,13 @@ isc__netmgr_create(isc_mem_t *mctx, uint32_t workers, isc_nm_t **netmgrp) { + uv_version_string(), UV_VERSION_STRING); + } + +- isc__nm_threadpool_initialize(workers); ++ isc__nm_threadpool_initialize(nworkers); + + mgr = isc_mem_get(mctx, sizeof(*mgr)); +- *mgr = (isc_nm_t){ .nworkers = workers }; ++ *mgr = (isc_nm_t){ ++ .nworkers = nworkers * 2, ++ .nlisteners = nworkers, ++ }; + + isc_mem_attach(mctx, &mgr->mctx); + isc_mutex_init(&mgr->lock); +@@ -272,11 +275,12 @@ isc__netmgr_create(isc_mem_t *mctx, uint32_t workers, isc_nm_t **netmgrp) { + atomic_init(&mgr->keepalive, 30000); + atomic_init(&mgr->advertised, 30000); + +- isc_barrier_init(&mgr->pausing, workers); +- isc_barrier_init(&mgr->resuming, workers); ++ isc_barrier_init(&mgr->pausing, mgr->nworkers); ++ isc_barrier_init(&mgr->resuming, mgr->nworkers); + +- mgr->workers = isc_mem_get(mctx, workers * sizeof(isc__networker_t)); +- for (size_t i = 0; i < workers; i++) { ++ mgr->workers = isc_mem_get(mctx, ++ mgr->nworkers * sizeof(isc__networker_t)); ++ for (int i = 0; i < mgr->nworkers; i++) { + isc__networker_t *worker = &mgr->workers[i]; + int r; + +@@ -310,7 +314,7 @@ isc__netmgr_create(isc_mem_t *mctx, uint32_t workers, isc_nm_t **netmgrp) { + mgr->workers_running++; + isc_thread_create(nm_thread, &mgr->workers[i], &worker->thread); + +- snprintf(name, sizeof(name), "isc-net-%04zu", i); ++ snprintf(name, sizeof(name), "isc-net-%04d", i); + isc_thread_setname(worker->thread, name); + } + +@@ -817,9 +821,15 @@ isc_nm_task_enqueue(isc_nm_t *nm, isc_task_t *task, int threadid) { + isc__networker_t *worker = NULL; + + if (threadid == -1) { +- tid = (int)isc_random_uniform(nm->nworkers); ++ tid = (int)isc_random_uniform(nm->nlisteners); ++ } else if (threadid == ISC_NM_TASK_SLOW_OFFSET) { ++ tid = nm->nlisteners + ++ (int)isc_random_uniform(nm->nworkers - nm->nlisteners); ++ } else if (threadid < ISC_NM_TASK_SLOW_OFFSET) { ++ tid = nm->nlisteners + (ISC_NM_TASK_SLOW(threadid) % ++ (nm->nworkers - nm->nlisteners)); + } else { +- tid = threadid % nm->nworkers; ++ tid = threadid % nm->nlisteners; + } + + worker = &nm->workers[tid]; +@@ -3778,7 +3788,7 @@ isc__nm_async_settlsctx(isc__networker_t *worker, isc__netievent_t *ev0) { + static void + set_tlsctx_workers(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx) { + /* Update the TLS context reference for every worker thread. */ +- for (size_t i = 0; i < (size_t)listener->mgr->nworkers; i++) { ++ for (size_t i = 0; i < (size_t)listener->mgr->nlisteners; i++) { + isc__netievent__tlsctx_t *ievent = + isc__nm_get_netievent_settlsctx(listener->mgr, listener, + tlsctx); +diff --git a/lib/isc/netmgr/tcp.c b/lib/isc/netmgr/tcp.c +index 2a644fe..16b53cc 100644 +--- a/lib/isc/netmgr/tcp.c ++++ b/lib/isc/netmgr/tcp.c +@@ -341,7 +341,7 @@ isc_nm_tcpconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer, + isc__nm_connectcb(sock, req, result, false); + } else { + isc__nmsocket_clearcb(sock); +- sock->tid = isc_random_uniform(mgr->nworkers); ++ sock->tid = isc_random_uniform(mgr->nlisteners); + isc__nm_connectcb(sock, req, result, true); + } + atomic_store(&sock->closed, true); +@@ -362,7 +362,7 @@ isc_nm_tcpconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer, + isc__nm_put_netievent_tcpconnect(mgr, ievent); + } else { + atomic_init(&sock->active, false); +- sock->tid = isc_random_uniform(mgr->nworkers); ++ sock->tid = isc_random_uniform(mgr->nlisteners); + isc__nm_enqueue_ievent(&mgr->workers[sock->tid], + (isc__netievent_t *)ievent); + } +@@ -457,7 +457,7 @@ isc_nm_listentcp(isc_nm_t *mgr, isc_sockaddr_t *iface, + isc__nmsocket_init(sock, mgr, isc_nm_tcplistener, iface); + + atomic_init(&sock->rchildren, 0); +- sock->nchildren = mgr->nworkers; ++ sock->nchildren = mgr->nlisteners; + children_size = sock->nchildren * sizeof(sock->children[0]); + sock->children = isc_mem_get(mgr->mctx, children_size); + memset(sock->children, 0, children_size); +diff --git a/lib/isc/netmgr/tcpdns.c b/lib/isc/netmgr/tcpdns.c +index eda6aa6..46958d0 100644 +--- a/lib/isc/netmgr/tcpdns.c ++++ b/lib/isc/netmgr/tcpdns.c +@@ -324,7 +324,7 @@ isc_nm_tcpdnsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer, + isc__nm_put_netievent_tcpdnsconnect(mgr, ievent); + } else { + atomic_init(&sock->active, false); +- sock->tid = isc_random_uniform(mgr->nworkers); ++ sock->tid = isc_random_uniform(mgr->nlisteners); + isc__nm_enqueue_ievent(&mgr->workers[sock->tid], + (isc__netievent_t *)ievent); + } +@@ -422,7 +422,7 @@ isc_nm_listentcpdns(isc_nm_t *mgr, isc_sockaddr_t *iface, + isc__nmsocket_init(sock, mgr, isc_nm_tcpdnslistener, iface); + + atomic_init(&sock->rchildren, 0); +- sock->nchildren = mgr->nworkers; ++ sock->nchildren = mgr->nlisteners; + children_size = sock->nchildren * sizeof(sock->children[0]); + sock->children = isc_mem_get(mgr->mctx, children_size); + memset(sock->children, 0, children_size); +diff --git a/lib/isc/netmgr/tlsdns.c b/lib/isc/netmgr/tlsdns.c +index d30e33f..40e6fc8 100644 +--- a/lib/isc/netmgr/tlsdns.c ++++ b/lib/isc/netmgr/tlsdns.c +@@ -419,7 +419,7 @@ isc_nm_tlsdnsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer, + isc__nm_put_netievent_tlsdnsconnect(mgr, ievent); + } else { + atomic_init(&sock->active, false); +- sock->tid = isc_random_uniform(mgr->nworkers); ++ sock->tid = isc_random_uniform(mgr->nlisteners); + isc__nm_enqueue_ievent(&mgr->workers[sock->tid], + (isc__netievent_t *)ievent); + } +@@ -532,7 +532,7 @@ isc_nm_listentlsdns(isc_nm_t *mgr, isc_sockaddr_t *iface, + isc__nmsocket_init(sock, mgr, isc_nm_tlsdnslistener, iface); + + atomic_init(&sock->rchildren, 0); +- sock->nchildren = mgr->nworkers; ++ sock->nchildren = mgr->nlisteners; + children_size = sock->nchildren * sizeof(sock->children[0]); + sock->children = isc_mem_get(mgr->mctx, children_size); + memset(sock->children, 0, children_size); +diff --git a/lib/isc/netmgr/tlsstream.c b/lib/isc/netmgr/tlsstream.c +index 7b49071..a3fc6d2 100644 +--- a/lib/isc/netmgr/tlsstream.c ++++ b/lib/isc/netmgr/tlsstream.c +@@ -1264,18 +1264,18 @@ isc__nm_tls_verify_tls_peer_result_string(const isc_nmhandle_t *handle) { + + static void + tls_init_listener_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *ctx) { +- size_t nworkers; ++ size_t nlisteners; + + REQUIRE(VALID_NM(listener->mgr)); + REQUIRE(ctx != NULL); + +- nworkers = (size_t)listener->mgr->nworkers; +- INSIST(nworkers > 0); ++ nlisteners = (size_t)listener->mgr->nlisteners; ++ INSIST(nlisteners > 0); + + listener->tlsstream.listener_tls_ctx = isc_mem_get( +- listener->mgr->mctx, sizeof(isc_tlsctx_t *) * nworkers); +- listener->tlsstream.n_listener_tls_ctx = nworkers; +- for (size_t i = 0; i < nworkers; i++) { ++ listener->mgr->mctx, sizeof(isc_tlsctx_t *) * nlisteners); ++ listener->tlsstream.n_listener_tls_ctx = nlisteners; ++ for (size_t i = 0; i < nlisteners; i++) { + listener->tlsstream.listener_tls_ctx[i] = NULL; + isc_tlsctx_attach(ctx, + &listener->tlsstream.listener_tls_ctx[i]); +diff --git a/lib/isc/netmgr/udp.c b/lib/isc/netmgr/udp.c +index 476c799..661de96 100644 +--- a/lib/isc/netmgr/udp.c ++++ b/lib/isc/netmgr/udp.c +@@ -157,14 +157,14 @@ isc_nm_listenudp(isc_nm_t *mgr, isc_sockaddr_t *iface, isc_nm_recv_cb_t cb, + REQUIRE(VALID_NM(mgr)); + + /* +- * We are creating mgr->nworkers duplicated sockets, one ++ * We are creating mgr->nlisteners duplicated sockets, one + * socket for each worker thread. + */ + sock = isc_mem_get(mgr->mctx, sizeof(isc_nmsocket_t)); + isc__nmsocket_init(sock, mgr, isc_nm_udplistener, iface); + + atomic_init(&sock->rchildren, 0); +- sock->nchildren = mgr->nworkers; ++ sock->nchildren = mgr->nlisteners; + children_size = sock->nchildren * sizeof(sock->children[0]); + sock->children = isc_mem_get(mgr->mctx, children_size); + memset(sock->children, 0, children_size); +@@ -1037,7 +1037,7 @@ isc_nm_udpconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer, + isc__nm_put_netievent_udpconnect(mgr, event); + } else { + atomic_init(&sock->active, false); +- sock->tid = isc_random_uniform(mgr->nworkers); ++ sock->tid = isc_random_uniform(mgr->nlisteners); + isc__nm_enqueue_ievent(&mgr->workers[sock->tid], + (isc__netievent_t *)event); + } +-- +2.33.0 + diff --git a/backport-CVE-2023-5517.patch b/backport-CVE-2023-5517.patch new file mode 100644 index 0000000..4f7f1bb --- /dev/null +++ b/backport-CVE-2023-5517.patch @@ -0,0 +1,108 @@ +From c73262493658cb8623927ef6cc2f023501f7e809 Mon Sep 17 00:00:00 2001 +From: Mark Andrews +Date: Tue, 10 Oct 2023 10:58:18 +1100 +Subject: [PATCH] Save the correct result value to resume with + nxdomain-redirect + +The wrong result value was being saved for resumption with +nxdomain-redirect when performing the fetch. This lead to an assert +when checking that RFC 1918 reverse queries where not leaking to +the global internet. + +Conflict:NA +Reference:https://downloads.isc.org/isc/bind/9.18.24/patches/0002-CVE-2023-5517.patch + +(cherry picked from commit 9d0fa07c5e7a39db89862a4f843d2190059afb4b) +--- + lib/ns/query.c | 22 ++++++++++------------ + 1 file changed, 10 insertions(+), 12 deletions(-) + +diff --git a/lib/ns/query.c b/lib/ns/query.c +index c1e9148..61749c8 100644 +--- a/lib/ns/query.c ++++ b/lib/ns/query.c +@@ -465,10 +465,10 @@ static void + query_addnxrrsetnsec(query_ctx_t *qctx); + + static isc_result_t +-query_nxdomain(query_ctx_t *qctx, isc_result_t res); ++query_nxdomain(query_ctx_t *qctx, isc_result_t result); + + static isc_result_t +-query_redirect(query_ctx_t *qctx); ++query_redirect(query_ctx_t *qctx, isc_result_t result); + + static isc_result_t + query_ncache(query_ctx_t *qctx, isc_result_t result); +@@ -7718,8 +7718,7 @@ query_usestale(query_ctx_t *qctx, isc_result_t result) { + * result from the search. + */ + static isc_result_t +-query_gotanswer(query_ctx_t *qctx, isc_result_t res) { +- isc_result_t result = res; ++query_gotanswer(query_ctx_t *qctx, isc_result_t result) { + char errmsg[256]; + + CCTRACE(ISC_LOG_DEBUG(3), "query_gotanswer"); +@@ -7795,7 +7794,7 @@ root_key_sentinel: + return (query_coveringnsec(qctx)); + + case DNS_R_NCACHENXDOMAIN: +- result = query_redirect(qctx); ++ result = query_redirect(qctx, result); + if (result != ISC_R_COMPLETE) { + return (result); + } +@@ -9612,11 +9611,10 @@ query_addnxrrsetnsec(query_ctx_t *qctx) { + * Handle NXDOMAIN and empty wildcard responses. + */ + static isc_result_t +-query_nxdomain(query_ctx_t *qctx, isc_result_t res) { ++query_nxdomain(query_ctx_t *qctx, isc_result_t result) { + dns_section_t section; + uint32_t ttl; +- isc_result_t result = res; +- bool empty_wild = (res == DNS_R_EMPTYWILD); ++ bool empty_wild = (result == DNS_R_EMPTYWILD); + + CCTRACE(ISC_LOG_DEBUG(3), "query_nxdomain"); + +@@ -9625,7 +9623,7 @@ query_nxdomain(query_ctx_t *qctx, isc_result_t res) { + INSIST(qctx->is_zone || REDIRECT(qctx->client)); + + if (!empty_wild) { +- result = query_redirect(qctx); ++ result = query_redirect(qctx, result); + if (result != ISC_R_COMPLETE) { + return (result); + } +@@ -9713,7 +9711,7 @@ cleanup: + * redirecting, so query processing should continue past it. + */ + static isc_result_t +-query_redirect(query_ctx_t *qctx) { ++query_redirect(query_ctx_t *qctx, isc_result_t saved_result) { + isc_result_t result; + + CCTRACE(ISC_LOG_DEBUG(3), "query_redirect"); +@@ -9754,7 +9752,7 @@ query_redirect(query_ctx_t *qctx) { + SAVE(qctx->client->query.redirect.rdataset, qctx->rdataset); + SAVE(qctx->client->query.redirect.sigrdataset, + qctx->sigrdataset); +- qctx->client->query.redirect.result = DNS_R_NCACHENXDOMAIN; ++ qctx->client->query.redirect.result = saved_result; + dns_name_copy(qctx->fname, qctx->client->query.redirect.fname); + qctx->client->query.redirect.authoritative = + qctx->authoritative; +@@ -10415,7 +10413,7 @@ query_coveringnsec(query_ctx_t *qctx) { + * We now have the proof that we have an NXDOMAIN. Apply + * NXDOMAIN redirection if configured. + */ +- result = query_redirect(qctx); ++ result = query_redirect(qctx, DNS_R_COVERINGNSEC); + if (result != ISC_R_COMPLETE) { + redirected = true; + goto cleanup; +-- +2.33.0 + diff --git a/backport-CVE-2023-5679.patch b/backport-CVE-2023-5679.patch new file mode 100644 index 0000000..d593b35 --- /dev/null +++ b/backport-CVE-2023-5679.patch @@ -0,0 +1,38 @@ +From 7db2796507127b40e2f091dafb842c6a7e86b9a8 Mon Sep 17 00:00:00 2001 +From: Mark Andrews +Date: Thu, 12 Oct 2023 12:01:46 +1100 +Subject: [PATCH] Restore dns64 state during serve-stale processing + +If we are in the process of looking for the A records as part of +dns64 processing and the server-stale timeout triggers, redo the +dns64 changes that had been made to the orignal qctx. + +Conflict:NA +Reference:https://downloads.isc.org/isc/bind/9.18.24/patches/0003-CVE-2023-5679.patch + +(cherry picked from commit 1fcc483df13e049b96f620e515f0d4d45f3680b7) +--- + lib/ns/query.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/lib/ns/query.c b/lib/ns/query.c +index 61749c8..40e1232 100644 +--- a/lib/ns/query.c ++++ b/lib/ns/query.c +@@ -6228,6 +6228,13 @@ query_lookup_stale(ns_client_t *client) { + query_ctx_t qctx; + + qctx_init(client, NULL, client->query.qtype, &qctx); ++ if (DNS64(client)) { ++ qctx.qtype = qctx.type = dns_rdatatype_a; ++ qctx.dns64 = true; ++ } ++ if (DNS64EXCLUDE(client)) { ++ qctx.dns64_exclude = true; ++ } + dns_db_attach(client->view->cachedb, &qctx.db); + client->query.attributes &= ~NS_QUERYATTR_RECURSIONOK; + client->query.dboptions |= DNS_DBFIND_STALETIMEOUT; +-- +2.33.0 + diff --git a/bind.spec b/bind.spec index ba95753..aec8d67 100644 --- a/bind.spec +++ b/bind.spec @@ -29,7 +29,7 @@ Summary: The Berkeley Internet Name Domain (BIND) DNS (Domain Name System) serv Name: bind License: MPLv2.0 Version: 9.18.21 -Release: 1 +Release: 2 Epoch: 32 Url: https://www.isc.org/downloads/bind/ # @@ -60,6 +60,10 @@ Source46: named-setup-rndc.service Source48: setup-named-softhsm.sh Source49: named-chroot.files +Patch6000:backport-CVE-2023-4408.patch +Patch6001:backport-CVE-2023-5517.patch +Patch6002:backport-CVE-2023-5679.patch +Patch6003:backport-CVE-2023-50387-CVE-2023-50868.patch # Common patches %{?systemd_ordering} @@ -899,6 +903,12 @@ fi; %endif %changelog +* Tue Mar 19 2024 chengyechun - 32:9.18.21-2 +- Type:CVE +- CVE:CVE-2023-4408 CVE-2023-5517 CVE-2023-5679 CVE-2023-50387 CVE-2023-50868 +- SUG:NA +- DESC:fix CVE-2023-4408 CVE-2023-5517 CVE-2023-5679 CVE-2023-50387 CVE-2023-50868 + * Sun Feb 04 2024 zhanghao - 32:9.18.21-1 - Type:requirement - CVE:NA -- Gitee