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