]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- For #1405: local-zone always_refuse also blocks queries of type DS.
authorW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Tue, 17 Feb 2026 14:36:08 +0000 (15:36 +0100)
committerW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Tue, 17 Feb 2026 14:36:08 +0000 (15:36 +0100)
doc/Changelog
doc/unbound.conf.rst
services/localzone.c
services/localzone.h

index 0b03ca3ed7f09c88b1a930d55587a821a3abf69a..6ac6a1038998f00f1009ea7e301b8f13978e6d05 100644 (file)
@@ -1,6 +1,7 @@
 17 February 2026: Wouter
        - Fix to remove unused conditional from cookie timestamp at
          worker env.
+       - For #1405: local-zone always_refuse also blocks queries of type DS.
 
 16 February 2026: Yorgos
        - Fix #1404: Priming the root key fails after loading ipfire.org RPZ
index e53282ee60d795c4e314b4df47a980b3c199ee05..817ebd317c763efd3f6d1fa5f9ee88de9e3e1532 100644 (file)
@@ -2704,6 +2704,9 @@ These options are part of the ``server:`` section.
     @@UAHL@unbound.conf.local-zone.type@always_refuse@@
         Like :ref:`refuse<unbound.conf.local-zone.type.refuse>`, but ignores
         local data and refuses the query.
+        This type also blocks queries of type DS for the zone name.
+        That can break the DNSSEC chain of trust, but it is refused anyway.
+        The block for type DS assists in more completely blocking the zone.
 
     @@UAHL@unbound.conf.local-zone.type@always_nxdomain@@
         Like :ref:`static<unbound.conf.local-zone.type.static>`, but ignores
index 9ea98c250907a03f2fc3e025e4febc62b1cfe9ab..ccbe0d522a6a061503041f8c2f95993d3e30606c 100644 (file)
@@ -650,7 +650,7 @@ lz_enter_rr_str(struct local_zones* zones, const char* rr)
        }
        labs = dname_count_size_labels(rr_name, &len);
        lock_rw_rdlock(&zones->lock);
-       z = local_zones_lookup(zones, rr_name, len, labs, rr_class, rr_type);
+       z = local_zones_lookup(zones, rr_name, len, labs, rr_class, rr_type, 1);
        if(!z) {
                lock_rw_unlock(&zones->lock);
                fatal_exit("internal error: no zone for rr %s", rr);
@@ -1062,14 +1062,15 @@ lz_setup_implicit(struct local_zones* zones, struct config_file* cfg)
                labs = dname_count_size_labels(rr_name, &len);
                lock_rw_rdlock(&zones->lock);
                if(!local_zones_lookup(zones, rr_name, len, labs, rr_class,
-                       rr_type)) {
+                       rr_type, 1)) {
                        /* Check if there is a zone that this could go
                         * under but for different class; created zones are
                         * always for LDNS_RR_CLASS_IN. Create the zone with
                         * a different class but the same configured
                         * local_zone_type. */
                        struct local_zone* z = local_zones_lookup(zones,
-                               rr_name, len, labs, LDNS_RR_CLASS_IN, rr_type);
+                               rr_name, len, labs, LDNS_RR_CLASS_IN, rr_type,
+                               1);
                        if(z) {
                                uint8_t* name = memdup(z->name, z->namelen);
                                size_t znamelen = z->namelen;
@@ -1231,28 +1232,48 @@ local_zones_apply_cfg(struct local_zones* zones, struct config_file* cfg)
 
 struct local_zone* 
 local_zones_lookup(struct local_zones* zones,
-        uint8_t* name, size_t len, int labs, uint16_t dclass, uint16_t dtype)
+        uint8_t* name, size_t len, int labs, uint16_t dclass, uint16_t dtype,
+       int foradd)
 {
        return local_zones_tags_lookup(zones, name, len, labs,
-               dclass, dtype, NULL, 0, 1);
+               dclass, dtype, NULL, 0, 1, foradd);
 }
 
 struct local_zone* 
 local_zones_tags_lookup(struct local_zones* zones,
         uint8_t* name, size_t len, int labs, uint16_t dclass, uint16_t dtype,
-       uint8_t* taglist, size_t taglen, int ignoretags)
+       uint8_t* taglist, size_t taglen, int ignoretags, int foradd)
 {
        rbnode_type* res = NULL;
        struct local_zone *result;
        struct local_zone key;
        int m;
+       key.node.key = &key;
+       key.dclass = dclass;
        /* for type DS use a zone higher when on a zonecut */
        if(dtype == LDNS_RR_TYPE_DS && !dname_is_root(name)) {
-               dname_remove_label(&name, &len);
-               labs--;
+               /* If this is at a zone cut, of a local-zone, and it is
+                * of type always_refuse. Then also refuse the type DS
+                * for it. That could make it DNSSEC bogus, but it is
+                * REFUSED anyway. It stops CNAME type answers in the
+                * type DS lookup. */
+               key.name = name;
+               key.namelen = len;
+               key.namelabs = labs;
+               /* For additions and removals, use the ordinary rule,
+                * to remove a label for type DS to locate the parent zone.
+                * That is where the DS RR needs to be put. */
+               if(!foradd &&
+                       (result=(struct local_zone*)rbtree_search(
+                       &zones->ztree, &key)) != NULL &&
+                       result->type == local_zone_always_refuse) {
+                       /* The type DS does not go up one label. */
+                       return result;
+               } else {
+                       dname_remove_label(&name, &len);
+                       labs--;
+               }
        }
-       key.node.key = &key;
-       key.dclass = dclass;
        key.name = name;
        key.namelen = len;
        key.namelabs = labs;
@@ -1863,7 +1884,7 @@ local_zones_answer(struct local_zones* zones, struct module_env* env,
                if(view->local_zones &&
                        (z = local_zones_lookup(view->local_zones,
                        qinfo->qname, qinfo->qname_len, labs,
-                       qinfo->qclass, qinfo->qtype))) {
+                       qinfo->qclass, qinfo->qtype, 0))) {
                        lock_rw_rdlock(&z->lock);
                        lzt = z->type;
                }
@@ -1897,7 +1918,7 @@ local_zones_answer(struct local_zones* zones, struct module_env* env,
                lock_rw_rdlock(&zones->lock);
                if(!(z = local_zones_tags_lookup(zones, qinfo->qname,
                        qinfo->qname_len, labs, qinfo->qclass, qinfo->qtype,
-                       taglist, taglen, 0))) {
+                       taglist, taglen, 0, 0))) {
                        lock_rw_unlock(&zones->lock);
                        return 0;
                }
@@ -2102,7 +2123,8 @@ local_zones_add_RR(struct local_zones* zones, const char* rr)
        /* could first try readlock then get writelock if zone does not exist,
         * but we do not add enough RRs (from multiple threads) to optimize */
        lock_rw_wrlock(&zones->lock);
-       z = local_zones_lookup(zones, rr_name, len, labs, rr_class, rr_type);
+       z = local_zones_lookup(zones, rr_name, len, labs, rr_class, rr_type,
+               1);
        if(!z) {
                z = local_zones_add_zone(zones, rr_name, len, labs, rr_class,
                        local_zone_transparent);
@@ -2180,7 +2202,8 @@ void local_zones_del_data(struct local_zones* zones,
 
        /* remove DS */
        lock_rw_rdlock(&zones->lock);
-       z = local_zones_lookup(zones, name, len, labs, dclass, LDNS_RR_TYPE_DS);
+       z = local_zones_lookup(zones, name, len, labs, dclass, LDNS_RR_TYPE_DS,
+               1);
        if(z) {
                lock_rw_wrlock(&z->lock);
                d = local_zone_find_data(z, name, len, labs);
@@ -2194,7 +2217,7 @@ void local_zones_del_data(struct local_zones* zones,
 
        /* remove other types */
        lock_rw_rdlock(&zones->lock);
-       z = local_zones_lookup(zones, name, len, labs, dclass, 0);
+       z = local_zones_lookup(zones, name, len, labs, dclass, 0, 1);
        if(!z) {
                /* no such zone, we're done */
                lock_rw_unlock(&zones->lock);
index 66102fd98f7e72de4bb7ff3978c6a8f8e51d5a68..3dc89b05882bbcb5d1967fb86c51c82a0501b310 100644 (file)
@@ -262,11 +262,13 @@ void local_zone_delete(struct local_zone* z);
  * @param taglen: length of taglist.
  * @param ignoretags: lookup zone by name and class, regardless the
  * local-zone's tags.
+ * @param foradd: if the lookup is for addition or removal of the type.
+ *     Used for type DS. The lookup for answers turns this off.
  * @return closest local_zone or NULL if no covering zone is found.
  */
 struct local_zone* local_zones_tags_lookup(struct local_zones* zones, 
        uint8_t* name, size_t len, int labs, uint16_t dclass, uint16_t dtype,
-       uint8_t* taglist, size_t taglen, int ignoretags);
+       uint8_t* taglist, size_t taglen, int ignoretags, int foradd);
 
 /**
  * Lookup zone that contains the given name, class.
@@ -278,10 +280,13 @@ struct local_zone* local_zones_tags_lookup(struct local_zones* zones,
  * @param dclass: class to lookup.
  * @param dtype: type of the record, if type DS then a zone higher up is found
  *   pass 0 to just plain find a zone for a name.
+ * @param foradd: if the lookup is for addition or removal of the type.
+ *     Used for type DS. The lookup for answers turns this off.
  * @return closest local_zone or NULL if no covering zone is found.
  */
 struct local_zone* local_zones_lookup(struct local_zones* zones, 
-       uint8_t* name, size_t len, int labs, uint16_t dclass, uint16_t dtype);
+       uint8_t* name, size_t len, int labs, uint16_t dclass, uint16_t dtype,
+       int foradd);
 
 /**
  * Debug helper. Print all zones