1 /* $NetBSD: acl.c,v 1.7 2021/02/19 16:42:15 christos Exp $ */ 2 3 /* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 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/util.h> 23 24 #include <dns/acl.h> 25 #include <dns/iptable.h> 26 27 /* 28 * Create a new ACL, including an IP table and an array with room 29 * for 'n' ACL elements. The elements are uninitialized and the 30 * length is 0. 31 */ 32 isc_result_t 33 dns_acl_create(isc_mem_t *mctx, int n, dns_acl_t **target) { 34 isc_result_t result; 35 dns_acl_t *acl; 36 37 /* 38 * Work around silly limitation of isc_mem_get(). 39 */ 40 if (n == 0) { 41 n = 1; 42 } 43 44 acl = isc_mem_get(mctx, sizeof(*acl)); 45 46 acl->mctx = NULL; 47 isc_mem_attach(mctx, &acl->mctx); 48 49 acl->name = NULL; 50 51 isc_refcount_init(&acl->refcount, 1); 52 53 result = dns_iptable_create(mctx, &acl->iptable); 54 if (result != ISC_R_SUCCESS) { 55 isc_mem_put(mctx, acl, sizeof(*acl)); 56 return (result); 57 } 58 59 acl->elements = NULL; 60 acl->alloc = 0; 61 acl->length = 0; 62 acl->has_negatives = false; 63 64 ISC_LINK_INIT(acl, nextincache); 65 /* 66 * Must set magic early because we use dns_acl_detach() to clean up. 67 */ 68 acl->magic = DNS_ACL_MAGIC; 69 70 acl->elements = isc_mem_get(mctx, n * sizeof(dns_aclelement_t)); 71 acl->alloc = n; 72 memset(acl->elements, 0, n * sizeof(dns_aclelement_t)); 73 *target = acl; 74 return (ISC_R_SUCCESS); 75 } 76 77 /* 78 * Create a new ACL and initialize it with the value "any" or "none", 79 * depending on the value of the "neg" parameter. 80 * "any" is a positive iptable entry with bit length 0. 81 * "none" is the same as "!any". 82 */ 83 static isc_result_t 84 dns_acl_anyornone(isc_mem_t *mctx, bool neg, dns_acl_t **target) { 85 isc_result_t result; 86 dns_acl_t *acl = NULL; 87 88 result = dns_acl_create(mctx, 0, &acl); 89 if (result != ISC_R_SUCCESS) { 90 return (result); 91 } 92 93 result = dns_iptable_addprefix(acl->iptable, NULL, 0, !neg); 94 if (result != ISC_R_SUCCESS) { 95 dns_acl_detach(&acl); 96 return (result); 97 } 98 99 *target = acl; 100 return (result); 101 } 102 103 /* 104 * Create a new ACL that matches everything. 105 */ 106 isc_result_t 107 dns_acl_any(isc_mem_t *mctx, dns_acl_t **target) { 108 return (dns_acl_anyornone(mctx, false, target)); 109 } 110 111 /* 112 * Create a new ACL that matches nothing. 113 */ 114 isc_result_t 115 dns_acl_none(isc_mem_t *mctx, dns_acl_t **target) { 116 return (dns_acl_anyornone(mctx, true, target)); 117 } 118 119 /* 120 * If pos is true, test whether acl is set to "{ any; }" 121 * If pos is false, test whether acl is set to "{ none; }" 122 */ 123 static bool 124 dns_acl_isanyornone(dns_acl_t *acl, bool pos) { 125 /* Should never happen but let's be safe */ 126 if (acl == NULL || acl->iptable == NULL || 127 acl->iptable->radix == NULL || acl->iptable->radix->head == NULL || 128 acl->iptable->radix->head->prefix == NULL) 129 { 130 return (false); 131 } 132 133 if (acl->length != 0 || dns_acl_node_count(acl) != 1) { 134 return (false); 135 } 136 137 if (acl->iptable->radix->head->prefix->bitlen == 0 && 138 acl->iptable->radix->head->data[0] != NULL && 139 acl->iptable->radix->head->data[0] == 140 acl->iptable->radix->head->data[1] && 141 *(bool *)(acl->iptable->radix->head->data[0]) == pos) 142 { 143 return (true); 144 } 145 146 return (false); /* All others */ 147 } 148 149 /* 150 * Test whether acl is set to "{ any; }" 151 */ 152 bool 153 dns_acl_isany(dns_acl_t *acl) { 154 return (dns_acl_isanyornone(acl, true)); 155 } 156 157 /* 158 * Test whether acl is set to "{ none; }" 159 */ 160 bool 161 dns_acl_isnone(dns_acl_t *acl) { 162 return (dns_acl_isanyornone(acl, false)); 163 } 164 165 /* 166 * Determine whether a given address or signer matches a given ACL. 167 * For a match with a positive ACL element or iptable radix entry, 168 * return with a positive value in match; for a match with a negated ACL 169 * element or radix entry, return with a negative value in match. 170 */ 171 172 isc_result_t 173 dns_acl_match(const isc_netaddr_t *reqaddr, const dns_name_t *reqsigner, 174 const dns_acl_t *acl, const dns_aclenv_t *env, int *match, 175 const dns_aclelement_t **matchelt) { 176 uint16_t bitlen; 177 isc_prefix_t pfx; 178 isc_radix_node_t *node = NULL; 179 const isc_netaddr_t *addr = reqaddr; 180 isc_netaddr_t v4addr; 181 isc_result_t result; 182 int match_num = -1; 183 unsigned int i; 184 185 REQUIRE(reqaddr != NULL); 186 REQUIRE(matchelt == NULL || *matchelt == NULL); 187 188 if (env != NULL && env->match_mapped && addr->family == AF_INET6 && 189 IN6_IS_ADDR_V4MAPPED(&addr->type.in6)) 190 { 191 isc_netaddr_fromv4mapped(&v4addr, addr); 192 addr = &v4addr; 193 } 194 195 /* Always match with host addresses. */ 196 bitlen = (addr->family == AF_INET6) ? 128 : 32; 197 NETADDR_TO_PREFIX_T(addr, pfx, bitlen); 198 199 /* Assume no match. */ 200 *match = 0; 201 202 /* Search radix. */ 203 result = isc_radix_search(acl->iptable->radix, &node, &pfx); 204 205 /* Found a match. */ 206 if (result == ISC_R_SUCCESS && node != NULL) { 207 int fam = ISC_RADIX_FAMILY(&pfx); 208 match_num = node->node_num[fam]; 209 if (*(bool *)node->data[fam]) { 210 *match = match_num; 211 } else { 212 *match = -match_num; 213 } 214 } 215 216 isc_refcount_destroy(&pfx.refcount); 217 218 /* Now search non-radix elements for a match with a lower node_num. */ 219 for (i = 0; i < acl->length; i++) { 220 dns_aclelement_t *e = &acl->elements[i]; 221 222 /* Already found a better match? */ 223 if (match_num != -1 && match_num < e->node_num) { 224 break; 225 } 226 227 if (dns_aclelement_match(reqaddr, reqsigner, e, env, matchelt)) 228 { 229 if (match_num == -1 || e->node_num < match_num) { 230 if (e->negative) { 231 *match = -e->node_num; 232 } else { 233 *match = e->node_num; 234 } 235 } 236 break; 237 } 238 } 239 240 return (ISC_R_SUCCESS); 241 } 242 243 /* 244 * Merge the contents of one ACL into another. Call dns_iptable_merge() 245 * for the IP tables, then concatenate the element arrays. 246 * 247 * If pos is set to false, then the nested ACL is to be negated. This 248 * means reverse the sense of each *positive* element or IP table node, 249 * but leave negatives alone, so as to prevent a double-negative causing 250 * an unexpected positive match in the parent ACL. 251 */ 252 isc_result_t 253 dns_acl_merge(dns_acl_t *dest, dns_acl_t *source, bool pos) { 254 isc_result_t result; 255 unsigned int newalloc, nelem, i; 256 int max_node = 0, nodes; 257 258 /* Resize the element array if needed. */ 259 if (dest->length + source->length > dest->alloc) { 260 void *newmem; 261 262 newalloc = dest->alloc + source->alloc; 263 if (newalloc < 4) { 264 newalloc = 4; 265 } 266 267 newmem = isc_mem_get(dest->mctx, 268 newalloc * sizeof(dns_aclelement_t)); 269 270 /* Zero. */ 271 memset(newmem, 0, newalloc * sizeof(dns_aclelement_t)); 272 273 /* Copy in the original elements */ 274 memmove(newmem, dest->elements, 275 dest->length * sizeof(dns_aclelement_t)); 276 277 /* Release the memory for the old elements array */ 278 isc_mem_put(dest->mctx, dest->elements, 279 dest->alloc * sizeof(dns_aclelement_t)); 280 dest->elements = newmem; 281 dest->alloc = newalloc; 282 } 283 284 /* 285 * Now copy in the new elements, increasing their node_num 286 * values so as to keep the new ACL consistent. If we're 287 * negating, then negate positive elements, but keep negative 288 * elements the same for security reasons. 289 */ 290 nelem = dest->length; 291 dest->length += source->length; 292 for (i = 0; i < source->length; i++) { 293 if (source->elements[i].node_num > max_node) { 294 max_node = source->elements[i].node_num; 295 } 296 297 /* Copy type. */ 298 dest->elements[nelem + i].type = source->elements[i].type; 299 300 /* Adjust node numbering. */ 301 dest->elements[nelem + i].node_num = 302 source->elements[i].node_num + dns_acl_node_count(dest); 303 304 /* Duplicate nested acl. */ 305 if (source->elements[i].type == dns_aclelementtype_nestedacl && 306 source->elements[i].nestedacl != NULL) 307 { 308 dns_acl_attach(source->elements[i].nestedacl, 309 &dest->elements[nelem + i].nestedacl); 310 } 311 312 /* Duplicate key name. */ 313 if (source->elements[i].type == dns_aclelementtype_keyname) { 314 dns_name_init(&dest->elements[nelem + i].keyname, NULL); 315 dns_name_dup(&source->elements[i].keyname, dest->mctx, 316 &dest->elements[nelem + i].keyname); 317 } 318 319 #if defined(HAVE_GEOIP2) 320 /* Duplicate GeoIP data */ 321 if (source->elements[i].type == dns_aclelementtype_geoip) { 322 dest->elements[nelem + i].geoip_elem = 323 source->elements[i].geoip_elem; 324 } 325 #endif /* if defined(HAVE_GEOIP2) */ 326 327 /* reverse sense of positives if this is a negative acl */ 328 if (!pos && !source->elements[i].negative) { 329 dest->elements[nelem + i].negative = true; 330 } else { 331 dest->elements[nelem + i].negative = 332 source->elements[i].negative; 333 } 334 } 335 336 /* 337 * Merge the iptables. Make sure the destination ACL's 338 * node_count value is set correctly afterward. 339 */ 340 nodes = max_node + dns_acl_node_count(dest); 341 result = dns_iptable_merge(dest->iptable, source->iptable, pos); 342 if (result != ISC_R_SUCCESS) { 343 return (result); 344 } 345 if (nodes > dns_acl_node_count(dest)) { 346 dns_acl_node_count(dest) = nodes; 347 } 348 349 return (ISC_R_SUCCESS); 350 } 351 352 /* 353 * Like dns_acl_match, but matches against the single ACL element 'e' 354 * rather than a complete ACL, and returns true iff it matched. 355 * 356 * To determine whether the match was positive or negative, the 357 * caller should examine e->negative. Since the element 'e' may be 358 * a reference to a named ACL or a nested ACL, a matching element 359 * returned through 'matchelt' is not necessarily 'e' itself. 360 */ 361 362 bool 363 dns_aclelement_match(const isc_netaddr_t *reqaddr, const dns_name_t *reqsigner, 364 const dns_aclelement_t *e, const dns_aclenv_t *env, 365 const dns_aclelement_t **matchelt) { 366 dns_acl_t *inner = NULL; 367 int indirectmatch; 368 isc_result_t result; 369 370 switch (e->type) { 371 case dns_aclelementtype_keyname: 372 if (reqsigner != NULL && dns_name_equal(reqsigner, &e->keyname)) 373 { 374 if (matchelt != NULL) { 375 *matchelt = e; 376 } 377 return (true); 378 } else { 379 return (false); 380 } 381 382 case dns_aclelementtype_nestedacl: 383 inner = e->nestedacl; 384 break; 385 386 case dns_aclelementtype_localhost: 387 if (env == NULL || env->localhost == NULL) { 388 return (false); 389 } 390 inner = env->localhost; 391 break; 392 393 case dns_aclelementtype_localnets: 394 if (env == NULL || env->localnets == NULL) { 395 return (false); 396 } 397 inner = env->localnets; 398 break; 399 400 #if defined(HAVE_GEOIP2) 401 case dns_aclelementtype_geoip: 402 if (env == NULL || env->geoip == NULL) { 403 return (false); 404 } 405 return (dns_geoip_match(reqaddr, env->geoip, &e->geoip_elem)); 406 #endif /* if defined(HAVE_GEOIP2) */ 407 default: 408 INSIST(0); 409 ISC_UNREACHABLE(); 410 } 411 412 result = dns_acl_match(reqaddr, reqsigner, inner, env, &indirectmatch, 413 matchelt); 414 INSIST(result == ISC_R_SUCCESS); 415 416 /* 417 * Treat negative matches in indirect ACLs as "no match". 418 * That way, a negated indirect ACL will never become a 419 * surprise positive match through double negation. 420 * XXXDCL this should be documented. 421 */ 422 if (indirectmatch > 0) { 423 if (matchelt != NULL) { 424 *matchelt = e; 425 } 426 return (true); 427 } 428 429 /* 430 * A negative indirect match may have set *matchelt, but we don't 431 * want it set when we return. 432 */ 433 if (matchelt != NULL) { 434 *matchelt = NULL; 435 } 436 437 return (false); 438 } 439 440 void 441 dns_acl_attach(dns_acl_t *source, dns_acl_t **target) { 442 REQUIRE(DNS_ACL_VALID(source)); 443 444 isc_refcount_increment(&source->refcount); 445 *target = source; 446 } 447 448 static void 449 destroy(dns_acl_t *dacl) { 450 unsigned int i; 451 452 INSIST(!ISC_LINK_LINKED(dacl, nextincache)); 453 454 for (i = 0; i < dacl->length; i++) { 455 dns_aclelement_t *de = &dacl->elements[i]; 456 if (de->type == dns_aclelementtype_keyname) { 457 dns_name_free(&de->keyname, dacl->mctx); 458 } else if (de->type == dns_aclelementtype_nestedacl) { 459 dns_acl_detach(&de->nestedacl); 460 } 461 } 462 if (dacl->elements != NULL) { 463 isc_mem_put(dacl->mctx, dacl->elements, 464 dacl->alloc * sizeof(dns_aclelement_t)); 465 } 466 if (dacl->name != NULL) { 467 isc_mem_free(dacl->mctx, dacl->name); 468 } 469 if (dacl->iptable != NULL) { 470 dns_iptable_detach(&dacl->iptable); 471 } 472 isc_refcount_destroy(&dacl->refcount); 473 dacl->magic = 0; 474 isc_mem_putanddetach(&dacl->mctx, dacl, sizeof(*dacl)); 475 } 476 477 void 478 dns_acl_detach(dns_acl_t **aclp) { 479 REQUIRE(aclp != NULL && DNS_ACL_VALID(*aclp)); 480 dns_acl_t *acl = *aclp; 481 *aclp = NULL; 482 483 if (isc_refcount_decrement(&acl->refcount) == 1) { 484 destroy(acl); 485 } 486 } 487 488 static isc_once_t insecure_prefix_once = ISC_ONCE_INIT; 489 static isc_mutex_t insecure_prefix_lock; 490 static bool insecure_prefix_found; 491 492 static void 493 initialize_action(void) { 494 isc_mutex_init(&insecure_prefix_lock); 495 } 496 497 /* 498 * Called via isc_radix_process() to find IP table nodes that are 499 * insecure. 500 */ 501 static void 502 is_insecure(isc_prefix_t *prefix, void **data) { 503 /* 504 * If all nonexistent or negative then this node is secure. 505 */ 506 if ((data[0] == NULL || !*(bool *)data[0]) && 507 (data[1] == NULL || !*(bool *)data[1])) 508 { 509 return; 510 } 511 512 /* 513 * If a loopback address found and the other family 514 * entry doesn't exist or is negative, return. 515 */ 516 if (prefix->bitlen == 32 && 517 htonl(prefix->add.sin.s_addr) == INADDR_LOOPBACK && 518 (data[1] == NULL || !*(bool *)data[1])) 519 { 520 return; 521 } 522 523 if (prefix->bitlen == 128 && IN6_IS_ADDR_LOOPBACK(&prefix->add.sin6) && 524 (data[0] == NULL || !*(bool *)data[0])) 525 { 526 return; 527 } 528 529 /* Non-negated, non-loopback */ 530 insecure_prefix_found = true; /* LOCKED */ 531 return; 532 } 533 534 /* 535 * Return true iff the acl 'a' is considered insecure, that is, 536 * if it contains IP addresses other than those of the local host. 537 * This is intended for applications such as printing warning 538 * messages for suspect ACLs; it is not intended for making access 539 * control decisions. We make no guarantee that an ACL for which 540 * this function returns false is safe. 541 */ 542 bool 543 dns_acl_isinsecure(const dns_acl_t *a) { 544 unsigned int i; 545 bool insecure; 546 547 RUNTIME_CHECK(isc_once_do(&insecure_prefix_once, initialize_action) == 548 ISC_R_SUCCESS); 549 550 /* 551 * Walk radix tree to find out if there are any non-negated, 552 * non-loopback prefixes. 553 */ 554 LOCK(&insecure_prefix_lock); 555 insecure_prefix_found = false; 556 isc_radix_process(a->iptable->radix, is_insecure); 557 insecure = insecure_prefix_found; 558 UNLOCK(&insecure_prefix_lock); 559 if (insecure) { 560 return (true); 561 } 562 563 /* Now check non-radix elements */ 564 for (i = 0; i < a->length; i++) { 565 dns_aclelement_t *e = &a->elements[i]; 566 567 /* A negated match can never be insecure. */ 568 if (e->negative) { 569 continue; 570 } 571 572 switch (e->type) { 573 case dns_aclelementtype_keyname: 574 case dns_aclelementtype_localhost: 575 continue; 576 577 case dns_aclelementtype_nestedacl: 578 if (dns_acl_isinsecure(e->nestedacl)) { 579 return (true); 580 } 581 continue; 582 583 #if defined(HAVE_GEOIP2) 584 case dns_aclelementtype_geoip: 585 #endif /* if defined(HAVE_GEOIP2) */ 586 case dns_aclelementtype_localnets: 587 return (true); 588 589 default: 590 INSIST(0); 591 ISC_UNREACHABLE(); 592 } 593 } 594 595 /* No insecure elements were found. */ 596 return (false); 597 } 598 599 /*% 600 * Check whether an address/signer is allowed by a given acl/aclenv. 601 */ 602 bool 603 dns_acl_allowed(isc_netaddr_t *addr, const dns_name_t *signer, dns_acl_t *acl, 604 dns_aclenv_t *aclenv) { 605 int match; 606 isc_result_t result; 607 608 if (acl == NULL) { 609 return (true); 610 } 611 result = dns_acl_match(addr, signer, acl, aclenv, &match, NULL); 612 if (result == ISC_R_SUCCESS && match > 0) { 613 return (true); 614 } 615 return (false); 616 } 617 618 /* 619 * Initialize ACL environment, setting up localhost and localnets ACLs 620 */ 621 isc_result_t 622 dns_aclenv_init(isc_mem_t *mctx, dns_aclenv_t *env) { 623 isc_result_t result; 624 625 env->localhost = NULL; 626 env->localnets = NULL; 627 result = dns_acl_create(mctx, 0, &env->localhost); 628 if (result != ISC_R_SUCCESS) { 629 goto cleanup_nothing; 630 } 631 result = dns_acl_create(mctx, 0, &env->localnets); 632 if (result != ISC_R_SUCCESS) { 633 goto cleanup_localhost; 634 } 635 env->match_mapped = false; 636 #if defined(HAVE_GEOIP2) 637 env->geoip = NULL; 638 #endif /* if defined(HAVE_GEOIP2) */ 639 return (ISC_R_SUCCESS); 640 641 cleanup_localhost: 642 dns_acl_detach(&env->localhost); 643 cleanup_nothing: 644 return (result); 645 } 646 647 void 648 dns_aclenv_copy(dns_aclenv_t *t, dns_aclenv_t *s) { 649 dns_acl_detach(&t->localhost); 650 dns_acl_attach(s->localhost, &t->localhost); 651 dns_acl_detach(&t->localnets); 652 dns_acl_attach(s->localnets, &t->localnets); 653 t->match_mapped = s->match_mapped; 654 #if defined(HAVE_GEOIP2) 655 t->geoip = s->geoip; 656 #endif /* if defined(HAVE_GEOIP2) */ 657 } 658 659 void 660 dns_aclenv_destroy(dns_aclenv_t *env) { 661 if (env->localhost != NULL) { 662 dns_acl_detach(&env->localhost); 663 } 664 if (env->localnets != NULL) { 665 dns_acl_detach(&env->localnets); 666 } 667 } 668