+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]
* 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 */
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);
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);
}
* 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 */
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);
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) {
"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);
}
* 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 */
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);
}
"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);
{
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;
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);
* 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 */
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;
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 &&
* 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 $ */
/*% */
}
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);
- 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>
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>
<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>
<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>
<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>
* 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 */
}
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);
continue;
tresult = cfg_acl_fromconfig(aclobj, config, logctx,
- actx, mctx, &acl);
+ actx, mctx, 0, &acl);
if (tresult != ISC_R_SUCCESS)
result = tresult;
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)
}
}
- 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;
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"));
"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>";
# 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@
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@ \
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 \
* 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;
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;
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,
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);
}
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)
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));
*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];
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;
# 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@
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 \
* 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
#include <dns/name.h>
#include <dns/types.h>
+#include <dns/iptable.h>
/***
*** Types
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 */
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
* 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);
/*%<
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);
* 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.
*/
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 */
* 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
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;
* 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 */
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))));
}
# 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@
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@ \
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
* 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>
#include <isccfg/aclconf.h>
#include <dns/acl.h>
+#include <dns/iptable.h>
#include <dns/fixedname.h>
#include <dns/log.h>
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)
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);
}
}
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;
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;
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);
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 "
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++;
}
* 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
typedef struct cfg_aclconfctx {
ISC_LIST(dns_acl_t) named_acl_cache;
+ ISC_LIST(dns_iptable_t) named_iptable_cache;
} cfg_aclconfctx_t;
/***
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