diff --git a/CHANGES b/CHANGES index c1dd2d8796..1556955716 100644 --- 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] diff --git a/bin/named/controlconf.c b/bin/named/controlconf.c index 30cde422a9..741467cc17 100644 --- a/bin/named/controlconf.c +++ b/bin/named/controlconf.c @@ -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); } diff --git a/bin/named/interfacemgr.c b/bin/named/interfacemgr.c index 9f11581e1e..47860214c4 100644 --- a/bin/named/interfacemgr.c +++ b/bin/named/interfacemgr.c @@ -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); } diff --git a/bin/named/server.c b/bin/named/server.c index af7d968048..f04f4e21cc 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -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); diff --git a/bin/named/sortlist.c b/bin/named/sortlist.c index 372002eb10..8004b175f3 100644 --- a/bin/named/sortlist.c +++ b/bin/named/sortlist.c @@ -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 && diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c index 92974c0013..bdc535ae86 100644 --- a/bin/named/zoneconf.c +++ b/bin/named/zoneconf.c @@ -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); diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml index 2e8566ab80..a2448e9e5f 100644 --- a/doc/arm/Bv9ARM-book.xml +++ b/doc/arm/Bv9ARM-book.xml @@ -18,7 +18,7 @@ - PERFORMANCE OF THIS SOFTWARE. --> - + BIND 9 Administrator Reference Manual @@ -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 listen-on and sortlist - 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: @@ -3053,28 +3052,30 @@ $ORIGIN 0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. 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. 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. 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. + + + 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. @@ -3093,23 +3094,25 @@ $ORIGIN 0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. allow-update-forwarding, and blackhole 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. - 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 - 1.2.3/24; ! 1.2.3.13; 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 ! 1.2.3.13; 1.2.3/24 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 + first 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 + 1.2.3/24; ! 1.2.3.13; + 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 ! 1.2.3.13; 1.2.3/24 fixes + that problem by having 1.2.3.13 blocked by the negation, but + all other 1.2.3.* hosts fall through. @@ -3390,8 +3393,7 @@ $ORIGIN 0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. Note that an address match list's name must be defined with acl before it can be used - elsewhere; no - forward references are allowed. + elsewhere; no forward references are allowed. diff --git a/lib/bind9/check.c b/lib/bind9/check.c index 0beeb70a0a..7d20a003b4 100644 --- a/lib/bind9/check.c +++ b/lib/bind9/check.c @@ -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 = ""; diff --git a/lib/dns/Makefile.in b/lib/dns/Makefile.in index bb4af14331..44f294f440 100644 --- a/lib/dns/Makefile.in +++ b/lib/dns/Makefile.in @@ -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 \ diff --git a/lib/dns/acl.c b/lib/dns/acl.c index 5a37910834..f43ccba0be 100644 --- a/lib/dns/acl.c +++ b/lib/dns/acl.c @@ -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 */ @@ -26,7 +26,13 @@ #include #include +#include +/* + * 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; + unsigned int newalloc, nelem, i; + int max_node = 0, nodes; - REQUIRE(elt != NULL); - REQUIRE(matchelt == NULL || *matchelt == NULL); - - for (i = 0; i < acl->length; i++) { - dns_aclelement_t *e = &acl->elements[i]; + /* Resize the element array if needed. */ + if (dest->length + source->length > dest->alloc) { + void *newmem; - if (dns_aclelement_equal(e, elt) == ISC_TRUE) { - if (matchelt != NULL) - *matchelt = e; - return (ISC_R_SUCCESS); - } - } + newalloc = dest->alloc + source->alloc; + if (newalloc < 4) + newalloc = 4; - return (ISC_R_NOTFOUND); + 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); + dns_name_equal(reqsigner, &e->keyname)) { + if (matchelt != NULL) + *matchelt = e; + return (ISC_TRUE); + } else { + return (ISC_FALSE); + } - /* - * 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; - - 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; - } - - case dns_aclelementtype_localnets: - if (env != NULL && env->localnets != NULL) { - inner = env->localnets; - goto nested; - } else { - break; - } - - default: - INSIST(0); - break; - } + 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); + } + + 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); + } + + /* + * 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); - } -} +static isc_boolean_t insecure_prefix_found; -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); -} +/* + * 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; + } -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); + /* 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; diff --git a/lib/dns/include/dns/Makefile.in b/lib/dns/include/dns/Makefile.in index 03a081eff6..7397d56d34 100644 --- a/lib/dns/include/dns/Makefile.in +++ b/lib/dns/include/dns/Makefile.in @@ -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 \ diff --git a/lib/dns/include/dns/acl.h b/lib/dns/include/dns/acl.h index 81422322a6..058e4aacf9 100644 --- a/lib/dns/include/dns/acl.h +++ b/lib/dns/include/dns/acl.h @@ -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 #include +#include /*** *** 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 */ diff --git a/lib/dns/include/dns/types.h b/lib/dns/include/dns/types.h index 8a3e3aaad5..fdaefd435d 100644 --- a/lib/dns/include/dns/types.h +++ b/lib/dns/include/dns/types.h @@ -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; diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 44e1a1ce23..6751b080af 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -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)))); } diff --git a/lib/isc/Makefile.in b/lib/isc/Makefile.in index d55e0f8055..503d17aba8 100644 --- a/lib/isc/Makefile.in +++ b/lib/isc/Makefile.in @@ -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 diff --git a/lib/isccfg/aclconf.c b/lib/isccfg/aclconf.c index 2626609f9f..c5e451f6a9 100644 --- a/lib/isccfg/aclconf.c +++ b/lib/isccfg/aclconf.c @@ -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 @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -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++; } diff --git a/lib/isccfg/include/isccfg/aclconf.h b/lib/isccfg/include/isccfg/aclconf.h index ee1e8294d3..778a19c45b 100644 --- a/lib/isccfg/include/isccfg/aclconf.h +++ b/lib/isccfg/include/isccfg/aclconf.h @@ -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