Several vulnerabilities were found in NSD.We released 4.15.1 as a security release on Wednesday 26 August including the relevant fixes.
The overview of the vulnerabilities with a brief description is: CVE-2026-18664 - severity: HIGH Wrong interpretation of ACL ranges CVE-2026-18916 - severity: MEDIUM Remote TCP DoS by throttling the TCP receive window CVE-2026-19401 - severity: HIGH Remote UDP DoS by sending multiple DNS Cookie options CVE-2026-19538 - severity: HIGH Bypass of BLOCKED ACL items on proxy protocol port over TCP or TLSYou can find detailed information on each vulnerability attached to this email along with their respective patches.
For ease of deployment we also provide a combined patch including all of them (patch_combined-4.15.1.diff).
The patches are tested to apply/work on 4.15.0. Best regards, -- Willem, on behalf of the NSD team.
The CVE number for this vulnerability is CVE-2026-18664 = Summary IP range access control restrictions are bypassed for some unintended IP addresses. == Affected products NSD from and including version 3.0.0 up to and including version 4.15.0 == Description When ranges are used for access control (i.e. of the form 1.2.3.4-1.2.3.25), because NSD wrongly compares the IP address with the range on little endian systems, IPs that were meant to be allowed may be denied, and, IPs that were meant to be denied access could be allowed. An IPv4 address is compared with IPv4 ranges as unsigned 32 bit numbers directly with the endianness of the host, but the values to compare are in network byte order (big-endian). With IPv6 addresses the comparison is done in 4 times a unsigned 32 bit number comparison, again with the endianness of the host where all values are actually in network bye order. == Mitigation === Downloading patched version NSD 4.15.1 is released with the patch https://nlnetlabs.nl/downloads/nsd/nsd-4.15.1.tar.gz === Applying the patch manually For NSD 4.15.0 the patch is: https://nlnetlabs.nl/downloads/nsd/patch_CVE-2026-18664.diff Apply the patch on the nsd source directory with: patch -p1 < patch_CVE-2026-18664.diff then run 'make install' to install nsd. The patch is tested to work on nsd 4.15.0. == Acknowledgments We would like to thank Qifan Zhang from Palo Alto Networks for discovering and responsibly disclosing the vulnerability.
diff --git a/options.c b/options.c
index ccfa13eb..486b61cf 100644
--- a/options.c
+++ b/options.c
@@ -2221,9 +2221,9 @@ acl_addr_match_range_v4(uint32_t* minval, uint32_t* x, uint32_t* maxval, size_t
/* check treats x as one huge number */
/* if outside bounds, we are done */
- if(*minval > *x)
+ if(ntohl(*minval) > ntohl(*x))
return 0;
- if(*maxval < *x)
+ if(ntohl(*maxval) < ntohl(*x))
return 0;
return 1;
@@ -2244,10 +2244,10 @@ acl_addr_match_range_v6(uint32_t* minval, uint32_t* x, uint32_t* maxval, size_t
{
/* if outside bounds, we are done */
if(checkmin)
- if(minval[i] > x[i])
+ if(ntohl(minval[i]) > ntohl(x[i]))
return 0;
if(checkmax)
- if(maxval[i] < x[i])
+ if(ntohl(maxval[i]) < ntohl(x[i]))
return 0;
/* if x is equal to a bound, that bound needs further checks */
if(checkmin && minval[i]!=x[i])
--
2.43.0
The CVE number for this vulnerability is CVE-2026-18916 = Summary Any remote client can denial TCP service by throttling the TCP receive window (down to 1). == Affected products NSD from and including version 3.2.11 up to and including version 4.15.0 == Description Any remote client can crash a NSD serve child, by throttling the TCP receive window after a TCP query. By continuously crashing the serve childs, the remote client can denial all TCP service to this NSD instance. == Mitigation === Downloading patched version NSD 4.15.1 is released with the patch https://nlnetlabs.nl/downloads/nsd/nsd-4.15.1.tar.gz === Applying the patch manually For NSD 4.15.0 the patch is: https://nlnetlabs.nl/downloads/nsd/patch_CVE-2026-18916.diff Apply the patch on the nsd source directory with: patch -p1 < patch_CVE-2026-18916.diff then run 'make install' to install nsd. The patch is tested to work on nsd 4.15.0. == Acknowledgments We would like to thank Akhil Koul (https://github.com/akoul) for discovering and responsibly disclosing the vulnerability.
diff --git a/server.c b/server.c
index ab18f473..822c2751 100644
--- a/server.c
+++ b/server.c
@@ -4850,7 +4850,8 @@ handle_tcp_writing(int fd, short event, void* arg)
}
#ifdef HAVE_WRITEV
- sent -= sizeof(n_tcplen);
+ /* The number of bytes transmitted for the message content. */
+ sent = data->bytes_transmitted - sizeof(n_tcplen);
/* handle potential 'packet done' code */
goto packet_could_be_done;
#endif
diff --git a/xfrd-tcp.c b/xfrd-tcp.c
index acea5e2d..5f3abb51 100644
--- a/xfrd-tcp.c
+++ b/xfrd-tcp.c
@@ -1252,8 +1252,8 @@ int conn_write(struct xfrd_tcp* tcp)
}
tcp->total_bytes += sent;
- if(sent > (ssize_t)sizeof(tcp->msglen))
- buffer_skip(tcp->packet, sent-sizeof(tcp->msglen));
+ if(tcp->total_bytes > (ssize_t)sizeof(tcp->msglen))
+ buffer_skip(tcp->packet, tcp->total_bytes-sizeof(tcp->msglen));
if(tcp->total_bytes < sizeof(tcp->msglen)) {
/* incomplete write, resume later */
return 0;
--
2.54.0
The CVE number for this vulnerability is CVE-2026-19401 = Summary Any remote client can denial UDP service by sending a specifically crafted query with multiple DNS Cookie options. == Affected products NSD from and including version 4.3.7 up to and including version 4.15.0 == Description Any remote client can crash a (debugging/non-release build type) NSD serve child by sending it a special crafted message with a specially tuned number of DNS Cookie options (17 when UDP payload size is 512). By continuously crashing the serve childs, the remote client can severely hamper or, when positioned sufficiently close, deny all DNS service. == Mitigation === Downloading patched version NSD 4.15.1 is released with the patch https://nlnetlabs.nl/downloads/nsd/nsd-4.15.1.tar.gz === Applying the patch manually For NSD 4.15.0 the patch is: https://nlnetlabs.nl/downloads/nsd/patch_CVE-2026-19401.diff Apply the patch on the nsd source directory with: patch -p1 < patch_CVE-2026-19401.diff then run 'make install' to install nsd. The patch is tested to work on nsd 4.15.0. == Acknowledgments We would like to thank Qifan Zhang from Palo Alto Networks for discovering and responsibly disclosing the vulnerability.
diff --git a/edns.c b/edns.c
index d77a146e..7a186fde 100644
--- a/edns.c
+++ b/edns.c
@@ -72,5 +72,6 @@ edns_init_record(edns_record_type *edns)
edns->nsid = 0;
edns->zoneversion = 0;
+ edns->cookie_seen = 0;
edns->cookie_status = COOKIE_NOT_PRESENT;
edns->cookie_len = 0;
edns->ede = -1; /* -1 means no Extended DNS Error */
@@ -89,7 +90,7 @@ edns_handle_option(uint16_t optcode, uint16_t optlen, buffer_type* packet,
switch(optcode) {
case NSID_CODE:
/* is NSID enabled? */
- if(nsd->nsid_len > 0) {
+ if(nsd->nsid_len > 0 && !edns->nsid) {
edns->nsid = 1;
/* we have to check optlen, and move the buffer along */
buffer_skip(packet, optlen);
@@ -102,7 +103,8 @@ edns_handle_option(uint16_t optcode, uint16_t optlen, buffer_type* packet,
break;
case COOKIE_CODE:
/* Cookies enabled? */
- if(nsd->do_answer_cookie) {
+ if(nsd->do_answer_cookie && !edns->cookie_seen) {
+ edns->cookie_seen = 1;
if (optlen == 8)
edns->cookie_status = COOKIE_INVALID;
else if (optlen < 16 || optlen > 40)
diff --git a/edns.h b/edns.h
--- a/edns.h
+++ b/edns.h
@@ -69,5 +69,6 @@ struct edns_record
int nsid;
int zoneversion;
+ int cookie_seen;
cookie_status_type cookie_status;
size_t cookie_len;
uint8_t cookie[40];
diff --git a/query.c b/query.c
index 5e5c9361..03cc0bf1 100644
--- a/query.c
+++ b/query.c
@@ -1783,6 +1783,16 @@ query_process(query_type *q, nsd_type *nsd, uint32_t *now_p)
cookie_verify(q, nsd, now_p);
query_prepare_response(q);
+ if(q->reserved_space + QHEADERSZ + (size_t)q->qname->name_size +
+ 2 /* qtype */ + 2 /* qclass */ > q->maxlen) {
+ /* Clear out some space, and return error, it does not fit. */
+ q->edns.status = EDNS_NOT_PRESENT;
+ q->tsig.status = TSIG_NOT_PRESENT;
+ if(q->tcp)
+ return query_error(q, NSD_RC_SERVFAIL);
+ TC_SET(q->packet);
+ return query_error(q, NSD_RC_OK);
+ }
if (q->qclass != CLASS_IN && q->qclass != CLASS_ANY) {
if (q->qclass == CLASS_CH) {
--
2.54.0
The CVE number for this vulnerability is CVE-2026-19538 = Summary Anyone with access to the proxy protocol port over TCP or TLS can bypass BLOCKED access control items. == Affected products NSD from and including version 4.8.0 up to and including version 4.15.0 == Description The BLOCKED access control list items that are evaluated to deny access on the the proxy protocol port can be bypassed completely when connecting over TCP or TLS and sending the query twice on connection that is kept open. With access to the proxy protocol port, the adversary can then spoof any IP and potentially bypass all IP based access control (for queries, transfers and notifies). == Mitigation === Downloading patched version NSD 4.15.1 is released with the patch https://nlnetlabs.nl/downloads/nsd/nsd-4.15.1.tar.gz === Applying the patch manually For NSD 4.15.0 the patch is: https://nlnetlabs.nl/downloads/nsd/patch_CVE-2026-19538.diff Apply the patch on the nsd source directory with: patch -p1 < patch_CVE-2026-19538.diff then run 'make install' to install nsd. The patch is tested to work on nsd 4.15.0. == Acknowledgments We would like to thank Qifan Zhang from Palo Alto Networks for discovering and responsibly disclosing the vulnerability.
diff --git a/query.c b/query.c index 5e5c9361..9bf6d153 100644 --- a/query.c +++ b/query.c @@ -249,7 +249,8 @@ query_reset(query_type *q, size_t maxlen, int is_tcp) region_free_all(q->region); q->remote_addrlen = (socklen_t)sizeof(q->remote_addr); q->client_addrlen = (socklen_t)sizeof(q->client_addr); - q->is_proxied = 0; + if(!is_tcp) + q->is_proxied = 0; q->maxlen = maxlen; q->reserved_space = 0; buffer_clear(q->packet); -- 2.54.0
diff --git a/edns.c b/edns.c
index 035ce4f4..e9e322d8 100644
--- a/edns.c
+++ b/edns.c
@@ -71,6 +71,7 @@ edns_init_record(edns_record_type *edns)
edns->dnssec_ok = 0;
edns->nsid = 0;
edns->zoneversion = 0;
+ edns->cookie_seen = 0;
edns->cookie_status = COOKIE_NOT_PRESENT;
edns->cookie_len = 0;
edns->ede = -1; /* -1 means no Extended DNS Error */
@@ -88,7 +89,7 @@ edns_handle_option(uint16_t optcode, uint16_t optlen, buffer_type* packet,
switch(optcode) {
case NSID_CODE:
/* is NSID enabled? */
- if(nsd->nsid_len > 0) {
+ if(nsd->nsid_len > 0 && !edns->nsid) {
edns->nsid = 1;
/* we have to check optlen, and move the buffer along */
buffer_skip(packet, optlen);
@@ -101,7 +102,8 @@ edns_handle_option(uint16_t optcode, uint16_t optlen, buffer_type* packet,
break;
case COOKIE_CODE:
/* Cookies enabled? */
- if(nsd->do_answer_cookie) {
+ if(nsd->do_answer_cookie && !edns->cookie_seen) {
+ edns->cookie_seen = 1;
if (optlen == 8)
edns->cookie_status = COOKIE_INVALID;
else if (optlen < 16 || optlen > 40)
diff --git a/edns.h b/edns.h
index 3e1f5e79..1b182e1b 100644
--- a/edns.h
+++ b/edns.h
@@ -64,6 +64,7 @@ struct edns_record
int dnssec_ok;
int nsid;
int zoneversion;
+ int cookie_seen;
cookie_status_type cookie_status;
size_t cookie_len;
uint8_t cookie[40];
diff --git a/options.c b/options.c
index ccfa13eb..486b61cf 100644
--- a/options.c
+++ b/options.c
@@ -2221,9 +2221,9 @@ acl_addr_match_range_v4(uint32_t* minval, uint32_t* x, uint32_t* maxval, size_t
/* check treats x as one huge number */
/* if outside bounds, we are done */
- if(*minval > *x)
+ if(ntohl(*minval) > ntohl(*x))
return 0;
- if(*maxval < *x)
+ if(ntohl(*maxval) < ntohl(*x))
return 0;
return 1;
@@ -2244,10 +2244,10 @@ acl_addr_match_range_v6(uint32_t* minval, uint32_t* x, uint32_t* maxval, size_t
{
/* if outside bounds, we are done */
if(checkmin)
- if(minval[i] > x[i])
+ if(ntohl(minval[i]) > ntohl(x[i]))
return 0;
if(checkmax)
- if(maxval[i] < x[i])
+ if(ntohl(maxval[i]) < ntohl(x[i]))
return 0;
/* if x is equal to a bound, that bound needs further checks */
if(checkmin && minval[i]!=x[i])
diff --git a/query.c b/query.c
index 9954c5f0..fd90a3a8 100644
--- a/query.c
+++ b/query.c
@@ -249,7 +249,8 @@ query_reset(query_type *q, size_t maxlen, int is_tcp)
region_free_all(q->region);
q->remote_addrlen = (socklen_t)sizeof(q->remote_addr);
q->client_addrlen = (socklen_t)sizeof(q->client_addr);
- q->is_proxied = 0;
+ if(!is_tcp)
+ q->is_proxied = 0;
q->maxlen = maxlen;
q->reserved_space = 0;
buffer_clear(q->packet);
@@ -1776,6 +1777,16 @@ query_process(query_type *q, nsd_type *nsd, uint32_t *now_p)
cookie_verify(q, nsd, now_p);
query_prepare_response(q);
+ if(q->reserved_space + QHEADERSZ + (size_t)q->qname->name_size +
+ 2 /* qtype */ + 2 /* qclass */ > q->maxlen) {
+ /* Clear out some space, and return error, it does not fit. */
+ q->edns.status = EDNS_NOT_PRESENT;
+ q->tsig.status = TSIG_NOT_PRESENT;
+ if(q->tcp)
+ return query_error(q, NSD_RC_SERVFAIL);
+ TC_SET(q->packet);
+ return query_error(q, NSD_RC_OK);
+ }
if (q->qclass != CLASS_IN && q->qclass != CLASS_ANY) {
if (q->qclass == CLASS_CH) {
diff --git a/server.c b/server.c
index ccf6e2dc..ea9a858a 100644
--- a/server.c
+++ b/server.c
@@ -4813,7 +4813,8 @@ handle_tcp_writing(int fd, short event, void* arg)
}
#ifdef HAVE_WRITEV
- sent -= sizeof(n_tcplen);
+ /* The number of bytes transmitted for the message content. */
+ sent = data->bytes_transmitted - sizeof(n_tcplen);
/* handle potential 'packet done' code */
goto packet_could_be_done;
#endif
diff --git a/xfrd-tcp.c b/xfrd-tcp.c
index acea5e2d..5f3abb51 100644
--- a/xfrd-tcp.c
+++ b/xfrd-tcp.c
@@ -1252,8 +1252,8 @@ int conn_write(struct xfrd_tcp* tcp)
}
tcp->total_bytes += sent;
- if(sent > (ssize_t)sizeof(tcp->msglen))
- buffer_skip(tcp->packet, sent-sizeof(tcp->msglen));
+ if(tcp->total_bytes > (ssize_t)sizeof(tcp->msglen))
+ buffer_skip(tcp->packet, tcp->total_bytes-sizeof(tcp->msglen));
if(tcp->total_bytes < sizeof(tcp->msglen)) {
/* incomplete write, resume later */
return 0;
OpenPGP_0xE5F8F8212F77A498.asc
Description: OpenPGP public key
OpenPGP_signature.asc
Description: OpenPGP digital signature
