Remove building with NOCRYPTO option
[minix.git] / external / bsd / bind / dist / lib / dns / acl.c
blob5f50d75fdd2d41358cf903db571406379858d7b5
1 /* $NetBSD: acl.c,v 1.7 2014/12/10 04:37:58 christos Exp $ */
3 /*
4 * Copyright (C) 2004-2009, 2011, 2013, 2014 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.55 2011/06/17 23:47:49 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>
36 * Create a new ACL, including an IP table and an array with room
37 * for 'n' ACL elements. The elements are uninitialized and the
38 * length is 0.
40 isc_result_t
41 dns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target) {
42 isc_result_t result;
43 dns_acl_t *acl;
46 * Work around silly limitation of isc_mem_get().
48 if (n == 0)
49 n = 1;
51 acl = isc_mem_get(mctx, sizeof(*acl));
52 if (acl == NULL)
53 return (ISC_R_NOMEMORY);
55 acl->mctx = NULL;
56 isc_mem_attach(mctx, &acl->mctx);
58 acl->name = NULL;
60 result = isc_refcount_init(&acl->refcount, 1);
61 if (result != ISC_R_SUCCESS) {
62 isc_mem_put(mctx, acl, sizeof(*acl));
63 return (result);
66 result = dns_iptable_create(mctx, &acl->iptable);
67 if (result != ISC_R_SUCCESS) {
68 isc_mem_put(mctx, acl, sizeof(*acl));
69 return (result);
72 acl->elements = NULL;
73 acl->alloc = 0;
74 acl->length = 0;
75 acl->has_negatives = ISC_FALSE;
77 ISC_LINK_INIT(acl, nextincache);
79 * Must set magic early because we use dns_acl_detach() to clean up.
81 acl->magic = DNS_ACL_MAGIC;
83 acl->elements = isc_mem_get(mctx, n * sizeof(dns_aclelement_t));
84 if (acl->elements == NULL) {
85 result = ISC_R_NOMEMORY;
86 goto cleanup;
88 acl->alloc = n;
89 memset(acl->elements, 0, n * sizeof(dns_aclelement_t));
90 *target = acl;
91 return (ISC_R_SUCCESS);
93 cleanup:
94 dns_acl_detach(&acl);
95 return (result);
99 * Create a new ACL and initialize it with the value "any" or "none",
100 * depending on the value of the "neg" parameter.
101 * "any" is a positive iptable entry with bit length 0.
102 * "none" is the same as "!any".
104 static isc_result_t
105 dns_acl_anyornone(isc_mem_t *mctx, isc_boolean_t neg, dns_acl_t **target) {
106 isc_result_t result;
107 dns_acl_t *acl = NULL;
109 result = dns_acl_create(mctx, 0, &acl);
110 if (result != ISC_R_SUCCESS)
111 return (result);
113 result = dns_iptable_addprefix(acl->iptable, NULL, 0, ISC_TF(!neg));
114 if (result != ISC_R_SUCCESS) {
115 dns_acl_detach(&acl);
116 return (result);
119 *target = acl;
120 return (result);
124 * Create a new ACL that matches everything.
126 isc_result_t
127 dns_acl_any(isc_mem_t *mctx, dns_acl_t **target) {
128 return (dns_acl_anyornone(mctx, ISC_FALSE, target));
132 * Create a new ACL that matches nothing.
134 isc_result_t
135 dns_acl_none(isc_mem_t *mctx, dns_acl_t **target) {
136 return (dns_acl_anyornone(mctx, ISC_TRUE, target));
140 * If pos is ISC_TRUE, test whether acl is set to "{ any; }"
141 * If pos is ISC_FALSE, test whether acl is set to "{ none; }"
143 static isc_boolean_t
144 dns_acl_isanyornone(dns_acl_t *acl, isc_boolean_t pos)
146 /* Should never happen but let's be safe */
147 if (acl == NULL ||
148 acl->iptable == NULL ||
149 acl->iptable->radix == NULL ||
150 acl->iptable->radix->head == NULL ||
151 acl->iptable->radix->head->prefix == NULL)
152 return (ISC_FALSE);
154 if (acl->length != 0 || acl->node_count != 1)
155 return (ISC_FALSE);
157 if (acl->iptable->radix->head->prefix->bitlen == 0 &&
158 acl->iptable->radix->head->data[0] != NULL &&
159 acl->iptable->radix->head->data[0] ==
160 acl->iptable->radix->head->data[1] &&
161 *(isc_boolean_t *) (acl->iptable->radix->head->data[0]) == pos)
162 return (ISC_TRUE);
164 return (ISC_FALSE); /* All others */
168 * Test whether acl is set to "{ any; }"
170 isc_boolean_t
171 dns_acl_isany(dns_acl_t *acl)
173 return (dns_acl_isanyornone(acl, ISC_TRUE));
177 * Test whether acl is set to "{ none; }"
179 isc_boolean_t
180 dns_acl_isnone(dns_acl_t *acl)
182 return (dns_acl_isanyornone(acl, ISC_FALSE));
186 * Determine whether a given address or signer matches a given ACL.
187 * For a match with a positive ACL element or iptable radix entry,
188 * return with a positive value in match; for a match with a negated ACL
189 * element or radix entry, return with a negative value in match.
191 isc_result_t
192 dns_acl_match(const isc_netaddr_t *reqaddr,
193 const dns_name_t *reqsigner,
194 const dns_acl_t *acl,
195 const dns_aclenv_t *env,
196 int *match,
197 const dns_aclelement_t **matchelt)
199 isc_uint16_t bitlen, family;
200 isc_prefix_t pfx;
201 isc_radix_node_t *node = NULL;
202 const isc_netaddr_t *addr;
203 isc_netaddr_t v4addr;
204 isc_result_t result;
205 int match_num = -1;
206 unsigned int i;
208 REQUIRE(reqaddr != NULL);
209 REQUIRE(matchelt == NULL || *matchelt == NULL);
211 if (env == NULL || env->match_mapped == ISC_FALSE ||
212 reqaddr->family != AF_INET6 ||
213 !IN6_IS_ADDR_V4MAPPED(&reqaddr->type.in6))
214 addr = reqaddr;
215 else {
216 isc_netaddr_fromv4mapped(&v4addr, reqaddr);
217 addr = &v4addr;
220 /* Always match with host addresses. */
221 family = addr->family;
222 bitlen = family == AF_INET6 ? 128 : 32;
223 NETADDR_TO_PREFIX_T(addr, pfx, bitlen);
225 /* Assume no match. */
226 *match = 0;
228 /* Search radix. */
229 result = isc_radix_search(acl->iptable->radix, &node, &pfx);
231 /* Found a match. */
232 if (result == ISC_R_SUCCESS && node != NULL) {
233 match_num = node->node_num[ISC_IS6(family)];
234 if (*(isc_boolean_t *) node->data[ISC_IS6(family)] == ISC_TRUE)
235 *match = match_num;
236 else
237 *match = -match_num;
240 /* Now search non-radix elements for a match with a lower node_num. */
241 for (i = 0; i < acl->length; i++) {
242 dns_aclelement_t *e = &acl->elements[i];
244 /* Already found a better match? */
245 if (match_num != -1 && match_num < e->node_num) {
246 isc_refcount_destroy(&pfx.refcount);
247 return (ISC_R_SUCCESS);
250 if (dns_aclelement_match(reqaddr, reqsigner,
251 e, env, matchelt)) {
252 if (match_num == -1 || e->node_num < match_num) {
253 if (e->negative == ISC_TRUE)
254 *match = -e->node_num;
255 else
256 *match = e->node_num;
258 isc_refcount_destroy(&pfx.refcount);
259 return (ISC_R_SUCCESS);
263 isc_refcount_destroy(&pfx.refcount);
264 return (ISC_R_SUCCESS);
268 * Merge the contents of one ACL into another. Call dns_iptable_merge()
269 * for the IP tables, then concatenate the element arrays.
271 * If pos is set to false, then the nested ACL is to be negated. This
272 * means reverse the sense of each *positive* element or IP table node,
273 * but leave negatives alone, so as to prevent a double-negative causing
274 * an unexpected positive match in the parent ACL.
276 isc_result_t
277 dns_acl_merge(dns_acl_t *dest, dns_acl_t *source, isc_boolean_t pos)
279 isc_result_t result;
280 unsigned int newalloc, nelem, i;
281 int max_node = 0, nodes;
283 /* Resize the element array if needed. */
284 if (dest->length + source->length > dest->alloc) {
285 void *newmem;
287 newalloc = dest->alloc + source->alloc;
288 if (newalloc < 4)
289 newalloc = 4;
291 newmem = isc_mem_get(dest->mctx,
292 newalloc * sizeof(dns_aclelement_t));
293 if (newmem == NULL)
294 return (ISC_R_NOMEMORY);
296 /* Zero. */
297 memset(newmem, 0, newalloc * sizeof(dns_aclelement_t));
299 /* Copy in the original elements */
300 memmove(newmem, dest->elements,
301 dest->length * sizeof(dns_aclelement_t));
303 /* Release the memory for the old elements array */
304 isc_mem_put(dest->mctx, dest->elements,
305 dest->alloc * sizeof(dns_aclelement_t));
306 dest->elements = newmem;
307 dest->alloc = newalloc;
311 * Now copy in the new elements, increasing their node_num
312 * values so as to keep the new ACL consistent. If we're
313 * negating, then negate positive elements, but keep negative
314 * elements the same for security reasons.
316 nelem = dest->length;
317 dest->length += source->length;
318 for (i = 0; i < source->length; i++) {
319 if (source->elements[i].node_num > max_node)
320 max_node = source->elements[i].node_num;
322 /* Copy type. */
323 dest->elements[nelem + i].type = source->elements[i].type;
325 /* Adjust node numbering. */
326 dest->elements[nelem + i].node_num =
327 source->elements[i].node_num + dest->node_count;
329 /* Duplicate nested acl. */
330 if (source->elements[i].type == dns_aclelementtype_nestedacl &&
331 source->elements[i].nestedacl != NULL)
332 dns_acl_attach(source->elements[i].nestedacl,
333 &dest->elements[nelem + i].nestedacl);
335 /* Duplicate key name. */
336 if (source->elements[i].type == dns_aclelementtype_keyname) {
337 dns_name_init(&dest->elements[nelem+i].keyname, NULL);
338 result = dns_name_dup(&source->elements[i].keyname,
339 dest->mctx,
340 &dest->elements[nelem+i].keyname);
341 if (result != ISC_R_SUCCESS)
342 return result;
345 #ifdef HAVE_GEOIP
346 /* Duplicate GeoIP data */
347 if (source->elements[i].type == dns_aclelementtype_geoip) {
348 dest->elements[nelem + i].geoip_elem =
349 source->elements[i].geoip_elem;
351 #endif
353 /* reverse sense of positives if this is a negative acl */
354 if (!pos && source->elements[i].negative == ISC_FALSE) {
355 dest->elements[nelem + i].negative = ISC_TRUE;
356 } else {
357 dest->elements[nelem + i].negative =
358 source->elements[i].negative;
363 * Merge the iptables. Make sure the destination ACL's
364 * node_count value is set correctly afterward.
366 nodes = max_node + dest->node_count;
367 result = dns_iptable_merge(dest->iptable, source->iptable, pos);
368 if (result != ISC_R_SUCCESS)
369 return (result);
370 if (nodes > dest->node_count)
371 dest->node_count = nodes;
373 return (ISC_R_SUCCESS);
377 * Like dns_acl_match, but matches against the single ACL element 'e'
378 * rather than a complete ACL, and returns ISC_TRUE iff it matched.
380 * To determine whether the match was positive or negative, the
381 * caller should examine e->negative. Since the element 'e' may be
382 * a reference to a named ACL or a nested ACL, a matching element
383 * returned through 'matchelt' is not necessarily 'e' itself.
385 isc_boolean_t
386 dns_aclelement_match(const isc_netaddr_t *reqaddr,
387 const dns_name_t *reqsigner,
388 const dns_aclelement_t *e,
389 const dns_aclenv_t *env,
390 const dns_aclelement_t **matchelt)
392 dns_acl_t *inner = NULL;
393 int indirectmatch;
394 isc_result_t result;
396 switch (e->type) {
397 case dns_aclelementtype_keyname:
398 if (reqsigner != NULL &&
399 dns_name_equal(reqsigner, &e->keyname)) {
400 if (matchelt != NULL)
401 *matchelt = e;
402 return (ISC_TRUE);
403 } else
404 return (ISC_FALSE);
406 case dns_aclelementtype_nestedacl:
407 inner = e->nestedacl;
408 break;
410 case dns_aclelementtype_localhost:
411 if (env == NULL || env->localhost == NULL)
412 return (ISC_FALSE);
413 inner = env->localhost;
414 break;
416 case dns_aclelementtype_localnets:
417 if (env == NULL || env->localnets == NULL)
418 return (ISC_FALSE);
419 inner = env->localnets;
420 break;
422 #ifdef HAVE_GEOIP
423 case dns_aclelementtype_geoip:
424 if (env == NULL || env->geoip == NULL)
425 return (ISC_FALSE);
426 return (dns_geoip_match(reqaddr, env->geoip, &e->geoip_elem));
427 #endif
428 default:
429 /* Should be impossible. */
430 INSIST(0);
433 result = dns_acl_match(reqaddr, reqsigner, inner, env,
434 &indirectmatch, matchelt);
435 INSIST(result == ISC_R_SUCCESS);
438 * Treat negative matches in indirect ACLs as "no match".
439 * That way, a negated indirect ACL will never become a
440 * surprise positive match through double negation.
441 * XXXDCL this should be documented.
444 if (indirectmatch > 0) {
445 if (matchelt != NULL)
446 *matchelt = e;
447 return (ISC_TRUE);
451 * A negative indirect match may have set *matchelt, but we don't
452 * want it set when we return.
455 if (matchelt != NULL)
456 *matchelt = NULL;
458 return (ISC_FALSE);
461 void
462 dns_acl_attach(dns_acl_t *source, dns_acl_t **target) {
463 REQUIRE(DNS_ACL_VALID(source));
465 isc_refcount_increment(&source->refcount, NULL);
466 *target = source;
469 static void
470 destroy(dns_acl_t *dacl) {
471 unsigned int i;
473 INSIST(!ISC_LINK_LINKED(dacl, nextincache));
475 for (i = 0; i < dacl->length; i++) {
476 dns_aclelement_t *de = &dacl->elements[i];
477 if (de->type == dns_aclelementtype_keyname) {
478 dns_name_free(&de->keyname, dacl->mctx);
479 } else if (de->type == dns_aclelementtype_nestedacl) {
480 dns_acl_detach(&de->nestedacl);
483 if (dacl->elements != NULL)
484 isc_mem_put(dacl->mctx, dacl->elements,
485 dacl->alloc * sizeof(dns_aclelement_t));
486 if (dacl->name != NULL)
487 isc_mem_free(dacl->mctx, dacl->name);
488 if (dacl->iptable != NULL)
489 dns_iptable_detach(&dacl->iptable);
490 isc_refcount_destroy(&dacl->refcount);
491 dacl->magic = 0;
492 isc_mem_putanddetach(&dacl->mctx, dacl, sizeof(*dacl));
495 void
496 dns_acl_detach(dns_acl_t **aclp) {
497 dns_acl_t *acl = *aclp;
498 unsigned int refs;
500 REQUIRE(DNS_ACL_VALID(acl));
502 isc_refcount_decrement(&acl->refcount, &refs);
503 if (refs == 0)
504 destroy(acl);
505 *aclp = NULL;
509 static isc_once_t insecure_prefix_once = ISC_ONCE_INIT;
510 static isc_mutex_t insecure_prefix_lock;
511 static isc_boolean_t insecure_prefix_found;
513 static void
514 initialize_action(void) {
515 RUNTIME_CHECK(isc_mutex_init(&insecure_prefix_lock) == ISC_R_SUCCESS);
519 * Called via isc_radix_walk() to find IP table nodes that are
520 * insecure.
522 static void
523 is_insecure(isc_prefix_t *prefix, void **data) {
524 isc_boolean_t secure;
525 int bitlen, family;
527 bitlen = prefix->bitlen;
528 family = prefix->family;
530 /* Negated entries are always secure. */
531 secure = * (isc_boolean_t *)data[ISC_IS6(family)];
532 if (!secure) {
533 return;
536 /* If loopback prefix found, return */
537 switch (family) {
538 case AF_INET:
539 if (bitlen == 32 &&
540 htonl(prefix->add.sin.s_addr) == INADDR_LOOPBACK)
541 return;
542 break;
543 case AF_INET6:
544 if (bitlen == 128 && IN6_IS_ADDR_LOOPBACK(&prefix->add.sin6))
545 return;
546 break;
547 default:
548 break;
551 /* Non-negated, non-loopback */
552 insecure_prefix_found = ISC_TRUE; /* LOCKED */
553 return;
557 * Return ISC_TRUE iff the acl 'a' is considered insecure, that is,
558 * if it contains IP addresses other than those of the local host.
559 * This is intended for applications such as printing warning
560 * messages for suspect ACLs; it is not intended for making access
561 * control decisions. We make no guarantee that an ACL for which
562 * this function returns ISC_FALSE is safe.
564 isc_boolean_t
565 dns_acl_isinsecure(const dns_acl_t *a) {
566 unsigned int i;
567 isc_boolean_t insecure;
569 RUNTIME_CHECK(isc_once_do(&insecure_prefix_once,
570 initialize_action) == ISC_R_SUCCESS);
573 * Walk radix tree to find out if there are any non-negated,
574 * non-loopback prefixes.
576 LOCK(&insecure_prefix_lock);
577 insecure_prefix_found = ISC_FALSE;
578 isc_radix_process(a->iptable->radix, is_insecure);
579 insecure = insecure_prefix_found;
580 UNLOCK(&insecure_prefix_lock);
581 if (insecure)
582 return (ISC_TRUE);
584 /* Now check non-radix elements */
585 for (i = 0; i < a->length; i++) {
586 dns_aclelement_t *e = &a->elements[i];
588 /* A negated match can never be insecure. */
589 if (e->negative)
590 continue;
592 switch (e->type) {
593 case dns_aclelementtype_keyname:
594 case dns_aclelementtype_localhost:
595 continue;
597 case dns_aclelementtype_nestedacl:
598 if (dns_acl_isinsecure(e->nestedacl))
599 return (ISC_TRUE);
600 continue;
602 case dns_aclelementtype_localnets:
603 return (ISC_TRUE);
605 default:
606 INSIST(0);
607 return (ISC_TRUE);
611 /* No insecure elements were found. */
612 return (ISC_FALSE);
616 * Initialize ACL environment, setting up localhost and localnets ACLs
618 isc_result_t
619 dns_aclenv_init(isc_mem_t *mctx, dns_aclenv_t *env) {
620 isc_result_t result;
622 env->localhost = NULL;
623 env->localnets = NULL;
624 result = dns_acl_create(mctx, 0, &env->localhost);
625 if (result != ISC_R_SUCCESS)
626 goto cleanup_nothing;
627 result = dns_acl_create(mctx, 0, &env->localnets);
628 if (result != ISC_R_SUCCESS)
629 goto cleanup_localhost;
630 env->match_mapped = ISC_FALSE;
631 #ifdef HAVE_GEOIP
632 env->geoip = NULL;
633 #endif
634 return (ISC_R_SUCCESS);
636 cleanup_localhost:
637 dns_acl_detach(&env->localhost);
638 cleanup_nothing:
639 return (result);
642 void
643 dns_aclenv_copy(dns_aclenv_t *t, dns_aclenv_t *s) {
644 dns_acl_detach(&t->localhost);
645 dns_acl_attach(s->localhost, &t->localhost);
646 dns_acl_detach(&t->localnets);
647 dns_acl_attach(s->localnets, &t->localnets);
648 t->match_mapped = s->match_mapped;
651 void
652 dns_aclenv_destroy(dns_aclenv_t *env) {
653 dns_acl_detach(&env->localhost);
654 dns_acl_detach(&env->localnets);