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