1 /*
2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * SPDX-License-Identifier: MPL-2.0
5  *
6  * This Source Code Form is subject to the terms of the Mozilla Public
7  * License, v. 2.0. If a copy of the MPL was not distributed with this
8  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
9  *
10  * See the COPYRIGHT file distributed with this work for additional
11  * information regarding copyright ownership.
12  */
13 
14 /*! \file */
15 
16 #include <inttypes.h>
17 #include <stdbool.h>
18 
19 #include <isc/mem.h>
20 #include <isc/once.h>
21 #include <isc/string.h>
22 #include <isc/util.h>
23 
24 #include <dns/acl.h>
25 #include <dns/iptable.h>
26 
27 /*
28  * Create a new ACL, including an IP table and an array with room
29  * for 'n' ACL elements.  The elements are uninitialized and the
30  * length is 0.
31  */
32 isc_result_t
dns_acl_create(isc_mem_t * mctx,int n,dns_acl_t ** target)33 dns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target) {
34 	isc_result_t result;
35 	dns_acl_t *acl;
36 
37 	/*
38 	 * Work around silly limitation of isc_mem_get().
39 	 */
40 	if (n == 0) {
41 		n = 1;
42 	}
43 
44 	acl = isc_mem_get(mctx, sizeof(*acl));
45 
46 	acl->mctx = NULL;
47 	isc_mem_attach(mctx, &acl->mctx);
48 
49 	acl->name = NULL;
50 
51 	isc_refcount_init(&acl->refcount, 1);
52 
53 	result = dns_iptable_create(mctx, &acl->iptable);
54 	if (result != ISC_R_SUCCESS) {
55 		isc_mem_put(mctx, acl, sizeof(*acl));
56 		return (result);
57 	}
58 
59 	acl->elements = NULL;
60 	acl->alloc = 0;
61 	acl->length = 0;
62 	acl->has_negatives = false;
63 
64 	ISC_LINK_INIT(acl, nextincache);
65 	/*
66 	 * Must set magic early because we use dns_acl_detach() to clean up.
67 	 */
68 	acl->magic = DNS_ACL_MAGIC;
69 
70 	acl->elements = isc_mem_get(mctx, n * sizeof(dns_aclelement_t));
71 	acl->alloc = n;
72 	memset(acl->elements, 0, n * sizeof(dns_aclelement_t));
73 	*target = acl;
74 	return (ISC_R_SUCCESS);
75 }
76 
77 /*
78  * Create a new ACL and initialize it with the value "any" or "none",
79  * depending on the value of the "neg" parameter.
80  * "any" is a positive iptable entry with bit length 0.
81  * "none" is the same as "!any".
82  */
83 static isc_result_t
dns_acl_anyornone(isc_mem_t * mctx,bool neg,dns_acl_t ** target)84 dns_acl_anyornone(isc_mem_t *mctx, bool neg, dns_acl_t **target) {
85 	isc_result_t result;
86 	dns_acl_t *acl = NULL;
87 
88 	result = dns_acl_create(mctx, 0, &acl);
89 	if (result != ISC_R_SUCCESS) {
90 		return (result);
91 	}
92 
93 	result = dns_iptable_addprefix(acl->iptable, NULL, 0, !neg);
94 	if (result != ISC_R_SUCCESS) {
95 		dns_acl_detach(&acl);
96 		return (result);
97 	}
98 
99 	*target = acl;
100 	return (result);
101 }
102 
103 /*
104  * Create a new ACL that matches everything.
105  */
106 isc_result_t
dns_acl_any(isc_mem_t * mctx,dns_acl_t ** target)107 dns_acl_any(isc_mem_t *mctx, dns_acl_t **target) {
108 	return (dns_acl_anyornone(mctx, false, target));
109 }
110 
111 /*
112  * Create a new ACL that matches nothing.
113  */
114 isc_result_t
dns_acl_none(isc_mem_t * mctx,dns_acl_t ** target)115 dns_acl_none(isc_mem_t *mctx, dns_acl_t **target) {
116 	return (dns_acl_anyornone(mctx, true, target));
117 }
118 
119 /*
120  * If pos is true, test whether acl is set to "{ any; }"
121  * If pos is false, test whether acl is set to "{ none; }"
122  */
123 static bool
dns_acl_isanyornone(dns_acl_t * acl,bool pos)124 dns_acl_isanyornone(dns_acl_t *acl, bool pos) {
125 	/* Should never happen but let's be safe */
126 	if (acl == NULL || acl->iptable == NULL ||
127 	    acl->iptable->radix == NULL || acl->iptable->radix->head == NULL ||
128 	    acl->iptable->radix->head->prefix == NULL)
129 	{
130 		return (false);
131 	}
132 
133 	if (acl->length != 0 || dns_acl_node_count(acl) != 1) {
134 		return (false);
135 	}
136 
137 	if (acl->iptable->radix->head->prefix->bitlen == 0 &&
138 	    acl->iptable->radix->head->data[0] != NULL &&
139 	    acl->iptable->radix->head->data[0] ==
140 		    acl->iptable->radix->head->data[1] &&
141 	    *(bool *)(acl->iptable->radix->head->data[0]) == pos)
142 	{
143 		return (true);
144 	}
145 
146 	return (false); /* All others */
147 }
148 
149 /*
150  * Test whether acl is set to "{ any; }"
151  */
152 bool
dns_acl_isany(dns_acl_t * acl)153 dns_acl_isany(dns_acl_t *acl) {
154 	return (dns_acl_isanyornone(acl, true));
155 }
156 
157 /*
158  * Test whether acl is set to "{ none; }"
159  */
160 bool
dns_acl_isnone(dns_acl_t * acl)161 dns_acl_isnone(dns_acl_t *acl) {
162 	return (dns_acl_isanyornone(acl, false));
163 }
164 
165 /*
166  * Determine whether a given address or signer matches a given ACL.
167  * For a match with a positive ACL element or iptable radix entry,
168  * return with a positive value in match; for a match with a negated ACL
169  * element or radix entry, return with a negative value in match.
170  */
171 
172 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)173 dns_acl_match(const isc_netaddr_t *reqaddr, const dns_name_t *reqsigner,
174 	      const dns_acl_t *acl, const dns_aclenv_t *env, int *match,
175 	      const dns_aclelement_t **matchelt) {
176 	uint16_t bitlen;
177 	isc_prefix_t pfx;
178 	isc_radix_node_t *node = NULL;
179 	const isc_netaddr_t *addr = reqaddr;
180 	isc_netaddr_t v4addr;
181 	isc_result_t result;
182 	int match_num = -1;
183 	unsigned int i;
184 
185 	REQUIRE(reqaddr != NULL);
186 	REQUIRE(matchelt == NULL || *matchelt == NULL);
187 
188 	if (env != NULL && env->match_mapped && addr->family == AF_INET6 &&
189 	    IN6_IS_ADDR_V4MAPPED(&addr->type.in6))
190 	{
191 		isc_netaddr_fromv4mapped(&v4addr, addr);
192 		addr = &v4addr;
193 	}
194 
195 	/* Always match with host addresses. */
196 	bitlen = (addr->family == AF_INET6) ? 128 : 32;
197 	NETADDR_TO_PREFIX_T(addr, pfx, bitlen);
198 
199 	/* Assume no match. */
200 	*match = 0;
201 
202 	/* Search radix. */
203 	result = isc_radix_search(acl->iptable->radix, &node, &pfx);
204 
205 	/* Found a match. */
206 	if (result == ISC_R_SUCCESS && node != NULL) {
207 		int fam = ISC_RADIX_FAMILY(&pfx);
208 		match_num = node->node_num[fam];
209 		if (*(bool *)node->data[fam]) {
210 			*match = match_num;
211 		} else {
212 			*match = -match_num;
213 		}
214 	}
215 
216 	isc_refcount_destroy(&pfx.refcount);
217 
218 	/* Now search non-radix elements for a match with a lower node_num. */
219 	for (i = 0; i < acl->length; i++) {
220 		dns_aclelement_t *e = &acl->elements[i];
221 
222 		/* Already found a better match? */
223 		if (match_num != -1 && match_num < e->node_num) {
224 			break;
225 		}
226 
227 		if (dns_aclelement_match(reqaddr, reqsigner, e, env, matchelt))
228 		{
229 			if (match_num == -1 || e->node_num < match_num) {
230 				if (e->negative) {
231 					*match = -e->node_num;
232 				} else {
233 					*match = e->node_num;
234 				}
235 			}
236 			break;
237 		}
238 	}
239 
240 	return (ISC_R_SUCCESS);
241 }
242 
243 /*
244  * Merge the contents of one ACL into another.  Call dns_iptable_merge()
245  * for the IP tables, then concatenate the element arrays.
246  *
247  * If pos is set to false, then the nested ACL is to be negated.  This
248  * means reverse the sense of each *positive* element or IP table node,
249  * but leave negatives alone, so as to prevent a double-negative causing
250  * an unexpected positive match in the parent ACL.
251  */
252 isc_result_t
dns_acl_merge(dns_acl_t * dest,dns_acl_t * source,bool pos)253 dns_acl_merge(dns_acl_t *dest, dns_acl_t *source, bool pos) {
254 	isc_result_t result;
255 	unsigned int newalloc, nelem, i;
256 	int max_node = 0, nodes;
257 
258 	/* Resize the element array if needed. */
259 	if (dest->length + source->length > dest->alloc) {
260 		void *newmem;
261 
262 		newalloc = dest->alloc + source->alloc;
263 		if (newalloc < 4) {
264 			newalloc = 4;
265 		}
266 
267 		newmem = isc_mem_get(dest->mctx,
268 				     newalloc * sizeof(dns_aclelement_t));
269 
270 		/* Zero. */
271 		memset(newmem, 0, newalloc * sizeof(dns_aclelement_t));
272 
273 		/* Copy in the original elements */
274 		memmove(newmem, dest->elements,
275 			dest->length * sizeof(dns_aclelement_t));
276 
277 		/* Release the memory for the old elements array */
278 		isc_mem_put(dest->mctx, dest->elements,
279 			    dest->alloc * sizeof(dns_aclelement_t));
280 		dest->elements = newmem;
281 		dest->alloc = newalloc;
282 	}
283 
284 	/*
285 	 * Now copy in the new elements, increasing their node_num
286 	 * values so as to keep the new ACL consistent.  If we're
287 	 * negating, then negate positive elements, but keep negative
288 	 * elements the same for security reasons.
289 	 */
290 	nelem = dest->length;
291 	dest->length += source->length;
292 	for (i = 0; i < source->length; i++) {
293 		if (source->elements[i].node_num > max_node) {
294 			max_node = source->elements[i].node_num;
295 		}
296 
297 		/* Copy type. */
298 		dest->elements[nelem + i].type = source->elements[i].type;
299 
300 		/* Adjust node numbering. */
301 		dest->elements[nelem + i].node_num =
302 			source->elements[i].node_num + dns_acl_node_count(dest);
303 
304 		/* Duplicate nested acl. */
305 		if (source->elements[i].type == dns_aclelementtype_nestedacl &&
306 		    source->elements[i].nestedacl != NULL)
307 		{
308 			dns_acl_attach(source->elements[i].nestedacl,
309 				       &dest->elements[nelem + i].nestedacl);
310 		}
311 
312 		/* Duplicate key name. */
313 		if (source->elements[i].type == dns_aclelementtype_keyname) {
314 			dns_name_init(&dest->elements[nelem + i].keyname, NULL);
315 			dns_name_dup(&source->elements[i].keyname, dest->mctx,
316 				     &dest->elements[nelem + i].keyname);
317 		}
318 
319 #if defined(HAVE_GEOIP2)
320 		/* Duplicate GeoIP data */
321 		if (source->elements[i].type == dns_aclelementtype_geoip) {
322 			dest->elements[nelem + i].geoip_elem =
323 				source->elements[i].geoip_elem;
324 		}
325 #endif /* if defined(HAVE_GEOIP2) */
326 
327 		/* reverse sense of positives if this is a negative acl */
328 		if (!pos && !source->elements[i].negative) {
329 			dest->elements[nelem + i].negative = true;
330 		} else {
331 			dest->elements[nelem + i].negative =
332 				source->elements[i].negative;
333 		}
334 	}
335 
336 	/*
337 	 * Merge the iptables.  Make sure the destination ACL's
338 	 * node_count value is set correctly afterward.
339 	 */
340 	nodes = max_node + dns_acl_node_count(dest);
341 	result = dns_iptable_merge(dest->iptable, source->iptable, pos);
342 	if (result != ISC_R_SUCCESS) {
343 		return (result);
344 	}
345 	if (nodes > dns_acl_node_count(dest)) {
346 		dns_acl_node_count(dest) = nodes;
347 	}
348 
349 	return (ISC_R_SUCCESS);
350 }
351 
352 /*
353  * Like dns_acl_match, but matches against the single ACL element 'e'
354  * rather than a complete ACL, and returns true iff it matched.
355  *
356  * To determine whether the match was positive or negative, the
357  * caller should examine e->negative.  Since the element 'e' may be
358  * a reference to a named ACL or a nested ACL, a matching element
359  * returned through 'matchelt' is not necessarily 'e' itself.
360  */
361 
362 bool
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)363 dns_aclelement_match(const isc_netaddr_t *reqaddr, const dns_name_t *reqsigner,
364 		     const dns_aclelement_t *e, const dns_aclenv_t *env,
365 		     const dns_aclelement_t **matchelt) {
366 	dns_acl_t *inner = NULL;
367 	int indirectmatch;
368 	isc_result_t result;
369 
370 	switch (e->type) {
371 	case dns_aclelementtype_keyname:
372 		if (reqsigner != NULL && dns_name_equal(reqsigner, &e->keyname))
373 		{
374 			if (matchelt != NULL) {
375 				*matchelt = e;
376 			}
377 			return (true);
378 		} else {
379 			return (false);
380 		}
381 
382 	case dns_aclelementtype_nestedacl:
383 		inner = e->nestedacl;
384 		break;
385 
386 	case dns_aclelementtype_localhost:
387 		if (env == NULL || env->localhost == NULL) {
388 			return (false);
389 		}
390 		inner = env->localhost;
391 		break;
392 
393 	case dns_aclelementtype_localnets:
394 		if (env == NULL || env->localnets == NULL) {
395 			return (false);
396 		}
397 		inner = env->localnets;
398 		break;
399 
400 #if defined(HAVE_GEOIP2)
401 	case dns_aclelementtype_geoip:
402 		if (env == NULL || env->geoip == NULL) {
403 			return (false);
404 		}
405 		return (dns_geoip_match(reqaddr, env->geoip, &e->geoip_elem));
406 #endif /* if defined(HAVE_GEOIP2) */
407 	default:
408 		INSIST(0);
409 		ISC_UNREACHABLE();
410 	}
411 
412 	result = dns_acl_match(reqaddr, reqsigner, inner, env, &indirectmatch,
413 			       matchelt);
414 	INSIST(result == ISC_R_SUCCESS);
415 
416 	/*
417 	 * Treat negative matches in indirect ACLs as "no match".
418 	 * That way, a negated indirect ACL will never become a
419 	 * surprise positive match through double negation.
420 	 * XXXDCL this should be documented.
421 	 */
422 	if (indirectmatch > 0) {
423 		if (matchelt != NULL) {
424 			*matchelt = e;
425 		}
426 		return (true);
427 	}
428 
429 	/*
430 	 * A negative indirect match may have set *matchelt, but we don't
431 	 * want it set when we return.
432 	 */
433 	if (matchelt != NULL) {
434 		*matchelt = NULL;
435 	}
436 
437 	return (false);
438 }
439 
440 void
dns_acl_attach(dns_acl_t * source,dns_acl_t ** target)441 dns_acl_attach(dns_acl_t *source, dns_acl_t **target) {
442 	REQUIRE(DNS_ACL_VALID(source));
443 
444 	isc_refcount_increment(&source->refcount);
445 	*target = source;
446 }
447 
448 static void
destroy(dns_acl_t * dacl)449 destroy(dns_acl_t *dacl) {
450 	unsigned int i;
451 
452 	INSIST(!ISC_LINK_LINKED(dacl, nextincache));
453 
454 	for (i = 0; i < dacl->length; i++) {
455 		dns_aclelement_t *de = &dacl->elements[i];
456 		if (de->type == dns_aclelementtype_keyname) {
457 			dns_name_free(&de->keyname, dacl->mctx);
458 		} else if (de->type == dns_aclelementtype_nestedacl) {
459 			dns_acl_detach(&de->nestedacl);
460 		}
461 	}
462 	if (dacl->elements != NULL) {
463 		isc_mem_put(dacl->mctx, dacl->elements,
464 			    dacl->alloc * sizeof(dns_aclelement_t));
465 	}
466 	if (dacl->name != NULL) {
467 		isc_mem_free(dacl->mctx, dacl->name);
468 	}
469 	if (dacl->iptable != NULL) {
470 		dns_iptable_detach(&dacl->iptable);
471 	}
472 	isc_refcount_destroy(&dacl->refcount);
473 	dacl->magic = 0;
474 	isc_mem_putanddetach(&dacl->mctx, dacl, sizeof(*dacl));
475 }
476 
477 void
dns_acl_detach(dns_acl_t ** aclp)478 dns_acl_detach(dns_acl_t **aclp) {
479 	REQUIRE(aclp != NULL && DNS_ACL_VALID(*aclp));
480 	dns_acl_t *acl = *aclp;
481 	*aclp = NULL;
482 
483 	if (isc_refcount_decrement(&acl->refcount) == 1) {
484 		destroy(acl);
485 	}
486 }
487 
488 static isc_once_t insecure_prefix_once = ISC_ONCE_INIT;
489 static isc_mutex_t insecure_prefix_lock;
490 static bool insecure_prefix_found;
491 
492 static void
initialize_action(void)493 initialize_action(void) {
494 	isc_mutex_init(&insecure_prefix_lock);
495 }
496 
497 /*
498  * Called via isc_radix_process() to find IP table nodes that are
499  * insecure.
500  */
501 static void
is_insecure(isc_prefix_t * prefix,void ** data)502 is_insecure(isc_prefix_t *prefix, void **data) {
503 	/*
504 	 * If all nonexistent or negative then this node is secure.
505 	 */
506 	if ((data[0] == NULL || !*(bool *)data[0]) &&
507 	    (data[1] == NULL || !*(bool *)data[1]))
508 	{
509 		return;
510 	}
511 
512 	/*
513 	 * If a loopback address found and the other family
514 	 * entry doesn't exist or is negative, return.
515 	 */
516 	if (prefix->bitlen == 32 &&
517 	    htonl(prefix->add.sin.s_addr) == INADDR_LOOPBACK &&
518 	    (data[1] == NULL || !*(bool *)data[1]))
519 	{
520 		return;
521 	}
522 
523 	if (prefix->bitlen == 128 && IN6_IS_ADDR_LOOPBACK(&prefix->add.sin6) &&
524 	    (data[0] == NULL || !*(bool *)data[0]))
525 	{
526 		return;
527 	}
528 
529 	/* Non-negated, non-loopback */
530 	insecure_prefix_found = true; /* LOCKED */
531 	return;
532 }
533 
534 /*
535  * Return true iff the acl 'a' is considered insecure, that is,
536  * if it contains IP addresses other than those of the local host.
537  * This is intended for applications such as printing warning
538  * messages for suspect ACLs; it is not intended for making access
539  * control decisions.  We make no guarantee that an ACL for which
540  * this function returns false is safe.
541  */
542 bool
dns_acl_isinsecure(const dns_acl_t * a)543 dns_acl_isinsecure(const dns_acl_t *a) {
544 	unsigned int i;
545 	bool insecure;
546 
547 	RUNTIME_CHECK(isc_once_do(&insecure_prefix_once, initialize_action) ==
548 		      ISC_R_SUCCESS);
549 
550 	/*
551 	 * Walk radix tree to find out if there are any non-negated,
552 	 * non-loopback prefixes.
553 	 */
554 	LOCK(&insecure_prefix_lock);
555 	insecure_prefix_found = false;
556 	isc_radix_process(a->iptable->radix, is_insecure);
557 	insecure = insecure_prefix_found;
558 	UNLOCK(&insecure_prefix_lock);
559 	if (insecure) {
560 		return (true);
561 	}
562 
563 	/* Now check non-radix elements */
564 	for (i = 0; i < a->length; i++) {
565 		dns_aclelement_t *e = &a->elements[i];
566 
567 		/* A negated match can never be insecure. */
568 		if (e->negative) {
569 			continue;
570 		}
571 
572 		switch (e->type) {
573 		case dns_aclelementtype_keyname:
574 		case dns_aclelementtype_localhost:
575 			continue;
576 
577 		case dns_aclelementtype_nestedacl:
578 			if (dns_acl_isinsecure(e->nestedacl)) {
579 				return (true);
580 			}
581 			continue;
582 
583 #if defined(HAVE_GEOIP2)
584 		case dns_aclelementtype_geoip:
585 #endif /* if defined(HAVE_GEOIP2) */
586 		case dns_aclelementtype_localnets:
587 			return (true);
588 
589 		default:
590 			INSIST(0);
591 			ISC_UNREACHABLE();
592 		}
593 	}
594 
595 	/* No insecure elements were found. */
596 	return (false);
597 }
598 
599 /*%
600  * Check whether an address/signer is allowed by a given acl/aclenv.
601  */
602 bool
dns_acl_allowed(isc_netaddr_t * addr,const dns_name_t * signer,dns_acl_t * acl,dns_aclenv_t * aclenv)603 dns_acl_allowed(isc_netaddr_t *addr, const dns_name_t *signer, dns_acl_t *acl,
604 		dns_aclenv_t *aclenv) {
605 	int match;
606 	isc_result_t result;
607 
608 	if (acl == NULL) {
609 		return (true);
610 	}
611 	result = dns_acl_match(addr, signer, acl, aclenv, &match, NULL);
612 	if (result == ISC_R_SUCCESS && match > 0) {
613 		return (true);
614 	}
615 	return (false);
616 }
617 
618 /*
619  * Initialize ACL environment, setting up localhost and localnets ACLs
620  */
621 isc_result_t
dns_aclenv_init(isc_mem_t * mctx,dns_aclenv_t * env)622 dns_aclenv_init(isc_mem_t *mctx, dns_aclenv_t *env) {
623 	isc_result_t result;
624 
625 	env->localhost = NULL;
626 	env->localnets = NULL;
627 	result = dns_acl_create(mctx, 0, &env->localhost);
628 	if (result != ISC_R_SUCCESS) {
629 		goto cleanup_nothing;
630 	}
631 	result = dns_acl_create(mctx, 0, &env->localnets);
632 	if (result != ISC_R_SUCCESS) {
633 		goto cleanup_localhost;
634 	}
635 	env->match_mapped = false;
636 #if defined(HAVE_GEOIP2)
637 	env->geoip = NULL;
638 #endif /* if defined(HAVE_GEOIP2) */
639 	return (ISC_R_SUCCESS);
640 
641 cleanup_localhost:
642 	dns_acl_detach(&env->localhost);
643 cleanup_nothing:
644 	return (result);
645 }
646 
647 void
dns_aclenv_copy(dns_aclenv_t * t,dns_aclenv_t * s)648 dns_aclenv_copy(dns_aclenv_t *t, dns_aclenv_t *s) {
649 	dns_acl_detach(&t->localhost);
650 	dns_acl_attach(s->localhost, &t->localhost);
651 	dns_acl_detach(&t->localnets);
652 	dns_acl_attach(s->localnets, &t->localnets);
653 	t->match_mapped = s->match_mapped;
654 #if defined(HAVE_GEOIP2)
655 	t->geoip = s->geoip;
656 #endif /* if defined(HAVE_GEOIP2) */
657 }
658 
659 void
dns_aclenv_destroy(dns_aclenv_t * env)660 dns_aclenv_destroy(dns_aclenv_t *env) {
661 	if (env->localhost != NULL) {
662 		dns_acl_detach(&env->localhost);
663 	}
664 	if (env->localnets != NULL) {
665 		dns_acl_detach(&env->localnets);
666 	}
667 }
668