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