1 /* $NetBSD: acl.c,v 1.7 2014/12/10 04:37:58 christos Exp $ */
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 */
28 #include <isc/string.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
41 dns_acl_create(isc_mem_t
*mctx
, int n
, dns_acl_t
**target
) {
46 * Work around silly limitation of isc_mem_get().
51 acl
= isc_mem_get(mctx
, sizeof(*acl
));
53 return (ISC_R_NOMEMORY
);
56 isc_mem_attach(mctx
, &acl
->mctx
);
60 result
= isc_refcount_init(&acl
->refcount
, 1);
61 if (result
!= ISC_R_SUCCESS
) {
62 isc_mem_put(mctx
, acl
, sizeof(*acl
));
66 result
= dns_iptable_create(mctx
, &acl
->iptable
);
67 if (result
!= ISC_R_SUCCESS
) {
68 isc_mem_put(mctx
, acl
, sizeof(*acl
));
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
;
89 memset(acl
->elements
, 0, n
* sizeof(dns_aclelement_t
));
91 return (ISC_R_SUCCESS
);
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".
105 dns_acl_anyornone(isc_mem_t
*mctx
, isc_boolean_t neg
, dns_acl_t
**target
) {
107 dns_acl_t
*acl
= NULL
;
109 result
= dns_acl_create(mctx
, 0, &acl
);
110 if (result
!= ISC_R_SUCCESS
)
113 result
= dns_iptable_addprefix(acl
->iptable
, NULL
, 0, ISC_TF(!neg
));
114 if (result
!= ISC_R_SUCCESS
) {
115 dns_acl_detach(&acl
);
124 * Create a new ACL that matches everything.
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.
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; }"
144 dns_acl_isanyornone(dns_acl_t
*acl
, isc_boolean_t pos
)
146 /* Should never happen but let's be safe */
148 acl
->iptable
== NULL
||
149 acl
->iptable
->radix
== NULL
||
150 acl
->iptable
->radix
->head
== NULL
||
151 acl
->iptable
->radix
->head
->prefix
== NULL
)
154 if (acl
->length
!= 0 || acl
->node_count
!= 1)
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
)
164 return (ISC_FALSE
); /* All others */
168 * Test whether acl is set to "{ any; }"
171 dns_acl_isany(dns_acl_t
*acl
)
173 return (dns_acl_isanyornone(acl
, ISC_TRUE
));
177 * Test whether acl is set to "{ none; }"
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.
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
,
197 const dns_aclelement_t
**matchelt
)
199 isc_uint16_t bitlen
, family
;
201 isc_radix_node_t
*node
= NULL
;
202 const isc_netaddr_t
*addr
;
203 isc_netaddr_t v4addr
;
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
))
216 isc_netaddr_fromv4mapped(&v4addr
, reqaddr
);
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. */
229 result
= isc_radix_search(acl
->iptable
->radix
, &node
, &pfx
);
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
)
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
,
252 if (match_num
== -1 || e
->node_num
< match_num
) {
253 if (e
->negative
== ISC_TRUE
)
254 *match
= -e
->node_num
;
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.
277 dns_acl_merge(dns_acl_t
*dest
, dns_acl_t
*source
, isc_boolean_t pos
)
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
) {
287 newalloc
= dest
->alloc
+ source
->alloc
;
291 newmem
= isc_mem_get(dest
->mctx
,
292 newalloc
* sizeof(dns_aclelement_t
));
294 return (ISC_R_NOMEMORY
);
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
;
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
,
340 &dest
->elements
[nelem
+i
].keyname
);
341 if (result
!= ISC_R_SUCCESS
)
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
;
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
;
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
)
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.
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
;
397 case dns_aclelementtype_keyname
:
398 if (reqsigner
!= NULL
&&
399 dns_name_equal(reqsigner
, &e
->keyname
)) {
400 if (matchelt
!= NULL
)
406 case dns_aclelementtype_nestedacl
:
407 inner
= e
->nestedacl
;
410 case dns_aclelementtype_localhost
:
411 if (env
== NULL
|| env
->localhost
== NULL
)
413 inner
= env
->localhost
;
416 case dns_aclelementtype_localnets
:
417 if (env
== NULL
|| env
->localnets
== NULL
)
419 inner
= env
->localnets
;
423 case dns_aclelementtype_geoip
:
424 if (env
== NULL
|| env
->geoip
== NULL
)
426 return (dns_geoip_match(reqaddr
, env
->geoip
, &e
->geoip_elem
));
429 /* Should be impossible. */
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
)
451 * A negative indirect match may have set *matchelt, but we don't
452 * want it set when we return.
455 if (matchelt
!= NULL
)
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
);
470 destroy(dns_acl_t
*dacl
) {
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
);
492 isc_mem_putanddetach(&dacl
->mctx
, dacl
, sizeof(*dacl
));
496 dns_acl_detach(dns_acl_t
**aclp
) {
497 dns_acl_t
*acl
= *aclp
;
500 REQUIRE(DNS_ACL_VALID(acl
));
502 isc_refcount_decrement(&acl
->refcount
, &refs
);
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
;
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
523 is_insecure(isc_prefix_t
*prefix
, void **data
) {
524 isc_boolean_t secure
;
527 bitlen
= prefix
->bitlen
;
528 family
= prefix
->family
;
530 /* Negated entries are always secure. */
531 secure
= * (isc_boolean_t
*)data
[ISC_IS6(family
)];
536 /* If loopback prefix found, return */
540 htonl(prefix
->add
.sin
.s_addr
) == INADDR_LOOPBACK
)
544 if (bitlen
== 128 && IN6_IS_ADDR_LOOPBACK(&prefix
->add
.sin6
))
551 /* Non-negated, non-loopback */
552 insecure_prefix_found
= ISC_TRUE
; /* LOCKED */
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.
565 dns_acl_isinsecure(const dns_acl_t
*a
) {
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
);
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. */
593 case dns_aclelementtype_keyname
:
594 case dns_aclelementtype_localhost
:
597 case dns_aclelementtype_nestedacl
:
598 if (dns_acl_isinsecure(e
->nestedacl
))
602 case dns_aclelementtype_localnets
:
611 /* No insecure elements were found. */
616 * Initialize ACL environment, setting up localhost and localnets ACLs
619 dns_aclenv_init(isc_mem_t
*mctx
, dns_aclenv_t
*env
) {
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
;
634 return (ISC_R_SUCCESS
);
637 dns_acl_detach(&env
->localhost
);
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
;
652 dns_aclenv_destroy(dns_aclenv_t
*env
) {
653 dns_acl_detach(&env
->localhost
);
654 dns_acl_detach(&env
->localnets
);