No empty .Rs/.Re
[netbsd-mini2440.git] / external / bsd / bind / dist / lib / isccfg / aclconf.c
blob65aaa01237115683636615f04dd5936203a038a2
1 /* $NetBSD$ */
3 /*
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 */
22 #include <config.h>
24 #include <isc/mem.h>
25 #include <isc/string.h> /* Required for HP/UX (and others?) */
26 #include <isc/util.h>
28 #include <isccfg/namedconf.h>
29 #include <isccfg/aclconf.h>
31 #include <dns/acl.h>
32 #include <dns/iptable.h>
33 #include <dns/fixedname.h>
34 #include <dns/log.h>
36 #define LOOP_MAGIC ISC_MAGIC('L','O','O','P')
38 void
39 cfg_aclconfctx_init(cfg_aclconfctx_t *ctx) {
40 ISC_LIST_INIT(ctx->named_acl_cache);
43 void
44 cfg_aclconfctx_destroy(cfg_aclconfctx_t *ctx) {
45 dns_acl_t *dacl, *next;
47 for (dacl = ISC_LIST_HEAD(ctx->named_acl_cache);
48 dacl != NULL;
49 dacl = next)
51 next = ISC_LIST_NEXT(dacl, nextincache);
52 dns_acl_detach(&dacl);
57 * Find the definition of the named acl whose name is "name".
59 static isc_result_t
60 get_acl_def(const cfg_obj_t *cctx, const char *name, const cfg_obj_t **ret) {
61 isc_result_t result;
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)
67 return (result);
68 for (elt = cfg_list_first(acls);
69 elt != NULL;
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) {
74 if (ret != NULL) {
75 *ret = cfg_tuple_get(acl, "value");
77 return (ISC_R_SUCCESS);
80 return (ISC_R_NOTFOUND);
83 static isc_result_t
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,
87 dns_acl_t **target)
89 isc_result_t result;
90 const cfg_obj_t *cacl = NULL;
91 dns_acl_t *dacl;
92 dns_acl_t loop;
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);
97 dacl != NULL;
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);
115 return (result);
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,
126 nest_level, &dacl);
127 ISC_LIST_UNLINK(ctx->named_acl_cache, &loop, nextincache);
128 loop.magic = 0;
129 loop.name = NULL;
130 if (result != ISC_R_SUCCESS)
131 return (result);
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);
140 static isc_result_t
141 convert_keyname(const cfg_obj_t *keyobj, isc_log_t *lctx, isc_mem_t *mctx,
142 dns_name_t *dnsname)
144 isc_result_t result;
145 isc_buffer_t buf;
146 dns_fixedname_t fixname;
147 unsigned int keylen;
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",
159 txtname);
160 return (result);
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
170 * parent.
172 static int
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;
178 isc_result_t result;
179 int n = 0;
181 if (has_negative != NULL)
182 *has_negative = ISC_FALSE;
184 for (elt = cfg_list_first(caml);
185 elt != NULL;
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)) {
197 n++;
198 } else if (cfg_obj_islist(ce)) {
199 isc_boolean_t negative;
200 n += count_acl_elements(ce, cctx, &negative);
201 if (negative)
202 n++;
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) {
207 n++;
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,
213 NULL) + 1;
218 return n;
221 isc_result_t
222 cfg_acl_fromconfig(const cfg_obj_t *caml,
223 const cfg_obj_t *cctx,
224 isc_log_t *lctx,
225 cfg_aclconfctx_t *ctx,
226 isc_mem_t *mctx,
227 unsigned int nest_level,
228 dns_acl_t **target)
230 isc_result_t result;
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;
237 if (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
248 * parent ACL.
250 dns_acl_attach(*target, &dacl);
251 dns_acl_detach(target);
252 } else {
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.)
259 int nelem;
261 if (nest_level == 0)
262 nelem = count_acl_elements(caml, cctx, NULL);
263 else
264 nelem = cfg_list_length(caml, ISC_FALSE);
266 result = dns_acl_create(mctx, nelem, &dacl);
267 if (result != ISC_R_SUCCESS)
268 return (result);
271 de = dacl->elements;
272 for (elt = cfg_list_first(caml);
273 elt != NULL;
274 elt = cfg_list_next(elt)) {
275 const cfg_obj_t *ce = cfg_listelt_value(elt);
276 isc_boolean_t neg;
278 if (cfg_obj_istuple(ce)) {
279 /* This must be a negated element. */
280 ce = cfg_tuple_get(ce, "value");
281 neg = ISC_TRUE;
282 dacl->has_negatives = ISC_TRUE;
283 } else
284 neg = ISC_FALSE;
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),
296 &de->nestedacl);
297 if (result != ISC_R_SUCCESS)
298 goto cleanup;
299 iptab = de->nestedacl->iptable;
302 if (cfg_obj_isnetprefix(ce)) {
303 /* Network prefix */
304 isc_netaddr_t addr;
305 unsigned int bitlen;
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)
316 goto cleanup;
318 if (nest_level > 0) {
319 de->type = dns_aclelementtype_nestedacl;
320 de->negative = neg;
321 } else
322 continue;
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,
335 &inneracl);
336 if (result != ISC_R_SUCCESS)
337 goto cleanup;
338 nested_acl:
339 if (nest_level > 0 || inneracl->has_negatives) {
340 de->type = dns_aclelementtype_nestedacl;
341 de->negative = neg;
342 if (de->nestedacl != NULL)
343 dns_acl_detach(&de->nestedacl);
344 dns_acl_attach(inneracl,
345 &de->nestedacl);
346 dns_acl_detach(&inneracl);
347 /* Fall through. */
348 } else {
349 dns_acl_merge(dacl, inneracl,
350 ISC_TF(!neg));
351 de += inneracl->length; /* elements added */
352 dns_acl_detach(&inneracl);
353 continue;
355 } else if (cfg_obj_istype(ce, &cfg_type_keyref)) {
356 /* Key name. */
357 de->type = dns_aclelementtype_keyname;
358 de->negative = neg;
359 dns_name_init(&de->keyname, NULL);
360 result = convert_keyname(ce, lctx, mctx,
361 &de->keyname);
362 if (result != ISC_R_SUCCESS)
363 goto cleanup;
364 } else if (cfg_obj_isstring(ce)) {
365 /* ACL name. */
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)
372 goto cleanup;
374 if (nest_level != 0) {
375 de->type = dns_aclelementtype_nestedacl;
376 de->negative = neg;
377 } else
378 continue;
379 } else if (strcasecmp(name, "none") == 0) {
380 /* none == !any */
382 * We don't unconditional set
383 * dacl->has_negatives and
384 * de->negative to true so we can handle
385 * "!none;".
387 result = dns_iptable_addprefix(iptab, NULL, 0,
388 ISC_TF(nest_level != 0 || neg));
389 if (result != ISC_R_SUCCESS)
390 goto cleanup;
392 if (!neg)
393 dacl->has_negatives = !neg;
395 if (nest_level != 0) {
396 de->type = dns_aclelementtype_nestedacl;
397 de->negative = !neg;
398 } else
399 continue;
400 } else if (strcasecmp(name, "localhost") == 0) {
401 de->type = dns_aclelementtype_localhost;
402 de->negative = neg;
403 } else if (strcasecmp(name, "localnets") == 0) {
404 de->type = dns_aclelementtype_localnets;
405 de->negative = neg;
406 } else {
407 if (inneracl != NULL)
408 dns_acl_detach(&inneracl);
409 result = convert_named_acl(ce, cctx, lctx, ctx,
410 mctx, new_nest_level,
411 &inneracl);
412 if (result != ISC_R_SUCCESS)
413 goto cleanup;
415 goto nested_acl;
417 } else {
418 cfg_obj_log(ce, lctx, ISC_LOG_WARNING,
419 "address match list contains "
420 "unsupported element type");
421 result = ISC_R_FAILURE;
422 goto cleanup;
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);
434 dacl->node_count++;
435 de->node_num = dacl->node_count;
437 dacl->length++;
438 de++;
439 INSIST(dacl->length <= dacl->alloc);
442 dns_acl_attach(dacl, target);
443 result = ISC_R_SUCCESS;
445 cleanup:
446 if (inneracl != NULL)
447 dns_acl_detach(&inneracl);
448 dns_acl_detach(&dacl);
449 return (result);