]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Validate DNS message CLASS early in request processing
authorOndřej Surý <ondrej@isc.org>
Wed, 4 Mar 2026 09:46:58 +0000 (10:46 +0100)
committerMichał Kępień <michal@isc.org>
Thu, 7 May 2026 11:32:15 +0000 (13:32 +0200)
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.

bin/tests/system/unknown/tests.sh
lib/ns/client.c

index eb61f21f287ded95e883d395c029b541de929e4e..cbc2943f174f181fb0f528920e830e31e90d6407 100644 (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
index 2faf971041f82298df8d7236f575005377c3263c..7a48d613af31037fe05353bce809dc60c2e618d5 100644 (file)
@@ -42,6 +42,7 @@
 #include <dns/dispatch.h>
 #include <dns/dnstap.h>
 #include <dns/edns.h>
+#include <dns/enumclass.h>
 #include <dns/message.h>
 #include <dns/peer.h>
 #include <dns/rcode.h>
@@ -2082,7 +2083,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->inner.attributes & NS_CLIENTATTR_WANTCOOKIE) !=
                            0 &&
                    client->message->opcode == dns_opcode_query &&
@@ -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;
        }
 
@@ -2192,9 +2229,6 @@ ns_client_request_continue(void *arg) {
                                      "SIG(0) checks quota reached");
 
                        if (can_log_sigchecks_quota()) {
-                               ns_client_log(client, NS_LOGCATEGORY_CLIENT,
-                                             NS_LOGMODULE_CLIENT, ISC_LOG_INFO,
-                                             "SIG(0) checks quota reached");
                                ns_client_dumpmessage(
                                        client, "SIG(0) checks quota reached");
                        }
@@ -2204,12 +2238,11 @@ ns_client_request_continue(void *arg) {
                        dns_rdataclass_format(client->message->rdclass,
                                              classname, sizeof(classname));
 
+                       ns_client_dumpmessage(client, NULL);
                        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");
                }
 
                dns_ede_add(&client->edectx, DNS_EDE_PROHIBITED, NULL);
@@ -2843,7 +2876,7 @@ ns_client_dumpmessage(ns_client_t *client, const char *reason) {
        int len = 1024;
        isc_result_t result;
 
-       if (!isc_log_wouldlog(ISC_LOG_DEBUG(1))) {
+       if (!isc_log_wouldlog(ISC_LOG_DEBUG(1)) || reason == NULL) {
                return;
        }