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);
293 /* Copy in the original elements */
294 memmove(newmem, dest->elements,
295 dest->length * sizeof(dns_aclelement_t));
297 /* Release the memory for the old elements array */
298 isc_mem_put(dest->mctx, dest->elements,
299 dest->alloc * sizeof(dns_aclelement_t));
300 dest->elements = newmem;
301 dest->alloc = newalloc;
305 * Now copy in the new elements, increasing their node_num
306 * values so as to keep the new ACL consistent. If we're
307 * negating, then negate positive elements, but keep negative
308 * elements the same for security reasons.
310 nelem = dest->length;
311 dest->length += source->length;
312 for (i = 0; i < source->length; i++) {
313 if (source->elements[i].node_num > max_node)
314 max_node = source->elements[i].node_num;
317 dest->elements[nelem + i].type = source->elements[i].type;
319 /* Adjust node numbering. */
320 dest->elements[nelem + i].node_num =
321 source->elements[i].node_num + dest->node_count;
323 /* Duplicate nested acl. */
324 if (source->elements[i].type == dns_aclelementtype_nestedacl &&
325 source->elements[i].nestedacl != NULL)
326 dns_acl_attach(source->elements[i].nestedacl,
327 &dest->elements[nelem + i].nestedacl);
329 /* Duplicate key name. */
330 if (source->elements[i].type == dns_aclelementtype_keyname) {
331 dns_name_init(&dest->elements[nelem+i].keyname, NULL);
332 result = dns_name_dup(&source->elements[i].keyname,
334 &dest->elements[nelem+i].keyname);
335 if (result != ISC_R_SUCCESS)
339 /* reverse sense of positives if this is a negative acl */
340 if (!pos && source->elements[i].negative == ISC_FALSE) {
341 dest->elements[nelem + i].negative = ISC_TRUE;
343 dest->elements[nelem + i].negative =
344 source->elements[i].negative;
349 * Merge the iptables. Make sure the destination ACL's
350 * node_count value is set correctly afterward.
352 nodes = max_node + dest->node_count;
353 result = dns_iptable_merge(dest->iptable, source->iptable, pos);
354 if (result != ISC_R_SUCCESS)
356 if (nodes > dest->node_count)
357 dest->node_count = nodes;
359 return (ISC_R_SUCCESS);
363 * Like dns_acl_match, but matches against the single ACL element 'e'
364 * rather than a complete ACL, and returns ISC_TRUE iff it matched.
366 * To determine whether the match was positive or negative, the
367 * caller should examine e->negative. Since the element 'e' may be
368 * a reference to a named ACL or a nested ACL, a matching element
369 * returned through 'matchelt' is not necessarily 'e' itself.
372 dns_aclelement_match(const isc_netaddr_t *reqaddr,
373 const dns_name_t *reqsigner,
374 const dns_aclelement_t *e,
375 const dns_aclenv_t *env,
376 const dns_aclelement_t **matchelt)
378 dns_acl_t *inner = NULL;
383 case dns_aclelementtype_keyname:
384 if (reqsigner != NULL &&
385 dns_name_equal(reqsigner, &e->keyname)) {
386 if (matchelt != NULL)
393 case dns_aclelementtype_nestedacl:
394 inner = e->nestedacl;
397 case dns_aclelementtype_localhost:
398 if (env == NULL || env->localhost == NULL)
400 inner = env->localhost;
403 case dns_aclelementtype_localnets:
404 if (env == NULL || env->localnets == NULL)
406 inner = env->localnets;
410 /* Should be impossible. */
414 result = dns_acl_match(reqaddr, reqsigner, inner, env,
415 &indirectmatch, matchelt);
416 INSIST(result == ISC_R_SUCCESS);
419 * Treat negative matches in indirect ACLs as "no match".
420 * That way, a negated indirect ACL will never become a
421 * surprise positive match through double negation.
422 * XXXDCL this should be documented.
425 if (indirectmatch > 0) {
426 if (matchelt != NULL)
432 * A negative indirect match may have set *matchelt, but we don't
433 * want it set when we return.
436 if (matchelt != NULL)
443 dns_acl_attach(dns_acl_t *source, dns_acl_t **target) {
444 REQUIRE(DNS_ACL_VALID(source));
446 isc_refcount_increment(&source->refcount, NULL);
451 destroy(dns_acl_t *dacl) {
454 INSIST(!ISC_LINK_LINKED(dacl, nextincache));
456 for (i = 0; i < dacl->length; i++) {
457 dns_aclelement_t *de = &dacl->elements[i];
458 if (de->type == dns_aclelementtype_keyname) {
459 dns_name_free(&de->keyname, dacl->mctx);
460 } else if (de->type == dns_aclelementtype_nestedacl) {
461 dns_acl_detach(&de->nestedacl);
464 if (dacl->elements != NULL)
465 isc_mem_put(dacl->mctx, dacl->elements,
466 dacl->alloc * sizeof(dns_aclelement_t));
467 if (dacl->name != NULL)
468 isc_mem_free(dacl->mctx, dacl->name);
469 if (dacl->iptable != NULL)
470 dns_iptable_detach(&dacl->iptable);
471 isc_refcount_destroy(&dacl->refcount);
473 isc_mem_putanddetach(&dacl->mctx, dacl, sizeof(*dacl));
477 dns_acl_detach(dns_acl_t **aclp) {
478 dns_acl_t *acl = *aclp;
481 REQUIRE(DNS_ACL_VALID(acl));
483 isc_refcount_decrement(&acl->refcount, &refs);
490 static isc_once_t insecure_prefix_once = ISC_ONCE_INIT;
491 static isc_mutex_t insecure_prefix_lock;
492 static isc_boolean_t insecure_prefix_found;
495 initialize_action(void) {
496 RUNTIME_CHECK(isc_mutex_init(&insecure_prefix_lock) == ISC_R_SUCCESS);
500 * Called via isc_radix_walk() to find IP table nodes that are
504 is_insecure(isc_prefix_t *prefix, void **data) {
505 isc_boolean_t secure;
508 bitlen = prefix->bitlen;
509 family = prefix->family;
511 /* Negated entries are always secure. */
512 secure = * (isc_boolean_t *)data[ISC_IS6(family)];
517 /* If loopback prefix found, return */
521 htonl(prefix->add.sin.s_addr) == INADDR_LOOPBACK)
525 if (bitlen == 128 && IN6_IS_ADDR_LOOPBACK(&prefix->add.sin6))
532 /* Non-negated, non-loopback */
533 insecure_prefix_found = ISC_TRUE; /* LOCKED */
538 * Return ISC_TRUE iff the acl 'a' is considered insecure, that is,
539 * if it contains IP addresses other than those of the local host.
540 * This is intended for applications such as printing warning
541 * messages for suspect ACLs; it is not intended for making access
542 * control decisions. We make no guarantee that an ACL for which
543 * this function returns ISC_FALSE is safe.
546 dns_acl_isinsecure(const dns_acl_t *a) {
548 isc_boolean_t insecure;
550 RUNTIME_CHECK(isc_once_do(&insecure_prefix_once,
551 initialize_action) == ISC_R_SUCCESS);
554 * Walk radix tree to find out if there are any non-negated,
555 * non-loopback prefixes.
557 LOCK(&insecure_prefix_lock);
558 insecure_prefix_found = ISC_FALSE;
559 isc_radix_process(a->iptable->radix, is_insecure);
560 insecure = insecure_prefix_found;
561 UNLOCK(&insecure_prefix_lock);
565 /* Now check non-radix elements */
566 for (i = 0; i < a->length; i++) {
567 dns_aclelement_t *e = &a->elements[i];
569 /* A negated match can never be insecure. */
574 case dns_aclelementtype_keyname:
575 case dns_aclelementtype_localhost:
578 case dns_aclelementtype_nestedacl:
579 if (dns_acl_isinsecure(e->nestedacl))
583 case dns_aclelementtype_localnets:
592 /* No insecure elements were found. */
597 * Initialize ACL environment, setting up localhost and localnets ACLs
600 dns_aclenv_init(isc_mem_t *mctx, dns_aclenv_t *env) {
603 env->localhost = NULL;
604 env->localnets = NULL;
605 result = dns_acl_create(mctx, 0, &env->localhost);
606 if (result != ISC_R_SUCCESS)
607 goto cleanup_nothing;
608 result = dns_acl_create(mctx, 0, &env->localnets);
609 if (result != ISC_R_SUCCESS)
610 goto cleanup_localhost;
611 env->match_mapped = ISC_FALSE;
612 return (ISC_R_SUCCESS);
615 dns_acl_detach(&env->localhost);
621 dns_aclenv_copy(dns_aclenv_t *t, dns_aclenv_t *s) {
622 dns_acl_detach(&t->localhost);
623 dns_acl_attach(s->localhost, &t->localhost);
624 dns_acl_detach(&t->localnets);
625 dns_acl_attach(s->localnets, &t->localnets);
626 t->match_mapped = s->match_mapped;
630 dns_aclenv_destroy(dns_aclenv_t *env) {
631 dns_acl_detach(&env->localhost);
632 dns_acl_detach(&env->localnets);