1 /* $NetBSD: acl.c,v 1.10 2025/01/26 16:25:21 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, 468 next) 469 { 470 ISC_LIST_DEQUEUE(acl->ports_and_transports, port_proto, link); 471 isc_mem_put(acl->mctx, port_proto, sizeof(*port_proto)); 472 } 473 } 474 475 static void 476 dns__acl_destroy(dns_acl_t *dacl) { 477 INSIST(!ISC_LINK_LINKED(dacl, nextincache)); 478 479 isc_refcount_destroy(&dacl->references); 480 dacl->magic = 0; 481 482 for (size_t i = 0; i < dacl->length; i++) { 483 dns_aclelement_t *de = &dacl->elements[i]; 484 if (de->type == dns_aclelementtype_keyname) { 485 dns_name_free(&de->keyname, dacl->mctx); 486 } else if (de->type == dns_aclelementtype_nestedacl) { 487 dns_acl_detach(&de->nestedacl); 488 } 489 } 490 if (dacl->elements != NULL) { 491 isc_mem_cput(dacl->mctx, dacl->elements, dacl->alloc, 492 sizeof(dacl->elements[0])); 493 } 494 if (dacl->name != NULL) { 495 isc_mem_free(dacl->mctx, dacl->name); 496 } 497 if (dacl->iptable != NULL) { 498 dns_iptable_detach(&dacl->iptable); 499 } 500 501 dns__acl_destroy_port_transports(dacl); 502 503 isc_mem_putanddetach(&dacl->mctx, dacl, sizeof(*dacl)); 504 } 505 506 #if DNS_ACL_TRACE 507 ISC_REFCOUNT_TRACE_IMPL(dns_acl, dns__acl_destroy); 508 #else 509 ISC_REFCOUNT_IMPL(dns_acl, dns__acl_destroy); 510 #endif 511 512 static isc_once_t insecure_prefix_once = ISC_ONCE_INIT; 513 static isc_mutex_t insecure_prefix_lock; 514 static bool insecure_prefix_found; 515 516 static void 517 initialize_action(void) { 518 isc_mutex_init(&insecure_prefix_lock); 519 } 520 521 /* 522 * Called via isc_radix_process() to find IP table nodes that are 523 * insecure. 524 */ 525 static void 526 is_insecure(isc_prefix_t *prefix, void **data) { 527 /* 528 * If all nonexistent or negative then this node is secure. 529 */ 530 if ((data[0] == NULL || !*(bool *)data[0]) && 531 (data[1] == NULL || !*(bool *)data[1])) 532 { 533 return; 534 } 535 536 /* 537 * If a loopback address found and the other family 538 * entry doesn't exist or is negative, return. 539 */ 540 if (prefix->bitlen == 32 && 541 htonl(prefix->add.sin.s_addr) == INADDR_LOOPBACK && 542 (data[1] == NULL || !*(bool *)data[1])) 543 { 544 return; 545 } 546 547 if (prefix->bitlen == 128 && IN6_IS_ADDR_LOOPBACK(&prefix->add.sin6) && 548 (data[0] == NULL || !*(bool *)data[0])) 549 { 550 return; 551 } 552 553 /* Non-negated, non-loopback */ 554 insecure_prefix_found = true; /* LOCKED */ 555 return; 556 } 557 558 /* 559 * Return true iff the acl 'a' is considered insecure, that is, 560 * if it contains IP addresses other than those of the local host. 561 * This is intended for applications such as printing warning 562 * messages for suspect ACLs; it is not intended for making access 563 * control decisions. We make no guarantee that an ACL for which 564 * this function returns false is safe. 565 */ 566 bool 567 dns_acl_isinsecure(const dns_acl_t *a) { 568 unsigned int i; 569 bool insecure; 570 571 isc_once_do(&insecure_prefix_once, initialize_action); 572 573 /* 574 * Walk radix tree to find out if there are any non-negated, 575 * non-loopback prefixes. 576 */ 577 LOCK(&insecure_prefix_lock); 578 insecure_prefix_found = false; 579 isc_radix_process(a->iptable->radix, is_insecure); 580 insecure = insecure_prefix_found; 581 UNLOCK(&insecure_prefix_lock); 582 if (insecure) { 583 return true; 584 } 585 586 /* Now check non-radix elements */ 587 for (i = 0; i < a->length; i++) { 588 dns_aclelement_t *e = &a->elements[i]; 589 590 /* A negated match can never be insecure. */ 591 if (e->negative) { 592 continue; 593 } 594 595 switch (e->type) { 596 case dns_aclelementtype_keyname: 597 case dns_aclelementtype_localhost: 598 continue; 599 600 case dns_aclelementtype_nestedacl: 601 if (dns_acl_isinsecure(e->nestedacl)) { 602 return true; 603 } 604 continue; 605 606 #if defined(HAVE_GEOIP2) 607 case dns_aclelementtype_geoip: 608 #endif /* if defined(HAVE_GEOIP2) */ 609 case dns_aclelementtype_localnets: 610 return true; 611 612 default: 613 UNREACHABLE(); 614 } 615 } 616 617 /* No insecure elements were found. */ 618 return false; 619 } 620 621 /*% 622 * Check whether an address/signer is allowed by a given acl/aclenv. 623 */ 624 bool 625 dns_acl_allowed(isc_netaddr_t *addr, const dns_name_t *signer, dns_acl_t *acl, 626 dns_aclenv_t *aclenv) { 627 int match; 628 isc_result_t result; 629 630 if (acl == NULL) { 631 return true; 632 } 633 result = dns_acl_match(addr, signer, acl, aclenv, &match, NULL); 634 if (result == ISC_R_SUCCESS && match > 0) { 635 return true; 636 } 637 return false; 638 } 639 640 /* 641 * Initialize ACL environment, setting up localhost and localnets ACLs 642 */ 643 void 644 dns_aclenv_create(isc_mem_t *mctx, dns_aclenv_t **envp) { 645 dns_aclenv_t *env = isc_mem_get(mctx, sizeof(*env)); 646 *env = (dns_aclenv_t){ 647 .references = ISC_REFCOUNT_INITIALIZER(1), 648 .magic = DNS_ACLENV_MAGIC, 649 }; 650 651 isc_mem_attach(mctx, &env->mctx); 652 isc_refcount_init(&env->references, 1); 653 654 dns_acl_create(mctx, 0, &env->localhost); 655 dns_acl_create(mctx, 0, &env->localnets); 656 657 *envp = env; 658 } 659 660 void 661 dns_aclenv_set(dns_aclenv_t *env, dns_acl_t *localhost, dns_acl_t *localnets) { 662 REQUIRE(VALID_ACLENV(env)); 663 REQUIRE(DNS_ACL_VALID(localhost)); 664 REQUIRE(DNS_ACL_VALID(localnets)); 665 666 localhost = rcu_xchg_pointer(&env->localhost, dns_acl_ref(localhost)); 667 localnets = rcu_xchg_pointer(&env->localnets, dns_acl_ref(localnets)); 668 669 /* 670 * This function is called only during interface scanning, so blocking 671 * a bit is acceptable. Wait until all ongoing attachments to old 672 * 'localhost' and 'localnets' are finished before we can detach and 673 * possibly destroy them. 674 * 675 * The problem here isn't the memory reclamation per se, but 676 * the reference counting race - we need to wait for the 677 * critical section to end before we decrement the value and 678 * possibly destroy the acl objects. 679 */ 680 synchronize_rcu(); 681 682 dns_acl_detach(&localhost); 683 dns_acl_detach(&localnets); 684 } 685 686 void 687 dns_aclenv_copy(dns_aclenv_t *target, dns_aclenv_t *source) { 688 REQUIRE(VALID_ACLENV(source)); 689 REQUIRE(VALID_ACLENV(target)); 690 691 rcu_read_lock(); 692 693 /* 694 * We need to acquire the reference inside the critical section. 695 */ 696 697 dns_acl_t *localhost = dns_acl_ref(rcu_dereference(source->localhost)); 698 INSIST(DNS_ACL_VALID(localhost)); 699 700 dns_acl_t *localnets = dns_acl_ref(rcu_dereference(source->localnets)); 701 INSIST(DNS_ACL_VALID(localnets)); 702 703 rcu_read_unlock(); 704 705 localhost = rcu_xchg_pointer(&target->localhost, localhost); 706 localnets = rcu_xchg_pointer(&target->localnets, localnets); 707 708 /* 709 * This function is called only during (re)configuration, so blocking 710 * a bit is acceptable. 711 * 712 * See the comment above in dns_aclenv_set() for more detail. 713 */ 714 synchronize_rcu(); 715 716 target->match_mapped = source->match_mapped; 717 #if defined(HAVE_GEOIP2) 718 target->geoip = source->geoip; 719 #endif /* if defined(HAVE_GEOIP2) */ 720 721 dns_acl_detach(&localhost); 722 dns_acl_detach(&localnets); 723 } 724 725 static void 726 dns__aclenv_destroy(dns_aclenv_t *aclenv) { 727 REQUIRE(VALID_ACLENV(aclenv)); 728 729 aclenv->magic = 0; 730 731 /* 732 * The last reference to the aclenv has been detached, so nobody should 733 * be reading from this aclenv. We can destroy the localhost and 734 * localnet directly without swapping the pointers. 735 */ 736 737 dns_acl_detach(&aclenv->localhost); 738 dns_acl_detach(&aclenv->localnets); 739 740 isc_mem_putanddetach(&aclenv->mctx, aclenv, sizeof(*aclenv)); 741 } 742 743 #if DNS_ACL_TRACE 744 ISC_REFCOUNT_TRACE_IMPL(dns_aclenv, dns__aclenv_destroy); 745 #else 746 ISC_REFCOUNT_IMPL(dns_aclenv, dns__aclenv_destroy); 747 #endif 748 749 void 750 dns_acl_add_port_transports(dns_acl_t *acl, const in_port_t port, 751 const uint32_t transports, const bool encrypted, 752 const bool negative) { 753 dns_acl_port_transports_t *port_proto; 754 REQUIRE(DNS_ACL_VALID(acl)); 755 REQUIRE(port != 0 || transports != 0); 756 757 port_proto = isc_mem_get(acl->mctx, sizeof(*port_proto)); 758 *port_proto = (dns_acl_port_transports_t){ .port = port, 759 .transports = transports, 760 .encrypted = encrypted, 761 .negative = negative }; 762 763 ISC_LINK_INIT(port_proto, link); 764 765 ISC_LIST_APPEND(acl->ports_and_transports, port_proto, link); 766 acl->port_proto_entries++; 767 } 768 769 void 770 dns_acl_merge_ports_transports(dns_acl_t *dest, dns_acl_t *source, bool pos) { 771 dns_acl_port_transports_t *next; 772 773 REQUIRE(DNS_ACL_VALID(dest)); 774 REQUIRE(DNS_ACL_VALID(source)); 775 776 const bool negative = !pos; 777 778 /* 779 * Merge ports and transports 780 */ 781 for (next = ISC_LIST_HEAD(source->ports_and_transports); next != NULL; 782 next = ISC_LIST_NEXT(next, link)) 783 { 784 const bool next_positive = !next->negative; 785 bool add_negative; 786 787 /* 788 * Reverse sense of positives if this is a negative acl. The 789 * logic is used (and, thus, enforced) by dns_acl_merge(), 790 * from which dns_acl_merge_ports_transports() is called. 791 */ 792 if (negative && next_positive) { 793 add_negative = true; 794 } else { 795 add_negative = next->negative; 796 } 797 798 dns_acl_add_port_transports(dest, next->port, next->transports, 799 next->encrypted, add_negative); 800 } 801 } 802