Validate DNS message CLASS early in request processing

Reject requests with unsupported or misused CLASS values before
further processing.  Only IN, CH, HS, RESERVED0 (for DNS Cookies),
ANY (for TKEY negotiation), and NONE (for DNS UPDATE) are accepted;
all other classes return NOTIMP.  Misuse of NONE or ANY outside
their allowed contexts returns FORMERR.

This adds further protection against bugs of the same general class
as YWH-PGM40640-70 and YWH-PGM40640-73.

(cherry picked from commit 0a687451505037e9f9a850c9cb113aed4995b03f)
This commit is contained in:
Ondřej Surý 2026-03-04 10:46:58 +01:00 committed by Michał Kępień
parent 04092ed136
commit b247dbb350
No known key found for this signature in database
2 changed files with 55 additions and 13 deletions

View file

@ -25,6 +25,11 @@ dig_cmd() {
"$DIG" $DIGOPTS "$@" | grep -v '^;'
}
dig_full() {
# shellcheck disable=SC2086
"$DIG" $DIGOPTS "$@"
}
n=$((n + 1))
echo_i "querying for various representations of an IN A record ($n)"
for i in 1 2 3 4 5 6 7 8 9 10 11 12; do
@ -81,8 +86,8 @@ n=$((n + 1))
echo_i "querying for various representations of a CLASS10 TYPE1 record ($n)"
for i in 1 2; do
ret=0
dig_cmd +short @10.53.0.1 a$i.example a class10 >dig.out.$i.test$n
echo '\# 4 0A000001' | diff - dig.out.$i.test$n || ret=1
dig_full @10.53.0.1 a$i.example a class10 >dig.out.$i.test$n
grep -q "NOTIMP" dig.out.$i.test$n || ret=1
if [ $ret != 0 ]; then
echo_i "#$i failed"
fi
@ -93,8 +98,8 @@ n=$((n + 1))
echo_i "querying for various representations of a CLASS10 TXT record ($n)"
for i in 1 2 3 4; do
ret=0
dig_cmd +short @10.53.0.1 txt$i.example txt class10 >dig.out.$i.test$n
echo '"hello"' | diff - dig.out.$i.test$n || ret=1
dig_full @10.53.0.1 txt$i.example txt class10 >dig.out.$i.test$n
grep -q "NOTIMP" dig.out.$i.test$n || ret=1
if [ $ret != 0 ]; then
echo_i "#$i failed"
fi
@ -105,8 +110,8 @@ n=$((n + 1))
echo_i "querying for various representations of a CLASS10 TYPE123 record ($n)"
for i in 1 2; do
ret=0
dig_cmd +short @10.53.0.1 unk$i.example type123 class10 >dig.out.$i.test$n
echo '\# 1 00' | diff - dig.out.$i.test$n || ret=1
dig_full @10.53.0.1 unk$i.example type123 class10 >dig.out.$i.test$n
grep -q "NOTIMP" dig.out.$i.test$n || ret=1
if [ $ret != 0 ]; then
echo_i "#$i failed"
fi

View file

@ -44,6 +44,7 @@
#include <dns/dispatch.h>
#include <dns/dnstap.h>
#include <dns/edns.h>
#include <dns/enumclass.h>
#include <dns/events.h>
#include <dns/message.h>
#include <dns/peer.h>
@ -2083,7 +2084,9 @@ ns__client_request(isc_nmhandle_t *handle, isc_result_t eresult,
}
}
if (client->message->rdclass == 0) {
char classbuf[DNS_RDATACLASS_FORMATSIZE];
switch (client->message->rdclass) {
case dns_rdataclass_reserved0:
if ((client->attributes & NS_CLIENTATTR_WANTCOOKIE) != 0 &&
client->message->opcode == dns_opcode_query &&
client->message->counts[DNS_SECTION_QUESTION] == 0U)
@ -2102,12 +2105,46 @@ ns__client_request(isc_nmhandle_t *handle, isc_result_t eresult,
return;
}
ns_client_dumpmessage(client,
"message class could not be determined");
ns_client_error(client, notimp ? DNS_R_NOTIMP : DNS_R_FORMERR);
return;
case dns_rdataclass_in:
break;
case dns_rdataclass_chaos:
break;
case dns_rdataclass_hs:
break;
case dns_rdataclass_none:
if (client->message->opcode != dns_opcode_update) {
ns_client_dumpmessage(client,
"message class NONE can be only "
"used in DNS updates");
ns_client_error(client, DNS_R_FORMERR);
return;
}
break;
case dns_rdataclass_any:
/*
* Required for TKEY negotiation.
*/
if (client->message->tkey == 0) {
ns_client_dumpmessage(client,
"message class ANY can be only "
"used for TKEY negotiation");
ns_client_error(client, DNS_R_FORMERR);
return;
}
break;
default:
dns_rdataclass_format(client->message->rdclass, classbuf,
sizeof(classbuf));
ns_client_dumpmessage(client, NULL);
ns_client_log(client, NS_LOGCATEGORY_CLIENT,
NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
"message class could not be determined");
ns_client_dumpmessage(client, "message class could not be "
"determined");
ns_client_error(client, notimp ? DNS_R_NOTIMP : DNS_R_FORMERR);
"invalid message class: %s", classbuf);
ns_client_error(client, DNS_R_NOTIMP);
return;
}
@ -2140,7 +2177,7 @@ ns__client_request(isc_nmhandle_t *handle, isc_result_t eresult,
ns_client_log(client, NS_LOGCATEGORY_CLIENT,
NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
"no matching view in class '%s'", classname);
ns_client_dumpmessage(client, "no matching view in class");
ns_client_dumpmessage(client, NULL);
ns_client_extendederror(client, DNS_EDE_PROHIBITED, NULL);
ns_client_error(client, notimp ? DNS_R_NOTIMP : DNS_R_REFUSED);
return;
@ -2781,7 +2818,7 @@ ns_client_dumpmessage(ns_client_t *client, const char *reason) {
int len = 1024;
isc_result_t result;
if (!isc_log_wouldlog(ns_lctx, ISC_LOG_DEBUG(1))) {
if (!isc_log_wouldlog(ns_lctx, ISC_LOG_DEBUG(1)) || reason == NULL) {
return;
}