]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
restrict RRSIGs claiming wildcard reach out of the zone [QZKF-005]
authorVladimír Čunát <vladimir.cunat@nic.cz>
Wed, 8 Jul 2026 13:25:10 +0000 (15:25 +0200)
committerVladimír Čunát <vladimir.cunat@nic.cz>
Wed, 22 Jul 2026 08:09:09 +0000 (10:09 +0200)
This issue was reported by Qifan Zhang from Palo Alto Networks.

Nit: this assignments of `ret` aren't important,
but they might be a bit useful when debugging.

(cherry picked from commit 29b55ceb426efaafcc955181637072a36a99486e)

lib/cache/api.c
lib/dnssec.c

index 73279039b120a2a1bb4a2bd94dec52744d83be07..ddcf9f46692ba3a44d33d1de633e59ed8222b439 100644 (file)
@@ -509,17 +509,39 @@ static ssize_t stash_rrset(struct kr_cache *cache, const struct kr_query *qry,
                return kr_error(EINVAL);
 
        int ret = kr_ok();
-       if (rrset_has_min_range_or_weird(rr, qry))
+       if (rrset_has_min_range_or_weird(rr, qry)) {
+               ret = kr_error(ERANGE);
                goto return_needs_pkt;
+       }
+
        const int wild_labels = rr_sigs == NULL ? 0 :
               knot_dname_labels(rr->owner, NULL) - knot_rrsig_labels(rr_sigs->rrs.rdata);
-       if (wild_labels < 0)
+       if (wild_labels < 0) {
+               ret = kr_error(ERANGE);
                goto return_needs_pkt;
+       }
        const knot_dname_t *encloser = rr->owner; /**< the closest encloser name */
        for (int i = 0; i < wild_labels; ++i) {
                encloser = knot_dname_next_label(encloser);
        }
 
+       /* Sanity-check that encloser didn't get too short,
+        * in particular not outside the zone which signed it.
+        * As we don't know which RRSIG succeeded, we take the maximum to be safe. */
+       if (rr_sigs && wild_labels) {
+               int signer_labels = 0;
+               const int count = rr_sigs->rrs.count;
+               knot_rdata_t *rd = rr_sigs->rrs.rdata;
+               for (int i = 0; i < count; ++i, rd = knot_rdataset_next(rd)) {
+                       int l = knot_dname_labels(knot_rrsig_signer_name(rd), NULL);
+                       signer_labels = MAX(signer_labels, l);
+               }
+               if (knot_dname_labels(encloser, NULL) < signer_labels) {
+                       ret = kr_error(ERANGE);
+                       goto return_needs_pkt;
+               }
+       }
+
        /* Construct the key under which RRs will be stored,
         * and add corresponding nsec_pmap item (if necessary). */
        struct key k_storage, *k = &k_storage;
index cad1e47eb483ec3403a1bb176efa0e18c8a758c1..4be6db61da1952a24f905a2635fdf4659eb66c45 100644 (file)
@@ -84,6 +84,11 @@ static int validate_rrsig_rr(int *flags, int cov_labels,
                if (rrsig_labels < cov_labels) {
                        *flags |= FLG_WILDCARD_EXPANSION;
                }
+               /* Also refuse if RRSIG claims rrsig_labels outside the zone. */
+               if (rrsig_labels < knot_dname_labels(signer_name, NULL)) {
+                       vctx->rrs_counters.labels_invalid++;
+                       return kr_error(EINVAL);
+               }
        }
 
        /* bullet 7