]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Fix "no DS" proofs for wildcard+CNAME delegations
authorMichał Kępień <michal@isc.org>
Thu, 10 Jun 2021 08:13:23 +0000 (10:13 +0200)
committerMichał Kępień <michal@isc.org>
Thu, 10 Jun 2021 08:13:23 +0000 (10:13 +0200)
When answering a query requires wildcard expansion, the AUTHORITY
section of the response needs to include NSEC(3) record(s) proving that
the QNAME does not exist.

When a response to a query is an insecure delegation, the AUTHORITY
section needs to include an NSEC(3) proof that no DS record exists at
the parent side of the zone cut.

These two conditions combined trip up the NSEC part of the logic
contained in query_addds(), which expects the NS RRset to be owned by
the first name found in the AUTHORITY section of a delegation response.
This may not always be true, for example if wildcard expansion causes an
NSEC record proving QNAME nonexistence to be added to the AUTHORITY
section before the delegation is added to the response.  In such a case,
named incorrectly omits the NSEC record proving nonexistence of QNAME
from the AUTHORITY section.

The same block of code is affected by another flaw: if the same NSEC
record proves nonexistence of both the QNAME and the DS record at the
parent side of the zone cut, this NSEC record will be added to the
AUTHORITY section twice.

Fix by looking for the NS RRset in the entire AUTHORITY section and
adding the NSEC record to the delegation using query_addrrset() (which
handles duplicate RRset detection).

lib/ns/query.c

index 749c748704bb3b4bf6c6142d2ebff3bf44f558b5..6639a754518cbc20b81b7842f676ef59479e4e66 100644 (file)
@@ -9017,26 +9017,40 @@ query_addds(query_ctx_t *qctx) {
 
        /*
         * We've already added the NS record, so if the name's not there,
-        * we have other problems.  Use this name rather than calling
-        * query_addrrset().
+        * we have other problems.
         */
        result = dns_message_firstname(client->message, DNS_SECTION_AUTHORITY);
        if (result != ISC_R_SUCCESS) {
                goto cleanup;
        }
 
-       rname = NULL;
-       dns_message_currentname(client->message, DNS_SECTION_AUTHORITY, &rname);
-       result = dns_message_findtype(rname, dns_rdatatype_ns, 0, NULL);
+       /*
+        * Find the delegation in the response message - it is not necessarily
+        * the first name in the AUTHORITY section when wildcard processing is
+        * involved.
+        */
+       while (result == ISC_R_SUCCESS) {
+               rname = NULL;
+               dns_message_currentname(client->message, DNS_SECTION_AUTHORITY,
+                                       &rname);
+               result = dns_message_findtype(rname, dns_rdatatype_ns, 0, NULL);
+               if (result == ISC_R_SUCCESS) {
+                       break;
+               }
+               result = dns_message_nextname(client->message,
+                                             DNS_SECTION_AUTHORITY);
+       }
+
        if (result != ISC_R_SUCCESS) {
                goto cleanup;
        }
 
-       ISC_LIST_APPEND(rname->list, rdataset, link);
-       ISC_LIST_APPEND(rname->list, sigrdataset, link);
-       rdataset = NULL;
-       sigrdataset = NULL;
-       return;
+       /*
+        * Add the NSEC record to the delegation.
+        */
+       query_addrrset(qctx, &rname, &rdataset, &sigrdataset, NULL,
+                      DNS_SECTION_AUTHORITY);
+       goto cleanup;
 
 addnsec3:
        if (!dns_db_iszone(qctx->db)) {