2 * Copyright (C) 2004-2009, 2011 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.53.426.2 2011-06-17 23:47:11 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);
54 result = isc_refcount_init(&acl->refcount, 1);
55 if (result != ISC_R_SUCCESS) {
56 isc_mem_put(mctx, acl, sizeof(*acl));
60 result = dns_iptable_create(mctx, &acl->iptable);
61 if (result != ISC_R_SUCCESS) {
62 isc_mem_put(mctx, acl, sizeof(*acl));
69 acl->has_negatives = ISC_FALSE;
71 ISC_LINK_INIT(acl, nextincache);
73 * Must set magic early because we use dns_acl_detach() to clean up.
75 acl->magic = DNS_ACL_MAGIC;
77 acl->elements = isc_mem_get(mctx, n * sizeof(dns_aclelement_t));
78 if (acl->elements == NULL) {
79 result = ISC_R_NOMEMORY;
83 memset(acl->elements, 0, n * sizeof(dns_aclelement_t));
85 return (ISC_R_SUCCESS);
93 * Create a new ACL and initialize it with the value "any" or "none",
94 * depending on the value of the "neg" parameter.
95 * "any" is a positive iptable entry with bit length 0.
96 * "none" is the same as "!any".
99 dns_acl_anyornone(isc_mem_t *mctx, isc_boolean_t neg, dns_acl_t **target) {
101 dns_acl_t *acl = NULL;
103 result = dns_acl_create(mctx, 0, &acl);
104 if (result != ISC_R_SUCCESS)
107 result = dns_iptable_addprefix(acl->iptable, NULL, 0, ISC_TF(!neg));
108 if (result != ISC_R_SUCCESS) {
109 dns_acl_detach(&acl);
118 * Create a new ACL that matches everything.
121 dns_acl_any(isc_mem_t *mctx, dns_acl_t **target) {
122 return (dns_acl_anyornone(mctx, ISC_FALSE, target));
126 * Create a new ACL that matches nothing.
129 dns_acl_none(isc_mem_t *mctx, dns_acl_t **target) {
130 return (dns_acl_anyornone(mctx, ISC_TRUE, target));
134 * If pos is ISC_TRUE, test whether acl is set to "{ any; }"
135 * If pos is ISC_FALSE, test whether acl is set to "{ none; }"
138 dns_acl_isanyornone(dns_acl_t *acl, isc_boolean_t pos)
140 /* Should never happen but let's be safe */
142 acl->iptable == NULL ||
143 acl->iptable->radix == NULL ||
144 acl->iptable->radix->head == NULL ||
145 acl->iptable->radix->head->prefix == NULL)
148 if (acl->length != 0 || acl->node_count != 1)
151 if (acl->iptable->radix->head->prefix->bitlen == 0 &&
152 acl->iptable->radix->head->data[0] != NULL &&
153 acl->iptable->radix->head->data[0] ==
154 acl->iptable->radix->head->data[1] &&
155 *(isc_boolean_t *) (acl->iptable->radix->head->data[0]) == pos)
158 return (ISC_FALSE); /* All others */
162 * Test whether acl is set to "{ any; }"
165 dns_acl_isany(dns_acl_t *acl)
167 return (dns_acl_isanyornone(acl, ISC_TRUE));
171 * Test whether acl is set to "{ none; }"
174 dns_acl_isnone(dns_acl_t *acl)
176 return (dns_acl_isanyornone(acl, ISC_FALSE));
180 * Determine whether a given address or signer matches a given ACL.
181 * For a match with a positive ACL element or iptable radix entry,
182 * return with a positive value in match; for a match with a negated ACL
183 * element or radix entry, return with a negative value in match.
186 dns_acl_match(const isc_netaddr_t *reqaddr,
187 const dns_name_t *reqsigner,
188 const dns_acl_t *acl,
189 const dns_aclenv_t *env,
191 const dns_aclelement_t **matchelt)
193 isc_uint16_t bitlen, family;
195 isc_radix_node_t *node = NULL;
196 const isc_netaddr_t *addr;
197 isc_netaddr_t v4addr;
202 REQUIRE(reqaddr != NULL);
203 REQUIRE(matchelt == NULL || *matchelt == NULL);
205 if (env == NULL || env->match_mapped == ISC_FALSE ||
206 reqaddr->family != AF_INET6 ||
207 !IN6_IS_ADDR_V4MAPPED(&reqaddr->type.in6))
210 isc_netaddr_fromv4mapped(&v4addr, reqaddr);
214 /* Always match with host addresses. */
215 family = addr->family;
216 bitlen = family == AF_INET6 ? 128 : 32;
217 NETADDR_TO_PREFIX_T(addr, pfx, bitlen);
219 /* Assume no match. */
223 result = isc_radix_search(acl->iptable->radix, &node, &pfx);
226 if (result == ISC_R_SUCCESS && node != NULL) {
227 match_num = node->node_num[ISC_IS6(family)];
228 if (*(isc_boolean_t *) node->data[ISC_IS6(family)] == ISC_TRUE)
234 /* Now search non-radix elements for a match with a lower node_num. */
235 for (i = 0; i < acl->length; i++) {
236 dns_aclelement_t *e = &acl->elements[i];
238 /* Already found a better match? */
239 if (match_num != -1 && match_num < e->node_num) {
240 isc_refcount_destroy(&pfx.refcount);
241 return (ISC_R_SUCCESS);
244 if (dns_aclelement_match(reqaddr, reqsigner,
246 if (match_num == -1 || e->node_num < match_num) {
247 if (e->negative == ISC_TRUE)
248 *match = -e->node_num;
250 *match = e->node_num;
252 isc_refcount_destroy(&pfx.refcount);
253 return (ISC_R_SUCCESS);
257 isc_refcount_destroy(&pfx.refcount);
258 return (ISC_R_SUCCESS);
262 * Merge the contents of one ACL into another. Call dns_iptable_merge()
263 * for the IP tables, then concatenate the element arrays.
265 * If pos is set to false, then the nested ACL is to be negated. This
266 * means reverse the sense of each *positive* element or IP table node,
267 * but leave negatives alone, so as to prevent a double-negative causing
268 * an unexpected positive match in the parent ACL.
271 dns_acl_merge(dns_acl_t *dest, dns_acl_t *source, isc_boolean_t pos)
274 unsigned int newalloc, nelem, i;
275 int max_node = 0, nodes;
277 /* Resize the element array if needed. */
278 if (dest->length + source->length > dest->alloc) {
281 newalloc = dest->alloc + source->alloc;
285 newmem = isc_mem_get(dest->mctx,
286 newalloc * sizeof(dns_aclelement_t));
288 return (ISC_R_NOMEMORY);
290 /* Copy in the original elements */
291 memcpy(newmem, dest->elements,
292 dest->length * sizeof(dns_aclelement_t));
294 /* Release the memory for the old elements array */
295 isc_mem_put(dest->mctx, dest->elements,
296 dest->alloc * sizeof(dns_aclelement_t));
297 dest->elements = newmem;
298 dest->alloc = newalloc;
302 * Now copy in the new elements, increasing their node_num
303 * values so as to keep the new ACL consistent. If we're
304 * negating, then negate positive elements, but keep negative
305 * elements the same for security reasons.
307 nelem = dest->length;
308 dest->length += source->length;
309 for (i = 0; i < source->length; i++) {
310 if (source->elements[i].node_num > max_node)
311 max_node = source->elements[i].node_num;
314 dest->elements[nelem + i].type = source->elements[i].type;
316 /* Adjust node numbering. */
317 dest->elements[nelem + i].node_num =
318 source->elements[i].node_num + dest->node_count;
320 /* Duplicate nested acl. */
321 if (source->elements[i].type == dns_aclelementtype_nestedacl &&
322 source->elements[i].nestedacl != NULL)
323 dns_acl_attach(source->elements[i].nestedacl,
324 &dest->elements[nelem + i].nestedacl);
326 /* Duplicate key name. */
327 if (source->elements[i].type == dns_aclelementtype_keyname) {
328 dns_name_init(&dest->elements[nelem+i].keyname, NULL);
329 result = dns_name_dup(&source->elements[i].keyname,
331 &dest->elements[nelem+i].keyname);
332 if (result != ISC_R_SUCCESS)
336 /* reverse sense of positives if this is a negative acl */
337 if (!pos && source->elements[i].negative == ISC_FALSE) {
338 dest->elements[nelem + i].negative = ISC_TRUE;
340 dest->elements[nelem + i].negative =
341 source->elements[i].negative;
346 * Merge the iptables. Make sure the destination ACL's
347 * node_count value is set correctly afterward.
349 nodes = max_node + dest->node_count;
350 result = dns_iptable_merge(dest->iptable, source->iptable, pos);
351 if (result != ISC_R_SUCCESS)
353 if (nodes > dest->node_count)
354 dest->node_count = nodes;
356 return (ISC_R_SUCCESS);
360 * Like dns_acl_match, but matches against the single ACL element 'e'
361 * rather than a complete ACL, and returns ISC_TRUE iff it matched.
363 * To determine whether the match was positive or negative, the
364 * caller should examine e->negative. Since the element 'e' may be
365 * a reference to a named ACL or a nested ACL, a matching element
366 * returned through 'matchelt' is not necessarily 'e' itself.
369 dns_aclelement_match(const isc_netaddr_t *reqaddr,
370 const dns_name_t *reqsigner,
371 const dns_aclelement_t *e,
372 const dns_aclenv_t *env,
373 const dns_aclelement_t **matchelt)
375 dns_acl_t *inner = NULL;
380 case dns_aclelementtype_keyname:
381 if (reqsigner != NULL &&
382 dns_name_equal(reqsigner, &e->keyname)) {
383 if (matchelt != NULL)
390 case dns_aclelementtype_nestedacl:
391 inner = e->nestedacl;
394 case dns_aclelementtype_localhost:
395 if (env == NULL || env->localhost == NULL)
397 inner = env->localhost;
400 case dns_aclelementtype_localnets:
401 if (env == NULL || env->localnets == NULL)
403 inner = env->localnets;
407 /* Should be impossible. */
411 result = dns_acl_match(reqaddr, reqsigner, inner, env,
412 &indirectmatch, matchelt);
413 INSIST(result == ISC_R_SUCCESS);
416 * Treat negative matches in indirect ACLs as "no match".
417 * That way, a negated indirect ACL will never become a
418 * surprise positive match through double negation.
419 * XXXDCL this should be documented.
422 if (indirectmatch > 0) {
423 if (matchelt != NULL)
429 * A negative indirect match may have set *matchelt, but we don't
430 * want it set when we return.
433 if (matchelt != NULL)
440 dns_acl_attach(dns_acl_t *source, dns_acl_t **target) {
441 REQUIRE(DNS_ACL_VALID(source));
443 isc_refcount_increment(&source->refcount, NULL);
448 destroy(dns_acl_t *dacl) {
451 INSIST(!ISC_LINK_LINKED(dacl, nextincache));
453 for (i = 0; i < dacl->length; i++) {
454 dns_aclelement_t *de = &dacl->elements[i];
455 if (de->type == dns_aclelementtype_keyname) {
456 dns_name_free(&de->keyname, dacl->mctx);
457 } else if (de->type == dns_aclelementtype_nestedacl) {
458 dns_acl_detach(&de->nestedacl);
461 if (dacl->elements != NULL)
462 isc_mem_put(dacl->mctx, dacl->elements,
463 dacl->alloc * sizeof(dns_aclelement_t));
464 if (dacl->name != NULL)
465 isc_mem_free(dacl->mctx, dacl->name);
466 if (dacl->iptable != NULL)
467 dns_iptable_detach(&dacl->iptable);
468 isc_refcount_destroy(&dacl->refcount);
470 isc_mem_put(dacl->mctx, dacl, sizeof(*dacl));
474 dns_acl_detach(dns_acl_t **aclp) {
475 dns_acl_t *acl = *aclp;
478 REQUIRE(DNS_ACL_VALID(acl));
480 isc_refcount_decrement(&acl->refcount, &refs);
487 static isc_once_t insecure_prefix_once = ISC_ONCE_INIT;
488 static isc_mutex_t insecure_prefix_lock;
489 static isc_boolean_t insecure_prefix_found;
492 initialize_action(void) {
493 RUNTIME_CHECK(isc_mutex_init(&insecure_prefix_lock) == ISC_R_SUCCESS);
497 * Called via isc_radix_walk() to find IP table nodes that are
501 is_insecure(isc_prefix_t *prefix, void **data) {
502 isc_boolean_t secure;
505 bitlen = prefix->bitlen;
506 family = prefix->family;
508 /* Negated entries are always secure. */
509 secure = * (isc_boolean_t *)data[ISC_IS6(family)];
514 /* If loopback prefix found, return */
518 htonl(prefix->add.sin.s_addr) == INADDR_LOOPBACK)
522 if (bitlen == 128 && IN6_IS_ADDR_LOOPBACK(&prefix->add.sin6))
529 /* Non-negated, non-loopback */
530 insecure_prefix_found = ISC_TRUE; /* LOCKED */
535 * Return ISC_TRUE iff the acl 'a' is considered insecure, that is,
536 * if it contains IP addresses other than those of the local host.
537 * This is intended for applications such as printing warning
538 * messages for suspect ACLs; it is not intended for making access
539 * control decisions. We make no guarantee that an ACL for which
540 * this function returns ISC_FALSE is safe.
543 dns_acl_isinsecure(const dns_acl_t *a) {
545 isc_boolean_t insecure;
547 RUNTIME_CHECK(isc_once_do(&insecure_prefix_once,
548 initialize_action) == ISC_R_SUCCESS);
551 * Walk radix tree to find out if there are any non-negated,
552 * non-loopback prefixes.
554 LOCK(&insecure_prefix_lock);
555 insecure_prefix_found = ISC_FALSE;
556 isc_radix_process(a->iptable->radix, is_insecure);
557 insecure = insecure_prefix_found;
558 UNLOCK(&insecure_prefix_lock);
562 /* Now check non-radix elements */
563 for (i = 0; i < a->length; i++) {
564 dns_aclelement_t *e = &a->elements[i];
566 /* A negated match can never be insecure. */
571 case dns_aclelementtype_keyname:
572 case dns_aclelementtype_localhost:
575 case dns_aclelementtype_nestedacl:
576 if (dns_acl_isinsecure(e->nestedacl))
580 case dns_aclelementtype_localnets:
589 /* No insecure elements were found. */
594 * Initialize ACL environment, setting up localhost and localnets ACLs
597 dns_aclenv_init(isc_mem_t *mctx, dns_aclenv_t *env) {
600 env->localhost = NULL;
601 env->localnets = NULL;
602 result = dns_acl_create(mctx, 0, &env->localhost);
603 if (result != ISC_R_SUCCESS)
604 goto cleanup_nothing;
605 result = dns_acl_create(mctx, 0, &env->localnets);
606 if (result != ISC_R_SUCCESS)
607 goto cleanup_localhost;
608 env->match_mapped = ISC_FALSE;
609 return (ISC_R_SUCCESS);
612 dns_acl_detach(&env->localhost);
618 dns_aclenv_copy(dns_aclenv_t *t, dns_aclenv_t *s) {
619 dns_acl_detach(&t->localhost);
620 dns_acl_attach(s->localhost, &t->localhost);
621 dns_acl_detach(&t->localnets);
622 dns_acl_attach(s->localnets, &t->localnets);
623 t->match_mapped = s->match_mapped;
627 dns_aclenv_destroy(dns_aclenv_t *env) {
628 dns_acl_detach(&env->localhost);
629 dns_acl_detach(&env->localnets);