2 * Copyright (C) 2004-2009, 2011, 2013, 2014 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 1999-2002 Internet Software Consortium.
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
18 /* $Id: acl.c,v 1.55 2011/06/17 23:47:49 tbox Exp $ */
26 #include <isc/string.h>
30 #include <dns/iptable.h>
33 * Create a new ACL, including an IP table and an array with room
34 * for 'n' ACL elements. The elements are uninitialized and the
38 dns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target) {
43 * Work around silly limitation of isc_mem_get().
48 acl = isc_mem_get(mctx, sizeof(*acl));
50 return (ISC_R_NOMEMORY);
53 isc_mem_attach(mctx, &acl->mctx);
57 result = isc_refcount_init(&acl->refcount, 1);
58 if (result != ISC_R_SUCCESS) {
59 isc_mem_put(mctx, acl, sizeof(*acl));
63 result = dns_iptable_create(mctx, &acl->iptable);
64 if (result != ISC_R_SUCCESS) {
65 isc_mem_put(mctx, acl, sizeof(*acl));
72 acl->has_negatives = ISC_FALSE;
74 ISC_LINK_INIT(acl, nextincache);
76 * Must set magic early because we use dns_acl_detach() to clean up.
78 acl->magic = DNS_ACL_MAGIC;
80 acl->elements = isc_mem_get(mctx, n * sizeof(dns_aclelement_t));
81 if (acl->elements == NULL) {
82 result = ISC_R_NOMEMORY;
86 memset(acl->elements, 0, n * sizeof(dns_aclelement_t));
88 return (ISC_R_SUCCESS);
96 * Create a new ACL and initialize it with the value "any" or "none",
97 * depending on the value of the "neg" parameter.
98 * "any" is a positive iptable entry with bit length 0.
99 * "none" is the same as "!any".
102 dns_acl_anyornone(isc_mem_t *mctx, isc_boolean_t neg, dns_acl_t **target) {
104 dns_acl_t *acl = NULL;
106 result = dns_acl_create(mctx, 0, &acl);
107 if (result != ISC_R_SUCCESS)
110 result = dns_iptable_addprefix(acl->iptable, NULL, 0, ISC_TF(!neg));
111 if (result != ISC_R_SUCCESS) {
112 dns_acl_detach(&acl);
121 * Create a new ACL that matches everything.
124 dns_acl_any(isc_mem_t *mctx, dns_acl_t **target) {
125 return (dns_acl_anyornone(mctx, ISC_FALSE, target));
129 * Create a new ACL that matches nothing.
132 dns_acl_none(isc_mem_t *mctx, dns_acl_t **target) {
133 return (dns_acl_anyornone(mctx, ISC_TRUE, target));
137 * If pos is ISC_TRUE, test whether acl is set to "{ any; }"
138 * If pos is ISC_FALSE, test whether acl is set to "{ none; }"
141 dns_acl_isanyornone(dns_acl_t *acl, isc_boolean_t pos)
143 /* Should never happen but let's be safe */
145 acl->iptable == NULL ||
146 acl->iptable->radix == NULL ||
147 acl->iptable->radix->head == NULL ||
148 acl->iptable->radix->head->prefix == NULL)
151 if (acl->length != 0 || acl->node_count != 1)
154 if (acl->iptable->radix->head->prefix->bitlen == 0 &&
155 acl->iptable->radix->head->data[0] != NULL &&
156 acl->iptable->radix->head->data[0] ==
157 acl->iptable->radix->head->data[1] &&
158 *(isc_boolean_t *) (acl->iptable->radix->head->data[0]) == pos)
161 return (ISC_FALSE); /* All others */
165 * Test whether acl is set to "{ any; }"
168 dns_acl_isany(dns_acl_t *acl)
170 return (dns_acl_isanyornone(acl, ISC_TRUE));
174 * Test whether acl is set to "{ none; }"
177 dns_acl_isnone(dns_acl_t *acl)
179 return (dns_acl_isanyornone(acl, ISC_FALSE));
183 * Determine whether a given address or signer matches a given ACL.
184 * For a match with a positive ACL element or iptable radix entry,
185 * return with a positive value in match; for a match with a negated ACL
186 * element or radix entry, return with a negative value in match.
189 dns_acl_match(const isc_netaddr_t *reqaddr,
190 const dns_name_t *reqsigner,
191 const dns_acl_t *acl,
192 const dns_aclenv_t *env,
194 const dns_aclelement_t **matchelt)
196 isc_uint16_t bitlen, family;
198 isc_radix_node_t *node = NULL;
199 const isc_netaddr_t *addr;
200 isc_netaddr_t v4addr;
205 REQUIRE(reqaddr != NULL);
206 REQUIRE(matchelt == NULL || *matchelt == NULL);
208 if (env == NULL || env->match_mapped == ISC_FALSE ||
209 reqaddr->family != AF_INET6 ||
210 !IN6_IS_ADDR_V4MAPPED(&reqaddr->type.in6))
213 isc_netaddr_fromv4mapped(&v4addr, reqaddr);
217 /* Always match with host addresses. */
218 family = addr->family;
219 bitlen = family == AF_INET6 ? 128 : 32;
220 NETADDR_TO_PREFIX_T(addr, pfx, bitlen);
222 /* Assume no match. */
226 result = isc_radix_search(acl->iptable->radix, &node, &pfx);
229 if (result == ISC_R_SUCCESS && node != NULL) {
230 match_num = node->node_num[ISC_IS6(family)];
231 if (*(isc_boolean_t *) node->data[ISC_IS6(family)] == ISC_TRUE)
237 /* Now search non-radix elements for a match with a lower node_num. */
238 for (i = 0; i < acl->length; i++) {
239 dns_aclelement_t *e = &acl->elements[i];
241 /* Already found a better match? */
242 if (match_num != -1 && match_num < e->node_num) {
243 isc_refcount_destroy(&pfx.refcount);
244 return (ISC_R_SUCCESS);
247 if (dns_aclelement_match(reqaddr, reqsigner,
249 if (match_num == -1 || e->node_num < match_num) {
250 if (e->negative == ISC_TRUE)
251 *match = -e->node_num;
253 *match = e->node_num;
255 isc_refcount_destroy(&pfx.refcount);
256 return (ISC_R_SUCCESS);
260 isc_refcount_destroy(&pfx.refcount);
261 return (ISC_R_SUCCESS);
265 * Merge the contents of one ACL into another. Call dns_iptable_merge()
266 * for the IP tables, then concatenate the element arrays.
268 * If pos is set to false, then the nested ACL is to be negated. This
269 * means reverse the sense of each *positive* element or IP table node,
270 * but leave negatives alone, so as to prevent a double-negative causing
271 * an unexpected positive match in the parent ACL.
274 dns_acl_merge(dns_acl_t *dest, dns_acl_t *source, isc_boolean_t pos)
277 unsigned int newalloc, nelem, i;
278 int max_node = 0, nodes;
280 /* Resize the element array if needed. */
281 if (dest->length + source->length > dest->alloc) {
284 newalloc = dest->alloc + source->alloc;
288 newmem = isc_mem_get(dest->mctx,
289 newalloc * sizeof(dns_aclelement_t));
291 return (ISC_R_NOMEMORY);
294 memset(newmem, 0, newalloc * sizeof(dns_aclelement_t));
296 /* Copy in the original elements */
297 memmove(newmem, dest->elements,
298 dest->length * sizeof(dns_aclelement_t));
300 /* Release the memory for the old elements array */
301 isc_mem_put(dest->mctx, dest->elements,
302 dest->alloc * sizeof(dns_aclelement_t));
303 dest->elements = newmem;
304 dest->alloc = newalloc;
308 * Now copy in the new elements, increasing their node_num
309 * values so as to keep the new ACL consistent. If we're
310 * negating, then negate positive elements, but keep negative
311 * elements the same for security reasons.
313 nelem = dest->length;
314 dest->length += source->length;
315 for (i = 0; i < source->length; i++) {
316 if (source->elements[i].node_num > max_node)
317 max_node = source->elements[i].node_num;
320 dest->elements[nelem + i].type = source->elements[i].type;
322 /* Adjust node numbering. */
323 dest->elements[nelem + i].node_num =
324 source->elements[i].node_num + dest->node_count;
326 /* Duplicate nested acl. */
327 if (source->elements[i].type == dns_aclelementtype_nestedacl &&
328 source->elements[i].nestedacl != NULL)
329 dns_acl_attach(source->elements[i].nestedacl,
330 &dest->elements[nelem + i].nestedacl);
332 /* Duplicate key name. */
333 if (source->elements[i].type == dns_aclelementtype_keyname) {
334 dns_name_init(&dest->elements[nelem+i].keyname, NULL);
335 result = dns_name_dup(&source->elements[i].keyname,
337 &dest->elements[nelem+i].keyname);
338 if (result != ISC_R_SUCCESS)
342 /* reverse sense of positives if this is a negative acl */
343 if (!pos && source->elements[i].negative == ISC_FALSE) {
344 dest->elements[nelem + i].negative = ISC_TRUE;
346 dest->elements[nelem + i].negative =
347 source->elements[i].negative;
352 * Merge the iptables. Make sure the destination ACL's
353 * node_count value is set correctly afterward.
355 nodes = max_node + dest->node_count;
356 result = dns_iptable_merge(dest->iptable, source->iptable, pos);
357 if (result != ISC_R_SUCCESS)
359 if (nodes > dest->node_count)
360 dest->node_count = nodes;
362 return (ISC_R_SUCCESS);
366 * Like dns_acl_match, but matches against the single ACL element 'e'
367 * rather than a complete ACL, and returns ISC_TRUE iff it matched.
369 * To determine whether the match was positive or negative, the
370 * caller should examine e->negative. Since the element 'e' may be
371 * a reference to a named ACL or a nested ACL, a matching element
372 * returned through 'matchelt' is not necessarily 'e' itself.
375 dns_aclelement_match(const isc_netaddr_t *reqaddr,
376 const dns_name_t *reqsigner,
377 const dns_aclelement_t *e,
378 const dns_aclenv_t *env,
379 const dns_aclelement_t **matchelt)
381 dns_acl_t *inner = NULL;
386 case dns_aclelementtype_keyname:
387 if (reqsigner != NULL &&
388 dns_name_equal(reqsigner, &e->keyname)) {
389 if (matchelt != NULL)
396 case dns_aclelementtype_nestedacl:
397 inner = e->nestedacl;
400 case dns_aclelementtype_localhost:
401 if (env == NULL || env->localhost == NULL)
403 inner = env->localhost;
406 case dns_aclelementtype_localnets:
407 if (env == NULL || env->localnets == NULL)
409 inner = env->localnets;
413 /* Should be impossible. */
417 result = dns_acl_match(reqaddr, reqsigner, inner, env,
418 &indirectmatch, matchelt);
419 INSIST(result == ISC_R_SUCCESS);
422 * Treat negative matches in indirect ACLs as "no match".
423 * That way, a negated indirect ACL will never become a
424 * surprise positive match through double negation.
425 * XXXDCL this should be documented.
428 if (indirectmatch > 0) {
429 if (matchelt != NULL)
435 * A negative indirect match may have set *matchelt, but we don't
436 * want it set when we return.
439 if (matchelt != NULL)
446 dns_acl_attach(dns_acl_t *source, dns_acl_t **target) {
447 REQUIRE(DNS_ACL_VALID(source));
449 isc_refcount_increment(&source->refcount, NULL);
454 destroy(dns_acl_t *dacl) {
457 INSIST(!ISC_LINK_LINKED(dacl, nextincache));
459 for (i = 0; i < dacl->length; i++) {
460 dns_aclelement_t *de = &dacl->elements[i];
461 if (de->type == dns_aclelementtype_keyname) {
462 dns_name_free(&de->keyname, dacl->mctx);
463 } else if (de->type == dns_aclelementtype_nestedacl) {
464 dns_acl_detach(&de->nestedacl);
467 if (dacl->elements != NULL)
468 isc_mem_put(dacl->mctx, dacl->elements,
469 dacl->alloc * sizeof(dns_aclelement_t));
470 if (dacl->name != NULL)
471 isc_mem_free(dacl->mctx, dacl->name);
472 if (dacl->iptable != NULL)
473 dns_iptable_detach(&dacl->iptable);
474 isc_refcount_destroy(&dacl->refcount);
476 isc_mem_putanddetach(&dacl->mctx, dacl, sizeof(*dacl));
480 dns_acl_detach(dns_acl_t **aclp) {
481 dns_acl_t *acl = *aclp;
484 REQUIRE(DNS_ACL_VALID(acl));
486 isc_refcount_decrement(&acl->refcount, &refs);
493 static isc_once_t insecure_prefix_once = ISC_ONCE_INIT;
494 static isc_mutex_t insecure_prefix_lock;
495 static isc_boolean_t insecure_prefix_found;
498 initialize_action(void) {
499 RUNTIME_CHECK(isc_mutex_init(&insecure_prefix_lock) == ISC_R_SUCCESS);
503 * Called via isc_radix_walk() to find IP table nodes that are
507 is_insecure(isc_prefix_t *prefix, void **data) {
508 isc_boolean_t secure;
511 bitlen = prefix->bitlen;
512 family = prefix->family;
514 /* Negated entries are always secure. */
515 secure = * (isc_boolean_t *)data[ISC_IS6(family)];
520 /* If loopback prefix found, return */
524 htonl(prefix->add.sin.s_addr) == INADDR_LOOPBACK)
528 if (bitlen == 128 && IN6_IS_ADDR_LOOPBACK(&prefix->add.sin6))
535 /* Non-negated, non-loopback */
536 insecure_prefix_found = ISC_TRUE; /* LOCKED */
541 * Return ISC_TRUE iff the acl 'a' is considered insecure, that is,
542 * if it contains IP addresses other than those of the local host.
543 * This is intended for applications such as printing warning
544 * messages for suspect ACLs; it is not intended for making access
545 * control decisions. We make no guarantee that an ACL for which
546 * this function returns ISC_FALSE is safe.
549 dns_acl_isinsecure(const dns_acl_t *a) {
551 isc_boolean_t insecure;
553 RUNTIME_CHECK(isc_once_do(&insecure_prefix_once,
554 initialize_action) == ISC_R_SUCCESS);
557 * Walk radix tree to find out if there are any non-negated,
558 * non-loopback prefixes.
560 LOCK(&insecure_prefix_lock);
561 insecure_prefix_found = ISC_FALSE;
562 isc_radix_process(a->iptable->radix, is_insecure);
563 insecure = insecure_prefix_found;
564 UNLOCK(&insecure_prefix_lock);
568 /* Now check non-radix elements */
569 for (i = 0; i < a->length; i++) {
570 dns_aclelement_t *e = &a->elements[i];
572 /* A negated match can never be insecure. */
577 case dns_aclelementtype_keyname:
578 case dns_aclelementtype_localhost:
581 case dns_aclelementtype_nestedacl:
582 if (dns_acl_isinsecure(e->nestedacl))
586 case dns_aclelementtype_localnets:
595 /* No insecure elements were found. */
600 * Initialize ACL environment, setting up localhost and localnets ACLs
603 dns_aclenv_init(isc_mem_t *mctx, dns_aclenv_t *env) {
606 env->localhost = NULL;
607 env->localnets = NULL;
608 result = dns_acl_create(mctx, 0, &env->localhost);
609 if (result != ISC_R_SUCCESS)
610 goto cleanup_nothing;
611 result = dns_acl_create(mctx, 0, &env->localnets);
612 if (result != ISC_R_SUCCESS)
613 goto cleanup_localhost;
614 env->match_mapped = ISC_FALSE;
615 return (ISC_R_SUCCESS);
618 dns_acl_detach(&env->localhost);
624 dns_aclenv_copy(dns_aclenv_t *t, dns_aclenv_t *s) {
625 dns_acl_detach(&t->localhost);
626 dns_acl_attach(s->localhost, &t->localhost);
627 dns_acl_detach(&t->localnets);
628 dns_acl_attach(s->localnets, &t->localnets);
629 t->match_mapped = s->match_mapped;
633 dns_aclenv_destroy(dns_aclenv_t *env) {
634 dns_acl_detach(&env->localhost);
635 dns_acl_detach(&env->localnets);