2 * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 1999-2001 Internet Software Consortium.
5 * Permission to use, copy, modify, and 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.23.2.1 2004/03/09 06:10:59 marka Exp $ */
23 #include <isc/string.h>
29 dns_acl_create(isc_mem_t
*mctx
, int n
, dns_acl_t
**target
) {
34 * Work around silly limitation of isc_mem_get().
39 acl
= isc_mem_get(mctx
, sizeof(*acl
));
41 return (ISC_R_NOMEMORY
);
44 isc_refcount_init(&acl
->refcount
, 1);
49 ISC_LINK_INIT(acl
, nextincache
);
51 * Must set magic early because we use dns_acl_detach() to clean up.
53 acl
->magic
= DNS_ACL_MAGIC
;
55 acl
->elements
= isc_mem_get(mctx
, n
* sizeof(dns_aclelement_t
));
56 if (acl
->elements
== NULL
) {
57 result
= ISC_R_NOMEMORY
;
61 memset(acl
->elements
, 0, n
* sizeof(dns_aclelement_t
));
63 return (ISC_R_SUCCESS
);
71 dns_acl_appendelement(dns_acl_t
*acl
, dns_aclelement_t
*elt
) {
72 if (acl
->length
+ 1 > acl
->alloc
) {
76 unsigned int newalloc
;
79 newalloc
= acl
->alloc
* 2;
82 newmem
= isc_mem_get(acl
->mctx
,
83 newalloc
* sizeof(dns_aclelement_t
));
85 return (ISC_R_NOMEMORY
);
86 memcpy(newmem
, acl
->elements
,
87 acl
->length
* sizeof(dns_aclelement_t
));
88 isc_mem_put(acl
->mctx
, acl
->elements
,
89 acl
->alloc
* sizeof(dns_aclelement_t
));
90 acl
->elements
= newmem
;
91 acl
->alloc
= newalloc
;
94 * Append the new element.
96 acl
->elements
[acl
->length
++] = *elt
;
98 return (ISC_R_SUCCESS
);
102 dns_acl_anyornone(isc_mem_t
*mctx
, isc_boolean_t neg
, dns_acl_t
**target
) {
104 dns_acl_t
*acl
= NULL
;
105 result
= dns_acl_create(mctx
, 1, &acl
);
106 if (result
!= ISC_R_SUCCESS
)
108 acl
->elements
[0].negative
= neg
;
109 acl
->elements
[0].type
= dns_aclelementtype_any
;
116 dns_acl_any(isc_mem_t
*mctx
, dns_acl_t
**target
) {
117 return (dns_acl_anyornone(mctx
, ISC_FALSE
, target
));
121 dns_acl_none(isc_mem_t
*mctx
, dns_acl_t
**target
) {
122 return (dns_acl_anyornone(mctx
, ISC_TRUE
, target
));
126 dns_acl_match(isc_netaddr_t
*reqaddr
,
127 dns_name_t
*reqsigner
,
131 dns_aclelement_t
**matchelt
)
135 REQUIRE(reqaddr
!= NULL
);
136 REQUIRE(matchelt
== NULL
|| *matchelt
== NULL
);
138 for (i
= 0; i
< acl
->length
; i
++) {
139 dns_aclelement_t
*e
= &acl
->elements
[i
];
141 if (dns_aclelement_match(reqaddr
, reqsigner
,
143 *match
= e
->negative
? -((int)i
+1) : ((int)i
+1);
144 return (ISC_R_SUCCESS
);
149 return (ISC_R_SUCCESS
);
153 dns_aclelement_match(isc_netaddr_t
*reqaddr
,
154 dns_name_t
*reqsigner
,
157 dns_aclelement_t
**matchelt
)
159 dns_acl_t
*inner
= NULL
;
161 isc_netaddr_t v4addr
;
166 case dns_aclelementtype_ipprefix
:
168 env
->match_mapped
== ISC_FALSE
||
169 reqaddr
->family
!= AF_INET6
||
170 !IN6_IS_ADDR_V4MAPPED(&reqaddr
->type
.in6
))
173 isc_netaddr_fromv4mapped(&v4addr
, reqaddr
);
177 if (isc_netaddr_eqprefix(addr
,
178 &e
->u
.ip_prefix
.address
,
179 e
->u
.ip_prefix
.prefixlen
))
183 case dns_aclelementtype_keyname
:
184 if (reqsigner
!= NULL
&&
185 dns_name_equal(reqsigner
, &e
->u
.keyname
))
189 case dns_aclelementtype_nestedacl
:
190 inner
= e
->u
.nestedacl
;
192 result
= dns_acl_match(reqaddr
, reqsigner
,
195 &indirectmatch
, matchelt
);
196 INSIST(result
== ISC_R_SUCCESS
);
199 * Treat negative matches in indirect ACLs as
201 * That way, a negated indirect ACL will never become
202 * a surprise positive match through double negation.
203 * XXXDCL this should be documented.
205 if (indirectmatch
> 0)
209 * A negative indirect match may have set *matchelt,
210 * but we don't want it set when we return.
212 if (matchelt
!= NULL
)
216 case dns_aclelementtype_any
:
218 if (matchelt
!= NULL
)
223 case dns_aclelementtype_localhost
:
224 if (env
!= NULL
&& env
->localhost
!= NULL
) {
225 inner
= env
->localhost
;
231 case dns_aclelementtype_localnets
:
232 if (env
!= NULL
&& env
->localnets
!= NULL
) {
233 inner
= env
->localnets
;
248 dns_acl_attach(dns_acl_t
*source
, dns_acl_t
**target
) {
249 REQUIRE(DNS_ACL_VALID(source
));
250 isc_refcount_increment(&source
->refcount
, NULL
);
255 destroy(dns_acl_t
*dacl
) {
257 for (i
= 0; i
< dacl
->length
; i
++) {
258 dns_aclelement_t
*de
= &dacl
->elements
[i
];
260 case dns_aclelementtype_keyname
:
261 dns_name_free(&de
->u
.keyname
, dacl
->mctx
);
263 case dns_aclelementtype_nestedacl
:
264 dns_acl_detach(&de
->u
.nestedacl
);
270 if (dacl
->elements
!= NULL
)
271 isc_mem_put(dacl
->mctx
, dacl
->elements
,
272 dacl
->alloc
* sizeof(dns_aclelement_t
));
273 if (dacl
->name
!= NULL
)
274 isc_mem_free(dacl
->mctx
, dacl
->name
);
275 isc_refcount_destroy(&dacl
->refcount
);
277 isc_mem_put(dacl
->mctx
, dacl
, sizeof(*dacl
));
281 dns_acl_detach(dns_acl_t
**aclp
) {
282 dns_acl_t
*acl
= *aclp
;
284 REQUIRE(DNS_ACL_VALID(acl
));
285 isc_refcount_decrement(&acl
->refcount
, &refs
);
292 dns_aclelement_equal(dns_aclelement_t
*ea
, dns_aclelement_t
*eb
) {
293 if (ea
->type
!= eb
->type
)
296 case dns_aclelementtype_ipprefix
:
297 if (ea
->u
.ip_prefix
.prefixlen
!=
298 eb
->u
.ip_prefix
.prefixlen
)
300 return (isc_netaddr_equal(&ea
->u
.ip_prefix
.address
,
301 &eb
->u
.ip_prefix
.address
));
302 case dns_aclelementtype_keyname
:
303 return (dns_name_equal(&ea
->u
.keyname
, &eb
->u
.keyname
));
304 case dns_aclelementtype_nestedacl
:
305 return (dns_acl_equal(ea
->u
.nestedacl
, eb
->u
.nestedacl
));
306 case dns_aclelementtype_localhost
:
307 case dns_aclelementtype_localnets
:
308 case dns_aclelementtype_any
:
317 dns_acl_equal(dns_acl_t
*a
, dns_acl_t
*b
) {
321 if (a
->length
!= b
->length
)
323 for (i
= 0; i
< a
->length
; i
++) {
324 if (! dns_aclelement_equal(&a
->elements
[i
],
332 is_loopback(dns_aclipprefix_t
*p
) {
333 switch (p
->address
.family
) {
335 if (p
->prefixlen
== 32 &&
336 htonl(p
->address
.type
.in
.s_addr
) == INADDR_LOOPBACK
)
340 if (p
->prefixlen
== 128 &&
341 IN6_IS_ADDR_LOOPBACK(&p
->address
.type
.in6
))
351 dns_acl_isinsecure(dns_acl_t
*a
) {
353 for (i
= 0; i
< a
->length
; i
++) {
354 dns_aclelement_t
*e
= &a
->elements
[i
];
356 /* A negated match can never be insecure. */
361 case dns_aclelementtype_ipprefix
:
362 /* The loopback address is considered secure. */
363 if (! is_loopback(&e
->u
.ip_prefix
))
367 case dns_aclelementtype_keyname
:
368 case dns_aclelementtype_localhost
:
371 case dns_aclelementtype_nestedacl
:
372 if (dns_acl_isinsecure(e
->u
.nestedacl
))
376 case dns_aclelementtype_localnets
:
377 case dns_aclelementtype_any
:
385 /* No insecure elements were found. */
390 dns_aclenv_init(isc_mem_t
*mctx
, dns_aclenv_t
*env
) {
392 env
->localhost
= NULL
;
393 env
->localnets
= NULL
;
394 result
= dns_acl_create(mctx
, 0, &env
->localhost
);
395 if (result
!= ISC_R_SUCCESS
)
396 goto cleanup_nothing
;
397 result
= dns_acl_create(mctx
, 0, &env
->localnets
);
398 if (result
!= ISC_R_SUCCESS
)
399 goto cleanup_localhost
;
400 env
->match_mapped
= ISC_FALSE
;
401 return (ISC_R_SUCCESS
);
404 dns_acl_detach(&env
->localhost
);
410 dns_aclenv_copy(dns_aclenv_t
*t
, dns_aclenv_t
*s
) {
411 dns_acl_detach(&t
->localhost
);
412 dns_acl_attach(s
->localhost
, &t
->localhost
);
413 dns_acl_detach(&t
->localnets
);
414 dns_acl_attach(s
->localnets
, &t
->localnets
);
415 t
->match_mapped
= s
->match_mapped
;
419 dns_aclenv_destroy(dns_aclenv_t
*env
) {
420 dns_acl_detach(&env
->localhost
);
421 dns_acl_detach(&env
->localnets
);