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