2 * Copyright (C) 2004, 2006 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 1999-2002 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.52.6 2006/03/02 00:37:20 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
, const 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(const isc_netaddr_t
*reqaddr
,
127 const dns_name_t
*reqsigner
,
128 const dns_acl_t
*acl
,
129 const dns_aclenv_t
*env
,
131 dns_aclelement_t
const**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_acl_elementmatch(const dns_acl_t
*acl
,
154 const dns_aclelement_t
*elt
,
155 const dns_aclelement_t
**matchelt
)
159 REQUIRE(elt
!= NULL
);
160 REQUIRE(matchelt
== NULL
|| *matchelt
== NULL
);
162 for (i
= 0; i
< acl
->length
; i
++) {
163 dns_aclelement_t
*e
= &acl
->elements
[i
];
165 if (dns_aclelement_equal(e
, elt
) == ISC_TRUE
) {
166 if (matchelt
!= NULL
)
168 return (ISC_R_SUCCESS
);
172 return (ISC_R_NOTFOUND
);
176 dns_aclelement_match(const isc_netaddr_t
*reqaddr
,
177 const dns_name_t
*reqsigner
,
178 const dns_aclelement_t
*e
,
179 const dns_aclenv_t
*env
,
180 const dns_aclelement_t
**matchelt
)
182 dns_acl_t
*inner
= NULL
;
183 const isc_netaddr_t
*addr
;
184 isc_netaddr_t v4addr
;
189 case dns_aclelementtype_ipprefix
:
191 env
->match_mapped
== ISC_FALSE
||
192 reqaddr
->family
!= AF_INET6
||
193 !IN6_IS_ADDR_V4MAPPED(&reqaddr
->type
.in6
))
196 isc_netaddr_fromv4mapped(&v4addr
, reqaddr
);
200 if (isc_netaddr_eqprefix(addr
,
201 &e
->u
.ip_prefix
.address
,
202 e
->u
.ip_prefix
.prefixlen
))
206 case dns_aclelementtype_keyname
:
207 if (reqsigner
!= NULL
&&
208 dns_name_equal(reqsigner
, &e
->u
.keyname
))
212 case dns_aclelementtype_nestedacl
:
213 inner
= e
->u
.nestedacl
;
215 result
= dns_acl_match(reqaddr
, reqsigner
,
218 &indirectmatch
, matchelt
);
219 INSIST(result
== ISC_R_SUCCESS
);
222 * Treat negative matches in indirect ACLs as
224 * That way, a negated indirect ACL will never become
225 * a surprise positive match through double negation.
226 * XXXDCL this should be documented.
228 if (indirectmatch
> 0)
232 * A negative indirect match may have set *matchelt,
233 * but we don't want it set when we return.
235 if (matchelt
!= NULL
)
239 case dns_aclelementtype_any
:
241 if (matchelt
!= NULL
)
246 case dns_aclelementtype_localhost
:
247 if (env
!= NULL
&& env
->localhost
!= NULL
) {
248 inner
= env
->localhost
;
254 case dns_aclelementtype_localnets
:
255 if (env
!= NULL
&& env
->localnets
!= NULL
) {
256 inner
= env
->localnets
;
271 dns_acl_attach(dns_acl_t
*source
, dns_acl_t
**target
) {
272 REQUIRE(DNS_ACL_VALID(source
));
273 isc_refcount_increment(&source
->refcount
, NULL
);
278 destroy(dns_acl_t
*dacl
) {
280 for (i
= 0; i
< dacl
->length
; i
++) {
281 dns_aclelement_t
*de
= &dacl
->elements
[i
];
283 case dns_aclelementtype_keyname
:
284 dns_name_free(&de
->u
.keyname
, dacl
->mctx
);
286 case dns_aclelementtype_nestedacl
:
287 dns_acl_detach(&de
->u
.nestedacl
);
293 if (dacl
->elements
!= NULL
)
294 isc_mem_put(dacl
->mctx
, dacl
->elements
,
295 dacl
->alloc
* sizeof(dns_aclelement_t
));
296 if (dacl
->name
!= NULL
)
297 isc_mem_free(dacl
->mctx
, dacl
->name
);
298 isc_refcount_destroy(&dacl
->refcount
);
300 isc_mem_put(dacl
->mctx
, dacl
, sizeof(*dacl
));
304 dns_acl_detach(dns_acl_t
**aclp
) {
305 dns_acl_t
*acl
= *aclp
;
307 REQUIRE(DNS_ACL_VALID(acl
));
308 isc_refcount_decrement(&acl
->refcount
, &refs
);
315 dns_aclelement_equal(const dns_aclelement_t
*ea
, const dns_aclelement_t
*eb
) {
316 if (ea
->type
!= eb
->type
)
319 case dns_aclelementtype_ipprefix
:
320 if (ea
->u
.ip_prefix
.prefixlen
!=
321 eb
->u
.ip_prefix
.prefixlen
)
323 return (isc_netaddr_eqprefix(&ea
->u
.ip_prefix
.address
,
324 &eb
->u
.ip_prefix
.address
,
325 ea
->u
.ip_prefix
.prefixlen
));
326 case dns_aclelementtype_keyname
:
327 return (dns_name_equal(&ea
->u
.keyname
, &eb
->u
.keyname
));
328 case dns_aclelementtype_nestedacl
:
329 return (dns_acl_equal(ea
->u
.nestedacl
, eb
->u
.nestedacl
));
330 case dns_aclelementtype_localhost
:
331 case dns_aclelementtype_localnets
:
332 case dns_aclelementtype_any
:
341 dns_acl_equal(const dns_acl_t
*a
, const dns_acl_t
*b
) {
345 if (a
->length
!= b
->length
)
347 for (i
= 0; i
< a
->length
; i
++) {
348 if (! dns_aclelement_equal(&a
->elements
[i
],
356 is_loopback(const dns_aclipprefix_t
*p
) {
357 switch (p
->address
.family
) {
359 if (p
->prefixlen
== 32 &&
360 htonl(p
->address
.type
.in
.s_addr
) == INADDR_LOOPBACK
)
364 if (p
->prefixlen
== 128 &&
365 IN6_IS_ADDR_LOOPBACK(&p
->address
.type
.in6
))
375 dns_acl_isinsecure(const dns_acl_t
*a
) {
377 for (i
= 0; i
< a
->length
; i
++) {
378 dns_aclelement_t
*e
= &a
->elements
[i
];
380 /* A negated match can never be insecure. */
385 case dns_aclelementtype_ipprefix
:
386 /* The loopback address is considered secure. */
387 if (! is_loopback(&e
->u
.ip_prefix
))
391 case dns_aclelementtype_keyname
:
392 case dns_aclelementtype_localhost
:
395 case dns_aclelementtype_nestedacl
:
396 if (dns_acl_isinsecure(e
->u
.nestedacl
))
400 case dns_aclelementtype_localnets
:
401 case dns_aclelementtype_any
:
409 /* No insecure elements were found. */
414 dns_aclenv_init(isc_mem_t
*mctx
, dns_aclenv_t
*env
) {
416 env
->localhost
= NULL
;
417 env
->localnets
= NULL
;
418 result
= dns_acl_create(mctx
, 0, &env
->localhost
);
419 if (result
!= ISC_R_SUCCESS
)
420 goto cleanup_nothing
;
421 result
= dns_acl_create(mctx
, 0, &env
->localnets
);
422 if (result
!= ISC_R_SUCCESS
)
423 goto cleanup_localhost
;
424 env
->match_mapped
= ISC_FALSE
;
425 return (ISC_R_SUCCESS
);
428 dns_acl_detach(&env
->localhost
);
434 dns_aclenv_copy(dns_aclenv_t
*t
, dns_aclenv_t
*s
) {
435 dns_acl_detach(&t
->localhost
);
436 dns_acl_attach(s
->localhost
, &t
->localhost
);
437 dns_acl_detach(&t
->localnets
);
438 dns_acl_attach(s
->localnets
, &t
->localnets
);
439 t
->match_mapped
= s
->match_mapped
;
443 dns_aclenv_destroy(dns_aclenv_t
*env
) {
444 dns_acl_detach(&env
->localhost
);
445 dns_acl_detach(&env
->localnets
);