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