No empty .Rs/.Re
[netbsd-mini2440.git] / external / bsd / bind / dist / lib / dns / acl.c
blobfe5e8dc68be450ab3b1d262f8c506b03826c190e
1 /* $NetBSD$ */
3 /*
4 * Copyright (C) 2004-2009 Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 1999-2002 Internet Software Consortium.
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
20 /* Id: acl.c,v 1.53 2009/01/17 23:47:42 tbox Exp */
22 /*! \file */
24 #include <config.h>
26 #include <isc/mem.h>
27 #include <isc/once.h>
28 #include <isc/string.h>
29 #include <isc/util.h>
31 #include <dns/acl.h>
32 #include <dns/iptable.h>
35 * Create a new ACL, including an IP table and an array with room
36 * for 'n' ACL elements. The elements are uninitialized and the
37 * length is 0.
39 isc_result_t
40 dns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target) {
41 isc_result_t result;
42 dns_acl_t *acl;
45 * Work around silly limitation of isc_mem_get().
47 if (n == 0)
48 n = 1;
50 acl = isc_mem_get(mctx, sizeof(*acl));
51 if (acl == NULL)
52 return (ISC_R_NOMEMORY);
53 acl->mctx = mctx;
54 acl->name = NULL;
56 result = isc_refcount_init(&acl->refcount, 1);
57 if (result != ISC_R_SUCCESS) {
58 isc_mem_put(mctx, acl, sizeof(*acl));
59 return (result);
62 result = dns_iptable_create(mctx, &acl->iptable);
63 if (result != ISC_R_SUCCESS) {
64 isc_mem_put(mctx, acl, sizeof(*acl));
65 return (result);
68 acl->elements = NULL;
69 acl->alloc = 0;
70 acl->length = 0;
71 acl->has_negatives = ISC_FALSE;
73 ISC_LINK_INIT(acl, nextincache);
75 * Must set magic early because we use dns_acl_detach() to clean up.
77 acl->magic = DNS_ACL_MAGIC;
79 acl->elements = isc_mem_get(mctx, n * sizeof(dns_aclelement_t));
80 if (acl->elements == NULL) {
81 result = ISC_R_NOMEMORY;
82 goto cleanup;
84 acl->alloc = n;
85 memset(acl->elements, 0, n * sizeof(dns_aclelement_t));
86 *target = acl;
87 return (ISC_R_SUCCESS);
89 cleanup:
90 dns_acl_detach(&acl);
91 return (result);
95 * Create a new ACL and initialize it with the value "any" or "none",
96 * depending on the value of the "neg" parameter.
97 * "any" is a positive iptable entry with bit length 0.
98 * "none" is the same as "!any".
100 static isc_result_t
101 dns_acl_anyornone(isc_mem_t *mctx, isc_boolean_t neg, dns_acl_t **target) {
102 isc_result_t result;
103 dns_acl_t *acl = NULL;
104 result = dns_acl_create(mctx, 0, &acl);
105 if (result != ISC_R_SUCCESS)
106 return (result);
108 result = dns_iptable_addprefix(acl->iptable, NULL, 0, ISC_TF(!neg));
109 if (result != ISC_R_SUCCESS) {
110 dns_acl_detach(&acl);
111 return (result);
114 *target = acl;
115 return (result);
119 * Create a new ACL that matches everything.
121 isc_result_t
122 dns_acl_any(isc_mem_t *mctx, dns_acl_t **target) {
123 return (dns_acl_anyornone(mctx, ISC_FALSE, target));
127 * Create a new ACL that matches nothing.
129 isc_result_t
130 dns_acl_none(isc_mem_t *mctx, dns_acl_t **target) {
131 return (dns_acl_anyornone(mctx, ISC_TRUE, target));
135 * If pos is ISC_TRUE, test whether acl is set to "{ any; }"
136 * If pos is ISC_FALSE, test whether acl is set to "{ none; }"
138 static isc_boolean_t
139 dns_acl_isanyornone(dns_acl_t *acl, isc_boolean_t pos)
141 /* Should never happen but let's be safe */
142 if (acl == NULL ||
143 acl->iptable == NULL ||
144 acl->iptable->radix == NULL ||
145 acl->iptable->radix->head == NULL ||
146 acl->iptable->radix->head->prefix == NULL)
147 return (ISC_FALSE);
149 if (acl->length != 0 || acl->node_count != 1)
150 return (ISC_FALSE);
152 if (acl->iptable->radix->head->prefix->bitlen == 0 &&
153 acl->iptable->radix->head->data[0] != NULL &&
154 acl->iptable->radix->head->data[0] ==
155 acl->iptable->radix->head->data[1] &&
156 *(isc_boolean_t *) (acl->iptable->radix->head->data[0]) == pos)
157 return (ISC_TRUE);
159 return (ISC_FALSE); /* All others */
163 * Test whether acl is set to "{ any; }"
165 isc_boolean_t
166 dns_acl_isany(dns_acl_t *acl)
168 return (dns_acl_isanyornone(acl, ISC_TRUE));
172 * Test whether acl is set to "{ none; }"
174 isc_boolean_t
175 dns_acl_isnone(dns_acl_t *acl)
177 return (dns_acl_isanyornone(acl, ISC_FALSE));
181 * Determine whether a given address or signer matches a given ACL.
182 * For a match with a positive ACL element or iptable radix entry,
183 * return with a positive value in match; for a match with a negated ACL
184 * element or radix entry, return with a negative value in match.
186 isc_result_t
187 dns_acl_match(const isc_netaddr_t *reqaddr,
188 const dns_name_t *reqsigner,
189 const dns_acl_t *acl,
190 const dns_aclenv_t *env,
191 int *match,
192 const dns_aclelement_t **matchelt)
194 isc_uint16_t bitlen, family;
195 isc_prefix_t pfx;
196 isc_radix_node_t *node = NULL;
197 const isc_netaddr_t *addr;
198 isc_netaddr_t v4addr;
199 isc_result_t result;
200 int match_num = -1;
201 unsigned int i;
203 REQUIRE(reqaddr != NULL);
204 REQUIRE(matchelt == NULL || *matchelt == NULL);
206 if (env == NULL || env->match_mapped == ISC_FALSE ||
207 reqaddr->family != AF_INET6 ||
208 !IN6_IS_ADDR_V4MAPPED(&reqaddr->type.in6))
209 addr = reqaddr;
210 else {
211 isc_netaddr_fromv4mapped(&v4addr, reqaddr);
212 addr = &v4addr;
215 /* Always match with host addresses. */
216 family = addr->family;
217 bitlen = family == AF_INET6 ? 128 : 32;
218 NETADDR_TO_PREFIX_T(addr, pfx, bitlen);
220 /* Assume no match. */
221 *match = 0;
223 /* Search radix. */
224 result = isc_radix_search(acl->iptable->radix, &node, &pfx);
226 /* Found a match. */
227 if (result == ISC_R_SUCCESS && node != NULL) {
228 match_num = node->node_num[ISC_IS6(family)];
229 if (*(isc_boolean_t *) node->data[ISC_IS6(family)] == ISC_TRUE)
230 *match = match_num;
231 else
232 *match = -match_num;
235 /* Now search non-radix elements for a match with a lower node_num. */
236 for (i = 0; i < acl->length; i++) {
237 dns_aclelement_t *e = &acl->elements[i];
239 /* Already found a better match? */
240 if (match_num != -1 && match_num < e->node_num) {
241 isc_refcount_destroy(&pfx.refcount);
242 return (ISC_R_SUCCESS);
245 if (dns_aclelement_match(reqaddr, reqsigner,
246 e, env, matchelt)) {
247 if (match_num == -1 || e->node_num < match_num) {
248 if (e->negative == ISC_TRUE)
249 *match = -e->node_num;
250 else
251 *match = e->node_num;
253 isc_refcount_destroy(&pfx.refcount);
254 return (ISC_R_SUCCESS);
258 isc_refcount_destroy(&pfx.refcount);
259 return (ISC_R_SUCCESS);
263 * Merge the contents of one ACL into another. Call dns_iptable_merge()
264 * for the IP tables, then concatenate the element arrays.
266 * If pos is set to false, then the nested ACL is to be negated. This
267 * means reverse the sense of each *positive* element or IP table node,
268 * but leave negatives alone, so as to prevent a double-negative causing
269 * an unexpected positive match in the parent ACL.
271 isc_result_t
272 dns_acl_merge(dns_acl_t *dest, dns_acl_t *source, isc_boolean_t pos)
274 isc_result_t result;
275 unsigned int newalloc, nelem, i;
276 int max_node = 0, nodes;
278 /* Resize the element array if needed. */
279 if (dest->length + source->length > dest->alloc) {
280 void *newmem;
282 newalloc = dest->alloc + source->alloc;
283 if (newalloc < 4)
284 newalloc = 4;
286 newmem = isc_mem_get(dest->mctx,
287 newalloc * sizeof(dns_aclelement_t));
288 if (newmem == NULL)
289 return (ISC_R_NOMEMORY);
291 /* Copy in the original elements */
292 memcpy(newmem, dest->elements,
293 dest->length * sizeof(dns_aclelement_t));
295 /* Release the memory for the old elements array */
296 isc_mem_put(dest->mctx, dest->elements,
297 dest->alloc * sizeof(dns_aclelement_t));
298 dest->elements = newmem;
299 dest->alloc = newalloc;
303 * Now copy in the new elements, increasing their node_num
304 * values so as to keep the new ACL consistent. If we're
305 * negating, then negate positive elements, but keep negative
306 * elements the same for security reasons.
308 nelem = dest->length;
309 dest->length += source->length;
310 for (i = 0; i < source->length; i++) {
311 if (source->elements[i].node_num > max_node)
312 max_node = source->elements[i].node_num;
314 /* Copy type. */
315 dest->elements[nelem + i].type = source->elements[i].type;
317 /* Adjust node numbering. */
318 dest->elements[nelem + i].node_num =
319 source->elements[i].node_num + dest->node_count;
321 /* Duplicate nested acl. */
322 if (source->elements[i].type == dns_aclelementtype_nestedacl &&
323 source->elements[i].nestedacl != NULL)
324 dns_acl_attach(source->elements[i].nestedacl,
325 &dest->elements[nelem + i].nestedacl);
327 /* Duplicate key name. */
328 if (source->elements[i].type == dns_aclelementtype_keyname) {
329 dns_name_init(&dest->elements[nelem+i].keyname, NULL);
330 result = dns_name_dup(&source->elements[i].keyname,
331 dest->mctx,
332 &dest->elements[nelem+i].keyname);
333 if (result != ISC_R_SUCCESS)
334 return result;
337 /* reverse sense of positives if this is a negative acl */
338 if (!pos && source->elements[i].negative == ISC_FALSE) {
339 dest->elements[nelem + i].negative = ISC_TRUE;
340 } else {
341 dest->elements[nelem + i].negative =
342 source->elements[i].negative;
348 * Merge the iptables. Make sure the destination ACL's
349 * node_count value is set correctly afterward.
351 nodes = max_node + dest->node_count;
352 result = dns_iptable_merge(dest->iptable, source->iptable, pos);
353 if (result != ISC_R_SUCCESS)
354 return (result);
355 if (nodes > dest->node_count)
356 dest->node_count = nodes;
358 return (ISC_R_SUCCESS);
362 * Like dns_acl_match, but matches against the single ACL element 'e'
363 * rather than a complete ACL, and returns ISC_TRUE iff it matched.
365 * To determine whether the match was positive or negative, the
366 * caller should examine e->negative. Since the element 'e' may be
367 * a reference to a named ACL or a nested ACL, a matching element
368 * returned through 'matchelt' is not necessarily 'e' itself.
370 isc_boolean_t
371 dns_aclelement_match(const isc_netaddr_t *reqaddr,
372 const dns_name_t *reqsigner,
373 const dns_aclelement_t *e,
374 const dns_aclenv_t *env,
375 const dns_aclelement_t **matchelt)
377 dns_acl_t *inner = NULL;
378 int indirectmatch;
379 isc_result_t result;
381 switch (e->type) {
382 case dns_aclelementtype_keyname:
383 if (reqsigner != NULL &&
384 dns_name_equal(reqsigner, &e->keyname)) {
385 if (matchelt != NULL)
386 *matchelt = e;
387 return (ISC_TRUE);
388 } else {
389 return (ISC_FALSE);
392 case dns_aclelementtype_nestedacl:
393 inner = e->nestedacl;
394 break;
396 case dns_aclelementtype_localhost:
397 if (env == NULL || env->localhost == NULL)
398 return (ISC_FALSE);
399 inner = env->localhost;
400 break;
402 case dns_aclelementtype_localnets:
403 if (env == NULL || env->localnets == NULL)
404 return (ISC_FALSE);
405 inner = env->localnets;
406 break;
408 default:
409 /* Should be impossible. */
410 INSIST(0);
413 result = dns_acl_match(reqaddr, reqsigner, inner, env,
414 &indirectmatch, matchelt);
415 INSIST(result == ISC_R_SUCCESS);
418 * Treat negative matches in indirect ACLs as "no match".
419 * That way, a negated indirect ACL will never become a
420 * surprise positive match through double negation.
421 * XXXDCL this should be documented.
424 if (indirectmatch > 0) {
425 if (matchelt != NULL)
426 *matchelt = e;
427 return (ISC_TRUE);
431 * A negative indirect match may have set *matchelt, but we don't
432 * want it set when we return.
435 if (matchelt != NULL)
436 *matchelt = NULL;
438 return (ISC_FALSE);
441 void
442 dns_acl_attach(dns_acl_t *source, dns_acl_t **target) {
443 REQUIRE(DNS_ACL_VALID(source));
444 isc_refcount_increment(&source->refcount, NULL);
445 *target = source;
448 static void
449 destroy(dns_acl_t *dacl) {
450 unsigned int i;
451 for (i = 0; i < dacl->length; i++) {
452 dns_aclelement_t *de = &dacl->elements[i];
453 if (de->type == dns_aclelementtype_keyname) {
454 dns_name_free(&de->keyname, dacl->mctx);
455 } else if (de->type == dns_aclelementtype_nestedacl) {
456 dns_acl_detach(&de->nestedacl);
459 if (dacl->elements != NULL)
460 isc_mem_put(dacl->mctx, dacl->elements,
461 dacl->alloc * sizeof(dns_aclelement_t));
462 if (dacl->name != NULL)
463 isc_mem_free(dacl->mctx, dacl->name);
464 if (dacl->iptable != NULL)
465 dns_iptable_detach(&dacl->iptable);
466 isc_refcount_destroy(&dacl->refcount);
467 dacl->magic = 0;
468 isc_mem_put(dacl->mctx, dacl, sizeof(*dacl));
471 void
472 dns_acl_detach(dns_acl_t **aclp) {
473 dns_acl_t *acl = *aclp;
474 unsigned int refs;
475 REQUIRE(DNS_ACL_VALID(acl));
476 isc_refcount_decrement(&acl->refcount, &refs);
477 if (refs == 0)
478 destroy(acl);
479 *aclp = NULL;
483 static isc_once_t insecure_prefix_once = ISC_ONCE_INIT;
484 static isc_mutex_t insecure_prefix_lock;
485 static isc_boolean_t insecure_prefix_found;
487 static void
488 initialize_action(void) {
489 RUNTIME_CHECK(isc_mutex_init(&insecure_prefix_lock) == ISC_R_SUCCESS);
493 * Called via isc_radix_walk() to find IP table nodes that are
494 * insecure.
496 static void
497 is_insecure(isc_prefix_t *prefix, void **data) {
498 isc_boolean_t secure;
499 int bitlen, family;
501 bitlen = prefix->bitlen;
502 family = prefix->family;
504 /* Negated entries are always secure. */
505 secure = * (isc_boolean_t *)data[ISC_IS6(family)];
506 if (!secure) {
507 return;
510 /* If loopback prefix found, return */
511 switch (family) {
512 case AF_INET:
513 if (bitlen == 32 &&
514 htonl(prefix->add.sin.s_addr) == INADDR_LOOPBACK)
515 return;
516 break;
517 case AF_INET6:
518 if (bitlen == 128 && IN6_IS_ADDR_LOOPBACK(&prefix->add.sin6))
519 return;
520 break;
521 default:
522 break;
525 /* Non-negated, non-loopback */
526 insecure_prefix_found = ISC_TRUE; /* LOCKED */
527 return;
531 * Return ISC_TRUE iff the acl 'a' is considered insecure, that is,
532 * if it contains IP addresses other than those of the local host.
533 * This is intended for applications such as printing warning
534 * messages for suspect ACLs; it is not intended for making access
535 * control decisions. We make no guarantee that an ACL for which
536 * this function returns ISC_FALSE is safe.
538 isc_boolean_t
539 dns_acl_isinsecure(const dns_acl_t *a) {
540 unsigned int i;
541 isc_boolean_t insecure;
543 RUNTIME_CHECK(isc_once_do(&insecure_prefix_once,
544 initialize_action) == ISC_R_SUCCESS);
547 * Walk radix tree to find out if there are any non-negated,
548 * non-loopback prefixes.
550 LOCK(&insecure_prefix_lock);
551 insecure_prefix_found = ISC_FALSE;
552 isc_radix_process(a->iptable->radix, is_insecure);
553 insecure = insecure_prefix_found;
554 UNLOCK(&insecure_prefix_lock);
555 if (insecure)
556 return(ISC_TRUE);
558 /* Now check non-radix elements */
559 for (i = 0; i < a->length; i++) {
560 dns_aclelement_t *e = &a->elements[i];
562 /* A negated match can never be insecure. */
563 if (e->negative)
564 continue;
566 switch (e->type) {
567 case dns_aclelementtype_keyname:
568 case dns_aclelementtype_localhost:
569 continue;
571 case dns_aclelementtype_nestedacl:
572 if (dns_acl_isinsecure(e->nestedacl))
573 return (ISC_TRUE);
574 continue;
576 case dns_aclelementtype_localnets:
577 return (ISC_TRUE);
579 default:
580 INSIST(0);
581 return (ISC_TRUE);
585 /* No insecure elements were found. */
586 return (ISC_FALSE);
590 * Initialize ACL environment, setting up localhost and localnets ACLs
592 isc_result_t
593 dns_aclenv_init(isc_mem_t *mctx, dns_aclenv_t *env) {
594 isc_result_t result;
595 env->localhost = NULL;
596 env->localnets = NULL;
597 result = dns_acl_create(mctx, 0, &env->localhost);
598 if (result != ISC_R_SUCCESS)
599 goto cleanup_nothing;
600 result = dns_acl_create(mctx, 0, &env->localnets);
601 if (result != ISC_R_SUCCESS)
602 goto cleanup_localhost;
603 env->match_mapped = ISC_FALSE;
604 return (ISC_R_SUCCESS);
606 cleanup_localhost:
607 dns_acl_detach(&env->localhost);
608 cleanup_nothing:
609 return (result);
612 void
613 dns_aclenv_copy(dns_aclenv_t *t, dns_aclenv_t *s) {
614 dns_acl_detach(&t->localhost);
615 dns_acl_attach(s->localhost, &t->localhost);
616 dns_acl_detach(&t->localnets);
617 dns_acl_attach(s->localnets, &t->localnets);
618 t->match_mapped = s->match_mapped;
621 void
622 dns_aclenv_destroy(dns_aclenv_t *env) {
623 dns_acl_detach(&env->localhost);
624 dns_acl_detach(&env->localnets);