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 https://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 #include <inttypes.h>
13 #include <stdbool.h>
14 
15 #include <isc/mem.h>
16 #include <isc/radix.h>
17 #include <isc/util.h>
18 
19 #include <dns/acl.h>
20 
21 static void
22 destroy_iptable(dns_iptable_t *dtab);
23 
24 /*
25  * Create a new IP table and the underlying radix structure
26  */
27 isc_result_t
dns_iptable_create(isc_mem_t * mctx,dns_iptable_t ** target)28 dns_iptable_create(isc_mem_t *mctx, dns_iptable_t **target) {
29 	isc_result_t result;
30 	dns_iptable_t *tab;
31 
32 	tab = isc_mem_get(mctx, sizeof(*tab));
33 	tab->mctx = NULL;
34 	isc_mem_attach(mctx, &tab->mctx);
35 	isc_refcount_init(&tab->refcount, 1);
36 	tab->radix = NULL;
37 	tab->magic = DNS_IPTABLE_MAGIC;
38 
39 	result = isc_radix_create(mctx, &tab->radix, RADIX_MAXBITS);
40 	if (result != ISC_R_SUCCESS) {
41 		goto cleanup;
42 	}
43 
44 	*target = tab;
45 	return (ISC_R_SUCCESS);
46 
47 cleanup:
48 	dns_iptable_detach(&tab);
49 	return (result);
50 }
51 
52 static bool dns_iptable_neg = false;
53 static bool dns_iptable_pos = true;
54 
55 /*
56  * Add an IP prefix to an existing IP table
57  */
58 isc_result_t
dns_iptable_addprefix(dns_iptable_t * tab,const isc_netaddr_t * addr,uint16_t bitlen,bool pos)59 dns_iptable_addprefix(dns_iptable_t *tab, const isc_netaddr_t *addr,
60 		      uint16_t bitlen, bool pos) {
61 	isc_result_t result;
62 	isc_prefix_t pfx;
63 	isc_radix_node_t *node = NULL;
64 	int i;
65 
66 	INSIST(DNS_IPTABLE_VALID(tab));
67 	INSIST(tab->radix != NULL);
68 
69 	NETADDR_TO_PREFIX_T(addr, pfx, bitlen);
70 
71 	result = isc_radix_insert(tab->radix, &node, NULL, &pfx);
72 	if (result != ISC_R_SUCCESS) {
73 		isc_refcount_destroy(&pfx.refcount);
74 		return (result);
75 	}
76 
77 	/* If a node already contains data, don't overwrite it */
78 	if (pfx.family == AF_UNSPEC) {
79 		/* "any" or "none" */
80 		INSIST(pfx.bitlen == 0);
81 		for (i = 0; i < RADIX_FAMILIES; i++) {
82 			if (node->data[i] == NULL) {
83 				node->data[i] = pos ? &dns_iptable_pos
84 						    : &dns_iptable_neg;
85 			}
86 		}
87 	} else {
88 		/* any other prefix */
89 		int fam = ISC_RADIX_FAMILY(&pfx);
90 		if (node->data[fam] == NULL) {
91 			node->data[fam] = pos ? &dns_iptable_pos
92 					      : &dns_iptable_neg;
93 		}
94 	}
95 
96 	isc_refcount_destroy(&pfx.refcount);
97 	return (ISC_R_SUCCESS);
98 }
99 
100 /*
101  * Merge one IP table into another one.
102  */
103 isc_result_t
dns_iptable_merge(dns_iptable_t * tab,dns_iptable_t * source,bool pos)104 dns_iptable_merge(dns_iptable_t *tab, dns_iptable_t *source, bool pos) {
105 	isc_result_t result;
106 	isc_radix_node_t *node, *new_node;
107 	int i, max_node = 0;
108 
109 	RADIX_WALK(source->radix->head, node) {
110 		new_node = NULL;
111 		result = isc_radix_insert(tab->radix, &new_node, node, NULL);
112 
113 		if (result != ISC_R_SUCCESS) {
114 			return (result);
115 		}
116 
117 		/*
118 		 * If we're negating a nested ACL, then we should
119 		 * reverse the sense of every node.  However, this
120 		 * could lead to a negative node in a nested ACL
121 		 * becoming a positive match in the parent, which
122 		 * could be a security risk.  To prevent this, we
123 		 * just leave the negative nodes negative.
124 		 */
125 		for (i = 0; i < RADIX_FAMILIES; i++) {
126 			if (!pos) {
127 				if (node->data[i] && *(bool *)node->data[i]) {
128 					new_node->data[i] = &dns_iptable_neg;
129 				}
130 			}
131 			if (node->node_num[i] > max_node) {
132 				max_node = node->node_num[i];
133 			}
134 		}
135 	}
136 	RADIX_WALK_END;
137 
138 	tab->radix->num_added_node += max_node;
139 	return (ISC_R_SUCCESS);
140 }
141 
142 void
dns_iptable_attach(dns_iptable_t * source,dns_iptable_t ** target)143 dns_iptable_attach(dns_iptable_t *source, dns_iptable_t **target) {
144 	REQUIRE(DNS_IPTABLE_VALID(source));
145 	isc_refcount_increment(&source->refcount);
146 	*target = source;
147 }
148 
149 void
dns_iptable_detach(dns_iptable_t ** tabp)150 dns_iptable_detach(dns_iptable_t **tabp) {
151 	REQUIRE(tabp != NULL && DNS_IPTABLE_VALID(*tabp));
152 	dns_iptable_t *tab = *tabp;
153 	*tabp = NULL;
154 
155 	if (isc_refcount_decrement(&tab->refcount) == 1) {
156 		isc_refcount_destroy(&tab->refcount);
157 		destroy_iptable(tab);
158 	}
159 }
160 
161 static void
destroy_iptable(dns_iptable_t * dtab)162 destroy_iptable(dns_iptable_t *dtab) {
163 	REQUIRE(DNS_IPTABLE_VALID(dtab));
164 
165 	if (dtab->radix != NULL) {
166 		isc_radix_destroy(dtab->radix, NULL);
167 		dtab->radix = NULL;
168 	}
169 
170 	dtab->magic = 0;
171 	isc_mem_putanddetach(&dtab->mctx, dtab, sizeof(*dtab));
172 }
173