kernel - Update swapcache manual page
[dragonfly.git] / contrib / bind / lib / dns / acl.c
blob188886e1736feeb2bbcdc6c60414b88091c28dd6
1 /*
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.37.2.14 2009/01/19 23:47:02 tbox Exp $ */
20 /*! \file */
22 #include <config.h>
24 #include <isc/mem.h>
25 #include <isc/once.h>
26 #include <isc/string.h>
27 #include <isc/util.h>
29 #include <dns/acl.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
35 * length is 0.
37 isc_result_t
38 dns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target) {
39 isc_result_t result;
40 dns_acl_t *acl;
43 * Work around silly limitation of isc_mem_get().
45 if (n == 0)
46 n = 1;
48 acl = isc_mem_get(mctx, sizeof(*acl));
49 if (acl == NULL)
50 return (ISC_R_NOMEMORY);
51 acl->mctx = mctx;
52 acl->name = NULL;
54 result = isc_refcount_init(&acl->refcount, 1);
55 if (result != ISC_R_SUCCESS) {
56 isc_mem_put(mctx, acl, sizeof(*acl));
57 return (result);
60 result = dns_iptable_create(mctx, &acl->iptable);
61 if (result != ISC_R_SUCCESS) {
62 isc_mem_put(mctx, acl, sizeof(*acl));
63 return (result);
66 acl->elements = NULL;
67 acl->alloc = 0;
68 acl->length = 0;
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;
80 goto cleanup;
82 acl->alloc = n;
83 memset(acl->elements, 0, n * sizeof(dns_aclelement_t));
84 *target = acl;
85 return (ISC_R_SUCCESS);
87 cleanup:
88 dns_acl_detach(&acl);
89 return (result);
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".
98 static isc_result_t
99 dns_acl_anyornone(isc_mem_t *mctx, isc_boolean_t neg, dns_acl_t **target) {
100 isc_result_t result;
101 dns_acl_t *acl = NULL;
102 result = dns_acl_create(mctx, 0, &acl);
103 if (result != ISC_R_SUCCESS)
104 return (result);
106 result = dns_iptable_addprefix(acl->iptable, NULL, 0, ISC_TF(!neg));
107 if (result != ISC_R_SUCCESS) {
108 dns_acl_detach(&acl);
109 return (result);
112 *target = acl;
113 return (result);
117 * Create a new ACL that matches everything.
119 isc_result_t
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.
127 isc_result_t
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; }"
136 static isc_boolean_t
137 dns_acl_isanyornone(dns_acl_t *acl, isc_boolean_t pos)
139 /* Should never happen but let's be safe */
140 if (acl == NULL ||
141 acl->iptable == NULL ||
142 acl->iptable->radix == NULL ||
143 acl->iptable->radix->head == NULL ||
144 acl->iptable->radix->head->prefix == NULL)
145 return (ISC_FALSE);
147 if (acl->length != 0 || acl->node_count != 1)
148 return (ISC_FALSE);
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)
155 return (ISC_TRUE);
157 return (ISC_FALSE); /* All others */
161 * Test whether acl is set to "{ any; }"
163 isc_boolean_t
164 dns_acl_isany(dns_acl_t *acl)
166 return (dns_acl_isanyornone(acl, ISC_TRUE));
170 * Test whether acl is set to "{ none; }"
172 isc_boolean_t
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.
184 isc_result_t
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,
189 int *match,
190 const dns_aclelement_t **matchelt)
192 isc_uint16_t bitlen, family;
193 isc_prefix_t pfx;
194 isc_radix_node_t *node = NULL;
195 const isc_netaddr_t *addr;
196 isc_netaddr_t v4addr;
197 isc_result_t result;
198 int match_num = -1;
199 unsigned int i;
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))
207 addr = reqaddr;
208 else {
209 isc_netaddr_fromv4mapped(&v4addr, reqaddr);
210 addr = &v4addr;
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. */
219 *match = 0;
221 /* Search radix. */
222 result = isc_radix_search(acl->iptable->radix, &node, &pfx);
224 /* Found a match. */
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)
228 *match = match_num;
229 else
230 *match = -match_num;
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,
244 e, env, matchelt)) {
245 if (match_num == -1 || e->node_num < match_num) {
246 if (e->negative == ISC_TRUE)
247 *match = -e->node_num;
248 else
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.
269 isc_result_t
270 dns_acl_merge(dns_acl_t *dest, dns_acl_t *source, isc_boolean_t pos)
272 isc_result_t result;
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) {
278 void *newmem;
280 newalloc = dest->alloc + source->alloc;
281 if (newalloc < 4)
282 newalloc = 4;
284 newmem = isc_mem_get(dest->mctx,
285 newalloc * sizeof(dns_aclelement_t));
286 if (newmem == NULL)
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;
312 /* Copy type. */
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,
329 dest->mctx,
330 &dest->elements[nelem+i].keyname);
331 if (result != ISC_R_SUCCESS)
332 return result;
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;
338 } else {
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)
352 return (result);
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.
368 isc_boolean_t
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;
376 int indirectmatch;
377 isc_result_t result;
379 switch (e->type) {
380 case dns_aclelementtype_keyname:
381 if (reqsigner != NULL &&
382 dns_name_equal(reqsigner, &e->keyname)) {
383 if (matchelt != NULL)
384 *matchelt = e;
385 return (ISC_TRUE);
386 } else {
387 return (ISC_FALSE);
390 case dns_aclelementtype_nestedacl:
391 inner = e->nestedacl;
392 break;
394 case dns_aclelementtype_localhost:
395 if (env == NULL || env->localhost == NULL)
396 return (ISC_FALSE);
397 inner = env->localhost;
398 break;
400 case dns_aclelementtype_localnets:
401 if (env == NULL || env->localnets == NULL)
402 return (ISC_FALSE);
403 inner = env->localnets;
404 break;
406 default:
407 /* Should be impossible. */
408 INSIST(0);
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)
424 *matchelt = e;
425 return (ISC_TRUE);
429 * A negative indirect match may have set *matchelt, but we don't
430 * want it set when we return.
433 if (matchelt != NULL)
434 *matchelt = NULL;
436 return (ISC_FALSE);
439 void
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);
443 *target = source;
446 static void
447 destroy(dns_acl_t *dacl) {
448 unsigned int i;
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);
465 dacl->magic = 0;
466 isc_mem_put(dacl->mctx, dacl, sizeof(*dacl));
469 void
470 dns_acl_detach(dns_acl_t **aclp) {
471 dns_acl_t *acl = *aclp;
472 unsigned int refs;
473 REQUIRE(DNS_ACL_VALID(acl));
474 isc_refcount_decrement(&acl->refcount, &refs);
475 if (refs == 0)
476 destroy(acl);
477 *aclp = NULL;
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;
485 static void
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
492 * insecure.
494 static void
495 is_insecure(isc_prefix_t *prefix, void **data) {
496 isc_boolean_t secure;
497 int bitlen, family;
499 bitlen = prefix->bitlen;
500 family = prefix->family;
502 /* Negated entries are always secure. */
503 secure = * (isc_boolean_t *)data[ISC_IS6(family)];
504 if (!secure) {
505 return;
508 /* If loopback prefix found, return */
509 switch (family) {
510 case AF_INET:
511 if (bitlen == 32 &&
512 htonl(prefix->add.sin.s_addr) == INADDR_LOOPBACK)
513 return;
514 break;
515 case AF_INET6:
516 if (bitlen == 128 && IN6_IS_ADDR_LOOPBACK(&prefix->add.sin6))
517 return;
518 break;
519 default:
520 break;
523 /* Non-negated, non-loopback */
524 insecure_prefix_found = ISC_TRUE; /* LOCKED */
525 return;
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.
536 isc_boolean_t
537 dns_acl_isinsecure(const dns_acl_t *a) {
538 unsigned int i;
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);
553 if (insecure)
554 return(ISC_TRUE);
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. */
561 if (e->negative)
562 continue;
564 switch (e->type) {
565 case dns_aclelementtype_keyname:
566 case dns_aclelementtype_localhost:
567 continue;
569 case dns_aclelementtype_nestedacl:
570 if (dns_acl_isinsecure(e->nestedacl))
571 return (ISC_TRUE);
572 continue;
574 case dns_aclelementtype_localnets:
575 return (ISC_TRUE);
577 default:
578 INSIST(0);
579 return (ISC_TRUE);
583 /* No insecure elements were found. */
584 return (ISC_FALSE);
588 * Initialize ACL environment, setting up localhost and localnets ACLs
590 isc_result_t
591 dns_aclenv_init(isc_mem_t *mctx, dns_aclenv_t *env) {
592 isc_result_t result;
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);
604 cleanup_localhost:
605 dns_acl_detach(&env->localhost);
606 cleanup_nothing:
607 return (result);
610 void
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;
619 void
620 dns_aclenv_destroy(dns_aclenv_t *env) {
621 dns_acl_detach(&env->localhost);
622 dns_acl_detach(&env->localnets);