]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Add support for O(1) ACL processing, based on radix tree code originally
authorEvan Hunt <each@isc.org>
Wed, 12 Sep 2007 01:09:08 +0000 (01:09 +0000)
committerEvan Hunt <each@isc.org>
Wed, 12 Sep 2007 01:09:08 +0000 (01:09 +0000)
written by kevin brintnall. [RT #16288]

17 files changed:
CHANGES
bin/named/controlconf.c
bin/named/interfacemgr.c
bin/named/server.c
bin/named/sortlist.c
bin/named/zoneconf.c
doc/arm/Bv9ARM-book.xml
lib/bind9/check.c
lib/dns/Makefile.in
lib/dns/acl.c
lib/dns/include/dns/Makefile.in
lib/dns/include/dns/acl.h
lib/dns/include/dns/types.h
lib/dns/zone.c
lib/isc/Makefile.in
lib/isccfg/aclconf.c
lib/isccfg/include/isccfg/aclconf.h

diff --git a/CHANGES b/CHANGES
index c1dd2d879621a65cbcd3a69b848fbb1c086b7409..15569557161163529861bb6049e2e195bbbbde30 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,7 @@
+2233.   [func]          Add support for O(1) ACL processing, based on
+                        radix tree code originally written by kevin
+                        brintnall. [RT #16288]
+
 2232.  [bug]           dns_adb_findaddrinfo() could fail and return
                        ISC_R_SUCCESS. [RT #17137]
 
index 30cde422a90e8ea5660592d683bfdbd9dc41df98..741467cc17987e4bfec96f6e033241e442f2c913 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: controlconf.c,v 1.54 2007/06/18 23:47:18 tbox Exp $ */
+/* $Id: controlconf.c,v 1.55 2007/09/12 01:09:07 each Exp $ */
 
 /*! \file */
 
@@ -1014,7 +1014,7 @@ update_listener(ns_controls_t *cp, controllistener_t **listenerp,
        if (control != NULL && type == isc_sockettype_tcp) {
                allow = cfg_tuple_get(control, "allow");
                result = cfg_acl_fromconfig(allow, config, ns_g_lctx,
-                                           aclconfctx, listener->mctx,
+                                           aclconfctx, listener->mctx, 0,
                                            &new_acl);
        } else {
                result = dns_acl_any(listener->mctx, &new_acl);
@@ -1101,7 +1101,8 @@ add_listener(ns_controls_t *cp, controllistener_t **listenerp,
                if (control != NULL && type == isc_sockettype_tcp) {
                        allow = cfg_tuple_get(control, "allow");
                        result = cfg_acl_fromconfig(allow, config, ns_g_lctx,
-                                                   aclconfctx, mctx, &new_acl);
+                                                   aclconfctx, mctx, 0,
+                                                    &new_acl);
                } else {
                        result = dns_acl_any(mctx, &new_acl);
                }
index 9f11581e1ee9ee17e9db8682610b3c9b10952f83..47860214c49eb3a392a9d44e7c37c5e4377d33db 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: interfacemgr.c,v 1.89 2007/06/18 23:47:18 tbox Exp $ */
+/* $Id: interfacemgr.c,v 1.90 2007/09/12 01:09:07 each Exp $ */
 
 /*! \file */
 
@@ -483,7 +483,7 @@ static isc_result_t
 clearacl(isc_mem_t *mctx, dns_acl_t **aclp) {
        dns_acl_t *newacl = NULL;
        isc_result_t result;
-       result = dns_acl_create(mctx, 10, &newacl);
+       result = dns_acl_create(mctx, 0, &newacl);
        if (result != ISC_R_SUCCESS)
                return (result);
        dns_acl_detach(aclp);
@@ -494,36 +494,31 @@ clearacl(isc_mem_t *mctx, dns_acl_t **aclp) {
 
 static isc_boolean_t
 listenon_is_ip6_any(ns_listenelt_t *elt) {
-       if (elt->acl->length != 1)
-               return (ISC_FALSE);
-       if (elt->acl->elements[0].negative == ISC_FALSE &&
-           elt->acl->elements[0].type == dns_aclelementtype_any)
-               return (ISC_TRUE);  /* listen-on-v6 { any; } */
-       return (ISC_FALSE); /* All others */
+        REQUIRE(elt && elt->acl);
+        return dns_acl_isany(elt->acl);
 }
 
 static isc_result_t
 setup_locals(ns_interfacemgr_t *mgr, isc_interface_t *interface) {
        isc_result_t result;
-       dns_aclelement_t elt;
-       unsigned int family;
        unsigned int prefixlen;
+        isc_netaddr_t *netaddr;
 
-       family = interface->address.family;
+        netaddr = &interface->address;
        
-       elt.type = dns_aclelementtype_ipprefix;
-       elt.negative = ISC_FALSE;
-       elt.u.ip_prefix.address = interface->address;
-       elt.u.ip_prefix.prefixlen = (family == AF_INET) ? 32 : 128;
-       result = dns_acl_appendelement(mgr->aclenv.localhost, &elt);
+        /* First add localhost address */
+       prefixlen = (netaddr->family == AF_INET) ? 32 : 128;
+        result = dns_iptable_addprefix(mgr->aclenv.localhost->iptable,
+                                       netaddr, prefixlen, ISC_TRUE);
        if (result != ISC_R_SUCCESS)
                return (result);
 
+        /* Then add localnets prefix */
        result = isc_netaddr_masktoprefixlen(&interface->netmask,
                                             &prefixlen);
 
        /* Non contigious netmasks not allowed by IPv6 arch. */
-       if (result != ISC_R_SUCCESS && family == AF_INET6)
+       if (result != ISC_R_SUCCESS && netaddr->family == AF_INET6)
                return (result);
 
        if (result != ISC_R_SUCCESS) {
@@ -533,17 +528,14 @@ setup_locals(ns_interfacemgr_t *mgr, isc_interface_t *interface) {
                              "localnets ACL: %s",
                              interface->name,
                              isc_result_totext(result));
-       } else {
-               elt.u.ip_prefix.prefixlen = prefixlen;
-               if (dns_acl_elementmatch(mgr->aclenv.localnets, &elt,
-                                        NULL) == ISC_R_NOTFOUND) {
-                       result = dns_acl_appendelement(mgr->aclenv.localnets,
-                                                      &elt);
-                       if (result != ISC_R_SUCCESS)
-                               return (result);
-               }
+               return (ISC_R_SUCCESS);
        }
 
+        result = dns_iptable_addprefix(mgr->aclenv.localnets->iptable,
+                                       netaddr, prefixlen, ISC_TRUE);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
        return (ISC_R_SUCCESS);
 }
 
index af7d96804874237c2f955dc69b6eb50e0aa7ebe9..f04f4e21ccf032b2b39ecbdf7683fa428941b6db 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: server.c,v 1.489 2007/07/09 02:12:42 marka Exp $ */
+/* $Id: server.c,v 1.490 2007/09/12 01:09:07 each Exp $ */
 
 /*! \file */
 
@@ -307,7 +307,51 @@ configure_view_acl(const cfg_obj_t *vconfig, const cfg_obj_t *config,
                return (ISC_R_SUCCESS);
 
        result = cfg_acl_fromconfig(aclobj, config, ns_g_lctx,
-                                   actx, mctx, aclp);
+                                   actx, mctx, 0, aclp);
+
+       return (result);
+}
+
+
+/*%
+ * Configure a sortlist at '*aclp'.  Essentially the same as
+ * configure_view_acl() except it calls cfg_acl_fromconfig with a
+ * nest_level value of 2.
+ */
+static isc_result_t
+configure_view_sortlist(const cfg_obj_t *vconfig, const cfg_obj_t *config,
+                       cfg_aclconfctx_t *actx, isc_mem_t *mctx,
+                        dns_acl_t **aclp)
+{
+       isc_result_t result;
+       const cfg_obj_t *maps[3];
+       const cfg_obj_t *aclobj = NULL;
+       int i = 0;
+
+       if (*aclp != NULL)
+               dns_acl_detach(aclp);
+       if (vconfig != NULL)
+               maps[i++] = cfg_tuple_get(vconfig, "options");
+       if (config != NULL) {
+               const cfg_obj_t *options = NULL;
+               (void)cfg_map_get(config, "options", &options);
+               if (options != NULL)
+                       maps[i++] = options;
+       }
+       maps[i] = NULL;
+
+       (void)ns_config_get(maps, "sortlist", &aclobj);
+       if (aclobj == NULL)
+               return (ISC_R_SUCCESS);
+
+        /*
+         * Use a nest level of 2 for the "top level" of the sortlist;
+         * this means each entry in the top two levels will be stored as
+         * lists of separate, nested ACLs, rather than merged together
+         * into IP tables as is usually done with ACLs.
+         */
+       result = cfg_acl_fromconfig(aclobj, config, ns_g_lctx,
+                                   actx, mctx, 2, aclp);
 
        return (result);
 }
@@ -1598,8 +1642,11 @@ configure_view(dns_view_t *view, const cfg_obj_t *config,
                                         "allow-query-cache", actx,
                                         ns_g_mctx, &view->queryacl));
 
-       CHECK(configure_view_acl(vconfig, config, "sortlist",
-                                actx, ns_g_mctx, &view->sortlist));
+        /*
+         * Configure sortlist, if set
+         */
+       CHECK(configure_view_sortlist(vconfig, config, actx, ns_g_mctx,
+                                      &view->sortlist));
 
        obj = NULL;
        result = ns_config_get(maps, "request-ixfr", &obj);
@@ -2490,25 +2537,23 @@ add_listenelt(isc_mem_t *mctx, ns_listenlist_t *list, isc_sockaddr_t *addr,
 {
        ns_listenelt_t *lelt = NULL;
        dns_acl_t *src_acl = NULL;
-       dns_aclelement_t aelt;
        isc_result_t result;
        isc_sockaddr_t any_sa6;
+        isc_netaddr_t netaddr;
 
        REQUIRE(isc_sockaddr_pf(addr) == AF_INET6);
 
        isc_sockaddr_any6(&any_sa6);
        if (!isc_sockaddr_equal(&any_sa6, addr) &&
            (wcardport_ok || isc_sockaddr_getport(addr) != 0)) {
-               aelt.type = dns_aclelementtype_ipprefix;
-               aelt.negative = ISC_FALSE;
-               aelt.u.ip_prefix.prefixlen = 128;
-               isc_netaddr_fromin6(&aelt.u.ip_prefix.address,
-                                   &addr->type.sin6.sin6_addr);
-
-               result = dns_acl_create(mctx, 1, &src_acl);
+               isc_netaddr_fromin6(&netaddr, &addr->type.sin6.sin6_addr);
+  
+               result = dns_acl_create(mctx, 0, &src_acl);
                if (result != ISC_R_SUCCESS)
                        return (result);
-               result = dns_acl_appendelement(src_acl, &aelt);
+
+                result = dns_iptable_addprefix(src_acl->iptable,
+                                               &netaddr, 128, ISC_TRUE);
                if (result != ISC_R_SUCCESS)
                        goto clean;
 
@@ -4391,7 +4436,8 @@ ns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
                return (result);
 
        result = cfg_acl_fromconfig(cfg_tuple_get(listener, "acl"),
-                                  config, ns_g_lctx, actx, mctx, &delt->acl);
+                                  config, ns_g_lctx, actx, mctx, 0,
+                                   &delt->acl);
        if (result != ISC_R_SUCCESS) {
                ns_listenelt_destroy(delt);
                return (result);
index 372002eb1061936806ccd85aeed76eea3336d286..8004b175f34b603882c049f461114bfc0630b9ca 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: sortlist.c,v 1.15 2007/06/19 23:46:59 tbox Exp $ */
+/* $Id: sortlist.c,v 1.16 2007/09/12 01:09:07 each Exp $ */
 
 /*! \file */
 
@@ -51,7 +51,7 @@ ns_sortlist_setup(dns_acl_t *acl, isc_netaddr_t *clientaddr,
                const dns_aclelement_t *matched_elt = NULL;
 
                if (e->type == dns_aclelementtype_nestedacl) {
-                       dns_acl_t *inner = e->u.nestedacl;
+                       dns_acl_t *inner = e->nestedacl;
 
                        if (inner->length < 1 || inner->length > 2)
                                goto dont_sort;
@@ -74,7 +74,7 @@ ns_sortlist_setup(dns_acl_t *acl, isc_netaddr_t *clientaddr,
                        if (order_elt != NULL) {
                                if (order_elt->type ==
                                    dns_aclelementtype_nestedacl) {
-                                       *argp = order_elt->u.nestedacl;
+                                       *argp = order_elt->nestedacl;
                                        return (NS_SORTLISTTYPE_2ELEMENT);
                                } else if (order_elt->type ==
                                           dns_aclelementtype_localhost &&
index 92974c0013208717b133aedaad309c2dff2ccf07..bdc535ae868e4a47936e3d1cbca5e29b08b2439b 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: zoneconf.c,v 1.137 2007/06/19 23:46:59 tbox Exp $ */
+/* $Id: zoneconf.c,v 1.138 2007/09/12 01:09:07 each Exp $ */
 
 /*% */
 
@@ -90,7 +90,7 @@ configure_zone_acl(const cfg_obj_t *zconfig, const cfg_obj_t *vconfig,
        }
 
        result = cfg_acl_fromconfig(aclobj, config, ns_g_lctx, actx,
-                                   dns_zone_getmctx(zone), &dacl);
+                                   dns_zone_getmctx(zone), 0, &dacl);
        if (result != ISC_R_SUCCESS)
                return (result);
        (*setzacl)(zone, dacl);
index 2e8566ab80bfca1d20e235b34d50ef901b88ab74..a2448e9e5fd4b6b495efc8d47f0b510a75b7251e 100644 (file)
@@ -18,7 +18,7 @@
  - PERFORMANCE OF THIS SOFTWARE.
 -->
 
-<!-- File: $Id: Bv9ARM-book.xml,v 1.333 2007/09/07 07:29:22 marka Exp $ -->
+<!-- File: $Id: Bv9ARM-book.xml,v 1.334 2007/09/12 01:09:08 each Exp $ -->
 <book xmlns:xi="http://www.w3.org/2001/XInclude">
   <title>BIND 9 Administrator Reference Manual</title>
 
@@ -3023,9 +3023,8 @@ $ORIGIN 0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.
             Address match lists are primarily used to determine access
             control for various server operations. They are also used in
             the <command>listen-on</command> and <command>sortlist</command>
-            statements. The elements
-            which constitute an address match list can be any of the
-            following:
+            statements. The elements which constitute an address match
+            list can be any of the following:
           </para>
           <itemizedlist>
             <listitem>
@@ -3053,28 +3052,30 @@ $ORIGIN 0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.
           <para>
             Elements can be negated with a leading exclamation mark (`!'),
             and the match list names "any", "none", "localhost", and
-            "localnets"
-            are predefined. More information on those names can be found in
-            the description of the acl statement.
+            "localnets" are predefined. More information on those names
+            can be found in the description of the acl statement.
           </para>
 
           <para>
             The addition of the key clause made the name of this syntactic
             element something of a misnomer, since security keys can be used
             to validate access without regard to a host or network address.
-            Nonetheless,
-            the term "address match list" is still used throughout the
-            documentation.
+            Nonetheless, the term "address match list" is still used
+            throughout the documentation.
           </para>
 
           <para>
             When a given IP address or prefix is compared to an address
-            match list, the list is traversed in order until an element
-            matches.
+            match list, the comparison takes place in approximately O(1)
+            time.  However, key comparisons require that the list of keys
+            be traversed until a matching key is found, and therefore may
+            be somewhat slower.
+          </para>
+
+          <para>
             The interpretation of a match depends on whether the list is being
-            used
-            for access control, defining listen-on ports, or in a sortlist,
-            and whether the element was negated.
+            used for access control, defining listen-on ports, or in a
+            sortlist, and whether the element was negated.
           </para>
 
          <para>
@@ -3093,23 +3094,25 @@ $ORIGIN 0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.
            <command>allow-update-forwarding</command>, and
            <command>blackhole</command> all use address match
            lists.  Similarly, the listen-on option will cause the
-           server to not accept queries on any of the machine's
+           server to refuse queries on any of the machine's
            addresses which do not match the list.
          </para>
 
           <para>
-            Because of the first-match aspect of the algorithm, an element
-            that defines a subset of another element in the list should come
-            before the broader element, regardless of whether either is
-            negated. For
-            example, in
-            <command>1.2.3/24; ! 1.2.3.13;</command> the 1.2.3.13
-            element is
-            completely useless because the algorithm will match any lookup for
-            1.2.3.13 to the 1.2.3/24 element.
-            Using <command>! 1.2.3.13; 1.2.3/24</command> fixes
-            that problem by having 1.2.3.13 blocked by the negation but all
-            other 1.2.3.* hosts fall through.
+            Order of insertion is signficant.  If more than one element
+            in an ACL is found to match a given IP address or prefix,
+            preference will be given to the one that came
+            <emphasis>first</emphasis> in the ACL definition.
+            Because of this first-match behavior, an element that
+            defines a subset of another element in the list should
+            come before the broader element, regardless of whether
+            either is negated. For example, in
+            <command>1.2.3/24; ! 1.2.3.13;</command>
+            the 1.2.3.13 element is completely useless because the
+            algorithm will match any lookup for 1.2.3.13 to the 1.2.3/24
+            element.  Using <command>! 1.2.3.13; 1.2.3/24</command> fixes
+            that problem by having 1.2.3.13 blocked by the negation, but
+            all other 1.2.3.* hosts fall through.
           </para>
         </sect3>
       </sect2>
@@ -3390,8 +3393,7 @@ $ORIGIN 0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.
         <para>
           Note that an address match list's name must be defined
           with <command>acl</command> before it can be used
-          elsewhere; no
-          forward references are allowed.
+          elsewhere; no forward references are allowed.
         </para>
 
         <para>
index 0beeb70a0a44e35a825cf549c2f9f53c5c5c0900..7d20a003b4b31360e4172cf8c7e108e9a4613c89 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: check.c,v 1.81 2007/08/29 03:23:46 marka Exp $ */
+/* $Id: check.c,v 1.82 2007/09/12 01:09:08 each Exp $ */
 
 /*! \file */
 
@@ -379,7 +379,8 @@ checkacl(const char *aclname, cfg_aclconfctx_t *actx, const cfg_obj_t *zconfig,
        }
        if (aclobj == NULL)
                return (ISC_R_SUCCESS);
-       result = cfg_acl_fromconfig(aclobj, config, logctx, actx, mctx, &acl);
+       result = cfg_acl_fromconfig(aclobj, config, logctx,
+                                    actx, mctx, 0, &acl);
        if (acl != NULL)
                dns_acl_detach(&acl);
        return (result);
@@ -459,7 +460,7 @@ check_recursionacls(cfg_aclconfctx_t *actx, const cfg_obj_t *voptions,
                        continue;
 
                tresult = cfg_acl_fromconfig(aclobj, config, logctx,
-                                           actx, mctx, &acl);
+                                           actx, mctx, 0, &acl);
 
                if (tresult != ISC_R_SUCCESS)
                        result = tresult;  
@@ -1932,7 +1933,7 @@ bind9_check_controls(const cfg_obj_t *config, isc_log_t *logctx,
                        control = cfg_listelt_value(element2);
                        allow = cfg_tuple_get(control, "allow");
                        tresult = cfg_acl_fromconfig(allow, config, logctx,
-                                                    &actx, mctx, &acl);
+                                                    &actx, mctx, 0, &acl);
                        if (acl != NULL)
                                dns_acl_detach(&acl);
                        if (tresult != ISC_R_SUCCESS)
@@ -2114,8 +2115,9 @@ bind9_check_namedconf(const cfg_obj_t *config, isc_log_t *logctx,
                }
        }
 
-        tresult = cfg_map_get(config, "acl", &acls);
-        if (tresult == ISC_R_SUCCESS) {
+        cfg_map_get(config, "acl", &acls);
+
+        if (acls != NULL) {
                const cfg_listelt_t *elt;
                const cfg_listelt_t *elt2;
                const char *aclname;
@@ -2124,6 +2126,7 @@ bind9_check_namedconf(const cfg_obj_t *config, isc_log_t *logctx,
                     elt != NULL;
                     elt = cfg_list_next(elt)) {
                        const cfg_obj_t *acl = cfg_listelt_value(elt);
+                       unsigned int line = cfg_obj_line(acl);
                        unsigned int i;
 
                        aclname = cfg_obj_asstring(cfg_tuple_get(acl, "name"));
@@ -2148,7 +2151,6 @@ bind9_check_namedconf(const cfg_obj_t *config, isc_log_t *logctx,
                                                                      "name"));
                                if (strcasecmp(aclname, name) == 0) {
                                        const char *file = cfg_obj_file(acl);
-                                       unsigned int line = cfg_obj_line(acl);
 
                                        if (file == NULL)
                                                file = "<unknown file>";
index bb4af14331e5a7a89f106da6e70e5b390d027823..44f294f44007e53369b303a396e10fb5808b51ec 100644 (file)
@@ -13,7 +13,7 @@
 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 
-# $Id: Makefile.in,v 1.157 2007/06/19 23:47:16 tbox Exp $
+# $Id: Makefile.in,v 1.158 2007/09/12 01:09:08 each Exp $
 
 srcdir =       @srcdir@
 VPATH =                @srcdir@
@@ -53,8 +53,8 @@ DSTOBJS =     @DST_EXTRA_OBJS@ \
 DNSOBJS =      acache.@O@ acl.@O@ adb.@O@ byaddr.@O@ \
                cache.@O@ callbacks.@O@ compress.@O@ \
                db.@O@ dbiterator.@O@ dbtable.@O@ diff.@O@ dispatch.@O@ \
-               dlz.@O@ dnssec.@O@ ds.@O@ forward.@O@ journal.@O@ keytable.@O@ \
-               lib.@O@ log.@O@ lookup.@O@ \
+               dlz.@O@ dnssec.@O@ ds.@O@ forward.@O@ iptable.@O@ journal.@O@ \
+               keytable.@O@ lib.@O@ log.@O@ lookup.@O@ \
                master.@O@ masterdump.@O@ message.@O@ \
                name.@O@ ncache.@O@ nsec.@O@ order.@O@ peer.@O@ portlist.@O@ \
                rbt.@O@ rbtdb.@O@ rbtdb64.@O@ rcode.@O@ rdata.@O@ \
@@ -79,8 +79,8 @@ DSTSRCS =     @DST_EXTRA_SRCS@ \
 DNSSRCS =      acache.c acl.c adb.c byaddr.c \
                cache.c callbacks.c compress.c \
                db.c dbiterator.c dbtable.c diff.c dispatch.c \
-               dlz.c dnssec.c ds.c forward.c journal.c keytable.c \
-               lib.c log.c lookup.c \
+               dlz.c dnssec.c ds.c forward.c iptable.c journal.c \
+               keytable.c lib.c log.c lookup.c \
                master.c masterdump.c message.c \
                name.c ncache.c nsec.c order.c peer.c portlist.c \
                rbt.c rbtdb.c rbtdb64.c rcode.c rdata.c \
index 5a3791083404733d290b3538bf9f98a33a8c2747..f43ccba0beb77df981f5bb04dbfcbb3e703f80c7 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: acl.c,v 1.32 2007/06/19 23:47:16 tbox Exp $ */
+/* $Id: acl.c,v 1.33 2007/09/12 01:09:08 each Exp $ */
 
 /*! \file */
 
 #include <isc/util.h>
 
 #include <dns/acl.h>
+#include <dns/iptable.h>
 
+/*
+ * Create a new ACL, including an IP table and an array with room
+ * for 'n' ACL elements.  The elements are uninitialized and the
+ * length is 0.
+ */
 isc_result_t
 dns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target) {
        isc_result_t result;
@@ -43,11 +49,19 @@ dns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target) {
                return (ISC_R_NOMEMORY);
        acl->mctx = mctx;
        acl->name = NULL;
+
        result = isc_refcount_init(&acl->refcount, 1);
        if (result != ISC_R_SUCCESS) {
                isc_mem_put(mctx, acl, sizeof(*acl));
                return (result);
        }
+
+        result = dns_iptable_create(mctx, &acl->iptable);
+       if (result != ISC_R_SUCCESS) {
+               isc_mem_put(mctx, acl, sizeof(*acl));
+               return (result);
+       }
+        
        acl->elements = NULL;
        acl->alloc = 0;
        acl->length = 0;
@@ -73,111 +87,234 @@ dns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target) {
        return (result);
 }
 
-isc_result_t
-dns_acl_appendelement(dns_acl_t *acl, const dns_aclelement_t *elt) {
-       if (acl->length + 1 > acl->alloc) {
-               /*
-                * Resize the ACL.
-                */
-               unsigned int newalloc;
-               void *newmem;
-
-               newalloc = acl->alloc * 2;
-               if (newalloc < 4)
-                       newalloc = 4;
-               newmem = isc_mem_get(acl->mctx,
-                                    newalloc * sizeof(dns_aclelement_t));
-               if (newmem == NULL)
-                       return (ISC_R_NOMEMORY);
-               memcpy(newmem, acl->elements,
-                      acl->length * sizeof(dns_aclelement_t));
-               isc_mem_put(acl->mctx, acl->elements,
-                           acl->alloc * sizeof(dns_aclelement_t));
-               acl->elements = newmem;
-               acl->alloc = newalloc;
-       }
-       /*
-        * Append the new element.
-        */
-       acl->elements[acl->length++] = *elt;
-
-       return (ISC_R_SUCCESS);
-}
-
+/*
+ * Create a new ACL and initialize it with the value "any" or "none",
+ * depending on the value of the "neg" parameter.
+ * "any" is a positive iptable entry with bit length 0.
+ * "none" is the same as "!any".
+ */
 static isc_result_t
 dns_acl_anyornone(isc_mem_t *mctx, isc_boolean_t neg, dns_acl_t **target) {
        isc_result_t result;
        dns_acl_t *acl = NULL;
-       result = dns_acl_create(mctx, 1, &acl);
+       result = dns_acl_create(mctx, 0, &acl);
        if (result != ISC_R_SUCCESS)
                return (result);
-       acl->elements[0].negative = neg;
-       acl->elements[0].type = dns_aclelementtype_any;
-       acl->length = 1;
+        dns_iptable_addprefix(acl->iptable, NULL, 0, ISC_TF(!neg));
        *target = acl;
        return (result);
 }
 
+/*
+ * Create a new ACL that matches everything.
+ */
 isc_result_t
 dns_acl_any(isc_mem_t *mctx, dns_acl_t **target) {
        return (dns_acl_anyornone(mctx, ISC_FALSE, target));
 }
 
+/*
+ * Create a new ACL that matches nothing.
+ */
 isc_result_t
 dns_acl_none(isc_mem_t *mctx, dns_acl_t **target) {
        return (dns_acl_anyornone(mctx, ISC_TRUE, target));
 }
 
+/*
+ * If pos is ISC_TRUE, test whether acl is set to "{ any; }"
+ * If pos is ISC_FALSE, test whether acl is set to "{ none; }"
+ */
+static isc_boolean_t
+dns_acl_isanyornone(dns_acl_t *acl, isc_boolean_t pos)
+{
+        /* Should never happen but let's be safe */
+        if (acl == NULL ||
+            acl->iptable == NULL ||
+            acl->iptable->radix == NULL ||
+            acl->iptable->radix->head == NULL ||
+            acl->iptable->radix->head->prefix == NULL)
+                return (ISC_FALSE);
+
+       if (acl->length != 0 && acl->node_count != 1)
+               return (ISC_FALSE);
+
+       if (acl->iptable->radix->head->prefix->bitlen == 0 &&
+           *(isc_boolean_t *) (acl->iptable->radix->head->data) == pos)
+               return (ISC_TRUE);
+
+       return (ISC_FALSE); /* All others */
+}
+
+/*
+ * Test whether acl is set to "{ any; }"
+ */
+isc_boolean_t
+dns_acl_isany(dns_acl_t *acl)
+{
+        return (dns_acl_isanyornone(acl, ISC_TRUE));
+}
+
+/*
+ * Test whether acl is set to "{ none; }"
+ */
+isc_boolean_t
+dns_acl_isnone(dns_acl_t *acl)
+{
+        return (dns_acl_isanyornone(acl, ISC_FALSE));
+}
+
+/*
+ * Determine whether a given address or signer matches a given ACL.
+ * For a match with a positive ACL element or iptable radix entry, 
+ * return with a positive value in match; for a match with a negated ACL
+ * element or radix entry, return with a negative value in match.
+ */
 isc_result_t
 dns_acl_match(const isc_netaddr_t *reqaddr,
              const dns_name_t *reqsigner,
              const dns_acl_t *acl,
              const dns_aclenv_t *env,
              int *match,
-             dns_aclelement_t const**matchelt)
+             const dns_aclelement_t **matchelt)
 {
+       isc_uint16_t bitlen;
+       isc_prefix_t pfx;
+       isc_radix_node_t *node;
+        const isc_netaddr_t *addr;
+        isc_netaddr_t v4addr;
+       isc_result_t result;
+        int match_num = -1;
        unsigned int i;
 
        REQUIRE(reqaddr != NULL);
        REQUIRE(matchelt == NULL || *matchelt == NULL);
-       
+
+       if (env == NULL || env->match_mapped == ISC_FALSE ||
+           reqaddr->family != AF_INET6 ||
+           !IN6_IS_ADDR_V4MAPPED(&reqaddr->type.in6))
+                addr = reqaddr;
+       else {
+               isc_netaddr_fromv4mapped(&v4addr, reqaddr);
+               addr = &v4addr;
+       }
+
+       /* Always match with host addresses. */
+       bitlen = reqaddr->family == AF_INET6 ? 128 : 32;
+       NETADDR_TO_PREFIX_T(addr, pfx, bitlen);
+
+       /* Assume no match. */
+       *match = 0;
+
+       /* Search radix. */
+       result = isc_radix_search(acl->iptable->radix, &node, &pfx);
+
+       /* Found a match. */
+       if (result == ISC_R_SUCCESS && node != NULL) {
+               match_num = node->node_num;
+                if (*(isc_boolean_t *) node->data == ISC_TRUE)
+                        *match = match_num;
+                else
+                        *match = -match_num;
+        }
+
+        /* Now search non-radix elements for a match with a lower node_num. */
        for (i = 0; i < acl->length; i++) {
                dns_aclelement_t *e = &acl->elements[i];
 
                if (dns_aclelement_match(reqaddr, reqsigner,
                                         e, env, matchelt)) {
-                       *match = e->negative ? -((int)i+1) : ((int)i+1);
+                        if (match_num == -1 || e->node_num < match_num) {
+                                if (e->negative)
+                                       *match = -e->node_num;
+                                else
+                                       *match = e->node_num;
+                        }
                        return (ISC_R_SUCCESS);
                }
        }
-       /* No match. */
-       *match = 0;
+
        return (ISC_R_SUCCESS);
 }
 
+/*
+ * Merge the contents of one ACL into another.  Call dns_iptable_merge()
+ * for the IP tables, then concatenate the element arrays.
+ *
+ * If pos is set to false, then the nested ACL is to be negated.  This
+ * means reverse the sense of each *positive* element or IP table node,
+ * but leave negatives alone, so as to prevent a double-negative causing
+ * an unexpected postive match in the parent ACL.
+ */
 isc_result_t
-dns_acl_elementmatch(const dns_acl_t *acl,
-                    const dns_aclelement_t *elt,
-                    const dns_aclelement_t **matchelt)
+dns_acl_merge(dns_acl_t *dest, dns_acl_t *source, isc_boolean_t pos)
 {
-       unsigned int i;
-
-       REQUIRE(elt != NULL);
-       REQUIRE(matchelt == NULL || *matchelt == NULL);
-       
-       for (i = 0; i < acl->length; i++) {
-               dns_aclelement_t *e = &acl->elements[i];
-
-               if (dns_aclelement_equal(e, elt) == ISC_TRUE) {
-                       if (matchelt != NULL)
-                               *matchelt = e;
-                       return (ISC_R_SUCCESS);
-               }
-       }
-
-       return (ISC_R_NOTFOUND);
+        unsigned int newalloc, nelem, i;
+        int max_node = 0, nodes;
+
+        /* Resize the element array if needed. */
+        if (dest->length + source->length > dest->alloc) {
+                void *newmem;
+
+                newalloc = dest->alloc + source->alloc;
+                if (newalloc < 4)
+                        newalloc = 4;
+
+                newmem = isc_mem_get(dest->mctx,
+                                     newalloc * sizeof(dns_aclelement_t));
+                if (newmem == NULL)
+                        return (ISC_R_NOMEMORY);
+
+                /* Copy in the original elements */
+                memcpy(newmem, dest->elements,
+                       dest->length * sizeof(dns_aclelement_t));
+
+                /* Release the memory for the old elements array */
+                isc_mem_put(dest->mctx, dest->elements,
+                            dest->alloc * sizeof(dns_aclelement_t));
+                dest->elements = newmem;
+                dest->alloc = newalloc;
+        }
+
+        /*
+         * Now copy in the new elements, increasing their node_num
+         * values so as to keep the new ACL consistent.  If we're
+         * negating, then negate positive elements, but keep negative
+         * elements the same for security reasons.
+         */
+        nelem = dest->length;
+        memcpy(&dest->elements[nelem], source->elements,
+               (source->length * sizeof(dns_aclelement_t)));
+        for (i = 0; i < source->length; i++) {
+                dest->elements[nelem + i].node_num = 
+                        source->elements[i].node_num + dest->node_count;
+                if (source->elements[i].node_num > max_node)
+                        max_node = source->elements[i].node_num;
+                if (!pos && source->elements[i].negative == ISC_FALSE)
+                        dest->elements[nelem + i].negative = ISC_TRUE;
+        }
+
+        /*
+         * Merge the iptables.  Make sure the destination ACL's
+         * node_count value is set correctly afterward.
+         */
+        nodes = max_node + dest->node_count;
+        dns_iptable_merge(dest->iptable, source->iptable, pos);
+        if (nodes > dest->node_count)
+                dest->node_count = nodes;
+
+        return (ISC_R_SUCCESS);
 }
 
+/*
+ * Like dns_acl_match, but matches against the single ACL element 'e'
+ * rather than a complete ACL, and returns ISC_TRUE iff it matched.
+ *
+ * To determine whether the match was prositive or negative, the 
+ * caller should examine e->negative.  Since the element 'e' may be
+ * a reference to a named ACL or a nested ACL, a matching element
+ * returned through 'matchelt' is not necessarily 'e' itself.
+ */
 isc_boolean_t
 dns_aclelement_match(const isc_netaddr_t *reqaddr,
                     const dns_name_t *reqsigner,
@@ -186,90 +323,66 @@ dns_aclelement_match(const isc_netaddr_t *reqaddr,
                     const dns_aclelement_t **matchelt)
 {
        dns_acl_t *inner = NULL;
-       const isc_netaddr_t *addr;
-       isc_netaddr_t v4addr;
        int indirectmatch;
        isc_result_t result;
 
-       switch (e->type) {
-       case dns_aclelementtype_ipprefix:
-               if (env == NULL ||
-                   env->match_mapped == ISC_FALSE ||
-                   reqaddr->family != AF_INET6 ||
-                   !IN6_IS_ADDR_V4MAPPED(&reqaddr->type.in6))
-                       addr = reqaddr;
-               else {
-                       isc_netaddr_fromv4mapped(&v4addr, reqaddr);
-                       addr = &v4addr;
-               }
-
-               if (isc_netaddr_eqprefix(addr,
-                                        &e->u.ip_prefix.address, 
-                                        e->u.ip_prefix.prefixlen))
-                       goto matched;
-               break;
-               
-       case dns_aclelementtype_keyname:
+        switch (e->type) {
+           case dns_aclelementtype_keyname:
                if (reqsigner != NULL &&
-                   dns_name_equal(reqsigner, &e->u.keyname))
-                       goto matched;
-               break;
-               
-       case dns_aclelementtype_nestedacl:
-               inner = e->u.nestedacl;
-       nested:
-               result = dns_acl_match(reqaddr, reqsigner,
-                                      inner,
-                                      env,
-                                      &indirectmatch, matchelt);
-               INSIST(result == ISC_R_SUCCESS);
-
-               /*
-                * Treat negative matches in indirect ACLs as
-                * "no match".
-                * That way, a negated indirect ACL will never become 
-                * a surprise positive match through double negation.
-                * XXXDCL this should be documented.
-                */
-               if (indirectmatch > 0)
-                       goto matchelt_set;
-               
-               /*
-                * A negative indirect match may have set *matchelt,
-                * but we don't want it set when we return.
-                */
-               if (matchelt != NULL)
-                       *matchelt = NULL;
-               break;
+                   dns_name_equal(reqsigner, &e->keyname)) {
+                       if (matchelt != NULL)
+                               *matchelt = e;
+                       return (ISC_TRUE);
+                } else {
+                        return (ISC_FALSE);
+                }
+
+           case dns_aclelementtype_nestedacl:
+                inner = e->nestedacl;
+                break;
+
+            case dns_aclelementtype_localhost:
+                if (env == NULL || env->localhost == NULL)
+                        return (ISC_FALSE);
+                inner = env->localhost;
+                break;
+
+            case dns_aclelementtype_localnets:
+                if (env == NULL || env->localnets == NULL)
+                        return (ISC_FALSE);
+                inner = env->localnets;
+                break;
+
+            default:
+                /* Should be impossible */
+                INSIST(0);
+        }
                
-       case dns_aclelementtype_any:
-       matched:
-               if (matchelt != NULL)
-                       *matchelt = e;
-       matchelt_set:
-               return (ISC_TRUE);
-                       
-       case dns_aclelementtype_localhost:
-               if (env != NULL && env->localhost != NULL) {
-                       inner = env->localhost;
-                       goto nested;
-               } else {
-                       break;
-               }
+        result = dns_acl_match(reqaddr, reqsigner, inner, env,
+                               &indirectmatch, matchelt);
+        INSIST(result == ISC_R_SUCCESS);
+
+        /*
+         * Treat negative matches in indirect ACLs as "no match".
+         * That way, a negated indirect ACL will never become a
+         * surprise positive match through double negation.
+         * XXXDCL this should be documented.
+         */
+
+        if (indirectmatch > 0) {
+                if (matchelt != NULL)
+                        *matchelt = e;
+                return (ISC_TRUE);
+        }
                
-       case dns_aclelementtype_localnets:
-               if (env != NULL && env->localnets != NULL) {
-                       inner = env->localnets;
-                       goto nested;
-               } else {
-                       break;
-               }
-               
-       default:
-               INSIST(0);
-               break;
-       }
+        /*
+         * A negative indirect match may have set *matchelt, but we don't
+         * want it set when we return.
+         */
 
+        if (matchelt != NULL)
+                *matchelt = NULL;
+               
        return (ISC_FALSE);
 }      
 
@@ -285,15 +398,8 @@ destroy(dns_acl_t *dacl) {
        unsigned int i;
        for (i = 0; i < dacl->length; i++) {
                dns_aclelement_t *de = &dacl->elements[i];
-               switch (de->type) {
-               case dns_aclelementtype_keyname:
-                       dns_name_free(&de->u.keyname, dacl->mctx);
-                       break;
-               case dns_aclelementtype_nestedacl:
-                       dns_acl_detach(&de->u.nestedacl);
-                       break;
-               default:
-                       break;
+               if (de->type == dns_aclelementtype_keyname) {
+                       dns_name_free(&de->keyname, dacl->mctx);
                }
        }
        if (dacl->elements != NULL)
@@ -301,6 +407,8 @@ destroy(dns_acl_t *dacl) {
                            dacl->alloc * sizeof(dns_aclelement_t));
        if (dacl->name != NULL)
                isc_mem_free(dacl->mctx, dacl->name);
+        if (dacl->iptable != NULL)
+                dns_iptable_detach(&dacl->iptable);
        isc_refcount_destroy(&dacl->refcount);
        dacl->magic = 0;
        isc_mem_put(dacl->mctx, dacl, sizeof(*dacl));
@@ -317,69 +425,62 @@ dns_acl_detach(dns_acl_t **aclp) {
        *aclp = NULL;
 }
 
-isc_boolean_t
-dns_aclelement_equal(const dns_aclelement_t *ea, const dns_aclelement_t *eb) {
-       if (ea->type != eb->type)
-               return (ISC_FALSE);
-       switch (ea->type) {
-       case dns_aclelementtype_ipprefix:
-               if (ea->u.ip_prefix.prefixlen !=
-                   eb->u.ip_prefix.prefixlen)
-                       return (ISC_FALSE);
-               return (isc_netaddr_eqprefix(&ea->u.ip_prefix.address,
-                                            &eb->u.ip_prefix.address,
-                                            ea->u.ip_prefix.prefixlen));
-       case dns_aclelementtype_keyname:
-               return (dns_name_equal(&ea->u.keyname, &eb->u.keyname));
-       case dns_aclelementtype_nestedacl:
-               return (dns_acl_equal(ea->u.nestedacl, eb->u.nestedacl));
-       case dns_aclelementtype_localhost:
-       case dns_aclelementtype_localnets:
-       case dns_aclelementtype_any:
-               return (ISC_TRUE);
-       default:
-               INSIST(0);
-               return (ISC_FALSE);
-       }
-}
-
-isc_boolean_t
-dns_acl_equal(const dns_acl_t *a, const dns_acl_t *b) {
-       unsigned int i;
-       if (a == b)
-               return (ISC_TRUE);
-       if (a->length != b->length)
-               return (ISC_FALSE);
-       for (i = 0; i < a->length; i++) {
-               if (! dns_aclelement_equal(&a->elements[i],
-                                          &b->elements[i]))
-                       return (ISC_FALSE);
-       }
-       return (ISC_TRUE);
-}
+static isc_boolean_t insecure_prefix_found;
 
-static isc_boolean_t
-is_loopback(const dns_aclipprefix_t *p) {
-       switch (p->address.family) {
-       case AF_INET:
-               if (p->prefixlen == 32 &&
-                   htonl(p->address.type.in.s_addr) == INADDR_LOOPBACK)
-                       return (ISC_TRUE);
+/*
+ * Called via isc_radix_walk() to find IP table nodes that are
+ * insecure.
+ */
+static void
+is_insecure(isc_prefix_t *prefix, isc_boolean_t *data) {
+        /* Negated entries are always secure */
+        if(* (isc_boolean_t *)data == ISC_FALSE) {
+                return; 
+        }
+
+        /* If loopback prefix found, return */
+       switch (prefix->family) {
+           case AF_INET:
+               if (prefix->bitlen == 32 &&
+                   htonl(prefix->add.sin.s_addr) == INADDR_LOOPBACK)
+                        return;
                break;
-       case AF_INET6:
-               if (p->prefixlen == 128 &&
-                   IN6_IS_ADDR_LOOPBACK(&p->address.type.in6))
-                       return (ISC_TRUE);
+           case AF_INET6:
+               if (prefix->bitlen == 128 &&
+                   IN6_IS_ADDR_LOOPBACK(&prefix->add.sin6))
+                        return;
                break;
-       default:
+           default:
                break;
        }
-       return (ISC_FALSE);
+
+        /* Non-negated, non-loopback */
+        insecure_prefix_found = ISC_TRUE;
+        return;
 }
 
+/*
+ * Return ISC_TRUE iff the acl 'a' is considered insecure, that is,
+ * if it contains IP addresses other than those of the local host.
+ * This is intended for applications such as printing warning 
+ * messages for suspect ACLs; it is not intended for making access
+ * control decisions.  We make no guarantee that an ACL for which
+ * this function returns ISC_FALSE is safe.
+ */
 isc_boolean_t
 dns_acl_isinsecure(const dns_acl_t *a) {
        unsigned int i;
+
+        /*
+         * Walk radix tree to find out if there are any non-negated,
+         * non-loopback prefixes.
+         */
+        insecure_prefix_found = ISC_FALSE;
+        isc_radix_process(a->iptable->radix, is_insecure);
+        if(insecure_prefix_found)
+                return(ISC_TRUE);
+                       
+        /* Now check non-radix elements */
        for (i = 0; i < a->length; i++) {
                dns_aclelement_t *e = &a->elements[i];
 
@@ -388,34 +489,31 @@ dns_acl_isinsecure(const dns_acl_t *a) {
                        continue;
 
                switch (e->type) {
-               case dns_aclelementtype_ipprefix:
-                       /* The loopback address is considered secure. */
-                       if (! is_loopback(&e->u.ip_prefix))
-                               return (ISC_TRUE);
-                       continue;
-                       
-               case dns_aclelementtype_keyname:
-               case dns_aclelementtype_localhost:
+                   case dns_aclelementtype_keyname:
+                   case dns_aclelementtype_localhost:
                        continue;
 
-               case dns_aclelementtype_nestedacl:
-                       if (dns_acl_isinsecure(e->u.nestedacl))
-                               return (ISC_TRUE);
-                       continue;
-                       
-               case dns_aclelementtype_localnets:
-               case dns_aclelementtype_any:
+                    case dns_aclelementtype_nestedacl:
+                        if (dns_acl_isinsecure(e->nestedacl))
+                                return (ISC_TRUE);
+                        continue;
+
+                   case dns_aclelementtype_localnets:
                        return (ISC_TRUE);
 
-               default:
+                   default:
                        INSIST(0);
                        return (ISC_TRUE);
                }
        }
+
        /* No insecure elements were found. */
        return (ISC_FALSE);
 }
 
+/*
+ * Initialize ACL environment, setting up localhost and localnets ACLs
+ */
 isc_result_t
 dns_aclenv_init(isc_mem_t *mctx, dns_aclenv_t *env) {
        isc_result_t result;
index 03a081eff648a498d14a9f12991aea9bdc48b329..7397d56d34f707669ece20d1436cf1a26b87025a 100644 (file)
@@ -13,7 +13,7 @@
 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 
-# $Id: Makefile.in,v 1.52 2007/06/19 23:47:16 tbox Exp $
+# $Id: Makefile.in,v 1.53 2007/09/12 01:09:08 each Exp $
 
 srcdir =       @srcdir@
 VPATH =                @srcdir@
@@ -24,7 +24,7 @@ top_srcdir =  @top_srcdir@
 HEADERS =      acl.h adb.h byaddr.h cache.h callbacks.h \
                cert.h compress.h \
                db.h dbiterator.h dbtable.h diff.h dispatch.h \
-               dnssec.h ds.h events.h fixedname.h journal.h keyflags.h \
+               dnssec.h ds.h events.h fixedname.h iptable.h journal.h keyflags.h \
                keytable.h keyvalues.h lib.h log.h master.h masterdump.h \
                message.h name.h ncache.h \
                nsec.h peer.h portlist.h rbt.h rcode.h \
index 81422322a699719730a860be777d1d796ce83395..058e4aacf92186c4b813861cb0e1b9351ebfde5f 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: acl.h,v 1.29 2007/06/19 23:47:16 tbox Exp $ */
+/* $Id: acl.h,v 1.30 2007/09/12 01:09:08 each Exp $ */
 
 #ifndef DNS_ACL_H
 #define DNS_ACL_H 1
@@ -40,6 +40,7 @@
 
 #include <dns/name.h>
 #include <dns/types.h>
+#include <dns/iptable.h>
 
 /***
  *** Types
@@ -64,17 +65,17 @@ struct dns_aclipprefix {
 struct dns_aclelement {
        dns_aclelemettype_t type;
        isc_boolean_t negative;
-       union {
-               dns_aclipprefix_t ip_prefix;
-               dns_name_t        keyname;
-               dns_acl_t         *nestedacl;
-       } u;
+       dns_name_t        keyname;
+        dns_acl_t         *nestedacl;
+        int node_num;
 };
 
 struct dns_acl {
        unsigned int            magic;
        isc_mem_t               *mctx;
        isc_refcount_t          refcount;
+       dns_iptable_t           *iptable;
+#define node_count              iptable->radix->num_added_node
        dns_aclelement_t        *elements;
        unsigned int            alloc;          /*%< Elements allocated */
        unsigned int            length;         /*%< Elements initialized */
@@ -100,14 +101,9 @@ ISC_LANG_BEGINDECLS
 isc_result_t
 dns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target);
 /*%<
- * Create a new ACL with room for 'n' elements.
- * The elements are uninitialized and the length is 0.
- */
-
-isc_result_t
-dns_acl_appendelement(dns_acl_t *acl, const dns_aclelement_t *elt);
-/*%<
- * Append an element to an existing ACL.
+ * Create a new ACL, including an IP table and an array with room
+ * for 'n' ACL elements.  The elements are uninitialized and the
+ * length is 0.
  */
 
 isc_result_t
@@ -122,18 +118,36 @@ dns_acl_none(isc_mem_t *mctx, dns_acl_t **target);
  * Create a new ACL that matches nothing.
  */
 
+isc_boolean_t
+dns_acl_isany(dns_acl_t *acl);
+/*%<
+ * Test whether ACL is set to "{ any; }"
+ */
+
+isc_boolean_t
+dns_acl_isnone(dns_acl_t *acl);
+/*%<
+ * Test whether ACL is set to "{ none; }"
+ */
+
+isc_result_t
+dns_acl_merge(dns_acl_t *dest, dns_acl_t *source, isc_boolean_t pos);
+/*%<
+ * Merge the contents of one ACL into another.  Call dns_iptable_merge()
+ * for the IP tables, then concatenate the element arrays.
+ *
+ * If pos is set to false, then the nested ACL is to be negated.  This
+ * means reverse the sense of each *positive* element or IP table node,
+ * but leave negatives alone, so as to prevent a double-negative causing
+ * an unexpected postive match in the parent ACL.
+ */
+
 void
 dns_acl_attach(dns_acl_t *source, dns_acl_t **target);
 
 void
 dns_acl_detach(dns_acl_t **aclp);
 
-isc_boolean_t
-dns_aclelement_equal(const dns_aclelement_t *ea, const dns_aclelement_t *eb);
-
-isc_boolean_t
-dns_acl_equal(const dns_acl_t *a, const dns_acl_t *b);
-
 isc_boolean_t
 dns_acl_isinsecure(const dns_acl_t *a);
 /*%<
@@ -147,6 +161,9 @@ dns_acl_isinsecure(const dns_acl_t *a);
 
 isc_result_t
 dns_aclenv_init(isc_mem_t *mctx, dns_aclenv_t *env);
+/*%<
+ * Initialize ACL environment, setting up localhost and localnets ACLs
+ */
 
 void
 dns_aclenv_copy(dns_aclenv_t *t, dns_aclenv_t *s);
@@ -168,19 +185,17 @@ dns_acl_match(const isc_netaddr_t *reqaddr,
  * Match the address 'reqaddr', and optionally the key name 'reqsigner',
  * against 'acl'.  'reqsigner' may be NULL.
  *
- * If there is a positive match, '*match' will be set to a positive value
- * indicating the distance from the beginning of the list.
- *
- * If there is a negative match, '*match' will be set to a negative value
- * whose absolute value indicates the distance from the beginning of
- * the list.
- *
- * If there is a match (either positive or negative) and 'matchelt' is
- * non-NULL, *matchelt will be attached to the primitive
- * (non-indirect) address match list element that matched.
+ * If there is a match, '*match' will be set to an integer whose absolute
+ * value corresponds to the order in which the matching value was inserted
+ * into the ACL.  For a positive match, this value will be positive; for a
+ * negative match, it will be negative.
  *
  * If there is no match, *match will be set to zero.
  *
+ * If there is a match in the element list (either positive or negative)
+ * and 'matchelt' is non-NULL, *matchelt will be pointed to the matching
+ * element.
+ *
  * Returns:
  *\li  #ISC_R_SUCCESS          Always succeeds.
  */
@@ -193,30 +208,14 @@ dns_aclelement_match(const isc_netaddr_t *reqaddr,
                     const dns_aclelement_t **matchelt);
 /*%<
  * Like dns_acl_match, but matches against the single ACL element 'e'
- * rather than a complete list and returns ISC_TRUE iff it matched.
+ * rather than a complete ACL, and returns ISC_TRUE iff it matched.
+ *
  * To determine whether the match was prositive or negative, the 
  * caller should examine e->negative.  Since the element 'e' may be
- * a reference to a named ACL or a nested ACL, the matching element
+ * a reference to a named ACL or a nested ACL, a matching element
  * returned through 'matchelt' is not necessarily 'e' itself.
  */
 
-isc_result_t
-dns_acl_elementmatch(const dns_acl_t *acl,
-                    const dns_aclelement_t *elt,
-                    const dns_aclelement_t **matchelt);
-/*%<
- * Search for an ACL element in 'acl' which is exactly the same as 'elt'.
- * If there is one, and 'matchelt' is non NULL, then '*matchelt' will point
- * to the entry.
- *
- * This function is intended to be used for avoiding duplicated ACL entries
- * before adding an entry.
- *
- * Returns:
- *\li  #ISC_R_SUCCESS          Match succeeds.
- *\li  #ISC_R_NOTFOUND         Match fails.
- */
-
 ISC_LANG_ENDDECLS
 
 #endif /* DNS_ACL_H */
index 8a3e3aaad59309d9162bb9cf438b60ca1942a4cd..fdaefd435ddc604b36d2174813ed88294879712a 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: types.h,v 1.125 2007/06/19 23:47:17 tbox Exp $ */
+/* $Id: types.h,v 1.126 2007/09/12 01:09:08 each Exp $ */
 
 #ifndef DNS_TYPES_H
 #define DNS_TYPES_H 1
@@ -68,6 +68,7 @@ typedef struct dns_fetch                      dns_fetch_t;
 typedef struct dns_fixedname                   dns_fixedname_t;
 typedef struct dns_forwarders                  dns_forwarders_t;
 typedef struct dns_fwdtable                    dns_fwdtable_t;
+typedef struct dns_iptable                     dns_iptable_t;
 typedef isc_uint16_t                           dns_keyflags_t;
 typedef struct dns_keynode                     dns_keynode_t;
 typedef struct dns_keytable                    dns_keytable_t;
index 44e1a1ce232ea393e2cdc5b8d7d7635f89191bfa..6751b080af45a1847a73313aa8fdd2e89d40fa5c 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: zone.c,v 1.467 2007/08/30 05:08:42 marka Exp $ */
+/* $Id: zone.c,v 1.468 2007/09/12 01:09:08 each Exp $ */
 
 /*! \file */
 
@@ -1144,11 +1144,7 @@ zone_isdynamic(dns_zone_t *zone) {
                       zone->type == dns_zone_stub ||
                       (!zone->update_disabled && zone->ssutable != NULL) ||
                       (!zone->update_disabled && zone->update_acl != NULL &&
-                       ! (zone->update_acl->length == 1 &&
-                          zone->update_acl->elements[0].negative == ISC_TRUE
-                          &&
-                          zone->update_acl->elements[0].type ==
-                          dns_aclelementtype_any))));
+                       !dns_acl_isnone(zone->update_acl))));
 }
 
 
index d55e0f8055e1014aa3f4828d02293f75bb4e883c..503d17aba85c99a6d76e8402c182f3bb82aff1dd 100644 (file)
@@ -13,7 +13,7 @@
 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 
-# $Id: Makefile.in,v 1.91 2007/06/19 23:47:17 tbox Exp $
+# $Id: Makefile.in,v 1.92 2007/09/12 01:09:08 each Exp $
 
 srcdir =       @srcdir@
 VPATH =                @srcdir@
@@ -58,7 +58,7 @@ OBJS =                @ISC_EXTRA_OBJS@ \
                lex.@O@ lfsr.@O@ lib.@O@ log.@O@ \
                md5.@O@ mem.@O@ mutexblock.@O@ \
                netaddr.@O@ netscope.@O@ ondestroy.@O@ \
-               parseint.@O@ quota.@O@ random.@O@ \
+               parseint.@O@ quota.@O@ radix.@O@ random.@O@ \
                ratelimiter.@O@ refcount.@O@ region.@O@ result.@O@ rwlock.@O@ \
                serial.@O@ sha1.@O@ sha2.@O@ sockaddr.@O@ \
                string.@O@ strtoul.@O@ symtab.@O@ task.@O@ taskpool.@O@ \
@@ -73,7 +73,7 @@ SRCS =                @ISC_EXTRA_SRCS@ \
                lex.c lfsr.c lib.c log.c \
                md5.c mem.c mutexblock.c \
                netaddr.c netscope.c ondestroy.c \
-               parseint.c quota.c random.c \
+               parseint.c quota.c radix.c random.c \
                ratelimiter.c refcount.c region.c result.c rwlock.c \
                serial.c sha1.c sha2.c sockaddr.c string.c strtoul.c \
                symtab.c task.c taskpool.c timer.c version.c
index 2626609f9f31d358c9e45548a9bb082e2c717d04..c5e451f6a96819c4b843c60a09d8cd1baf47f994 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: aclconf.c,v 1.9 2007/06/19 23:47:22 tbox Exp $ */
+/* $Id: aclconf.c,v 1.10 2007/09/12 01:09:08 each Exp $ */
 
 #include <config.h>
 
@@ -27,6 +27,7 @@
 #include <isccfg/aclconf.h>
 
 #include <dns/acl.h>
+#include <dns/iptable.h>
 #include <dns/fixedname.h>
 #include <dns/log.h>
 
@@ -40,6 +41,7 @@ cfg_aclconfctx_init(cfg_aclconfctx_t *ctx) {
 void
 cfg_aclconfctx_destroy(cfg_aclconfctx_t *ctx) {
        dns_acl_t *dacl, *next;
+
        for (dacl = ISC_LIST_HEAD(ctx->named_acl_cache);
             dacl != NULL;
             dacl = next)
@@ -67,7 +69,9 @@ get_acl_def(const cfg_obj_t *cctx, const char *name, const cfg_obj_t **ret) {
                const cfg_obj_t *acl = cfg_listelt_value(elt);
                const char *aclname = cfg_obj_asstring(cfg_tuple_get(acl, "name"));
                if (strcasecmp(aclname, name) == 0) {
-                       *ret = cfg_tuple_get(acl, "value");
+                       if (ret != NULL) {
+                               *ret = cfg_tuple_get(acl, "value");
+                       }
                        return (ISC_R_SUCCESS);
                }
        }
@@ -77,7 +81,8 @@ get_acl_def(const cfg_obj_t *cctx, const char *name, const cfg_obj_t **ret) {
 static isc_result_t
 convert_named_acl(const cfg_obj_t *nameobj, const cfg_obj_t *cctx,
                  isc_log_t *lctx, cfg_aclconfctx_t *ctx,
-                 isc_mem_t *mctx, dns_acl_t **target)
+                 isc_mem_t *mctx, int nest_level,
+                  dns_acl_t **target)
 {
        isc_result_t result;
        const cfg_obj_t *cacl = NULL;
@@ -115,7 +120,8 @@ convert_named_acl(const cfg_obj_t *nameobj, const cfg_obj_t *cctx,
        DE_CONST(aclname, loop.name);
        loop.magic = LOOP_MAGIC;
        ISC_LIST_APPEND(ctx->named_acl_cache, &loop, nextincache);
-       result = cfg_acl_fromconfig(cacl, cctx, lctx, ctx, mctx, &dacl);
+       result = cfg_acl_fromconfig(cacl, cctx, lctx, ctx, mctx,
+                                    nest_level, &dacl);
        ISC_LIST_UNLINK(ctx->named_acl_cache, &loop, nextincache);
        loop.magic = 0;
        loop.name = NULL;
@@ -160,25 +166,47 @@ cfg_acl_fromconfig(const cfg_obj_t *caml,
                   isc_log_t *lctx,
                   cfg_aclconfctx_t *ctx,
                   isc_mem_t *mctx,
+                   int nest_level,
                   dns_acl_t **target)
 {
        isc_result_t result;
-       unsigned int count;
-       dns_acl_t *dacl = NULL;
+       dns_acl_t *dacl = NULL, *inneracl = NULL;
        dns_aclelement_t *de;
        const cfg_listelt_t *elt;
+        dns_iptable_t *iptab;
 
-       REQUIRE(target != NULL && *target == NULL);
+       REQUIRE(target != NULL);
+        REQUIRE(*target == NULL || ISC_MAGIC_VALID(target, DNS_ACL_MAGIC));
 
-       count = 0;
-       for (elt = cfg_list_first(caml);
-            elt != NULL;
-            elt = cfg_list_next(elt))
-               count++;
-
-       result = dns_acl_create(mctx, count, &dacl);
-       if (result != ISC_R_SUCCESS)
-               return (result);
+        if (*target != NULL) {
+                /*
+                 * If target already points to an ACL, then we're being
+                 * called recursively to configure a nested ACL.  The
+                 * nested ACL's contents should just be absorbed into its
+                 * parent ACL.
+                 */
+                dacl = *target;
+        } else {
+                /*
+                 * Need to allocate a new ACL structure.  Count the items
+                 * in the ACL definition and allocate space for that many
+                 * elements (even though some or all of them may end up in
+                 * the iptable instead of the element array).
+                 */
+                unsigned int element_count = 0;
+                for (elt = cfg_list_first(caml);
+                     elt != NULL;
+                     elt = cfg_list_next(elt)) {
+                        const cfg_obj_t *ce = cfg_listelt_value(elt);
+                        if (cfg_obj_istuple(ce))
+                                ce = cfg_tuple_get(ce, "value");
+                        if (cfg_obj_isnetprefix(ce))
+                                element_count++;
+                }
+               result = dns_acl_create(mctx, element_count, &dacl);
+               if (result != ISC_R_SUCCESS)
+                       return (result);
+        }
 
        de = dacl->elements;
        for (elt = cfg_list_first(caml);
@@ -186,56 +214,114 @@ cfg_acl_fromconfig(const cfg_obj_t *caml,
             elt = cfg_list_next(elt))
        {
                const cfg_obj_t *ce = cfg_listelt_value(elt);
+               isc_boolean_t   neg;
+
                if (cfg_obj_istuple(ce)) {
                        /* This must be a negated element. */
                        ce = cfg_tuple_get(ce, "value");
-                       de->negative = ISC_TRUE;
-               } else {
-                       de->negative = ISC_FALSE;
-               }
+                       neg = ISC_TRUE;
+               } else
+                       neg = ISC_FALSE;
+
+                /*
+                 * If nest_level is nonzero, then every element is
+                 * to be stored as a separate, nested ACL rather than
+                 * merged into the main iptable.
+                 */
+                iptab = dacl->iptable;
+               if (nest_level) {
+                        result = dns_acl_create(mctx, 0, &de->nestedacl);
+                        if (result != ISC_R_SUCCESS)
+                               goto cleanup;
+                        iptab = de->nestedacl->iptable;
+                }
 
                if (cfg_obj_isnetprefix(ce)) {
                        /* Network prefix */
-                       de->type = dns_aclelementtype_ipprefix;
+                       isc_netaddr_t   addr;
+                       unsigned int    bitlen;
 
-                       cfg_obj_asnetprefix(ce,
-                                           &de->u.ip_prefix.address,
-                                           &de->u.ip_prefix.prefixlen);
-               } else if (cfg_obj_istype(ce, &cfg_type_keyref)) {
-                       /* Key name */
-                       de->type = dns_aclelementtype_keyname;
-                       dns_name_init(&de->u.keyname, NULL);
-                       result = convert_keyname(ce, lctx, mctx,
-                                                &de->u.keyname);
+                        cfg_obj_asnetprefix(ce, &addr, &bitlen);
+                       result = dns_iptable_addprefix(iptab, &addr, bitlen,
+                                                       ISC_TF(!neg));
                        if (result != ISC_R_SUCCESS)
                                goto cleanup;
+                        continue;
                } else if (cfg_obj_islist(ce)) {
-                       /* Nested ACL */
-                       de->type = dns_aclelementtype_nestedacl;
-                       result = cfg_acl_fromconfig(ce, cctx, lctx, ctx,
-                                                   mctx, &de->u.nestedacl);
+                        /*
+                         * If we're nesting ACLs, put the nested
+                         * ACL onto the elements list; otherwise
+                         * merge it into *this* ACL.
+                         */
+                        if (nest_level == 0) {
+                               result = cfg_acl_fromconfig(ce,
+                                                 cctx, lctx, ctx, mctx, 0,
+                                                 &dacl);
+                        } else {
+                               de->type = dns_aclelementtype_nestedacl;
+                               de->negative = neg;
+                               result = cfg_acl_fromconfig(ce,
+                                                 cctx, lctx, ctx, mctx,
+                                                 nest_level - 1,
+                                                 &de->nestedacl);
+                        }
                        if (result != ISC_R_SUCCESS)
-                               goto cleanup;
+                               goto cleanup;
+                        continue;
                } else if (cfg_obj_isstring(ce)) {
                        /* ACL name */
                        const char *name = cfg_obj_asstring(ce);
-                       if (strcasecmp(name, "localhost") == 0) {
+                        if (strcasecmp(name, "any") == 0) {
+                                /* iptable entry with zero bit length */
+                                dns_iptable_addprefix(iptab, NULL, 0,
+                                                      ISC_TRUE);
+                                continue;
+                       } else if (strcasecmp(name, "none") == 0) {
+                                /* negated "any" */
+                                dns_iptable_addprefix(iptab, NULL, 0,
+                                                      ISC_FALSE);
+                                continue;
+                       } else if (strcasecmp(name, "localhost") == 0) {
                                de->type = dns_aclelementtype_localhost;
+                               de->negative = neg;
                        } else if (strcasecmp(name, "localnets") == 0) {
                                de->type = dns_aclelementtype_localnets;
-                       }  else if (strcasecmp(name, "any") == 0) {
-                               de->type = dns_aclelementtype_any;
-                       }  else if (strcasecmp(name, "none") == 0) {
-                               de->type = dns_aclelementtype_any;
-                               de->negative = ISC_TF(! de->negative);
+                               de->negative = neg;
                        } else {
-                               de->type = dns_aclelementtype_nestedacl;
-                               result = convert_named_acl(ce, cctx, lctx,
-                                                          ctx, mctx,
-                                                          &de->u.nestedacl);
+                               result = get_acl_def(cctx, name, NULL);
+                               if (result == ISC_R_SUCCESS) {
+                                       /* found it in acl definitions */
+                                        inneracl = NULL;
+                                       result = convert_named_acl(ce, cctx,
+                                                       lctx, ctx, mctx,
+                                                        nest_level
+                                                          ?  (nest_level - 1)
+                                                          : 0,
+                                                        &inneracl);
+                               }
                                if (result != ISC_R_SUCCESS)
                                        goto cleanup;
+
+                                if (nest_level) {
+                                        de->type = dns_aclelementtype_nestedacl,
+                                        de->negative = neg;
+                                        de->nestedacl = inneracl;
+                                } else {
+                                        dns_acl_merge(dacl, inneracl,
+                                                      ISC_TF(!neg));
+                                        dns_acl_detach(&inneracl);
+                                }
+                                continue;
                        }
+               } else if (cfg_obj_istype(ce, &cfg_type_keyref)) {
+                       /* Key name */
+                       de->type = dns_aclelementtype_keyname;
+                       de->negative = neg;
+                       dns_name_init(&de->keyname, NULL);
+                       result = convert_keyname(ce, lctx, mctx,
+                                                &de->keyname);
+                       if (result != ISC_R_SUCCESS)
+                               goto cleanup;
                } else {
                        cfg_obj_log(ce, lctx, ISC_LOG_WARNING,
                                    "address match list contains "
@@ -243,6 +329,16 @@ cfg_acl_fromconfig(const cfg_obj_t *caml,
                        result = ISC_R_FAILURE;
                        goto cleanup;
                }
+
+                /*
+                 * XXX each: This should only be reached for localhost,
+                 * localnets and keyname elements -- probably should
+                 * be refactored for clearer flow
+                 */
+                if (nest_level && de->type != dns_aclelementtype_nestedacl)
+                        dns_acl_detach(&de->nestedacl);
+
+                de->node_num = dacl->node_count++;
                de++;
                dacl->length++;
        }
index ee1e8294d399152bad3f2a4d92a27f5bb23e7379..778a19c45b1e508a4956db43443f9ebaa50de1b2 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: aclconf.h,v 1.8 2007/06/19 23:47:22 tbox Exp $ */
+/* $Id: aclconf.h,v 1.9 2007/09/12 01:09:08 each Exp $ */
 
 #ifndef ISCCFG_ACLCONF_H
 #define ISCCFG_ACLCONF_H 1
@@ -28,6 +28,7 @@
 
 typedef struct cfg_aclconfctx {
        ISC_LIST(dns_acl_t) named_acl_cache;
+       ISC_LIST(dns_iptable_t) named_iptable_cache;
 } cfg_aclconfctx_t;
 
 /***
@@ -54,6 +55,7 @@ cfg_acl_fromconfig(const cfg_obj_t *caml,
                   isc_log_t *lctx,
                   cfg_aclconfctx_t *ctx,
                   isc_mem_t *mctx,
+                   int nest_level,
                   dns_acl_t **target);
 /*
  * Construct a new dns_acl_t from configuration data in 'caml' and