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: aclconf.c,v 1.27 2009/10/01 23:48:08 tbox Exp */
25 #include <isc/string.h> /* Required for HP/UX (and others?) */
28 #include <isccfg/namedconf.h>
29 #include <isccfg/aclconf.h>
32 #include <dns/iptable.h>
33 #include <dns/fixedname.h>
36 #define LOOP_MAGIC ISC_MAGIC('L','O','O','P')
39 cfg_aclconfctx_init(cfg_aclconfctx_t
*ctx
) {
40 ISC_LIST_INIT(ctx
->named_acl_cache
);
44 cfg_aclconfctx_destroy(cfg_aclconfctx_t
*ctx
) {
45 dns_acl_t
*dacl
, *next
;
47 for (dacl
= ISC_LIST_HEAD(ctx
->named_acl_cache
);
51 next
= ISC_LIST_NEXT(dacl
, nextincache
);
52 dns_acl_detach(&dacl
);
57 * Find the definition of the named acl whose name is "name".
60 get_acl_def(const cfg_obj_t
*cctx
, const char *name
, const cfg_obj_t
**ret
) {
62 const cfg_obj_t
*acls
= NULL
;
63 const cfg_listelt_t
*elt
;
65 result
= cfg_map_get(cctx
, "acl", &acls
);
66 if (result
!= ISC_R_SUCCESS
)
68 for (elt
= cfg_list_first(acls
);
70 elt
= cfg_list_next(elt
)) {
71 const cfg_obj_t
*acl
= cfg_listelt_value(elt
);
72 const char *aclname
= cfg_obj_asstring(cfg_tuple_get(acl
, "name"));
73 if (strcasecmp(aclname
, name
) == 0) {
75 *ret
= cfg_tuple_get(acl
, "value");
77 return (ISC_R_SUCCESS
);
80 return (ISC_R_NOTFOUND
);
84 convert_named_acl(const cfg_obj_t
*nameobj
, const cfg_obj_t
*cctx
,
85 isc_log_t
*lctx
, cfg_aclconfctx_t
*ctx
,
86 isc_mem_t
*mctx
, unsigned int nest_level
,
90 const cfg_obj_t
*cacl
= NULL
;
93 const char *aclname
= cfg_obj_asstring(nameobj
);
95 /* Look for an already-converted version. */
96 for (dacl
= ISC_LIST_HEAD(ctx
->named_acl_cache
);
98 dacl
= ISC_LIST_NEXT(dacl
, nextincache
))
100 if (strcasecmp(aclname
, dacl
->name
) == 0) {
101 if (ISC_MAGIC_VALID(dacl
, LOOP_MAGIC
)) {
102 cfg_obj_log(nameobj
, lctx
, ISC_LOG_ERROR
,
103 "acl loop detected: %s", aclname
);
104 return (ISC_R_FAILURE
);
106 dns_acl_attach(dacl
, target
);
107 return (ISC_R_SUCCESS
);
110 /* Not yet converted. Convert now. */
111 result
= get_acl_def(cctx
, aclname
, &cacl
);
112 if (result
!= ISC_R_SUCCESS
) {
113 cfg_obj_log(nameobj
, lctx
, ISC_LOG_WARNING
,
114 "undefined ACL '%s'", aclname
);
118 * Add a loop detection element.
120 memset(&loop
, 0, sizeof(loop
));
121 ISC_LINK_INIT(&loop
, nextincache
);
122 DE_CONST(aclname
, loop
.name
);
123 loop
.magic
= LOOP_MAGIC
;
124 ISC_LIST_APPEND(ctx
->named_acl_cache
, &loop
, nextincache
);
125 result
= cfg_acl_fromconfig(cacl
, cctx
, lctx
, ctx
, mctx
,
127 ISC_LIST_UNLINK(ctx
->named_acl_cache
, &loop
, nextincache
);
130 if (result
!= ISC_R_SUCCESS
)
132 dacl
->name
= isc_mem_strdup(dacl
->mctx
, aclname
);
133 if (dacl
->name
== NULL
)
134 return (ISC_R_NOMEMORY
);
135 ISC_LIST_APPEND(ctx
->named_acl_cache
, dacl
, nextincache
);
136 dns_acl_attach(dacl
, target
);
137 return (ISC_R_SUCCESS
);
141 convert_keyname(const cfg_obj_t
*keyobj
, isc_log_t
*lctx
, isc_mem_t
*mctx
,
146 dns_fixedname_t fixname
;
148 const char *txtname
= cfg_obj_asstring(keyobj
);
150 keylen
= strlen(txtname
);
151 isc_buffer_init(&buf
, txtname
, keylen
);
152 isc_buffer_add(&buf
, keylen
);
153 dns_fixedname_init(&fixname
);
154 result
= dns_name_fromtext(dns_fixedname_name(&fixname
), &buf
,
155 dns_rootname
, 0, NULL
);
156 if (result
!= ISC_R_SUCCESS
) {
157 cfg_obj_log(keyobj
, lctx
, ISC_LOG_WARNING
,
158 "key name '%s' is not a valid domain name",
162 return (dns_name_dup(dns_fixedname_name(&fixname
), mctx
, dnsname
));
166 * Recursively pre-parse an ACL definition to find the total number
167 * of non-IP-prefix elements (localhost, localnets, key) in all nested
168 * ACLs, so that the parent will have enough space allocated for the
169 * elements table after all the nested ACLs have been merged in to the
173 count_acl_elements(const cfg_obj_t
*caml
, const cfg_obj_t
*cctx
,
174 isc_boolean_t
*has_negative
)
176 const cfg_listelt_t
*elt
;
177 const cfg_obj_t
*cacl
= NULL
;
181 if (has_negative
!= NULL
)
182 *has_negative
= ISC_FALSE
;
184 for (elt
= cfg_list_first(caml
);
186 elt
= cfg_list_next(elt
)) {
187 const cfg_obj_t
*ce
= cfg_listelt_value(elt
);
189 /* negated element; just get the value. */
190 if (cfg_obj_istuple(ce
)) {
191 ce
= cfg_tuple_get(ce
, "value");
192 if (has_negative
!= NULL
)
193 *has_negative
= ISC_TRUE
;
196 if (cfg_obj_istype(ce
, &cfg_type_keyref
)) {
198 } else if (cfg_obj_islist(ce
)) {
199 isc_boolean_t negative
;
200 n
+= count_acl_elements(ce
, cctx
, &negative
);
203 } else if (cfg_obj_isstring(ce
)) {
204 const char *name
= cfg_obj_asstring(ce
);
205 if (strcasecmp(name
, "localhost") == 0 ||
206 strcasecmp(name
, "localnets") == 0) {
208 } else if (strcasecmp(name
, "any") != 0 &&
209 strcasecmp(name
, "none") != 0) {
210 result
= get_acl_def(cctx
, name
, &cacl
);
211 if (result
== ISC_R_SUCCESS
)
212 n
+= count_acl_elements(cacl
, cctx
,
222 cfg_acl_fromconfig(const cfg_obj_t
*caml
,
223 const cfg_obj_t
*cctx
,
225 cfg_aclconfctx_t
*ctx
,
227 unsigned int nest_level
,
231 dns_acl_t
*dacl
= NULL
, *inneracl
= NULL
;
232 dns_aclelement_t
*de
;
233 const cfg_listelt_t
*elt
;
234 dns_iptable_t
*iptab
;
235 int new_nest_level
= 0;
238 new_nest_level
= nest_level
- 1;
240 REQUIRE(target
!= NULL
);
241 REQUIRE(*target
== NULL
|| DNS_ACL_VALID(*target
));
243 if (*target
!= NULL
) {
245 * If target already points to an ACL, then we're being
246 * called recursively to configure a nested ACL. The
247 * nested ACL's contents should just be absorbed into its
250 dns_acl_attach(*target
, &dacl
);
251 dns_acl_detach(target
);
254 * Need to allocate a new ACL structure. Count the items
255 * in the ACL definition that will require space in the
256 * elements table. (Note that if nest_level is nonzero,
257 * *everything* goes in the elements table.)
262 nelem
= count_acl_elements(caml
, cctx
, NULL
);
264 nelem
= cfg_list_length(caml
, ISC_FALSE
);
266 result
= dns_acl_create(mctx
, nelem
, &dacl
);
267 if (result
!= ISC_R_SUCCESS
)
272 for (elt
= cfg_list_first(caml
);
274 elt
= cfg_list_next(elt
)) {
275 const cfg_obj_t
*ce
= cfg_listelt_value(elt
);
278 if (cfg_obj_istuple(ce
)) {
279 /* This must be a negated element. */
280 ce
= cfg_tuple_get(ce
, "value");
282 dacl
->has_negatives
= ISC_TRUE
;
287 * If nest_level is nonzero, then every element is
288 * to be stored as a separate, nested ACL rather than
289 * merged into the main iptable.
291 iptab
= dacl
->iptable
;
293 if (nest_level
!= 0) {
294 result
= dns_acl_create(mctx
,
295 cfg_list_length(ce
, ISC_FALSE
),
297 if (result
!= ISC_R_SUCCESS
)
299 iptab
= de
->nestedacl
->iptable
;
302 if (cfg_obj_isnetprefix(ce
)) {
307 cfg_obj_asnetprefix(ce
, &addr
, &bitlen
);
310 * If nesting ACLs (nest_level != 0), we negate
311 * the nestedacl element, not the iptable entry.
313 result
= dns_iptable_addprefix(iptab
, &addr
, bitlen
,
314 ISC_TF(nest_level
!= 0 || !neg
));
315 if (result
!= ISC_R_SUCCESS
)
318 if (nest_level
> 0) {
319 de
->type
= dns_aclelementtype_nestedacl
;
323 } else if (cfg_obj_islist(ce
)) {
325 * If we're nesting ACLs, put the nested
326 * ACL onto the elements list; otherwise
327 * merge it into *this* ACL. We nest ACLs
328 * in two cases: 1) sortlist, 2) if the
329 * nested ACL contains negated members.
331 if (inneracl
!= NULL
)
332 dns_acl_detach(&inneracl
);
333 result
= cfg_acl_fromconfig(ce
, cctx
, lctx
,
334 ctx
, mctx
, new_nest_level
,
336 if (result
!= ISC_R_SUCCESS
)
339 if (nest_level
> 0 || inneracl
->has_negatives
) {
340 de
->type
= dns_aclelementtype_nestedacl
;
342 if (de
->nestedacl
!= NULL
)
343 dns_acl_detach(&de
->nestedacl
);
344 dns_acl_attach(inneracl
,
346 dns_acl_detach(&inneracl
);
349 dns_acl_merge(dacl
, inneracl
,
351 de
+= inneracl
->length
; /* elements added */
352 dns_acl_detach(&inneracl
);
355 } else if (cfg_obj_istype(ce
, &cfg_type_keyref
)) {
357 de
->type
= dns_aclelementtype_keyname
;
359 dns_name_init(&de
->keyname
, NULL
);
360 result
= convert_keyname(ce
, lctx
, mctx
,
362 if (result
!= ISC_R_SUCCESS
)
364 } else if (cfg_obj_isstring(ce
)) {
366 const char *name
= cfg_obj_asstring(ce
);
367 if (strcasecmp(name
, "any") == 0) {
368 /* Iptable entry with zero bit length. */
369 result
= dns_iptable_addprefix(iptab
, NULL
, 0,
370 ISC_TF(nest_level
!= 0 || !neg
));
371 if (result
!= ISC_R_SUCCESS
)
374 if (nest_level
!= 0) {
375 de
->type
= dns_aclelementtype_nestedacl
;
379 } else if (strcasecmp(name
, "none") == 0) {
382 * We don't unconditional set
383 * dacl->has_negatives and
384 * de->negative to true so we can handle
387 result
= dns_iptable_addprefix(iptab
, NULL
, 0,
388 ISC_TF(nest_level
!= 0 || neg
));
389 if (result
!= ISC_R_SUCCESS
)
393 dacl
->has_negatives
= !neg
;
395 if (nest_level
!= 0) {
396 de
->type
= dns_aclelementtype_nestedacl
;
400 } else if (strcasecmp(name
, "localhost") == 0) {
401 de
->type
= dns_aclelementtype_localhost
;
403 } else if (strcasecmp(name
, "localnets") == 0) {
404 de
->type
= dns_aclelementtype_localnets
;
407 if (inneracl
!= NULL
)
408 dns_acl_detach(&inneracl
);
409 result
= convert_named_acl(ce
, cctx
, lctx
, ctx
,
410 mctx
, new_nest_level
,
412 if (result
!= ISC_R_SUCCESS
)
418 cfg_obj_log(ce
, lctx
, ISC_LOG_WARNING
,
419 "address match list contains "
420 "unsupported element type");
421 result
= ISC_R_FAILURE
;
426 * This should only be reached for localhost, localnets
427 * and keyname elements, and nested ACLs if nest_level is
428 * nonzero (i.e., in sortlists).
430 if (de
->nestedacl
!= NULL
&&
431 de
->type
!= dns_aclelementtype_nestedacl
)
432 dns_acl_detach(&de
->nestedacl
);
435 de
->node_num
= dacl
->node_count
;
439 INSIST(dacl
->length
<= dacl
->alloc
);
442 dns_acl_attach(dacl
, target
);
443 result
= ISC_R_SUCCESS
;
446 if (inneracl
!= NULL
)
447 dns_acl_detach(&inneracl
);
448 dns_acl_detach(&dacl
);