2 * Copyright (C) 2004-2009 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.50.44.3 2009/01/18 23:47:35 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;
102 result = dns_acl_create(mctx, 0, &acl);
103 if (result != ISC_R_SUCCESS)
106 result = dns_iptable_addprefix(acl->iptable, NULL, 0, ISC_TF(!neg));
107 if (result != ISC_R_SUCCESS) {
108 dns_acl_detach(&acl);
117 * Create a new ACL that matches everything.
120 dns_acl_any(isc_mem_t *mctx, dns_acl_t **target) {
121 return (dns_acl_anyornone(mctx, ISC_FALSE, target));
125 * Create a new ACL that matches nothing.
128 dns_acl_none(isc_mem_t *mctx, dns_acl_t **target) {
129 return (dns_acl_anyornone(mctx, ISC_TRUE, target));
133 * If pos is ISC_TRUE, test whether acl is set to "{ any; }"
134 * If pos is ISC_FALSE, test whether acl is set to "{ none; }"
137 dns_acl_isanyornone(dns_acl_t *acl, isc_boolean_t pos)
139 /* Should never happen but let's be safe */
141 acl->iptable == NULL ||
142 acl->iptable->radix == NULL ||
143 acl->iptable->radix->head == NULL ||
144 acl->iptable->radix->head->prefix == NULL)
147 if (acl->length != 0 || acl->node_count != 1)
150 if (acl->iptable->radix->head->prefix->bitlen == 0 &&
151 acl->iptable->radix->head->data[0] != NULL &&
152 acl->iptable->radix->head->data[0] ==
153 acl->iptable->radix->head->data[1] &&
154 *(isc_boolean_t *) (acl->iptable->radix->head->data[0]) == pos)
157 return (ISC_FALSE); /* All others */
161 * Test whether acl is set to "{ any; }"
164 dns_acl_isany(dns_acl_t *acl)
166 return (dns_acl_isanyornone(acl, ISC_TRUE));
170 * Test whether acl is set to "{ none; }"
173 dns_acl_isnone(dns_acl_t *acl)
175 return (dns_acl_isanyornone(acl, ISC_FALSE));
179 * Determine whether a given address or signer matches a given ACL.
180 * For a match with a positive ACL element or iptable radix entry,
181 * return with a positive value in match; for a match with a negated ACL
182 * element or radix entry, return with a negative value in match.
185 dns_acl_match(const isc_netaddr_t *reqaddr,
186 const dns_name_t *reqsigner,
187 const dns_acl_t *acl,
188 const dns_aclenv_t *env,
190 const dns_aclelement_t **matchelt)
192 isc_uint16_t bitlen, family;
194 isc_radix_node_t *node = NULL;
195 const isc_netaddr_t *addr;
196 isc_netaddr_t v4addr;
201 REQUIRE(reqaddr != NULL);
202 REQUIRE(matchelt == NULL || *matchelt == NULL);
204 if (env == NULL || env->match_mapped == ISC_FALSE ||
205 reqaddr->family != AF_INET6 ||
206 !IN6_IS_ADDR_V4MAPPED(&reqaddr->type.in6))
209 isc_netaddr_fromv4mapped(&v4addr, reqaddr);
213 /* Always match with host addresses. */
214 family = addr->family;
215 bitlen = family == AF_INET6 ? 128 : 32;
216 NETADDR_TO_PREFIX_T(addr, pfx, bitlen);
218 /* Assume no match. */
222 result = isc_radix_search(acl->iptable->radix, &node, &pfx);
225 if (result == ISC_R_SUCCESS && node != NULL) {
226 match_num = node->node_num[ISC_IS6(family)];
227 if (*(isc_boolean_t *) node->data[ISC_IS6(family)] == ISC_TRUE)
233 /* Now search non-radix elements for a match with a lower node_num. */
234 for (i = 0; i < acl->length; i++) {
235 dns_aclelement_t *e = &acl->elements[i];
237 /* Already found a better match? */
238 if (match_num != -1 && match_num < e->node_num) {
239 isc_refcount_destroy(&pfx.refcount);
240 return (ISC_R_SUCCESS);
243 if (dns_aclelement_match(reqaddr, reqsigner,
245 if (match_num == -1 || e->node_num < match_num) {
246 if (e->negative == ISC_TRUE)
247 *match = -e->node_num;
249 *match = e->node_num;
251 isc_refcount_destroy(&pfx.refcount);
252 return (ISC_R_SUCCESS);
256 isc_refcount_destroy(&pfx.refcount);
257 return (ISC_R_SUCCESS);
261 * Merge the contents of one ACL into another. Call dns_iptable_merge()
262 * for the IP tables, then concatenate the element arrays.
264 * If pos is set to false, then the nested ACL is to be negated. This
265 * means reverse the sense of each *positive* element or IP table node,
266 * but leave negatives alone, so as to prevent a double-negative causing
267 * an unexpected positive match in the parent ACL.
270 dns_acl_merge(dns_acl_t *dest, dns_acl_t *source, isc_boolean_t pos)
273 unsigned int newalloc, nelem, i;
274 int max_node = 0, nodes;
276 /* Resize the element array if needed. */
277 if (dest->length + source->length > dest->alloc) {
280 newalloc = dest->alloc + source->alloc;
284 newmem = isc_mem_get(dest->mctx,
285 newalloc * sizeof(dns_aclelement_t));
287 return (ISC_R_NOMEMORY);
289 /* Copy in the original elements */
290 memcpy(newmem, dest->elements,
291 dest->length * sizeof(dns_aclelement_t));
293 /* Release the memory for the old elements array */
294 isc_mem_put(dest->mctx, dest->elements,
295 dest->alloc * sizeof(dns_aclelement_t));
296 dest->elements = newmem;
297 dest->alloc = newalloc;
301 * Now copy in the new elements, increasing their node_num
302 * values so as to keep the new ACL consistent. If we're
303 * negating, then negate positive elements, but keep negative
304 * elements the same for security reasons.
306 nelem = dest->length;
307 dest->length += source->length;
308 for (i = 0; i < source->length; i++) {
309 if (source->elements[i].node_num > max_node)
310 max_node = source->elements[i].node_num;
313 dest->elements[nelem + i].type = source->elements[i].type;
315 /* Adjust node numbering. */
316 dest->elements[nelem + i].node_num =
317 source->elements[i].node_num + dest->node_count;
319 /* Duplicate nested acl. */
320 if (source->elements[i].type == dns_aclelementtype_nestedacl &&
321 source->elements[i].nestedacl != NULL)
322 dns_acl_attach(source->elements[i].nestedacl,
323 &dest->elements[nelem + i].nestedacl);
325 /* Duplicate key name. */
326 if (source->elements[i].type == dns_aclelementtype_keyname) {
327 dns_name_init(&dest->elements[nelem+i].keyname, NULL);
328 result = dns_name_dup(&source->elements[i].keyname,
330 &dest->elements[nelem+i].keyname);
331 if (result != ISC_R_SUCCESS)
335 /* reverse sense of positives if this is a negative acl */
336 if (!pos && source->elements[i].negative == ISC_FALSE) {
337 dest->elements[nelem + i].negative = ISC_TRUE;
339 dest->elements[nelem + i].negative =
340 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));
442 isc_refcount_increment(&source->refcount, NULL);
447 destroy(dns_acl_t *dacl) {
449 for (i = 0; i < dacl->length; i++) {
450 dns_aclelement_t *de = &dacl->elements[i];
451 if (de->type == dns_aclelementtype_keyname) {
452 dns_name_free(&de->keyname, dacl->mctx);
453 } else if (de->type == dns_aclelementtype_nestedacl) {
454 dns_acl_detach(&de->nestedacl);
457 if (dacl->elements != NULL)
458 isc_mem_put(dacl->mctx, dacl->elements,
459 dacl->alloc * sizeof(dns_aclelement_t));
460 if (dacl->name != NULL)
461 isc_mem_free(dacl->mctx, dacl->name);
462 if (dacl->iptable != NULL)
463 dns_iptable_detach(&dacl->iptable);
464 isc_refcount_destroy(&dacl->refcount);
466 isc_mem_put(dacl->mctx, dacl, sizeof(*dacl));
470 dns_acl_detach(dns_acl_t **aclp) {
471 dns_acl_t *acl = *aclp;
473 REQUIRE(DNS_ACL_VALID(acl));
474 isc_refcount_decrement(&acl->refcount, &refs);
481 static isc_once_t insecure_prefix_once = ISC_ONCE_INIT;
482 static isc_mutex_t insecure_prefix_lock;
483 static isc_boolean_t insecure_prefix_found;
486 initialize_action(void) {
487 RUNTIME_CHECK(isc_mutex_init(&insecure_prefix_lock) == ISC_R_SUCCESS);
491 * Called via isc_radix_walk() to find IP table nodes that are
495 is_insecure(isc_prefix_t *prefix, void **data) {
496 isc_boolean_t secure;
499 bitlen = prefix->bitlen;
500 family = prefix->family;
502 /* Negated entries are always secure. */
503 secure = * (isc_boolean_t *)data[ISC_IS6(family)];
508 /* If loopback prefix found, return */
512 htonl(prefix->add.sin.s_addr) == INADDR_LOOPBACK)
516 if (bitlen == 128 && IN6_IS_ADDR_LOOPBACK(&prefix->add.sin6))
523 /* Non-negated, non-loopback */
524 insecure_prefix_found = ISC_TRUE; /* LOCKED */
529 * Return ISC_TRUE iff the acl 'a' is considered insecure, that is,
530 * if it contains IP addresses other than those of the local host.
531 * This is intended for applications such as printing warning
532 * messages for suspect ACLs; it is not intended for making access
533 * control decisions. We make no guarantee that an ACL for which
534 * this function returns ISC_FALSE is safe.
537 dns_acl_isinsecure(const dns_acl_t *a) {
539 isc_boolean_t insecure;
541 RUNTIME_CHECK(isc_once_do(&insecure_prefix_once,
542 initialize_action) == ISC_R_SUCCESS);
545 * Walk radix tree to find out if there are any non-negated,
546 * non-loopback prefixes.
548 LOCK(&insecure_prefix_lock);
549 insecure_prefix_found = ISC_FALSE;
550 isc_radix_process(a->iptable->radix, is_insecure);
551 insecure = insecure_prefix_found;
552 UNLOCK(&insecure_prefix_lock);
556 /* Now check non-radix elements */
557 for (i = 0; i < a->length; i++) {
558 dns_aclelement_t *e = &a->elements[i];
560 /* A negated match can never be insecure. */
565 case dns_aclelementtype_keyname:
566 case dns_aclelementtype_localhost:
569 case dns_aclelementtype_nestedacl:
570 if (dns_acl_isinsecure(e->nestedacl))
574 case dns_aclelementtype_localnets:
583 /* No insecure elements were found. */
588 * Initialize ACL environment, setting up localhost and localnets ACLs
591 dns_aclenv_init(isc_mem_t *mctx, dns_aclenv_t *env) {
593 env->localhost = NULL;
594 env->localnets = NULL;
595 result = dns_acl_create(mctx, 0, &env->localhost);
596 if (result != ISC_R_SUCCESS)
597 goto cleanup_nothing;
598 result = dns_acl_create(mctx, 0, &env->localnets);
599 if (result != ISC_R_SUCCESS)
600 goto cleanup_localhost;
601 env->match_mapped = ISC_FALSE;
602 return (ISC_R_SUCCESS);
605 dns_acl_detach(&env->localhost);
611 dns_aclenv_copy(dns_aclenv_t *t, dns_aclenv_t *s) {
612 dns_acl_detach(&t->localhost);
613 dns_acl_attach(s->localhost, &t->localhost);
614 dns_acl_detach(&t->localnets);
615 dns_acl_attach(s->localnets, &t->localnets);
616 t->match_mapped = s->match_mapped;
620 dns_aclenv_destroy(dns_aclenv_t *env) {
621 dns_acl_detach(&env->localhost);
622 dns_acl_detach(&env->localnets);