From nobody Tue Jun 9 19:16:56 2026 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4gZdvD6PXTz6gTRV for ; Tue, 09 Jun 2026 19:16:56 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R13" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4gZdvD3kzCz3KBD for ; Tue, 09 Jun 2026 19:16:56 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1781032616; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=lcJ4CtYBqb/Uu7VGniZLls+1cYGGQA8jr3J2NGZ1aPM=; b=tGRi5ne6B2o5hpWSa+89/2DCp+5uC9zjHczusPgqxrumW37JJoP4WtFBK+gwjK442hxEJ4 Z0yxRmAPrZ68+3yTn4/SwRCRJCQ8j13+6D+m/xU0oR5O/GAlRPpsZUl/ocaoLIi9UAgjP2 1Vg9+0uitBj2uDU4W0OVI5PaLIkZRo9F3wYnfyFA5nptwktpeN6okPV+UrE9GCRmhVB9WQ L8dhGzMCxG4if5Z+Abd6/qm31NyDDP9pB2z+6g1UUzP/RMtSAHzNhIkKDjtLvsfrrUol8r XR8BmQ3uN1b+CTc7jg16T4dL7te4lsB+RrT6Kx4pcATr3YWvQSFvxCwxxNNqTg== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1781032616; a=rsa-sha256; cv=none; b=kWWogeilaOgQ+te/ACxL+PmQ5KCR1RrrN6c9qWKAgTxyDhZ0IA4lsWpnj0DL8rG3bT8JyO 5snmCNnDk8FkJjZpwG6hGrtdV3x8yUaYxpqKj25as0wSnrpp/VHEJ6E0lLUKhxkgPkRmCZ 6Ft+F7i/5B+fw7CgGSAHym9AYYGED/WNI6Q0mHru+XGMyWSRxavzH+/gq0akt/cJpAV1TI k25f1oU1y8t60F/NDrZO/oNnunQBCk78Pn2swLyK7p6MvLgX7HAWfWMnCKnYEqJc8wAOPe onOpmZ4vCZcGobAi5iaKS42b7XePcIyocNX6fUKNHUTuz2xwJLOndMJ0Z1I7aw== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1781032616; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=lcJ4CtYBqb/Uu7VGniZLls+1cYGGQA8jr3J2NGZ1aPM=; b=lYh5Q36Z6otftymMeS+cuRnCKohPuu/A42K7IYdfNrNYizT93jvII9qkCpTt51MQw/s0QW dupdcHfLmxpXKb3jI5jQ3v2n1VW7b6dOMlXQ5FzK51Nmr/tQjqUcnnceeGluhPhdJ2eMxa JZCK+86aP/NwDgKadaelf0PD563Ih8LQBxoItaENZ0HSn92VQedilIBpTEz+C0XyXc4pw9 B9o90kN+Ty8z8k7JlwKCJncOPnPNcqtgc2tUWTmM7HW7FrHS8UcdtkMpwmBygOeu+t97G0 vj1Qy7YsebrIyAD8+dYHTHywPck3ygVjQTopOVsbbJXw3wV1tKomFEba38tcEA== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4gZdvD35pRznvk for ; Tue, 09 Jun 2026 19:16:56 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 3e495 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Tue, 09 Jun 2026 19:16:56 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Cc: Gordon Tetlow From: Mark Johnston Subject: git: 980ba4177b69 - main - ldns: Fix query response validation List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: markj X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 980ba4177b69655726485daa5ff3e931f19aa738 Auto-Submitted: auto-generated Date: Tue, 09 Jun 2026 19:16:56 +0000 Message-Id: <6a2866a8.3e495.33ee7e98@gitrepo.freebsd.org> The branch main has been updated by markj: URL: https://cgit.FreeBSD.org/src/commit/?id=980ba4177b69655726485daa5ff3e931f19aa738 commit 980ba4177b69655726485daa5ff3e931f19aa738 Author: Gordon Tetlow AuthorDate: 2026-06-07 15:09:39 +0000 Commit: Mark Johnston CommitDate: 2026-06-09 19:13:21 +0000 ldns: Fix query response validation Approved by: so Security: FreeBSD-SA-26:36.ldns Security: CVE-2026-10846 --- contrib/ldns/error.c | 6 ++++ contrib/ldns/ldns/error.h | 5 ++- contrib/ldns/net.c | 92 +++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 100 insertions(+), 3 deletions(-) diff --git a/contrib/ldns/error.c b/contrib/ldns/error.c index 5723aea9b4c2..4fc05d6d0d8f 100644 --- a/contrib/ldns/error.c +++ b/contrib/ldns/error.c @@ -191,6 +191,12 @@ ldns_lookup_table ldns_error_str[] = { "at least 2 bytes of option data" }, { LDNS_STATUS_EQUAL_RR, "An identical RR already existed in the zone" }, + { 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/contrib/ldns/ldns/error.h b/contrib/ldns/ldns/error.h index a76eb2ecab5d..41d64cc0815f 100644 --- a/contrib/ldns/ldns/error.h +++ b/contrib/ldns/ldns/error.h @@ -144,7 +144,10 @@ enum ldns_enum_status { LDNS_STATUS_INVALID_SVCPARAM_VALUE, LDNS_STATUS_NOT_EDE, LDNS_STATUS_EDE_OPTION_MALFORMED, - LDNS_STATUS_EQUAL_RR + LDNS_STATUS_EQUAL_RR, + 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/contrib/ldns/net.c b/contrib/ldns/net.c index e944d018b357..4c1f405419fb 100644 --- a/contrib/ldns/net.c +++ b/contrib/ldns/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; }