xref: /minix3/external/bsd/bind/dist/lib/dns/acl.c (revision 00b67f09dd46474d133c95011a48590a8e8f94c7)
1 /*	$NetBSD: acl.c,v 1.7 2014/12/10 04:37:58 christos Exp $	*/
2 
3 /*
4  * Copyright (C) 2004-2009, 2011, 2013, 2014  Internet Systems Consortium, Inc. ("ISC")
5  * Copyright (C) 1999-2002  Internet Software Consortium.
6  *
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.
10  *
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.
18  */
19 
20 /* Id: acl.c,v 1.55 2011/06/17 23:47:49 tbox Exp  */
21 
22 /*! \file */
23 
24 #include <config.h>
25 
26 #include <isc/mem.h>
27 #include <isc/once.h>
28 #include <isc/string.h>
29 #include <isc/util.h>
30 
31 #include <dns/acl.h>
32 #include <dns/iptable.h>
33 
34 
35 /*
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
38  * length is 0.
39  */
40 isc_result_t
dns_acl_create(isc_mem_t * mctx,int n,dns_acl_t ** target)41 dns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target) {
42 	isc_result_t result;
43 	dns_acl_t *acl;
44 
45 	/*
46 	 * Work around silly limitation of isc_mem_get().
47 	 */
48 	if (n == 0)
49 		n = 1;
50 
51 	acl = isc_mem_get(mctx, sizeof(*acl));
52 	if (acl == NULL)
53 		return (ISC_R_NOMEMORY);
54 
55 	acl->mctx = NULL;
56 	isc_mem_attach(mctx, &acl->mctx);
57 
58 	acl->name = NULL;
59 
60 	result = isc_refcount_init(&acl->refcount, 1);
61 	if (result != ISC_R_SUCCESS) {
62 		isc_mem_put(mctx, acl, sizeof(*acl));
63 		return (result);
64 	}
65 
66 	result = dns_iptable_create(mctx, &acl->iptable);
67 	if (result != ISC_R_SUCCESS) {
68 		isc_mem_put(mctx, acl, sizeof(*acl));
69 		return (result);
70 	}
71 
72 	acl->elements = NULL;
73 	acl->alloc = 0;
74 	acl->length = 0;
75 	acl->has_negatives = ISC_FALSE;
76 
77 	ISC_LINK_INIT(acl, nextincache);
78 	/*
79 	 * Must set magic early because we use dns_acl_detach() to clean up.
80 	 */
81 	acl->magic = DNS_ACL_MAGIC;
82 
83 	acl->elements = isc_mem_get(mctx, n * sizeof(dns_aclelement_t));
84 	if (acl->elements == NULL) {
85 		result = ISC_R_NOMEMORY;
86 		goto cleanup;
87 	}
88 	acl->alloc = n;
89 	memset(acl->elements, 0, n * sizeof(dns_aclelement_t));
90 	*target = acl;
91 	return (ISC_R_SUCCESS);
92 
93  cleanup:
94 	dns_acl_detach(&acl);
95 	return (result);
96 }
97 
98 /*
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".
103  */
104 static isc_result_t
dns_acl_anyornone(isc_mem_t * mctx,isc_boolean_t neg,dns_acl_t ** target)105 dns_acl_anyornone(isc_mem_t *mctx, isc_boolean_t neg, dns_acl_t **target) {
106 	isc_result_t result;
107 	dns_acl_t *acl = NULL;
108 
109 	result = dns_acl_create(mctx, 0, &acl);
110 	if (result != ISC_R_SUCCESS)
111 		return (result);
112 
113 	result = dns_iptable_addprefix(acl->iptable, NULL, 0, ISC_TF(!neg));
114 	if (result != ISC_R_SUCCESS) {
115 		dns_acl_detach(&acl);
116 		return (result);
117 	}
118 
119 	*target = acl;
120 	return (result);
121 }
122 
123 /*
124  * Create a new ACL that matches everything.
125  */
126 isc_result_t
dns_acl_any(isc_mem_t * mctx,dns_acl_t ** target)127 dns_acl_any(isc_mem_t *mctx, dns_acl_t **target) {
128 	return (dns_acl_anyornone(mctx, ISC_FALSE, target));
129 }
130 
131 /*
132  * Create a new ACL that matches nothing.
133  */
134 isc_result_t
dns_acl_none(isc_mem_t * mctx,dns_acl_t ** target)135 dns_acl_none(isc_mem_t *mctx, dns_acl_t **target) {
136 	return (dns_acl_anyornone(mctx, ISC_TRUE, target));
137 }
138 
139 /*
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; }"
142  */
143 static isc_boolean_t
dns_acl_isanyornone(dns_acl_t * acl,isc_boolean_t pos)144 dns_acl_isanyornone(dns_acl_t *acl, isc_boolean_t pos)
145 {
146 	/* Should never happen but let's be safe */
147 	if (acl == NULL ||
148 	    acl->iptable == NULL ||
149 	    acl->iptable->radix == NULL ||
150 	    acl->iptable->radix->head == NULL ||
151 	    acl->iptable->radix->head->prefix == NULL)
152 		return (ISC_FALSE);
153 
154 	if (acl->length != 0 || acl->node_count != 1)
155 		return (ISC_FALSE);
156 
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)
162 		return (ISC_TRUE);
163 
164 	return (ISC_FALSE); /* All others */
165 }
166 
167 /*
168  * Test whether acl is set to "{ any; }"
169  */
170 isc_boolean_t
dns_acl_isany(dns_acl_t * acl)171 dns_acl_isany(dns_acl_t *acl)
172 {
173 	return (dns_acl_isanyornone(acl, ISC_TRUE));
174 }
175 
176 /*
177  * Test whether acl is set to "{ none; }"
178  */
179 isc_boolean_t
dns_acl_isnone(dns_acl_t * acl)180 dns_acl_isnone(dns_acl_t *acl)
181 {
182 	return (dns_acl_isanyornone(acl, ISC_FALSE));
183 }
184 
185 /*
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.
190  */
191 isc_result_t
dns_acl_match(const isc_netaddr_t * reqaddr,const dns_name_t * reqsigner,const dns_acl_t * acl,const dns_aclenv_t * env,int * match,const dns_aclelement_t ** matchelt)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,
196 	      int *match,
197 	      const dns_aclelement_t **matchelt)
198 {
199 	isc_uint16_t bitlen, family;
200 	isc_prefix_t pfx;
201 	isc_radix_node_t *node = NULL;
202 	const isc_netaddr_t *addr;
203 	isc_netaddr_t v4addr;
204 	isc_result_t result;
205 	int match_num = -1;
206 	unsigned int i;
207 
208 	REQUIRE(reqaddr != NULL);
209 	REQUIRE(matchelt == NULL || *matchelt == NULL);
210 
211 	if (env == NULL || env->match_mapped == ISC_FALSE ||
212 	    reqaddr->family != AF_INET6 ||
213 	    !IN6_IS_ADDR_V4MAPPED(&reqaddr->type.in6))
214 		addr = reqaddr;
215 	else {
216 		isc_netaddr_fromv4mapped(&v4addr, reqaddr);
217 		addr = &v4addr;
218 	}
219 
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);
224 
225 	/* Assume no match. */
226 	*match = 0;
227 
228 	/* Search radix. */
229 	result = isc_radix_search(acl->iptable->radix, &node, &pfx);
230 
231 	/* Found a match. */
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)
235 			*match = match_num;
236 		else
237 			*match = -match_num;
238 	}
239 
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];
243 
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);
248 		}
249 
250 		if (dns_aclelement_match(reqaddr, reqsigner,
251 					 e, env, matchelt)) {
252 			if (match_num == -1 || e->node_num < match_num) {
253 				if (e->negative == ISC_TRUE)
254 					*match = -e->node_num;
255 				else
256 					*match = e->node_num;
257 			}
258 			isc_refcount_destroy(&pfx.refcount);
259 			return (ISC_R_SUCCESS);
260 		}
261 	}
262 
263 	isc_refcount_destroy(&pfx.refcount);
264 	return (ISC_R_SUCCESS);
265 }
266 
267 /*
268  * Merge the contents of one ACL into another.  Call dns_iptable_merge()
269  * for the IP tables, then concatenate the element arrays.
270  *
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.
275  */
276 isc_result_t
dns_acl_merge(dns_acl_t * dest,dns_acl_t * source,isc_boolean_t pos)277 dns_acl_merge(dns_acl_t *dest, dns_acl_t *source, isc_boolean_t pos)
278 {
279 	isc_result_t result;
280 	unsigned int newalloc, nelem, i;
281 	int max_node = 0, nodes;
282 
283 	/* Resize the element array if needed. */
284 	if (dest->length + source->length > dest->alloc) {
285 		void *newmem;
286 
287 		newalloc = dest->alloc + source->alloc;
288 		if (newalloc < 4)
289 			newalloc = 4;
290 
291 		newmem = isc_mem_get(dest->mctx,
292 				     newalloc * sizeof(dns_aclelement_t));
293 		if (newmem == NULL)
294 			return (ISC_R_NOMEMORY);
295 
296 		/* Zero. */
297 		memset(newmem, 0, newalloc * sizeof(dns_aclelement_t));
298 
299 		/* Copy in the original elements */
300 		memmove(newmem, dest->elements,
301 			dest->length * sizeof(dns_aclelement_t));
302 
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;
308 	}
309 
310 	/*
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.
315 	 */
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;
321 
322 		/* Copy type. */
323 		dest->elements[nelem + i].type = source->elements[i].type;
324 
325 		/* Adjust node numbering. */
326 		dest->elements[nelem + i].node_num =
327 			source->elements[i].node_num + dest->node_count;
328 
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);
334 
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,
339 					      dest->mctx,
340 					      &dest->elements[nelem+i].keyname);
341 			if (result != ISC_R_SUCCESS)
342 				return result;
343 		}
344 
345 #ifdef HAVE_GEOIP
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;
350 		}
351 #endif
352 
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;
356 		} else {
357 			dest->elements[nelem + i].negative =
358 				source->elements[i].negative;
359 		}
360 	}
361 
362 	/*
363 	 * Merge the iptables.  Make sure the destination ACL's
364 	 * node_count value is set correctly afterward.
365 	 */
366 	nodes = max_node + dest->node_count;
367 	result = dns_iptable_merge(dest->iptable, source->iptable, pos);
368 	if (result != ISC_R_SUCCESS)
369 		return (result);
370 	if (nodes > dest->node_count)
371 		dest->node_count = nodes;
372 
373 	return (ISC_R_SUCCESS);
374 }
375 
376 /*
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.
379  *
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.
384  */
385 isc_boolean_t
dns_aclelement_match(const isc_netaddr_t * reqaddr,const dns_name_t * reqsigner,const dns_aclelement_t * e,const dns_aclenv_t * env,const dns_aclelement_t ** matchelt)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)
391 {
392 	dns_acl_t *inner = NULL;
393 	int indirectmatch;
394 	isc_result_t result;
395 
396 	switch (e->type) {
397 	case dns_aclelementtype_keyname:
398 		if (reqsigner != NULL &&
399 		    dns_name_equal(reqsigner, &e->keyname)) {
400 			if (matchelt != NULL)
401 				*matchelt = e;
402 			return (ISC_TRUE);
403 		} else
404 			return (ISC_FALSE);
405 
406 	case dns_aclelementtype_nestedacl:
407 		inner = e->nestedacl;
408 		break;
409 
410 	case dns_aclelementtype_localhost:
411 		if (env == NULL || env->localhost == NULL)
412 			return (ISC_FALSE);
413 		inner = env->localhost;
414 		break;
415 
416 	case dns_aclelementtype_localnets:
417 		if (env == NULL || env->localnets == NULL)
418 			return (ISC_FALSE);
419 		inner = env->localnets;
420 		break;
421 
422 #ifdef HAVE_GEOIP
423 	case dns_aclelementtype_geoip:
424 		if (env == NULL || env->geoip == NULL)
425 			return (ISC_FALSE);
426 		return (dns_geoip_match(reqaddr, env->geoip, &e->geoip_elem));
427 #endif
428 	default:
429 		/* Should be impossible. */
430 		INSIST(0);
431 	}
432 
433 	result = dns_acl_match(reqaddr, reqsigner, inner, env,
434 			       &indirectmatch, matchelt);
435 	INSIST(result == ISC_R_SUCCESS);
436 
437 	/*
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.
442 	 */
443 
444 	if (indirectmatch > 0) {
445 		if (matchelt != NULL)
446 			*matchelt = e;
447 		return (ISC_TRUE);
448 	}
449 
450 	/*
451 	 * A negative indirect match may have set *matchelt, but we don't
452 	 * want it set when we return.
453 	 */
454 
455 	if (matchelt != NULL)
456 		*matchelt = NULL;
457 
458 	return (ISC_FALSE);
459 }
460 
461 void
dns_acl_attach(dns_acl_t * source,dns_acl_t ** target)462 dns_acl_attach(dns_acl_t *source, dns_acl_t **target) {
463 	REQUIRE(DNS_ACL_VALID(source));
464 
465 	isc_refcount_increment(&source->refcount, NULL);
466 	*target = source;
467 }
468 
469 static void
destroy(dns_acl_t * dacl)470 destroy(dns_acl_t *dacl) {
471 	unsigned int i;
472 
473 	INSIST(!ISC_LINK_LINKED(dacl, nextincache));
474 
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);
481 		}
482 	}
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);
491 	dacl->magic = 0;
492 	isc_mem_putanddetach(&dacl->mctx, dacl, sizeof(*dacl));
493 }
494 
495 void
dns_acl_detach(dns_acl_t ** aclp)496 dns_acl_detach(dns_acl_t **aclp) {
497 	dns_acl_t *acl = *aclp;
498 	unsigned int refs;
499 
500 	REQUIRE(DNS_ACL_VALID(acl));
501 
502 	isc_refcount_decrement(&acl->refcount, &refs);
503 	if (refs == 0)
504 		destroy(acl);
505 	*aclp = NULL;
506 }
507 
508 
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;
512 
513 static void
initialize_action(void)514 initialize_action(void) {
515 	RUNTIME_CHECK(isc_mutex_init(&insecure_prefix_lock) == ISC_R_SUCCESS);
516 }
517 
518 /*
519  * Called via isc_radix_walk() to find IP table nodes that are
520  * insecure.
521  */
522 static void
is_insecure(isc_prefix_t * prefix,void ** data)523 is_insecure(isc_prefix_t *prefix, void **data) {
524 	isc_boolean_t secure;
525 	int bitlen, family;
526 
527 	bitlen = prefix->bitlen;
528 	family = prefix->family;
529 
530 	/* Negated entries are always secure. */
531 	secure = * (isc_boolean_t *)data[ISC_IS6(family)];
532 	if (!secure) {
533 		return;
534 	}
535 
536 	/* If loopback prefix found, return */
537 	switch (family) {
538 	case AF_INET:
539 		if (bitlen == 32 &&
540 		    htonl(prefix->add.sin.s_addr) == INADDR_LOOPBACK)
541 			return;
542 		break;
543 	case AF_INET6:
544 		if (bitlen == 128 && IN6_IS_ADDR_LOOPBACK(&prefix->add.sin6))
545 			return;
546 		break;
547 	default:
548 		break;
549 	}
550 
551 	/* Non-negated, non-loopback */
552 	insecure_prefix_found = ISC_TRUE;	/* LOCKED */
553 	return;
554 }
555 
556 /*
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.
563  */
564 isc_boolean_t
dns_acl_isinsecure(const dns_acl_t * a)565 dns_acl_isinsecure(const dns_acl_t *a) {
566 	unsigned int i;
567 	isc_boolean_t insecure;
568 
569 	RUNTIME_CHECK(isc_once_do(&insecure_prefix_once,
570 				  initialize_action) == ISC_R_SUCCESS);
571 
572 	/*
573 	 * Walk radix tree to find out if there are any non-negated,
574 	 * non-loopback prefixes.
575 	 */
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);
581 	if (insecure)
582 		return (ISC_TRUE);
583 
584 	/* Now check non-radix elements */
585 	for (i = 0; i < a->length; i++) {
586 		dns_aclelement_t *e = &a->elements[i];
587 
588 		/* A negated match can never be insecure. */
589 		if (e->negative)
590 			continue;
591 
592 		switch (e->type) {
593 		case dns_aclelementtype_keyname:
594 		case dns_aclelementtype_localhost:
595 			continue;
596 
597 		case dns_aclelementtype_nestedacl:
598 			if (dns_acl_isinsecure(e->nestedacl))
599 				return (ISC_TRUE);
600 			continue;
601 
602 		case dns_aclelementtype_localnets:
603 			return (ISC_TRUE);
604 
605 		default:
606 			INSIST(0);
607 			return (ISC_TRUE);
608 		}
609 	}
610 
611 	/* No insecure elements were found. */
612 	return (ISC_FALSE);
613 }
614 
615 /*
616  * Initialize ACL environment, setting up localhost and localnets ACLs
617  */
618 isc_result_t
dns_aclenv_init(isc_mem_t * mctx,dns_aclenv_t * env)619 dns_aclenv_init(isc_mem_t *mctx, dns_aclenv_t *env) {
620 	isc_result_t result;
621 
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;
631 #ifdef HAVE_GEOIP
632 	env->geoip = NULL;
633 #endif
634 	return (ISC_R_SUCCESS);
635 
636  cleanup_localhost:
637 	dns_acl_detach(&env->localhost);
638  cleanup_nothing:
639 	return (result);
640 }
641 
642 void
dns_aclenv_copy(dns_aclenv_t * t,dns_aclenv_t * s)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;
649 }
650 
651 void
dns_aclenv_destroy(dns_aclenv_t * env)652 dns_aclenv_destroy(dns_aclenv_t *env) {
653 	dns_acl_detach(&env->localhost);
654 	dns_acl_detach(&env->localnets);
655 }
656