From cbd978b3682330513274713e41f496c0164b86d3 Mon Sep 17 00:00:00 2001 From: AlmaLinux RelEng Bot Date: Wed, 5 Aug 2026 05:52:01 -0400 Subject: [PATCH] import Oracle_OSS ldns-1.7.1-12.el9_8.1 --- SOURCES/ldns-1.7.1-CVE-2026-10846.patch | 200 ++++++++++++++++++++++++ SPECS/ldns.spec | 9 +- 2 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 SOURCES/ldns-1.7.1-CVE-2026-10846.patch diff --git a/SOURCES/ldns-1.7.1-CVE-2026-10846.patch b/SOURCES/ldns-1.7.1-CVE-2026-10846.patch new file mode 100644 index 0000000..9f24dfd --- /dev/null +++ b/SOURCES/ldns-1.7.1-CVE-2026-10846.patch @@ -0,0 +1,200 @@ +From 734e9e1fa9bd115ca416dcfde53ff199c390f01d Mon Sep 17 00:00:00 2001 +From: RHEL Packaging Agent +Date: Fri, 17 Jul 2026 09:26:15 +0000 +Subject: [PATCH] Fix CVE-2026-10846: Add address/port/TXID and question + matching checks + +Backport of the official NLnet Labs patch for CVE-2026-10846 to ldns 1.7.1. +This adds verification of the reply source address and port, transaction ID +matching, qdcount validation, and question section matching to prevent +DNS response spoofing attacks. +--- + ldns-1.7.1_python3/error.c | 6 +++ + ldns-1.7.1_python3/ldns/error.h | 5 +- + ldns-1.7.1_python3/net.c | 92 ++++++++++++++++++++++++++++++++- + 3 files changed, 100 insertions(+), 3 deletions(-) + +diff --git a/ldns-1.7.1_python3/error.c b/ldns-1.7.1_python3/error.c +index 7943017..6874f6b 100644 +--- a/ldns-1.7.1_python3/error.c ++++ b/ldns-1.7.1_python3/error.c +@@ -157,6 +157,12 @@ ldns_lookup_table ldns_error_str[] = { + "X509_STORE_CTX_set0_dane() functions within OpenSSL >= 1.1.0 " + "to be able to verify the DANE-TA usage type." }, + #endif ++ { LDNS_STATUS_ID_DID_NOT_MATCH, ++ "Response ID did not match the query ID" }, ++ { LDNS_STATUS_QDCOUNT_MUST_BE_ONE, ++ "The query section MUST contain exactly one question" }, ++ { LDNS_STATUS_QUERY_DID_NOT_MATCH, ++ "The question in the response did not match the query" }, + { 0, NULL } + }; + +diff --git a/ldns-1.7.1_python3/ldns/error.h b/ldns-1.7.1_python3/ldns/error.h +index 15f49a2..936df38 100644 +--- a/ldns-1.7.1_python3/ldns/error.h ++++ b/ldns-1.7.1_python3/ldns/error.h +@@ -129,7 +129,10 @@ enum ldns_enum_status { + LDNS_STATUS_RDATA_OVERFLOW, + LDNS_STATUS_SYNTAX_SUPERFLUOUS_TEXT_ERR, + LDNS_STATUS_NSEC3_DOMAINNAME_OVERFLOW, +- LDNS_STATUS_DANE_NEED_OPENSSL_GE_1_1_FOR_DANE_TA ++ LDNS_STATUS_DANE_NEED_OPENSSL_GE_1_1_FOR_DANE_TA, ++ LDNS_STATUS_ID_DID_NOT_MATCH, ++ LDNS_STATUS_QDCOUNT_MUST_BE_ONE, ++ LDNS_STATUS_QUERY_DID_NOT_MATCH + }; + typedef enum ldns_enum_status ldns_status; + +diff --git a/ldns-1.7.1_python3/net.c b/ldns-1.7.1_python3/net.c +index 38d00cf..e075757 100644 +--- a/ldns-1.7.1_python3/net.c ++++ b/ldns-1.7.1_python3/net.c +@@ -441,6 +441,50 @@ ldns_udp_bgsend2(ldns_buffer *qbin, + return ldns_udp_bgsend_from(qbin, to, tolen, NULL, 0, timeout); + } + ++/** helper sockaddr compare function. returns -1, 0 or 1. */ ++static int ++ldns_sockaddr_cmp(const struct sockaddr_storage* addr1, socklen_t len1, ++ const struct sockaddr_storage* addr2, socklen_t len2) ++{ ++ struct sockaddr_in* p1_in = (struct sockaddr_in*)addr1; ++ struct sockaddr_in* p2_in = (struct sockaddr_in*)addr2; ++ struct sockaddr_in6* p1_in6 = (struct sockaddr_in6*)addr1; ++ struct sockaddr_in6* p2_in6 = (struct sockaddr_in6*)addr2; ++ if(len1 < len2) ++ return -1; ++ if(len1 > len2) ++ return 1; ++ assert(len1 == len2); ++ if( p1_in->sin_family < p2_in->sin_family) ++ return -1; ++ if( p1_in->sin_family > p2_in->sin_family) ++ return 1; ++ assert( p1_in->sin_family == p2_in->sin_family ); ++ /* compare ip4 */ ++ if( p1_in->sin_family == AF_INET ) { ++ /* just order it, ntohs not required */ ++ if(p1_in->sin_port < p2_in->sin_port) ++ return -1; ++ if(p1_in->sin_port > p2_in->sin_port) ++ return 1; ++ assert(p1_in->sin_port == p2_in->sin_port); ++ return memcmp(&p1_in->sin_addr, &p2_in->sin_addr, ++ sizeof(p1_in->sin_addr)); ++ } else if (p1_in6->sin6_family == AF_INET6) { ++ /* just order it, ntohs not required */ ++ if(p1_in6->sin6_port < p2_in6->sin6_port) ++ return -1; ++ if(p1_in6->sin6_port > p2_in6->sin6_port) ++ return 1; ++ assert(p1_in6->sin6_port == p2_in6->sin6_port); ++ return memcmp(&p1_in6->sin6_addr, &p2_in6->sin6_addr, ++ sizeof(p1_in6->sin6_addr)); ++ } else { ++ /* eek unknown type, perform this comparison for sanity. */ ++ return memcmp(addr1, addr2, len1); ++ } ++} ++ + static ldns_status + ldns_udp_send_from(uint8_t **result, ldns_buffer *qbin, + const struct sockaddr_storage *to , socklen_t tolen, +@@ -449,6 +493,8 @@ ldns_udp_send_from(uint8_t **result, ldns_buffer *qbin, + { + int sockfd; + uint8_t *answer; ++ struct sockaddr_storage reply_addr; ++ socklen_t reply_addr_len; + + sockfd = ldns_udp_bgsend_from(qbin, to, tolen, from, fromlen, timeout); + +@@ -467,13 +513,21 @@ ldns_udp_send_from(uint8_t **result, ldns_buffer *qbin, + * but returns a 'NETWORK_ERROR' much like a timeout. */ + ldns_sock_nonblock(sockfd); + +- answer = ldns_udp_read_wire(sockfd, answer_size, NULL, NULL); ++ reply_addr_len = sizeof(reply_addr); ++ memset(&reply_addr, 0, reply_addr_len); ++ answer = ldns_udp_read_wire(sockfd, answer_size, &reply_addr, ++ &reply_addr_len); + close_socket(sockfd); + + if (!answer) { + /* oops */ + return LDNS_STATUS_NETWORK_ERR; + } ++ /* Check that the reply came from the to addr. */ ++ if(ldns_sockaddr_cmp(to, tolen, &reply_addr, reply_addr_len) != 0) { ++ free(answer); ++ return LDNS_STATUS_NETWORK_ERR; ++ } + + *result = answer; + return LDNS_STATUS_OK; +@@ -512,6 +566,10 @@ ldns_send_buffer(ldns_pkt **result, ldns_resolver *r, ldns_buffer *qb, ldns_rdf + + assert(r != NULL); + ++ /* The query should at least have one question */ ++ if(ldns_buffer_limit(qb) < 6 || ldns_buffer_read_u16_at(qb, 4) != 1) ++ return LDNS_STATUS_QDCOUNT_MUST_BE_ONE; ++ + status = LDNS_STATUS_OK; + rtt = ldns_resolver_rtt(r); + ns_array = ldns_resolver_nameservers(r); +@@ -599,6 +657,16 @@ ldns_send_buffer(ldns_pkt **result, ldns_resolver *r, ldns_buffer *qb, ldns_rdf + ldns_resolver_set_nameserver_rtt(r, i, LDNS_RESOLV_RTT_INF); + status = send_status; + } ++ if(reply_bytes && ldns_buffer_limit(qb) >= 2) { ++ uint16_t txid = ldns_buffer_read_u16_at(qb, 0); ++ if(reply_size < 2 || ++ ldns_read_uint16(reply_bytes) != txid) { ++ status = LDNS_STATUS_ID_DID_NOT_MATCH; ++ LDNS_FREE(reply_bytes); ++ reply_bytes = NULL; ++ reply_size = 0; ++ } ++ } + + /* obey the fail directive */ + if (!reply_bytes) { +@@ -608,7 +676,7 @@ ldns_send_buffer(ldns_pkt **result, ldns_resolver *r, ldns_buffer *qb, ldns_rdf + LDNS_FREE(src); + } + LDNS_FREE(ns); +- return LDNS_STATUS_ERR; ++ return status ? status : LDNS_STATUS_ERR; + } else { + LDNS_FREE(ns); + continue; +@@ -670,6 +738,26 @@ ldns_send_buffer(ldns_pkt **result, ldns_resolver *r, ldns_buffer *qb, ldns_rdf + #endif /* HAVE_SSL */ + + LDNS_FREE(reply_bytes); ++ if (reply) { ++ ldns_pkt *query = NULL; ++ ++ if(ldns_pkt_qdcount(reply) != 1) { ++ status = LDNS_STATUS_QDCOUNT_MUST_BE_ONE; ++ ldns_pkt_free(reply); ++ reply = NULL; ++ ++ } else if(ldns_wire2pkt(&query ++ , ldns_buffer_begin(qb) ++ , ldns_buffer_position(qb)) != LDNS_STATUS_OK ++ || ldns_pkt_qdcount(query) != 1 ++ || ldns_rr_compare(ldns_rr_list_rr(ldns_pkt_question(query),0) ++ ,ldns_rr_list_rr(ldns_pkt_question(reply),0))){ ++ status = LDNS_STATUS_QUERY_DID_NOT_MATCH; ++ ldns_pkt_free(reply); ++ reply = NULL; ++ } ++ ldns_pkt_free(query); ++ } + if (result) { + *result = reply; + } diff --git a/SPECS/ldns.spec b/SPECS/ldns.spec index a286824..e93032c 100644 --- a/SPECS/ldns.spec +++ b/SPECS/ldns.spec @@ -39,7 +39,7 @@ Summary: Low-level DNS(SEC) library with API Name: ldns Version: 1.7.1 -Release: 12%{?dist} +Release: 12%{?dist}.1 License: BSD Url: https://www.nlnetlabs.nl/%{name}/ @@ -57,6 +57,9 @@ Patch3: ldns-1.7.1-out-of-boud-read-vuln.patch Patch4: ldns-1.7.1-Support-sysconfig-python-module-in-python_devel.patch # https://github.com/NLnetLabs/ldns/commit/a5a5dd867fdb934a7ce3637dd9def598f0979247 Patch5: ldns-1.7.1-Use-PYTHON_LIBS-instead-of-PYTHON_LDFLAGS.patch +# https://issues.redhat.com/browse/RHEL-210701 +# https://nlnetlabs.nl/downloads/ldns/patch_cve_2026-10846.diff +Patch6: ldns-1.7.1-CVE-2026-10846.patch # Only needed for builds from svn snapshot @@ -359,6 +362,10 @@ rm -rf doc/man %doc doc %changelog +* Fri Jul 17 2026 RHEL Packaging Agent - 1.7.1-12.1 +- Fix CVE-2026-10846: DNS response spoofing via missing + address/port/TXID and question section validation (RHEL-210701) + * Tue Dec 03 2024 Petr Menšík - 1.7.1-12 - Fix digest calculation of sha256 (RHEL-20391)