xref: /netbsd-src/external/mpl/bind/dist/lib/dns/acl.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: acl.c,v 1.10 2025/01/26 16:25:21 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0
7  *
8  * This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11  *
12  * See the COPYRIGHT file distributed with this work for additional
13  * information regarding copyright ownership.
14  */
15 
16 /*! \file */
17 
18 #include <inttypes.h>
19 #include <stdbool.h>
20 
21 #include <isc/mem.h>
22 #include <isc/once.h>
23 #include <isc/string.h>
24 #include <isc/urcu.h>
25 #include <isc/util.h>
26 
27 #include <dns/acl.h>
28 #include <dns/iptable.h>
29 
30 #define DNS_ACLENV_MAGIC ISC_MAGIC('a', 'c', 'n', 'v')
31 #define VALID_ACLENV(a)	 ISC_MAGIC_VALID(a, DNS_ACLENV_MAGIC)
32 
33 /*
34  * Create a new ACL, including an IP table and an array with room
35  * for 'n' ACL elements.  The elements are uninitialized and the
36  * length is 0.
37  */
38 void
39 dns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target) {
40 	REQUIRE(target != NULL && *target == NULL);
41 
42 	dns_acl_t *acl = isc_mem_get(mctx, sizeof(*acl));
43 	*acl = (dns_acl_t){
44 		.references = ISC_REFCOUNT_INITIALIZER(1),
45 		.nextincache = ISC_LINK_INITIALIZER,
46 		.elements = isc_mem_cget(mctx, n, sizeof(acl->elements[0])),
47 		.alloc = n,
48 		.ports_and_transports = ISC_LIST_INITIALIZER,
49 		.magic = DNS_ACL_MAGIC,
50 	};
51 
52 	isc_mem_attach(mctx, &acl->mctx);
53 	dns_iptable_create(acl->mctx, &acl->iptable);
54 
55 	*target = acl;
56 }
57 
58 /*
59  * Create a new ACL and initialize it with the value "any" or "none",
60  * depending on the value of the "neg" parameter.
61  * "any" is a positive iptable entry with bit length 0.
62  * "none" is the same as "!any".
63  */
64 static isc_result_t
65 dns_acl_anyornone(isc_mem_t *mctx, bool neg, dns_acl_t **target) {
66 	isc_result_t result;
67 	dns_acl_t *acl = NULL;
68 
69 	dns_acl_create(mctx, 0, &acl);
70 
71 	result = dns_iptable_addprefix(acl->iptable, NULL, 0, !neg);
72 	if (result != ISC_R_SUCCESS) {
73 		dns_acl_detach(&acl);
74 		return result;
75 	}
76 
77 	*target = acl;
78 	return result;
79 }
80 
81 /*
82  * Create a new ACL that matches everything.
83  */
84 isc_result_t
85 dns_acl_any(isc_mem_t *mctx, dns_acl_t **target) {
86 	return dns_acl_anyornone(mctx, false, target);
87 }
88 
89 /*
90  * Create a new ACL that matches nothing.
91  */
92 isc_result_t
93 dns_acl_none(isc_mem_t *mctx, dns_acl_t **target) {
94 	return dns_acl_anyornone(mctx, true, target);
95 }
96 
97 /*
98  * If pos is true, test whether acl is set to "{ any; }"
99  * If pos is false, test whether acl is set to "{ none; }"
100  */
101 static bool
102 dns_acl_isanyornone(dns_acl_t *acl, bool pos) {
103 	/* Should never happen but let's be safe */
104 	if (acl == NULL || acl->iptable == NULL ||
105 	    acl->iptable->radix == NULL || acl->iptable->radix->head == NULL ||
106 	    acl->iptable->radix->head->prefix == NULL)
107 	{
108 		return false;
109 	}
110 
111 	if (acl->length != 0 || dns_acl_node_count(acl) != 1) {
112 		return false;
113 	}
114 
115 	if (acl->iptable->radix->head->prefix->bitlen == 0 &&
116 	    acl->iptable->radix->head->data[0] != NULL &&
117 	    acl->iptable->radix->head->data[0] ==
118 		    acl->iptable->radix->head->data[1] &&
119 	    *(bool *)(acl->iptable->radix->head->data[0]) == pos)
120 	{
121 		return true;
122 	}
123 
124 	return false; /* All others */
125 }
126 
127 /*
128  * Test whether acl is set to "{ any; }"
129  */
130 bool
131 dns_acl_isany(dns_acl_t *acl) {
132 	return dns_acl_isanyornone(acl, true);
133 }
134 
135 /*
136  * Test whether acl is set to "{ none; }"
137  */
138 bool
139 dns_acl_isnone(dns_acl_t *acl) {
140 	return dns_acl_isanyornone(acl, false);
141 }
142 
143 /*
144  * Determine whether a given address or signer matches a given ACL.
145  * For a match with a positive ACL element or iptable radix entry,
146  * return with a positive value in match; for a match with a negated ACL
147  * element or radix entry, return with a negative value in match.
148  */
149 
150 isc_result_t
151 dns_acl_match(const isc_netaddr_t *reqaddr, const dns_name_t *reqsigner,
152 	      const dns_acl_t *acl, dns_aclenv_t *env, int *match,
153 	      const dns_aclelement_t **matchelt) {
154 	uint16_t bitlen;
155 	isc_prefix_t pfx;
156 	isc_radix_node_t *node = NULL;
157 	const isc_netaddr_t *addr = reqaddr;
158 	isc_netaddr_t v4addr;
159 	isc_result_t result;
160 	int match_num = -1;
161 	unsigned int i;
162 
163 	REQUIRE(reqaddr != NULL);
164 	REQUIRE(matchelt == NULL || *matchelt == NULL);
165 
166 	if (env != NULL && env->match_mapped && addr->family == AF_INET6 &&
167 	    IN6_IS_ADDR_V4MAPPED(&addr->type.in6))
168 	{
169 		isc_netaddr_fromv4mapped(&v4addr, addr);
170 		addr = &v4addr;
171 	}
172 
173 	/* Always match with host addresses. */
174 	bitlen = (addr->family == AF_INET6) ? 128 : 32;
175 	NETADDR_TO_PREFIX_T(addr, pfx, bitlen);
176 
177 	/* Assume no match. */
178 	*match = 0;
179 
180 	/* Search radix. */
181 	result = isc_radix_search(acl->iptable->radix, &node, &pfx);
182 
183 	/* Found a match. */
184 	if (result == ISC_R_SUCCESS && node != NULL) {
185 		int fam = ISC_RADIX_FAMILY(&pfx);
186 		match_num = node->node_num[fam];
187 		if (*(bool *)node->data[fam]) {
188 			*match = match_num;
189 		} else {
190 			*match = -match_num;
191 		}
192 	}
193 
194 	isc_refcount_destroy(&pfx.refcount);
195 
196 	/* Now search non-radix elements for a match with a lower node_num. */
197 	for (i = 0; i < acl->length; i++) {
198 		dns_aclelement_t *e = &acl->elements[i];
199 
200 		/* Already found a better match? */
201 		if (match_num != -1 && match_num < e->node_num) {
202 			break;
203 		}
204 
205 		if (dns_aclelement_match(reqaddr, reqsigner, e, env, matchelt))
206 		{
207 			if (match_num == -1 || e->node_num < match_num) {
208 				if (e->negative) {
209 					*match = -e->node_num;
210 				} else {
211 					*match = e->node_num;
212 				}
213 			}
214 			break;
215 		}
216 	}
217 
218 	return ISC_R_SUCCESS;
219 }
220 
221 isc_result_t
222 dns_acl_match_port_transport(const isc_netaddr_t *reqaddr,
223 			     const in_port_t local_port,
224 			     const isc_nmsocket_type_t transport,
225 			     const bool encrypted, const dns_name_t *reqsigner,
226 			     const dns_acl_t *acl, dns_aclenv_t *env,
227 			     int *match, const dns_aclelement_t **matchelt) {
228 	isc_result_t result = ISC_R_SUCCESS;
229 	dns_acl_port_transports_t *next;
230 
231 	REQUIRE(reqaddr != NULL);
232 	REQUIRE(DNS_ACL_VALID(acl));
233 
234 	if (!ISC_LIST_EMPTY(acl->ports_and_transports)) {
235 		result = ISC_R_FAILURE;
236 		for (next = ISC_LIST_HEAD(acl->ports_and_transports);
237 		     next != NULL; next = ISC_LIST_NEXT(next, link))
238 		{
239 			bool match_port = true;
240 			bool match_transport = true;
241 
242 			if (next->port != 0) {
243 				/* Port is specified. */
244 				match_port = (local_port == next->port);
245 			}
246 			if (next->transports != 0) {
247 				/* Transport protocol is specified. */
248 				match_transport =
249 					((transport & next->transports) ==
250 						 transport &&
251 					 next->encrypted == encrypted);
252 			}
253 
254 			if (match_port && match_transport) {
255 				result = next->negative ? ISC_R_FAILURE
256 							: ISC_R_SUCCESS;
257 				break;
258 			}
259 		}
260 	}
261 
262 	if (result != ISC_R_SUCCESS) {
263 		return result;
264 	}
265 
266 	return dns_acl_match(reqaddr, reqsigner, acl, env, match, matchelt);
267 }
268 
269 /*
270  * Merge the contents of one ACL into another.  Call dns_iptable_merge()
271  * for the IP tables, then concatenate the element arrays.
272  *
273  * If pos is set to false, then the nested ACL is to be negated.  This
274  * means reverse the sense of each *positive* element or IP table node,
275  * but leave negatives alone, so as to prevent a double-negative causing
276  * an unexpected positive match in the parent ACL.
277  */
278 isc_result_t
279 dns_acl_merge(dns_acl_t *dest, dns_acl_t *source, bool pos) {
280 	isc_result_t result;
281 	unsigned int nelem, i;
282 	int max_node = 0, nodes;
283 
284 	/* Resize the element array if needed. */
285 	if (dest->length + source->length > dest->alloc) {
286 		size_t newalloc = dest->alloc + source->alloc;
287 		if (newalloc < 4) {
288 			newalloc = 4;
289 		}
290 
291 		dest->elements = isc_mem_creget(dest->mctx, dest->elements,
292 						dest->alloc, newalloc,
293 						sizeof(dest->elements[0]));
294 		dest->alloc = newalloc;
295 	}
296 
297 	/*
298 	 * Now copy in the new elements, increasing their node_num
299 	 * values so as to keep the new ACL consistent.  If we're
300 	 * negating, then negate positive elements, but keep negative
301 	 * elements the same for security reasons.
302 	 */
303 	nelem = dest->length;
304 	dest->length += source->length;
305 	for (i = 0; i < source->length; i++) {
306 		if (source->elements[i].node_num > max_node) {
307 			max_node = source->elements[i].node_num;
308 		}
309 
310 		/* Copy type. */
311 		dest->elements[nelem + i].type = source->elements[i].type;
312 
313 		/* Adjust node numbering. */
314 		dest->elements[nelem + i].node_num =
315 			source->elements[i].node_num + dns_acl_node_count(dest);
316 
317 		/* Duplicate nested acl. */
318 		if (source->elements[i].type == dns_aclelementtype_nestedacl &&
319 		    source->elements[i].nestedacl != NULL)
320 		{
321 			dns_acl_attach(source->elements[i].nestedacl,
322 				       &dest->elements[nelem + i].nestedacl);
323 		}
324 
325 		/* Duplicate key name. */
326 		if (source->elements[i].type == dns_aclelementtype_keyname) {
327 			dns_name_init(&dest->elements[nelem + i].keyname, NULL);
328 			dns_name_dup(&source->elements[i].keyname, dest->mctx,
329 				     &dest->elements[nelem + i].keyname);
330 		}
331 
332 #if defined(HAVE_GEOIP2)
333 		/* Duplicate GeoIP data */
334 		if (source->elements[i].type == dns_aclelementtype_geoip) {
335 			dest->elements[nelem + i].geoip_elem =
336 				source->elements[i].geoip_elem;
337 		}
338 #endif /* if defined(HAVE_GEOIP2) */
339 
340 		/* reverse sense of positives if this is a negative acl */
341 		if (!pos && !source->elements[i].negative) {
342 			dest->elements[nelem + i].negative = true;
343 		} else {
344 			dest->elements[nelem + i].negative =
345 				source->elements[i].negative;
346 		}
347 	}
348 
349 	/*
350 	 * Merge the iptables.  Make sure the destination ACL's
351 	 * node_count value is set correctly afterward.
352 	 */
353 	nodes = max_node + dns_acl_node_count(dest);
354 	result = dns_iptable_merge(dest->iptable, source->iptable, pos);
355 	if (result != ISC_R_SUCCESS) {
356 		return result;
357 	}
358 	if (nodes > dns_acl_node_count(dest)) {
359 		dns_acl_node_count(dest) = nodes;
360 	}
361 
362 	/*
363 	 * Merge ports and transports
364 	 */
365 	dns_acl_merge_ports_transports(dest, source, pos);
366 
367 	return ISC_R_SUCCESS;
368 }
369 
370 /*
371  * Like dns_acl_match, but matches against the single ACL element 'e'
372  * rather than a complete ACL, and returns true iff it matched.
373  *
374  * To determine whether the match was positive or negative, the
375  * caller should examine e->negative.  Since the element 'e' may be
376  * a reference to a named ACL or a nested ACL, a matching element
377  * returned through 'matchelt' is not necessarily 'e' itself.
378  */
379 
380 bool
381 dns_aclelement_match(const isc_netaddr_t *reqaddr, const dns_name_t *reqsigner,
382 		     const dns_aclelement_t *e, dns_aclenv_t *env,
383 		     const dns_aclelement_t **matchelt) {
384 	dns_acl_t *inner = NULL;
385 	int indirectmatch;
386 	isc_result_t result;
387 
388 	switch (e->type) {
389 	case dns_aclelementtype_keyname:
390 		if (reqsigner != NULL && dns_name_equal(reqsigner, &e->keyname))
391 		{
392 			if (matchelt != NULL) {
393 				*matchelt = e;
394 			}
395 			return true;
396 		} else {
397 			return false;
398 		}
399 
400 	case dns_aclelementtype_nestedacl:
401 		dns_acl_attach(e->nestedacl, &inner);
402 		break;
403 
404 	case dns_aclelementtype_localhost:
405 		if (env == NULL) {
406 			return false;
407 		}
408 		rcu_read_lock();
409 		dns_acl_attach(rcu_dereference(env->localhost), &inner);
410 		rcu_read_unlock();
411 		break;
412 
413 	case dns_aclelementtype_localnets:
414 		if (env == NULL) {
415 			return false;
416 		}
417 		rcu_read_lock();
418 		dns_acl_attach(rcu_dereference(env->localnets), &inner);
419 		rcu_read_unlock();
420 		break;
421 
422 #if defined(HAVE_GEOIP2)
423 	case dns_aclelementtype_geoip:
424 		if (env == NULL || env->geoip == NULL) {
425 			return false;
426 		}
427 		return dns_geoip_match(reqaddr, env->geoip, &e->geoip_elem);
428 #endif /* if defined(HAVE_GEOIP2) */
429 	default:
430 		UNREACHABLE();
431 	}
432 
433 	result = dns_acl_match(reqaddr, reqsigner, inner, env, &indirectmatch,
434 			       matchelt);
435 	INSIST(result == ISC_R_SUCCESS);
436 
437 	dns_acl_detach(&inner);
438 
439 	/*
440 	 * Treat negative matches in indirect ACLs as "no match".
441 	 * That way, a negated indirect ACL will never become a
442 	 * surprise positive match through double negation.
443 	 * XXXDCL this should be documented.
444 	 */
445 	if (indirectmatch > 0) {
446 		if (matchelt != NULL) {
447 			*matchelt = e;
448 		}
449 		return true;
450 	}
451 
452 	/*
453 	 * A negative indirect match may have set *matchelt, but we don't
454 	 * want it set when we return.
455 	 */
456 	if (matchelt != NULL) {
457 		*matchelt = NULL;
458 	}
459 
460 	return false;
461 }
462 
463 static void
464 dns__acl_destroy_port_transports(dns_acl_t *acl) {
465 	dns_acl_port_transports_t *port_proto = NULL;
466 	dns_acl_port_transports_t *next = NULL;
467 	ISC_LIST_FOREACH_SAFE (acl->ports_and_transports, port_proto, link,
468 			       next)
469 	{
470 		ISC_LIST_DEQUEUE(acl->ports_and_transports, port_proto, link);
471 		isc_mem_put(acl->mctx, port_proto, sizeof(*port_proto));
472 	}
473 }
474 
475 static void
476 dns__acl_destroy(dns_acl_t *dacl) {
477 	INSIST(!ISC_LINK_LINKED(dacl, nextincache));
478 
479 	isc_refcount_destroy(&dacl->references);
480 	dacl->magic = 0;
481 
482 	for (size_t i = 0; i < dacl->length; i++) {
483 		dns_aclelement_t *de = &dacl->elements[i];
484 		if (de->type == dns_aclelementtype_keyname) {
485 			dns_name_free(&de->keyname, dacl->mctx);
486 		} else if (de->type == dns_aclelementtype_nestedacl) {
487 			dns_acl_detach(&de->nestedacl);
488 		}
489 	}
490 	if (dacl->elements != NULL) {
491 		isc_mem_cput(dacl->mctx, dacl->elements, dacl->alloc,
492 			     sizeof(dacl->elements[0]));
493 	}
494 	if (dacl->name != NULL) {
495 		isc_mem_free(dacl->mctx, dacl->name);
496 	}
497 	if (dacl->iptable != NULL) {
498 		dns_iptable_detach(&dacl->iptable);
499 	}
500 
501 	dns__acl_destroy_port_transports(dacl);
502 
503 	isc_mem_putanddetach(&dacl->mctx, dacl, sizeof(*dacl));
504 }
505 
506 #if DNS_ACL_TRACE
507 ISC_REFCOUNT_TRACE_IMPL(dns_acl, dns__acl_destroy);
508 #else
509 ISC_REFCOUNT_IMPL(dns_acl, dns__acl_destroy);
510 #endif
511 
512 static isc_once_t insecure_prefix_once = ISC_ONCE_INIT;
513 static isc_mutex_t insecure_prefix_lock;
514 static bool insecure_prefix_found;
515 
516 static void
517 initialize_action(void) {
518 	isc_mutex_init(&insecure_prefix_lock);
519 }
520 
521 /*
522  * Called via isc_radix_process() to find IP table nodes that are
523  * insecure.
524  */
525 static void
526 is_insecure(isc_prefix_t *prefix, void **data) {
527 	/*
528 	 * If all nonexistent or negative then this node is secure.
529 	 */
530 	if ((data[0] == NULL || !*(bool *)data[0]) &&
531 	    (data[1] == NULL || !*(bool *)data[1]))
532 	{
533 		return;
534 	}
535 
536 	/*
537 	 * If a loopback address found and the other family
538 	 * entry doesn't exist or is negative, return.
539 	 */
540 	if (prefix->bitlen == 32 &&
541 	    htonl(prefix->add.sin.s_addr) == INADDR_LOOPBACK &&
542 	    (data[1] == NULL || !*(bool *)data[1]))
543 	{
544 		return;
545 	}
546 
547 	if (prefix->bitlen == 128 && IN6_IS_ADDR_LOOPBACK(&prefix->add.sin6) &&
548 	    (data[0] == NULL || !*(bool *)data[0]))
549 	{
550 		return;
551 	}
552 
553 	/* Non-negated, non-loopback */
554 	insecure_prefix_found = true; /* LOCKED */
555 	return;
556 }
557 
558 /*
559  * Return true iff the acl 'a' is considered insecure, that is,
560  * if it contains IP addresses other than those of the local host.
561  * This is intended for applications such as printing warning
562  * messages for suspect ACLs; it is not intended for making access
563  * control decisions.  We make no guarantee that an ACL for which
564  * this function returns false is safe.
565  */
566 bool
567 dns_acl_isinsecure(const dns_acl_t *a) {
568 	unsigned int i;
569 	bool insecure;
570 
571 	isc_once_do(&insecure_prefix_once, initialize_action);
572 
573 	/*
574 	 * Walk radix tree to find out if there are any non-negated,
575 	 * non-loopback prefixes.
576 	 */
577 	LOCK(&insecure_prefix_lock);
578 	insecure_prefix_found = false;
579 	isc_radix_process(a->iptable->radix, is_insecure);
580 	insecure = insecure_prefix_found;
581 	UNLOCK(&insecure_prefix_lock);
582 	if (insecure) {
583 		return true;
584 	}
585 
586 	/* Now check non-radix elements */
587 	for (i = 0; i < a->length; i++) {
588 		dns_aclelement_t *e = &a->elements[i];
589 
590 		/* A negated match can never be insecure. */
591 		if (e->negative) {
592 			continue;
593 		}
594 
595 		switch (e->type) {
596 		case dns_aclelementtype_keyname:
597 		case dns_aclelementtype_localhost:
598 			continue;
599 
600 		case dns_aclelementtype_nestedacl:
601 			if (dns_acl_isinsecure(e->nestedacl)) {
602 				return true;
603 			}
604 			continue;
605 
606 #if defined(HAVE_GEOIP2)
607 		case dns_aclelementtype_geoip:
608 #endif /* if defined(HAVE_GEOIP2) */
609 		case dns_aclelementtype_localnets:
610 			return true;
611 
612 		default:
613 			UNREACHABLE();
614 		}
615 	}
616 
617 	/* No insecure elements were found. */
618 	return false;
619 }
620 
621 /*%
622  * Check whether an address/signer is allowed by a given acl/aclenv.
623  */
624 bool
625 dns_acl_allowed(isc_netaddr_t *addr, const dns_name_t *signer, dns_acl_t *acl,
626 		dns_aclenv_t *aclenv) {
627 	int match;
628 	isc_result_t result;
629 
630 	if (acl == NULL) {
631 		return true;
632 	}
633 	result = dns_acl_match(addr, signer, acl, aclenv, &match, NULL);
634 	if (result == ISC_R_SUCCESS && match > 0) {
635 		return true;
636 	}
637 	return false;
638 }
639 
640 /*
641  * Initialize ACL environment, setting up localhost and localnets ACLs
642  */
643 void
644 dns_aclenv_create(isc_mem_t *mctx, dns_aclenv_t **envp) {
645 	dns_aclenv_t *env = isc_mem_get(mctx, sizeof(*env));
646 	*env = (dns_aclenv_t){
647 		.references = ISC_REFCOUNT_INITIALIZER(1),
648 		.magic = DNS_ACLENV_MAGIC,
649 	};
650 
651 	isc_mem_attach(mctx, &env->mctx);
652 	isc_refcount_init(&env->references, 1);
653 
654 	dns_acl_create(mctx, 0, &env->localhost);
655 	dns_acl_create(mctx, 0, &env->localnets);
656 
657 	*envp = env;
658 }
659 
660 void
661 dns_aclenv_set(dns_aclenv_t *env, dns_acl_t *localhost, dns_acl_t *localnets) {
662 	REQUIRE(VALID_ACLENV(env));
663 	REQUIRE(DNS_ACL_VALID(localhost));
664 	REQUIRE(DNS_ACL_VALID(localnets));
665 
666 	localhost = rcu_xchg_pointer(&env->localhost, dns_acl_ref(localhost));
667 	localnets = rcu_xchg_pointer(&env->localnets, dns_acl_ref(localnets));
668 
669 	/*
670 	 * This function is called only during interface scanning, so blocking
671 	 * a bit is acceptable. Wait until all ongoing attachments to old
672 	 * 'localhost' and 'localnets' are finished before we can detach and
673 	 * possibly destroy them.
674 	 *
675 	 * The problem here isn't the memory reclamation per se, but
676 	 * the reference counting race - we need to wait for the
677 	 * critical section to end before we decrement the value and
678 	 * possibly destroy the acl objects.
679 	 */
680 	synchronize_rcu();
681 
682 	dns_acl_detach(&localhost);
683 	dns_acl_detach(&localnets);
684 }
685 
686 void
687 dns_aclenv_copy(dns_aclenv_t *target, dns_aclenv_t *source) {
688 	REQUIRE(VALID_ACLENV(source));
689 	REQUIRE(VALID_ACLENV(target));
690 
691 	rcu_read_lock();
692 
693 	/*
694 	 * We need to acquire the reference inside the critical section.
695 	 */
696 
697 	dns_acl_t *localhost = dns_acl_ref(rcu_dereference(source->localhost));
698 	INSIST(DNS_ACL_VALID(localhost));
699 
700 	dns_acl_t *localnets = dns_acl_ref(rcu_dereference(source->localnets));
701 	INSIST(DNS_ACL_VALID(localnets));
702 
703 	rcu_read_unlock();
704 
705 	localhost = rcu_xchg_pointer(&target->localhost, localhost);
706 	localnets = rcu_xchg_pointer(&target->localnets, localnets);
707 
708 	/*
709 	 * This function is called only during (re)configuration, so blocking
710 	 * a bit is acceptable.
711 	 *
712 	 * See the comment above in dns_aclenv_set() for more detail.
713 	 */
714 	synchronize_rcu();
715 
716 	target->match_mapped = source->match_mapped;
717 #if defined(HAVE_GEOIP2)
718 	target->geoip = source->geoip;
719 #endif /* if defined(HAVE_GEOIP2) */
720 
721 	dns_acl_detach(&localhost);
722 	dns_acl_detach(&localnets);
723 }
724 
725 static void
726 dns__aclenv_destroy(dns_aclenv_t *aclenv) {
727 	REQUIRE(VALID_ACLENV(aclenv));
728 
729 	aclenv->magic = 0;
730 
731 	/*
732 	 * The last reference to the aclenv has been detached, so nobody should
733 	 * be reading from this aclenv.  We can destroy the localhost and
734 	 * localnet directly without swapping the pointers.
735 	 */
736 
737 	dns_acl_detach(&aclenv->localhost);
738 	dns_acl_detach(&aclenv->localnets);
739 
740 	isc_mem_putanddetach(&aclenv->mctx, aclenv, sizeof(*aclenv));
741 }
742 
743 #if DNS_ACL_TRACE
744 ISC_REFCOUNT_TRACE_IMPL(dns_aclenv, dns__aclenv_destroy);
745 #else
746 ISC_REFCOUNT_IMPL(dns_aclenv, dns__aclenv_destroy);
747 #endif
748 
749 void
750 dns_acl_add_port_transports(dns_acl_t *acl, const in_port_t port,
751 			    const uint32_t transports, const bool encrypted,
752 			    const bool negative) {
753 	dns_acl_port_transports_t *port_proto;
754 	REQUIRE(DNS_ACL_VALID(acl));
755 	REQUIRE(port != 0 || transports != 0);
756 
757 	port_proto = isc_mem_get(acl->mctx, sizeof(*port_proto));
758 	*port_proto = (dns_acl_port_transports_t){ .port = port,
759 						   .transports = transports,
760 						   .encrypted = encrypted,
761 						   .negative = negative };
762 
763 	ISC_LINK_INIT(port_proto, link);
764 
765 	ISC_LIST_APPEND(acl->ports_and_transports, port_proto, link);
766 	acl->port_proto_entries++;
767 }
768 
769 void
770 dns_acl_merge_ports_transports(dns_acl_t *dest, dns_acl_t *source, bool pos) {
771 	dns_acl_port_transports_t *next;
772 
773 	REQUIRE(DNS_ACL_VALID(dest));
774 	REQUIRE(DNS_ACL_VALID(source));
775 
776 	const bool negative = !pos;
777 
778 	/*
779 	 * Merge ports and transports
780 	 */
781 	for (next = ISC_LIST_HEAD(source->ports_and_transports); next != NULL;
782 	     next = ISC_LIST_NEXT(next, link))
783 	{
784 		const bool next_positive = !next->negative;
785 		bool add_negative;
786 
787 		/*
788 		 * Reverse sense of positives if this is a negative acl.  The
789 		 * logic is used (and, thus, enforced) by dns_acl_merge(),
790 		 * from which dns_acl_merge_ports_transports() is called.
791 		 */
792 		if (negative && next_positive) {
793 			add_negative = true;
794 		} else {
795 			add_negative = next->negative;
796 		}
797 
798 		dns_acl_add_port_transports(dest, next->port, next->transports,
799 					    next->encrypted, add_negative);
800 	}
801 }
802