1/* 2 * Copyright (C) 2004-2012 Internet Systems Consortium, Inc. ("ISC") 3 * Copyright (C) 2001-2003 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 <stdlib.h> 25 26#include <isc/base64.h> 27#include <isc/buffer.h> 28#include <isc/log.h> 29#include <isc/mem.h> 30#include <isc/netaddr.h> 31#include <isc/parseint.h> 32#include <isc/region.h> 33#include <isc/result.h> 34#include <isc/sockaddr.h> 35#include <isc/string.h> 36#include <isc/symtab.h> 37#include <isc/util.h> 38 39#include <dns/acl.h> 40#include <dns/fixedname.h> 41#include <dns/rdataclass.h> 42#include <dns/rdatatype.h> 43#include <dns/secalg.h> 44 45#include <dst/dst.h> 46 47#include <isccfg/aclconf.h> 48#include <isccfg/cfg.h> 49 50#include <bind9/check.h> 51 52static void 53freekey(char *key, unsigned int type, isc_symvalue_t value, void *userarg) { 54 UNUSED(type); 55 UNUSED(value); 56 isc_mem_free(userarg, key); 57} 58 59static isc_result_t 60check_orderent(const cfg_obj_t *ent, isc_log_t *logctx) { 61 isc_result_t result = ISC_R_SUCCESS; 62 isc_result_t tresult; 63 isc_textregion_t r; 64 dns_fixedname_t fixed; 65 const cfg_obj_t *obj; 66 dns_rdataclass_t rdclass; 67 dns_rdatatype_t rdtype; 68 isc_buffer_t b; 69 const char *str; 70 71 dns_fixedname_init(&fixed); 72 obj = cfg_tuple_get(ent, "class"); 73 if (cfg_obj_isstring(obj)) { 74 75 DE_CONST(cfg_obj_asstring(obj), r.base); 76 r.length = strlen(r.base); 77 tresult = dns_rdataclass_fromtext(&rdclass, &r); 78 if (tresult != ISC_R_SUCCESS) { 79 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 80 "rrset-order: invalid class '%s'", 81 r.base); 82 result = ISC_R_FAILURE; 83 } 84 } 85 86 obj = cfg_tuple_get(ent, "type"); 87 if (cfg_obj_isstring(obj)) { 88 89 DE_CONST(cfg_obj_asstring(obj), r.base); 90 r.length = strlen(r.base); 91 tresult = dns_rdatatype_fromtext(&rdtype, &r); 92 if (tresult != ISC_R_SUCCESS) { 93 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 94 "rrset-order: invalid type '%s'", 95 r.base); 96 result = ISC_R_FAILURE; 97 } 98 } 99 100 obj = cfg_tuple_get(ent, "name"); 101 if (cfg_obj_isstring(obj)) { 102 str = cfg_obj_asstring(obj); 103 isc_buffer_init(&b, str, strlen(str)); 104 isc_buffer_add(&b, strlen(str)); 105 tresult = dns_name_fromtext(dns_fixedname_name(&fixed), &b, 106 dns_rootname, 0, NULL); 107 if (tresult != ISC_R_SUCCESS) { 108 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 109 "rrset-order: invalid name '%s'", str); 110 result = ISC_R_FAILURE; 111 } 112 } 113 114 obj = cfg_tuple_get(ent, "order"); 115 if (!cfg_obj_isstring(obj) || 116 strcasecmp("order", cfg_obj_asstring(obj)) != 0) { 117 cfg_obj_log(ent, logctx, ISC_LOG_ERROR, 118 "rrset-order: keyword 'order' missing"); 119 result = ISC_R_FAILURE; 120 } 121 122 obj = cfg_tuple_get(ent, "ordering"); 123 if (!cfg_obj_isstring(obj)) { 124 cfg_obj_log(ent, logctx, ISC_LOG_ERROR, 125 "rrset-order: missing ordering"); 126 result = ISC_R_FAILURE; 127 } else if (strcasecmp(cfg_obj_asstring(obj), "fixed") == 0) { 128#if !DNS_RDATASET_FIXED 129 cfg_obj_log(obj, logctx, ISC_LOG_WARNING, 130 "rrset-order: order 'fixed' was disabled at " 131 "compilation time"); 132#endif 133 } else if (strcasecmp(cfg_obj_asstring(obj), "random") != 0 && 134 strcasecmp(cfg_obj_asstring(obj), "cyclic") != 0) { 135 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 136 "rrset-order: invalid order '%s'", 137 cfg_obj_asstring(obj)); 138 result = ISC_R_FAILURE; 139 } 140 return (result); 141} 142 143static isc_result_t 144check_order(const cfg_obj_t *options, isc_log_t *logctx) { 145 isc_result_t result = ISC_R_SUCCESS; 146 isc_result_t tresult; 147 const cfg_listelt_t *element; 148 const cfg_obj_t *obj = NULL; 149 150 if (cfg_map_get(options, "rrset-order", &obj) != ISC_R_SUCCESS) 151 return (result); 152 153 for (element = cfg_list_first(obj); 154 element != NULL; 155 element = cfg_list_next(element)) 156 { 157 tresult = check_orderent(cfg_listelt_value(element), logctx); 158 if (tresult != ISC_R_SUCCESS) 159 result = tresult; 160 } 161 return (result); 162} 163 164static isc_result_t 165check_dual_stack(const cfg_obj_t *options, isc_log_t *logctx) { 166 const cfg_listelt_t *element; 167 const cfg_obj_t *alternates = NULL; 168 const cfg_obj_t *value; 169 const cfg_obj_t *obj; 170 const char *str; 171 dns_fixedname_t fixed; 172 dns_name_t *name; 173 isc_buffer_t buffer; 174 isc_result_t result = ISC_R_SUCCESS; 175 isc_result_t tresult; 176 177 (void)cfg_map_get(options, "dual-stack-servers", &alternates); 178 179 if (alternates == NULL) 180 return (ISC_R_SUCCESS); 181 182 obj = cfg_tuple_get(alternates, "port"); 183 if (cfg_obj_isuint32(obj)) { 184 isc_uint32_t val = cfg_obj_asuint32(obj); 185 if (val > ISC_UINT16_MAX) { 186 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 187 "port '%u' out of range", val); 188 result = ISC_R_FAILURE; 189 } 190 } 191 obj = cfg_tuple_get(alternates, "addresses"); 192 for (element = cfg_list_first(obj); 193 element != NULL; 194 element = cfg_list_next(element)) { 195 value = cfg_listelt_value(element); 196 if (cfg_obj_issockaddr(value)) 197 continue; 198 obj = cfg_tuple_get(value, "name"); 199 str = cfg_obj_asstring(obj); 200 isc_buffer_init(&buffer, str, strlen(str)); 201 isc_buffer_add(&buffer, strlen(str)); 202 dns_fixedname_init(&fixed); 203 name = dns_fixedname_name(&fixed); 204 tresult = dns_name_fromtext(name, &buffer, dns_rootname, 205 0, NULL); 206 if (tresult != ISC_R_SUCCESS) { 207 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 208 "bad name '%s'", str); 209 result = ISC_R_FAILURE; 210 } 211 obj = cfg_tuple_get(value, "port"); 212 if (cfg_obj_isuint32(obj)) { 213 isc_uint32_t val = cfg_obj_asuint32(obj); 214 if (val > ISC_UINT16_MAX) { 215 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 216 "port '%u' out of range", val); 217 result = ISC_R_FAILURE; 218 } 219 } 220 } 221 return (result); 222} 223 224static isc_result_t 225check_forward(const cfg_obj_t *options, const cfg_obj_t *global, 226 isc_log_t *logctx) 227{ 228 const cfg_obj_t *forward = NULL; 229 const cfg_obj_t *forwarders = NULL; 230 231 (void)cfg_map_get(options, "forward", &forward); 232 (void)cfg_map_get(options, "forwarders", &forwarders); 233 234 if (forwarders != NULL && global != NULL) { 235 const char *file = cfg_obj_file(global); 236 unsigned int line = cfg_obj_line(global); 237 cfg_obj_log(forwarders, logctx, ISC_LOG_ERROR, 238 "forwarders declared in root zone and " 239 "in general configuration: %s:%u", 240 file, line); 241 return (ISC_R_FAILURE); 242 } 243 if (forward != NULL && forwarders == NULL) { 244 cfg_obj_log(forward, logctx, ISC_LOG_ERROR, 245 "no matching 'forwarders' statement"); 246 return (ISC_R_FAILURE); 247 } 248 return (ISC_R_SUCCESS); 249} 250 251static isc_result_t 252disabled_algorithms(const cfg_obj_t *disabled, isc_log_t *logctx) { 253 isc_result_t result = ISC_R_SUCCESS; 254 isc_result_t tresult; 255 const cfg_listelt_t *element; 256 const char *str; 257 isc_buffer_t b; 258 dns_fixedname_t fixed; 259 dns_name_t *name; 260 const cfg_obj_t *obj; 261 262 dns_fixedname_init(&fixed); 263 name = dns_fixedname_name(&fixed); 264 obj = cfg_tuple_get(disabled, "name"); 265 str = cfg_obj_asstring(obj); 266 isc_buffer_init(&b, str, strlen(str)); 267 isc_buffer_add(&b, strlen(str)); 268 tresult = dns_name_fromtext(name, &b, dns_rootname, 0, NULL); 269 if (tresult != ISC_R_SUCCESS) { 270 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 271 "bad domain name '%s'", str); 272 result = tresult; 273 } 274 275 obj = cfg_tuple_get(disabled, "algorithms"); 276 277 for (element = cfg_list_first(obj); 278 element != NULL; 279 element = cfg_list_next(element)) 280 { 281 isc_textregion_t r; 282 dns_secalg_t alg; 283 isc_result_t tresult; 284 285 DE_CONST(cfg_obj_asstring(cfg_listelt_value(element)), r.base); 286 r.length = strlen(r.base); 287 288 tresult = dns_secalg_fromtext(&alg, &r); 289 if (tresult != ISC_R_SUCCESS) { 290 isc_uint8_t ui; 291 result = isc_parse_uint8(&ui, r.base, 10); 292 } 293 if (tresult != ISC_R_SUCCESS) { 294 cfg_obj_log(cfg_listelt_value(element), logctx, 295 ISC_LOG_ERROR, "invalid algorithm '%s'", 296 r.base); 297 result = tresult; 298 } 299 } 300 return (result); 301} 302 303static isc_result_t 304nameexist(const cfg_obj_t *obj, const char *name, int value, 305 isc_symtab_t *symtab, const char *fmt, isc_log_t *logctx, 306 isc_mem_t *mctx) 307{ 308 char *key; 309 const char *file; 310 unsigned int line; 311 isc_result_t result; 312 isc_symvalue_t symvalue; 313 314 key = isc_mem_strdup(mctx, name); 315 if (key == NULL) 316 return (ISC_R_NOMEMORY); 317 symvalue.as_cpointer = obj; 318 result = isc_symtab_define(symtab, key, value, symvalue, 319 isc_symexists_reject); 320 if (result == ISC_R_EXISTS) { 321 RUNTIME_CHECK(isc_symtab_lookup(symtab, key, value, 322 &symvalue) == ISC_R_SUCCESS); 323 file = cfg_obj_file(symvalue.as_cpointer); 324 line = cfg_obj_line(symvalue.as_cpointer); 325 326 if (file == NULL) 327 file = "<unknown file>"; 328 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, fmt, key, file, line); 329 isc_mem_free(mctx, key); 330 result = ISC_R_EXISTS; 331 } else if (result != ISC_R_SUCCESS) { 332 isc_mem_free(mctx, key); 333 } 334 return (result); 335} 336 337static isc_result_t 338mustbesecure(const cfg_obj_t *secure, isc_symtab_t *symtab, isc_log_t *logctx, 339 isc_mem_t *mctx) 340{ 341 const cfg_obj_t *obj; 342 char namebuf[DNS_NAME_FORMATSIZE]; 343 const char *str; 344 dns_fixedname_t fixed; 345 dns_name_t *name; 346 isc_buffer_t b; 347 isc_result_t result = ISC_R_SUCCESS; 348 349 dns_fixedname_init(&fixed); 350 name = dns_fixedname_name(&fixed); 351 obj = cfg_tuple_get(secure, "name"); 352 str = cfg_obj_asstring(obj); 353 isc_buffer_init(&b, str, strlen(str)); 354 isc_buffer_add(&b, strlen(str)); 355 result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL); 356 if (result != ISC_R_SUCCESS) { 357 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 358 "bad domain name '%s'", str); 359 } else { 360 dns_name_format(name, namebuf, sizeof(namebuf)); 361 result = nameexist(secure, namebuf, 1, symtab, 362 "dnssec-must-be-secure '%s': already " 363 "exists previous definition: %s:%u", 364 logctx, mctx); 365 } 366 return (result); 367} 368 369static isc_result_t 370checkacl(const char *aclname, cfg_aclconfctx_t *actx, const cfg_obj_t *zconfig, 371 const cfg_obj_t *voptions, const cfg_obj_t *config, 372 isc_log_t *logctx, isc_mem_t *mctx) 373{ 374 isc_result_t result; 375 const cfg_obj_t *aclobj = NULL; 376 const cfg_obj_t *options; 377 dns_acl_t *acl = NULL; 378 379 if (zconfig != NULL) { 380 options = cfg_tuple_get(zconfig, "options"); 381 cfg_map_get(options, aclname, &aclobj); 382 } 383 if (voptions != NULL && aclobj == NULL) 384 cfg_map_get(voptions, aclname, &aclobj); 385 if (config != NULL && aclobj == NULL) { 386 options = NULL; 387 cfg_map_get(config, "options", &options); 388 if (options != NULL) 389 cfg_map_get(options, aclname, &aclobj); 390 } 391 if (aclobj == NULL) 392 return (ISC_R_SUCCESS); 393 result = cfg_acl_fromconfig(aclobj, config, logctx, 394 actx, mctx, 0, &acl); 395 if (acl != NULL) 396 dns_acl_detach(&acl); 397 return (result); 398} 399 400static isc_result_t 401check_viewacls(cfg_aclconfctx_t *actx, const cfg_obj_t *voptions, 402 const cfg_obj_t *config, isc_log_t *logctx, isc_mem_t *mctx) 403{ 404 isc_result_t result = ISC_R_SUCCESS, tresult; 405 int i = 0; 406 407 static const char *acls[] = { "allow-query", "allow-query-on", 408 "allow-query-cache", "allow-query-cache-on", 409 "blackhole", "match-clients", "match-destinations", 410 "sortlist", "filter-aaaa", NULL }; 411 412 while (acls[i] != NULL) { 413 tresult = checkacl(acls[i++], actx, NULL, voptions, config, 414 logctx, mctx); 415 if (tresult != ISC_R_SUCCESS) 416 result = tresult; 417 } 418 return (result); 419} 420 421static const unsigned char zeros[16]; 422 423static isc_result_t 424check_dns64(cfg_aclconfctx_t *actx, const cfg_obj_t *voptions, 425 const cfg_obj_t *config, isc_log_t *logctx, isc_mem_t *mctx) 426{ 427 isc_result_t result = ISC_R_SUCCESS; 428 const cfg_obj_t *dns64 = NULL; 429 const cfg_obj_t *options; 430 const cfg_listelt_t *element; 431 const cfg_obj_t *map, *obj; 432 isc_netaddr_t na, sa; 433 unsigned int prefixlen; 434 int nbytes; 435 int i; 436 437 static const char *acls[] = { "clients", "exclude", "mapped", NULL}; 438 439 if (voptions != NULL) 440 cfg_map_get(voptions, "dns64", &dns64); 441 if (config != NULL && dns64 == NULL) { 442 options = NULL; 443 cfg_map_get(config, "options", &options); 444 if (options != NULL) 445 cfg_map_get(options, "dns64", &dns64); 446 } 447 if (dns64 == NULL) 448 return (ISC_R_SUCCESS); 449 450 for (element = cfg_list_first(dns64); 451 element != NULL; 452 element = cfg_list_next(element)) 453 { 454 map = cfg_listelt_value(element); 455 obj = cfg_map_getname(map); 456 457 cfg_obj_asnetprefix(obj, &na, &prefixlen); 458 if (na.family != AF_INET6) { 459 cfg_obj_log(map, logctx, ISC_LOG_ERROR, 460 "dns64 requires a IPv6 prefix"); 461 result = ISC_R_FAILURE; 462 continue; 463 } 464 465 if (prefixlen != 32 && prefixlen != 40 && prefixlen != 48 && 466 prefixlen != 56 && prefixlen != 64 && prefixlen != 96) { 467 cfg_obj_log(map, logctx, ISC_LOG_ERROR, 468 "bad prefix length %u [32/40/48/56/64/96]", 469 prefixlen); 470 result = ISC_R_FAILURE; 471 continue; 472 } 473 474 for (i = 0; acls[i] != NULL; i++) { 475 obj = NULL; 476 (void)cfg_map_get(map, acls[i], &obj); 477 if (obj != NULL) { 478 dns_acl_t *acl = NULL; 479 isc_result_t tresult; 480 481 tresult = cfg_acl_fromconfig(obj, config, 482 logctx, actx, 483 mctx, 0, &acl); 484 if (acl != NULL) 485 dns_acl_detach(&acl); 486 if (tresult != ISC_R_SUCCESS) 487 result = tresult; 488 } 489 } 490 491 obj = NULL; 492 (void)cfg_map_get(map, "suffix", &obj); 493 if (obj != NULL) { 494 isc_netaddr_fromsockaddr(&sa, cfg_obj_assockaddr(obj)); 495 if (sa.family != AF_INET6) { 496 cfg_obj_log(map, logctx, ISC_LOG_ERROR, 497 "dns64 requires a IPv6 suffix"); 498 result = ISC_R_FAILURE; 499 continue; 500 } 501 nbytes = prefixlen / 8 + 4; 502 if (prefixlen >= 32 && prefixlen <= 64) 503 nbytes++; 504 if (memcmp(sa.type.in6.s6_addr, zeros, nbytes) != 0) { 505 char netaddrbuf[ISC_NETADDR_FORMATSIZE]; 506 isc_netaddr_format(&sa, netaddrbuf, 507 sizeof(netaddrbuf)); 508 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 509 "bad suffix '%s' leading " 510 "%u octets not zeros", 511 netaddrbuf, nbytes); 512 result = ISC_R_FAILURE; 513 } 514 } 515 } 516 517 return (result); 518} 519 520 521/* 522 * Check allow-recursion and allow-recursion-on acls, and also log a 523 * warning if they're inconsistent with the "recursion" option. 524 */ 525static isc_result_t 526check_recursionacls(cfg_aclconfctx_t *actx, const cfg_obj_t *voptions, 527 const char *viewname, const cfg_obj_t *config, 528 isc_log_t *logctx, isc_mem_t *mctx) 529{ 530 const cfg_obj_t *options, *aclobj, *obj = NULL; 531 dns_acl_t *acl = NULL; 532 isc_result_t result = ISC_R_SUCCESS, tresult; 533 isc_boolean_t recursion; 534 const char *forview = " for view "; 535 int i = 0; 536 537 static const char *acls[] = { "allow-recursion", "allow-recursion-on", 538 NULL }; 539 540 if (voptions != NULL) 541 cfg_map_get(voptions, "recursion", &obj); 542 if (obj == NULL && config != NULL) { 543 options = NULL; 544 cfg_map_get(config, "options", &options); 545 if (options != NULL) 546 cfg_map_get(options, "recursion", &obj); 547 } 548 if (obj == NULL) 549 recursion = ISC_TRUE; 550 else 551 recursion = cfg_obj_asboolean(obj); 552 553 if (viewname == NULL) { 554 viewname = ""; 555 forview = ""; 556 } 557 558 for (i = 0; acls[i] != NULL; i++) { 559 aclobj = options = NULL; 560 acl = NULL; 561 562 if (voptions != NULL) 563 cfg_map_get(voptions, acls[i], &aclobj); 564 if (config != NULL && aclobj == NULL) { 565 options = NULL; 566 cfg_map_get(config, "options", &options); 567 if (options != NULL) 568 cfg_map_get(options, acls[i], &aclobj); 569 } 570 if (aclobj == NULL) 571 continue; 572 573 tresult = cfg_acl_fromconfig(aclobj, config, logctx, 574 actx, mctx, 0, &acl); 575 576 if (tresult != ISC_R_SUCCESS) 577 result = tresult; 578 579 if (acl == NULL) 580 continue; 581 582 if (recursion == ISC_FALSE && !dns_acl_isnone(acl)) { 583 cfg_obj_log(aclobj, logctx, ISC_LOG_WARNING, 584 "both \"recursion no;\" and " 585 "\"%s\" active%s%s", 586 acls[i], forview, viewname); 587 } 588 589 if (acl != NULL) 590 dns_acl_detach(&acl); 591 } 592 593 return (result); 594} 595 596static isc_result_t 597check_filteraaaa(cfg_aclconfctx_t *actx, const cfg_obj_t *voptions, 598 const char *viewname, const cfg_obj_t *config, 599 isc_log_t *logctx, isc_mem_t *mctx) 600{ 601 const cfg_obj_t *options, *aclobj, *obj = NULL; 602 dns_acl_t *acl = NULL; 603 isc_result_t result = ISC_R_SUCCESS, tresult; 604 dns_v4_aaaa_t filter; 605 const char *forview = " for view "; 606 607 if (voptions != NULL) 608 cfg_map_get(voptions, "filter-aaaa-on-v4", &obj); 609 if (obj == NULL && config != NULL) { 610 options = NULL; 611 cfg_map_get(config, "options", &options); 612 if (options != NULL) 613 cfg_map_get(options, "filter-aaaa-on-v4", &obj); 614 } 615 616 if (obj == NULL) 617 filter = dns_v4_aaaa_ok; /* default */ 618 else if (cfg_obj_isboolean(obj)) 619 filter = cfg_obj_asboolean(obj) ? dns_v4_aaaa_filter : 620 dns_v4_aaaa_ok; 621 else 622 filter = dns_v4_aaaa_break_dnssec; /* break-dnssec */ 623 624 if (viewname == NULL) { 625 viewname = ""; 626 forview = ""; 627 } 628 629 aclobj = options = NULL; 630 acl = NULL; 631 632 if (voptions != NULL) 633 cfg_map_get(voptions, "filter-aaaa", &aclobj); 634 if (config != NULL && aclobj == NULL) { 635 options = NULL; 636 cfg_map_get(config, "options", &options); 637 if (options != NULL) 638 cfg_map_get(options, "filter-aaaa", &aclobj); 639 } 640 if (aclobj == NULL) 641 return (result); 642 643 tresult = cfg_acl_fromconfig(aclobj, config, logctx, 644 actx, mctx, 0, &acl); 645 646 if (tresult != ISC_R_SUCCESS) { 647 result = tresult; 648 } else if (filter != dns_v4_aaaa_ok && dns_acl_isnone(acl)) { 649 cfg_obj_log(aclobj, logctx, ISC_LOG_WARNING, 650 "both \"filter-aaaa-on-v4 %s;\" and " 651 "\"filter-aaaa\" is 'none;'%s%s", 652 filter == dns_v4_aaaa_break_dnssec ? 653 "break-dnssec" : "yes", forview, viewname); 654 result = ISC_R_FAILURE; 655 } else if (filter == dns_v4_aaaa_ok && !dns_acl_isnone(acl)) { 656 cfg_obj_log(aclobj, logctx, ISC_LOG_WARNING, 657 "both \"filter-aaaa-on-v4 no;\" and " 658 "\"filter-aaaa\" is set%s%s", forview, viewname); 659 result = ISC_R_FAILURE; 660 } 661 662 if (acl != NULL) 663 dns_acl_detach(&acl); 664 665 return (result); 666} 667 668typedef struct { 669 const char *name; 670 unsigned int scale; 671 unsigned int max; 672} intervaltable; 673 674typedef enum { 675 optlevel_config, 676 optlevel_options, 677 optlevel_view, 678 optlevel_zone 679} optlevel_t; 680 681static isc_result_t 682check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx, 683 optlevel_t optlevel) 684{ 685 isc_result_t result = ISC_R_SUCCESS; 686 isc_result_t tresult; 687 unsigned int i; 688 const cfg_obj_t *obj = NULL; 689 const cfg_obj_t *resignobj = NULL; 690 const cfg_listelt_t *element; 691 isc_symtab_t *symtab = NULL; 692 dns_fixedname_t fixed; 693 const char *str; 694 dns_name_t *name; 695 isc_buffer_t b; 696 697 static intervaltable intervals[] = { 698 { "cleaning-interval", 60, 28 * 24 * 60 }, /* 28 days */ 699 { "heartbeat-interval", 60, 28 * 24 * 60 }, /* 28 days */ 700 { "interface-interval", 60, 28 * 24 * 60 }, /* 28 days */ 701 { "max-transfer-idle-in", 60, 28 * 24 * 60 }, /* 28 days */ 702 { "max-transfer-idle-out", 60, 28 * 24 * 60 }, /* 28 days */ 703 { "max-transfer-time-in", 60, 28 * 24 * 60 }, /* 28 days */ 704 { "max-transfer-time-out", 60, 28 * 24 * 60 }, /* 28 days */ 705 { "statistics-interval", 60, 28 * 24 * 60 }, /* 28 days */ 706 }; 707 708 static const char *server_contact[] = { 709 "empty-server", "empty-contact", 710 "dns64-server", "dns64-contact", 711 NULL 712 }; 713 714 /* 715 * Check that fields specified in units of time other than seconds 716 * have reasonable values. 717 */ 718 for (i = 0; i < sizeof(intervals) / sizeof(intervals[0]); i++) { 719 isc_uint32_t val; 720 obj = NULL; 721 (void)cfg_map_get(options, intervals[i].name, &obj); 722 if (obj == NULL) 723 continue; 724 val = cfg_obj_asuint32(obj); 725 if (val > intervals[i].max) { 726 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 727 "%s '%u' is out of range (0..%u)", 728 intervals[i].name, val, 729 intervals[i].max); 730 result = ISC_R_RANGE; 731 } else if (val > (ISC_UINT32_MAX / intervals[i].scale)) { 732 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 733 "%s '%d' is out of range", 734 intervals[i].name, val); 735 result = ISC_R_RANGE; 736 } 737 } 738 739 obj = NULL; 740 cfg_map_get(options, "sig-validity-interval", &obj); 741 if (obj != NULL) { 742 isc_uint32_t validity, resign = 0; 743 744 validity = cfg_obj_asuint32(cfg_tuple_get(obj, "validity")); 745 resignobj = cfg_tuple_get(obj, "re-sign"); 746 if (!cfg_obj_isvoid(resignobj)) 747 resign = cfg_obj_asuint32(resignobj); 748 749 if (validity > 3660 || validity == 0) { /* 10 years */ 750 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 751 "%s '%u' is out of range (1..3660)", 752 "sig-validity-interval", validity); 753 result = ISC_R_RANGE; 754 } 755 756 if (!cfg_obj_isvoid(resignobj)) { 757 if (resign > 3660 || resign == 0) { /* 10 years */ 758 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 759 "%s '%u' is out of range (1..3660)", 760 "sig-validity-interval (re-sign)", 761 validity); 762 result = ISC_R_RANGE; 763 } else if ((validity > 7 && validity < resign) || 764 (validity <= 7 && validity * 24 < resign)) { 765 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 766 "validity interval (%u days) " 767 "less than re-signing interval " 768 "(%u %s)", validity, resign, 769 (validity > 7) ? "days" : "hours"); 770 result = ISC_R_RANGE; 771 } 772 } 773 } 774 775 obj = NULL; 776 (void)cfg_map_get(options, "preferred-glue", &obj); 777 if (obj != NULL) { 778 const char *str; 779 str = cfg_obj_asstring(obj); 780 if (strcasecmp(str, "a") != 0 && 781 strcasecmp(str, "aaaa") != 0 && 782 strcasecmp(str, "none") != 0) 783 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 784 "preferred-glue unexpected value '%s'", 785 str); 786 } 787 788 obj = NULL; 789 (void)cfg_map_get(options, "root-delegation-only", &obj); 790 if (obj != NULL) { 791 if (!cfg_obj_isvoid(obj)) { 792 const cfg_listelt_t *element; 793 const cfg_obj_t *exclude; 794 const char *str; 795 dns_fixedname_t fixed; 796 dns_name_t *name; 797 isc_buffer_t b; 798 799 dns_fixedname_init(&fixed); 800 name = dns_fixedname_name(&fixed); 801 for (element = cfg_list_first(obj); 802 element != NULL; 803 element = cfg_list_next(element)) { 804 exclude = cfg_listelt_value(element); 805 str = cfg_obj_asstring(exclude); 806 isc_buffer_init(&b, str, strlen(str)); 807 isc_buffer_add(&b, strlen(str)); 808 tresult = dns_name_fromtext(name, &b, 809 dns_rootname, 810 0, NULL); 811 if (tresult != ISC_R_SUCCESS) { 812 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 813 "bad domain name '%s'", 814 str); 815 result = tresult; 816 } 817 } 818 } 819 } 820 821 /* 822 * Set supported DNSSEC algorithms. 823 */ 824 obj = NULL; 825 (void)cfg_map_get(options, "disable-algorithms", &obj); 826 if (obj != NULL) { 827 for (element = cfg_list_first(obj); 828 element != NULL; 829 element = cfg_list_next(element)) 830 { 831 obj = cfg_listelt_value(element); 832 tresult = disabled_algorithms(obj, logctx); 833 if (tresult != ISC_R_SUCCESS) 834 result = tresult; 835 } 836 } 837 838 dns_fixedname_init(&fixed); 839 name = dns_fixedname_name(&fixed); 840 841 /* 842 * Check the DLV zone name. 843 */ 844 obj = NULL; 845 (void)cfg_map_get(options, "dnssec-lookaside", &obj); 846 if (obj != NULL) { 847 tresult = isc_symtab_create(mctx, 100, freekey, mctx, 848 ISC_FALSE, &symtab); 849 if (tresult != ISC_R_SUCCESS) 850 result = tresult; 851 for (element = cfg_list_first(obj); 852 element != NULL; 853 element = cfg_list_next(element)) 854 { 855 const char *dlv; 856 const cfg_obj_t *dlvobj, *anchor; 857 858 obj = cfg_listelt_value(element); 859 860 anchor = cfg_tuple_get(obj, "trust-anchor"); 861 dlvobj = cfg_tuple_get(obj, "domain"); 862 dlv = cfg_obj_asstring(dlvobj); 863 864 /* 865 * If domain is "auto" or "no" and trust anchor 866 * is missing, skip remaining tests 867 */ 868 if (cfg_obj_isvoid(anchor)) { 869 if (!strcasecmp(dlv, "no") || 870 !strcasecmp(dlv, "auto")) 871 continue; 872 } 873 874 isc_buffer_init(&b, dlv, strlen(dlv)); 875 isc_buffer_add(&b, strlen(dlv)); 876 tresult = dns_name_fromtext(name, &b, dns_rootname, 877 0, NULL); 878 if (tresult != ISC_R_SUCCESS) { 879 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 880 "bad domain name '%s'", dlv); 881 result = tresult; 882 continue; 883 } 884 if (symtab != NULL) { 885 tresult = nameexist(obj, dlv, 1, symtab, 886 "dnssec-lookaside '%s': " 887 "already exists previous " 888 "definition: %s:%u", 889 logctx, mctx); 890 if (tresult != ISC_R_SUCCESS && 891 result == ISC_R_SUCCESS) 892 result = tresult; 893 } 894 /* 895 * XXXMPA to be removed when multiple lookaside 896 * namespaces are supported. 897 */ 898 if (!dns_name_equal(dns_rootname, name)) { 899 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 900 "dnssec-lookaside '%s': " 901 "non-root not yet supported", dlv); 902 if (result == ISC_R_SUCCESS) 903 result = ISC_R_FAILURE; 904 } 905 906 if (!cfg_obj_isvoid(anchor)) { 907 dlv = cfg_obj_asstring(anchor); 908 isc_buffer_init(&b, dlv, strlen(dlv)); 909 isc_buffer_add(&b, strlen(dlv)); 910 tresult = dns_name_fromtext(name, &b, 911 dns_rootname, 912 DNS_NAME_DOWNCASE, 913 NULL); 914 if (tresult != ISC_R_SUCCESS) { 915 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 916 "bad domain name '%s'", 917 dlv); 918 if (result == ISC_R_SUCCESS) 919 result = tresult; 920 } 921 } else { 922 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 923 "dnssec-lookaside requires " 924 "either 'auto' or 'no', or a " 925 "domain and trust anchor"); 926 if (result == ISC_R_SUCCESS) 927 result = ISC_R_FAILURE; 928 } 929 } 930 931 if (symtab != NULL) 932 isc_symtab_destroy(&symtab); 933 } 934 935 /* 936 * Check auto-dnssec at the view/options level 937 */ 938 obj = NULL; 939 (void)cfg_map_get(options, "auto-dnssec", &obj); 940 if (obj != NULL) { 941 const char *arg = cfg_obj_asstring(obj); 942 if (optlevel != optlevel_zone && strcasecmp(arg, "off") != 0) { 943 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 944 "auto-dnssec may only be activated at the " 945 "zone level"); 946 result = ISC_R_FAILURE; 947 } 948 } 949 950 /* 951 * Check dnssec-must-be-secure. 952 */ 953 obj = NULL; 954 (void)cfg_map_get(options, "dnssec-must-be-secure", &obj); 955 if (obj != NULL) { 956 isc_symtab_t *symtab = NULL; 957 tresult = isc_symtab_create(mctx, 100, freekey, mctx, 958 ISC_FALSE, &symtab); 959 if (tresult != ISC_R_SUCCESS) 960 result = tresult; 961 for (element = cfg_list_first(obj); 962 element != NULL; 963 element = cfg_list_next(element)) 964 { 965 obj = cfg_listelt_value(element); 966 tresult = mustbesecure(obj, symtab, logctx, mctx); 967 if (tresult != ISC_R_SUCCESS) 968 result = tresult; 969 } 970 if (symtab != NULL) 971 isc_symtab_destroy(&symtab); 972 } 973 974 /* 975 * Check server/contacts for syntactic validity. 976 */ 977 for (i= 0; server_contact[i] != NULL; i++) { 978 obj = NULL; 979 (void)cfg_map_get(options, server_contact[i], &obj); 980 if (obj != NULL) { 981 str = cfg_obj_asstring(obj); 982 isc_buffer_init(&b, str, strlen(str)); 983 isc_buffer_add(&b, strlen(str)); 984 tresult = dns_name_fromtext(dns_fixedname_name(&fixed), 985 &b, dns_rootname, 0, NULL); 986 if (tresult != ISC_R_SUCCESS) { 987 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 988 "%s: invalid name '%s'", 989 server_contact[i], str); 990 result = ISC_R_FAILURE; 991 } 992 } 993 } 994 995 /* 996 * Check empty zone configuration. 997 */ 998 obj = NULL; 999 (void)cfg_map_get(options, "disable-empty-zone", &obj); 1000 for (element = cfg_list_first(obj); 1001 element != NULL; 1002 element = cfg_list_next(element)) 1003 { 1004 obj = cfg_listelt_value(element); 1005 str = cfg_obj_asstring(obj); 1006 isc_buffer_init(&b, str, strlen(str)); 1007 isc_buffer_add(&b, strlen(str)); 1008 tresult = dns_name_fromtext(dns_fixedname_name(&fixed), &b, 1009 dns_rootname, 0, NULL); 1010 if (tresult != ISC_R_SUCCESS) { 1011 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 1012 "disable-empty-zone: invalid name '%s'", 1013 str); 1014 result = ISC_R_FAILURE; 1015 } 1016 } 1017 1018 /* 1019 * Check that server-id is not too long. 1020 * 1024 bytes should be big enough. 1021 */ 1022 obj = NULL; 1023 (void)cfg_map_get(options, "server-id", &obj); 1024 if (obj != NULL && cfg_obj_isstring(obj) && 1025 strlen(cfg_obj_asstring(obj)) > 1024U) { 1026 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 1027 "'server-id' too big (>1024 bytes)"); 1028 result = ISC_R_FAILURE; 1029 } 1030 1031 return (result); 1032} 1033 1034static isc_result_t 1035get_masters_def(const cfg_obj_t *cctx, const char *name, const cfg_obj_t **ret) { 1036 isc_result_t result; 1037 const cfg_obj_t *masters = NULL; 1038 const cfg_listelt_t *elt; 1039 1040 result = cfg_map_get(cctx, "masters", &masters); 1041 if (result != ISC_R_SUCCESS) 1042 return (result); 1043 for (elt = cfg_list_first(masters); 1044 elt != NULL; 1045 elt = cfg_list_next(elt)) { 1046 const cfg_obj_t *list; 1047 const char *listname; 1048 1049 list = cfg_listelt_value(elt); 1050 listname = cfg_obj_asstring(cfg_tuple_get(list, "name")); 1051 1052 if (strcasecmp(listname, name) == 0) { 1053 *ret = list; 1054 return (ISC_R_SUCCESS); 1055 } 1056 } 1057 return (ISC_R_NOTFOUND); 1058} 1059 1060static isc_result_t 1061validate_masters(const cfg_obj_t *obj, const cfg_obj_t *config, 1062 isc_uint32_t *countp, isc_log_t *logctx, isc_mem_t *mctx) 1063{ 1064 isc_result_t result = ISC_R_SUCCESS; 1065 isc_result_t tresult; 1066 isc_uint32_t count = 0; 1067 isc_symtab_t *symtab = NULL; 1068 isc_symvalue_t symvalue; 1069 const cfg_listelt_t *element; 1070 const cfg_listelt_t **stack = NULL; 1071 isc_uint32_t stackcount = 0, pushed = 0; 1072 const cfg_obj_t *list; 1073 1074 REQUIRE(countp != NULL); 1075 result = isc_symtab_create(mctx, 100, NULL, NULL, ISC_FALSE, &symtab); 1076 if (result != ISC_R_SUCCESS) { 1077 *countp = count; 1078 return (result); 1079 } 1080 1081 newlist: 1082 list = cfg_tuple_get(obj, "addresses"); 1083 element = cfg_list_first(list); 1084 resume: 1085 for ( ; 1086 element != NULL; 1087 element = cfg_list_next(element)) 1088 { 1089 const char *listname; 1090 const cfg_obj_t *addr; 1091 const cfg_obj_t *key; 1092 1093 addr = cfg_tuple_get(cfg_listelt_value(element), 1094 "masterselement"); 1095 key = cfg_tuple_get(cfg_listelt_value(element), "key"); 1096 1097 if (cfg_obj_issockaddr(addr)) { 1098 count++; 1099 continue; 1100 } 1101 if (!cfg_obj_isvoid(key)) { 1102 cfg_obj_log(key, logctx, ISC_LOG_ERROR, 1103 "unexpected token '%s'", 1104 cfg_obj_asstring(key)); 1105 if (result == ISC_R_SUCCESS) 1106 result = ISC_R_FAILURE; 1107 } 1108 listname = cfg_obj_asstring(addr); 1109 symvalue.as_cpointer = addr; 1110 tresult = isc_symtab_define(symtab, listname, 1, symvalue, 1111 isc_symexists_reject); 1112 if (tresult == ISC_R_EXISTS) 1113 continue; 1114 tresult = get_masters_def(config, listname, &obj); 1115 if (tresult != ISC_R_SUCCESS) { 1116 if (result == ISC_R_SUCCESS) 1117 result = tresult; 1118 cfg_obj_log(addr, logctx, ISC_LOG_ERROR, 1119 "unable to find masters list '%s'", 1120 listname); 1121 continue; 1122 } 1123 /* Grow stack? */ 1124 if (stackcount == pushed) { 1125 void * new; 1126 isc_uint32_t newlen = stackcount + 16; 1127 size_t newsize, oldsize; 1128 1129 newsize = newlen * sizeof(*stack); 1130 oldsize = stackcount * sizeof(*stack); 1131 new = isc_mem_get(mctx, newsize); 1132 if (new == NULL) 1133 goto cleanup; 1134 if (stackcount != 0) { 1135 void *ptr; 1136 1137 DE_CONST(stack, ptr); 1138 memcpy(new, stack, oldsize); 1139 isc_mem_put(mctx, ptr, oldsize); 1140 } 1141 stack = new; 1142 stackcount = newlen; 1143 } 1144 stack[pushed++] = cfg_list_next(element); 1145 goto newlist; 1146 } 1147 if (pushed != 0) { 1148 element = stack[--pushed]; 1149 goto resume; 1150 } 1151 cleanup: 1152 if (stack != NULL) { 1153 void *ptr; 1154 1155 DE_CONST(stack, ptr); 1156 isc_mem_put(mctx, ptr, stackcount * sizeof(*stack)); 1157 } 1158 isc_symtab_destroy(&symtab); 1159 *countp = count; 1160 return (result); 1161} 1162 1163static isc_result_t 1164check_update_policy(const cfg_obj_t *policy, isc_log_t *logctx) { 1165 isc_result_t result = ISC_R_SUCCESS; 1166 isc_result_t tresult; 1167 const cfg_listelt_t *element; 1168 const cfg_listelt_t *element2; 1169 dns_fixedname_t fixed; 1170 const char *str; 1171 isc_buffer_t b; 1172 1173 /* Check for "update-policy local;" */ 1174 if (cfg_obj_isstring(policy) && 1175 strcmp("local", cfg_obj_asstring(policy)) == 0) 1176 return (ISC_R_SUCCESS); 1177 1178 /* Now check the grant policy */ 1179 for (element = cfg_list_first(policy); 1180 element != NULL; 1181 element = cfg_list_next(element)) 1182 { 1183 const cfg_obj_t *stmt = cfg_listelt_value(element); 1184 const cfg_obj_t *identity = cfg_tuple_get(stmt, "identity"); 1185 const cfg_obj_t *matchtype = cfg_tuple_get(stmt, "matchtype"); 1186 const cfg_obj_t *dname = cfg_tuple_get(stmt, "name"); 1187 const cfg_obj_t *typelist = cfg_tuple_get(stmt, "types"); 1188 1189 dns_fixedname_init(&fixed); 1190 str = cfg_obj_asstring(identity); 1191 isc_buffer_init(&b, str, strlen(str)); 1192 isc_buffer_add(&b, strlen(str)); 1193 tresult = dns_name_fromtext(dns_fixedname_name(&fixed), &b, 1194 dns_rootname, 0, NULL); 1195 if (tresult != ISC_R_SUCCESS) { 1196 cfg_obj_log(identity, logctx, ISC_LOG_ERROR, 1197 "'%s' is not a valid name", str); 1198 result = tresult; 1199 } 1200 1201 if (tresult == ISC_R_SUCCESS && 1202 strcasecmp(cfg_obj_asstring(matchtype), "zonesub") != 0) { 1203 dns_fixedname_init(&fixed); 1204 str = cfg_obj_asstring(dname); 1205 isc_buffer_init(&b, str, strlen(str)); 1206 isc_buffer_add(&b, strlen(str)); 1207 tresult = dns_name_fromtext(dns_fixedname_name(&fixed), 1208 &b, dns_rootname, 0, NULL); 1209 if (tresult != ISC_R_SUCCESS) { 1210 cfg_obj_log(dname, logctx, ISC_LOG_ERROR, 1211 "'%s' is not a valid name", str); 1212 result = tresult; 1213 } 1214 } 1215 1216 if (tresult == ISC_R_SUCCESS && 1217 strcasecmp(cfg_obj_asstring(matchtype), "wildcard") == 0 && 1218 !dns_name_iswildcard(dns_fixedname_name(&fixed))) { 1219 cfg_obj_log(identity, logctx, ISC_LOG_ERROR, 1220 "'%s' is not a wildcard", str); 1221 result = ISC_R_FAILURE; 1222 } 1223 1224 for (element2 = cfg_list_first(typelist); 1225 element2 != NULL; 1226 element2 = cfg_list_next(element2)) 1227 { 1228 const cfg_obj_t *typeobj; 1229 isc_textregion_t r; 1230 dns_rdatatype_t type; 1231 1232 typeobj = cfg_listelt_value(element2); 1233 DE_CONST(cfg_obj_asstring(typeobj), r.base); 1234 r.length = strlen(r.base); 1235 1236 tresult = dns_rdatatype_fromtext(&type, &r); 1237 if (tresult != ISC_R_SUCCESS) { 1238 cfg_obj_log(typeobj, logctx, ISC_LOG_ERROR, 1239 "'%s' is not a valid type", r.base); 1240 result = tresult; 1241 } 1242 } 1243 } 1244 return (result); 1245} 1246 1247#define MASTERZONE 1 1248#define SLAVEZONE 2 1249#define STUBZONE 4 1250#define HINTZONE 8 1251#define FORWARDZONE 16 1252#define DELEGATIONZONE 32 1253#define STATICSTUBZONE 64 1254#define CHECKACL 128 1255 1256typedef struct { 1257 const char *name; 1258 int allowed; 1259} optionstable; 1260 1261static isc_result_t 1262check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, 1263 const cfg_obj_t *config, isc_symtab_t *symtab, 1264 dns_rdataclass_t defclass, cfg_aclconfctx_t *actx, 1265 isc_log_t *logctx, isc_mem_t *mctx) 1266{ 1267 const char *znamestr; 1268 const char *typestr; 1269 unsigned int ztype; 1270 const cfg_obj_t *zoptions; 1271 const cfg_obj_t *obj = NULL; 1272 isc_result_t result = ISC_R_SUCCESS; 1273 isc_result_t tresult; 1274 unsigned int i; 1275 dns_rdataclass_t zclass; 1276 dns_fixedname_t fixedname; 1277 dns_name_t *zname = NULL; 1278 isc_buffer_t b; 1279 isc_boolean_t root = ISC_FALSE; 1280 const cfg_listelt_t *element; 1281 1282 static optionstable options[] = { 1283 { "allow-query", MASTERZONE | SLAVEZONE | STUBZONE | CHECKACL | 1284 STATICSTUBZONE }, 1285 { "allow-notify", SLAVEZONE | CHECKACL }, 1286 { "allow-transfer", MASTERZONE | SLAVEZONE | CHECKACL }, 1287 { "notify", MASTERZONE | SLAVEZONE }, 1288 { "also-notify", MASTERZONE | SLAVEZONE }, 1289 { "dialup", MASTERZONE | SLAVEZONE | STUBZONE }, 1290 { "delegation-only", HINTZONE | STUBZONE | DELEGATIONZONE }, 1291 { "forward", MASTERZONE | SLAVEZONE | STUBZONE | FORWARDZONE }, 1292 { "forwarders", MASTERZONE | SLAVEZONE | STUBZONE | FORWARDZONE }, 1293 { "maintain-ixfr-base", MASTERZONE | SLAVEZONE }, 1294 { "max-ixfr-log-size", MASTERZONE | SLAVEZONE }, 1295 { "notify-source", MASTERZONE | SLAVEZONE }, 1296 { "notify-source-v6", MASTERZONE | SLAVEZONE }, 1297 { "transfer-source", SLAVEZONE | STUBZONE }, 1298 { "transfer-source-v6", SLAVEZONE | STUBZONE }, 1299 { "max-transfer-time-in", SLAVEZONE | STUBZONE }, 1300 { "max-transfer-time-out", MASTERZONE | SLAVEZONE }, 1301 { "max-transfer-idle-in", SLAVEZONE | STUBZONE }, 1302 { "max-transfer-idle-out", MASTERZONE | SLAVEZONE }, 1303 { "max-retry-time", SLAVEZONE | STUBZONE }, 1304 { "min-retry-time", SLAVEZONE | STUBZONE }, 1305 { "max-refresh-time", SLAVEZONE | STUBZONE }, 1306 { "min-refresh-time", SLAVEZONE | STUBZONE }, 1307 { "dnssec-secure-to-insecure", MASTERZONE }, 1308 { "sig-validity-interval", MASTERZONE }, 1309 { "sig-re-signing-interval", MASTERZONE }, 1310 { "sig-signing-nodes", MASTERZONE }, 1311 { "sig-signing-type", MASTERZONE }, 1312 { "sig-signing-signatures", MASTERZONE }, 1313 { "zone-statistics", MASTERZONE | SLAVEZONE | STUBZONE | 1314 STATICSTUBZONE}, 1315 { "allow-update", MASTERZONE | CHECKACL }, 1316 { "allow-update-forwarding", SLAVEZONE | CHECKACL }, 1317 { "file", MASTERZONE | SLAVEZONE | STUBZONE | HINTZONE }, 1318 { "journal", MASTERZONE | SLAVEZONE }, 1319 { "ixfr-base", MASTERZONE | SLAVEZONE }, 1320 { "ixfr-tmp-file", MASTERZONE | SLAVEZONE }, 1321 { "masters", SLAVEZONE | STUBZONE }, 1322 { "pubkey", MASTERZONE | SLAVEZONE | STUBZONE }, 1323 { "update-policy", MASTERZONE }, 1324 { "database", MASTERZONE | SLAVEZONE | STUBZONE }, 1325 { "key-directory", MASTERZONE }, 1326 { "check-wildcard", MASTERZONE }, 1327 { "check-mx", MASTERZONE }, 1328 { "check-dup-records", MASTERZONE }, 1329 { "integrity-check", MASTERZONE }, 1330 { "check-mx-cname", MASTERZONE }, 1331 { "check-srv-cname", MASTERZONE }, 1332 { "masterfile-format", MASTERZONE | SLAVEZONE | STUBZONE | HINTZONE }, 1333 { "update-check-ksk", MASTERZONE }, 1334 { "dnssec-dnskey-kskonly", MASTERZONE }, 1335 { "auto-dnssec", MASTERZONE }, 1336 { "try-tcp-refresh", SLAVEZONE }, 1337 { "server-addresses", STATICSTUBZONE }, 1338 { "server-names", STATICSTUBZONE }, 1339 }; 1340 1341 static optionstable dialups[] = { 1342 { "notify", MASTERZONE | SLAVEZONE }, 1343 { "notify-passive", SLAVEZONE }, 1344 { "refresh", SLAVEZONE | STUBZONE }, 1345 { "passive", SLAVEZONE | STUBZONE }, 1346 }; 1347 1348 znamestr = cfg_obj_asstring(cfg_tuple_get(zconfig, "name")); 1349 1350 zoptions = cfg_tuple_get(zconfig, "options"); 1351 1352 obj = NULL; 1353 (void)cfg_map_get(zoptions, "type", &obj); 1354 if (obj == NULL) { 1355 cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR, 1356 "zone '%s': type not present", znamestr); 1357 return (ISC_R_FAILURE); 1358 } 1359 1360 typestr = cfg_obj_asstring(obj); 1361 if (strcasecmp(typestr, "master") == 0) 1362 ztype = MASTERZONE; 1363 else if (strcasecmp(typestr, "slave") == 0) 1364 ztype = SLAVEZONE; 1365 else if (strcasecmp(typestr, "stub") == 0) 1366 ztype = STUBZONE; 1367 else if (strcasecmp(typestr, "static-stub") == 0) 1368 ztype = STATICSTUBZONE; 1369 else if (strcasecmp(typestr, "forward") == 0) 1370 ztype = FORWARDZONE; 1371 else if (strcasecmp(typestr, "hint") == 0) 1372 ztype = HINTZONE; 1373 else if (strcasecmp(typestr, "delegation-only") == 0) 1374 ztype = DELEGATIONZONE; 1375 else { 1376 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 1377 "zone '%s': invalid type %s", 1378 znamestr, typestr); 1379 return (ISC_R_FAILURE); 1380 } 1381 1382 obj = cfg_tuple_get(zconfig, "class"); 1383 if (cfg_obj_isstring(obj)) { 1384 isc_textregion_t r; 1385 1386 DE_CONST(cfg_obj_asstring(obj), r.base); 1387 r.length = strlen(r.base); 1388 result = dns_rdataclass_fromtext(&zclass, &r); 1389 if (result != ISC_R_SUCCESS) { 1390 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 1391 "zone '%s': invalid class %s", 1392 znamestr, r.base); 1393 return (ISC_R_FAILURE); 1394 } 1395 if (zclass != defclass) { 1396 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 1397 "zone '%s': class '%s' does not " 1398 "match view/default class", 1399 znamestr, r.base); 1400 return (ISC_R_FAILURE); 1401 } 1402 } 1403 1404 /* 1405 * Look for an already existing zone. 1406 * We need to make this canonical as isc_symtab_define() 1407 * deals with strings. 1408 */ 1409 dns_fixedname_init(&fixedname); 1410 isc_buffer_init(&b, znamestr, strlen(znamestr)); 1411 isc_buffer_add(&b, strlen(znamestr)); 1412 tresult = dns_name_fromtext(dns_fixedname_name(&fixedname), &b, 1413 dns_rootname, DNS_NAME_DOWNCASE, NULL); 1414 if (tresult != ISC_R_SUCCESS) { 1415 cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR, 1416 "zone '%s': is not a valid name", znamestr); 1417 result = ISC_R_FAILURE; 1418 } else { 1419 char namebuf[DNS_NAME_FORMATSIZE]; 1420 1421 zname = dns_fixedname_name(&fixedname); 1422 dns_name_format(zname, namebuf, sizeof(namebuf)); 1423 tresult = nameexist(zconfig, namebuf, ztype == HINTZONE ? 1 : 2, 1424 symtab, "zone '%s': already exists " 1425 "previous definition: %s:%u", logctx, mctx); 1426 if (tresult != ISC_R_SUCCESS) 1427 result = tresult; 1428 if (dns_name_equal(zname, dns_rootname)) 1429 root = ISC_TRUE; 1430 } 1431 1432 /* 1433 * Look for inappropriate options for the given zone type. 1434 * Check that ACLs expand correctly. 1435 */ 1436 for (i = 0; i < sizeof(options) / sizeof(options[0]); i++) { 1437 obj = NULL; 1438 if ((options[i].allowed & ztype) == 0 && 1439 cfg_map_get(zoptions, options[i].name, &obj) == 1440 ISC_R_SUCCESS) 1441 { 1442 if (strcmp(options[i].name, "allow-update") != 0 || 1443 ztype != SLAVEZONE) { 1444 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 1445 "option '%s' is not allowed " 1446 "in '%s' zone '%s'", 1447 options[i].name, typestr, 1448 znamestr); 1449 result = ISC_R_FAILURE; 1450 } else 1451 cfg_obj_log(obj, logctx, ISC_LOG_WARNING, 1452 "option '%s' is not allowed " 1453 "in '%s' zone '%s'", 1454 options[i].name, typestr, 1455 znamestr); 1456 } 1457 obj = NULL; 1458 if ((options[i].allowed & ztype) != 0 && 1459 (options[i].allowed & CHECKACL) != 0) { 1460 1461 tresult = checkacl(options[i].name, actx, zconfig, 1462 voptions, config, logctx, mctx); 1463 if (tresult != ISC_R_SUCCESS) 1464 result = tresult; 1465 } 1466 1467 } 1468 1469 /* 1470 * Slave & stub zones must have a "masters" field. 1471 */ 1472 if (ztype == SLAVEZONE || ztype == STUBZONE) { 1473 obj = NULL; 1474 if (cfg_map_get(zoptions, "masters", &obj) != ISC_R_SUCCESS) { 1475 cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR, 1476 "zone '%s': missing 'masters' entry", 1477 znamestr); 1478 result = ISC_R_FAILURE; 1479 } else { 1480 isc_uint32_t count; 1481 tresult = validate_masters(obj, config, &count, 1482 logctx, mctx); 1483 if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS) 1484 result = tresult; 1485 if (tresult == ISC_R_SUCCESS && count == 0) { 1486 cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR, 1487 "zone '%s': empty 'masters' entry", 1488 znamestr); 1489 result = ISC_R_FAILURE; 1490 } 1491 } 1492 } 1493 1494 /* 1495 * Master zones can't have both "allow-update" and "update-policy". 1496 */ 1497 if (ztype == MASTERZONE) { 1498 isc_result_t res1, res2, res3; 1499 const char *arg; 1500 isc_boolean_t ddns; 1501 1502 obj = NULL; 1503 res1 = cfg_map_get(zoptions, "allow-update", &obj); 1504 obj = NULL; 1505 res2 = cfg_map_get(zoptions, "update-policy", &obj); 1506 if (res1 == ISC_R_SUCCESS && res2 == ISC_R_SUCCESS) { 1507 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 1508 "zone '%s': 'allow-update' is ignored " 1509 "when 'update-policy' is present", 1510 znamestr); 1511 result = ISC_R_FAILURE; 1512 } else if (res2 == ISC_R_SUCCESS && 1513 check_update_policy(obj, logctx) != ISC_R_SUCCESS) 1514 result = ISC_R_FAILURE; 1515 ddns = ISC_TF(res1 == ISC_R_SUCCESS || res2 == ISC_R_SUCCESS); 1516 1517 obj = NULL; 1518 arg = "off"; 1519 res3 = cfg_map_get(zoptions, "auto-dnssec", &obj); 1520 if (res3 == ISC_R_SUCCESS) 1521 arg = cfg_obj_asstring(obj); 1522 if (strcasecmp(arg, "off") != 0 && !ddns) { 1523 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 1524 "'auto-dnssec %s;' requires " 1525 "dynamic DNS to be configured in the zone", 1526 arg); 1527 result = ISC_R_FAILURE; 1528 } 1529 if (strcasecmp(arg, "create") == 0) { 1530 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 1531 "'auto-dnssec create;' is not " 1532 "yet implemented"); 1533 result = ISC_R_FAILURE; 1534 } 1535 1536 obj = NULL; 1537 res1 = cfg_map_get(zoptions, "sig-signing-type", &obj); 1538 if (res1 == ISC_R_SUCCESS) { 1539 isc_uint32_t type = cfg_obj_asuint32(obj); 1540 if (type < 0xff00U || type > 0xffffU) 1541 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 1542 "sig-signing-type: %u out of " 1543 "range [%u..%u]", type, 1544 0xff00U, 0xffffU); 1545 result = ISC_R_FAILURE; 1546 } 1547 } 1548 1549 /* 1550 * Check the excessively complicated "dialup" option. 1551 */ 1552 if (ztype == MASTERZONE || ztype == SLAVEZONE || ztype == STUBZONE) { 1553 const cfg_obj_t *dialup = NULL; 1554 (void)cfg_map_get(zoptions, "dialup", &dialup); 1555 if (dialup != NULL && cfg_obj_isstring(dialup)) { 1556 const char *str = cfg_obj_asstring(dialup); 1557 for (i = 0; 1558 i < sizeof(dialups) / sizeof(dialups[0]); 1559 i++) 1560 { 1561 if (strcasecmp(dialups[i].name, str) != 0) 1562 continue; 1563 if ((dialups[i].allowed & ztype) == 0) { 1564 cfg_obj_log(obj, logctx, 1565 ISC_LOG_ERROR, 1566 "dialup type '%s' is not " 1567 "allowed in '%s' " 1568 "zone '%s'", 1569 str, typestr, znamestr); 1570 result = ISC_R_FAILURE; 1571 } 1572 break; 1573 } 1574 if (i == sizeof(dialups) / sizeof(dialups[0])) { 1575 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 1576 "invalid dialup type '%s' in zone " 1577 "'%s'", str, znamestr); 1578 result = ISC_R_FAILURE; 1579 } 1580 } 1581 } 1582 1583 /* 1584 * Check that forwarding is reasonable. 1585 */ 1586 obj = NULL; 1587 if (root) { 1588 if (voptions != NULL) 1589 (void)cfg_map_get(voptions, "forwarders", &obj); 1590 if (obj == NULL) { 1591 const cfg_obj_t *options = NULL; 1592 (void)cfg_map_get(config, "options", &options); 1593 if (options != NULL) 1594 (void)cfg_map_get(options, "forwarders", &obj); 1595 } 1596 } 1597 if (check_forward(zoptions, obj, logctx) != ISC_R_SUCCESS) 1598 result = ISC_R_FAILURE; 1599 1600 /* 1601 * Check validity of static stub server addresses. 1602 */ 1603 obj = NULL; 1604 (void)cfg_map_get(zoptions, "server-addresses", &obj); 1605 if (ztype == STATICSTUBZONE && obj != NULL) { 1606 for (element = cfg_list_first(obj); 1607 element != NULL; 1608 element = cfg_list_next(element)) 1609 { 1610 isc_sockaddr_t sa; 1611 isc_netaddr_t na; 1612 obj = cfg_listelt_value(element); 1613 sa = *cfg_obj_assockaddr(obj); 1614 1615 if (isc_sockaddr_getport(&sa) != 0) { 1616 result = ISC_R_FAILURE; 1617 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 1618 "port is not configurable for " 1619 "static stub server-addresses"); 1620 } 1621 1622 isc_netaddr_fromsockaddr(&na, &sa); 1623 if (isc_netaddr_getzone(&na) != 0) { 1624 result = ISC_R_FAILURE; 1625 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 1626 "scoped address is not allowed " 1627 "for static stub " 1628 "server-addresses"); 1629 } 1630 } 1631 } 1632 1633 /* 1634 * Check validity of static stub server names. 1635 */ 1636 obj = NULL; 1637 (void)cfg_map_get(zoptions, "server-names", &obj); 1638 if (zname != NULL && ztype == STATICSTUBZONE && obj != NULL) { 1639 for (element = cfg_list_first(obj); 1640 element != NULL; 1641 element = cfg_list_next(element)) 1642 { 1643 const char *snamestr; 1644 dns_fixedname_t fixed_sname; 1645 isc_buffer_t b2; 1646 dns_name_t *sname; 1647 1648 obj = cfg_listelt_value(element); 1649 snamestr = cfg_obj_asstring(obj); 1650 1651 dns_fixedname_init(&fixed_sname); 1652 isc_buffer_init(&b2, snamestr, strlen(snamestr)); 1653 isc_buffer_add(&b2, strlen(snamestr)); 1654 sname = dns_fixedname_name(&fixed_sname); 1655 tresult = dns_name_fromtext(sname, &b2, dns_rootname, 1656 0, NULL); 1657 if (tresult != ISC_R_SUCCESS) { 1658 cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR, 1659 "server-name '%s' is not a valid " 1660 "name", snamestr); 1661 result = ISC_R_FAILURE; 1662 } else if (dns_name_issubdomain(sname, zname)) { 1663 cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR, 1664 "server-name '%s' must not be a " 1665 "subdomain of zone name '%s'", 1666 snamestr, znamestr); 1667 result = ISC_R_FAILURE; 1668 } 1669 } 1670 } 1671 1672 /* 1673 * Check various options. 1674 */ 1675 tresult = check_options(zoptions, logctx, mctx, optlevel_zone); 1676 if (tresult != ISC_R_SUCCESS) 1677 result = tresult; 1678 1679 /* 1680 * If the zone type is rbt/rbt64 then master/hint zones 1681 * require file clauses. 1682 */ 1683 obj = NULL; 1684 tresult = cfg_map_get(zoptions, "database", &obj); 1685 if (tresult == ISC_R_NOTFOUND || 1686 (tresult == ISC_R_SUCCESS && 1687 (strcmp("rbt", cfg_obj_asstring(obj)) == 0 || 1688 strcmp("rbt64", cfg_obj_asstring(obj)) == 0))) { 1689 obj = NULL; 1690 tresult = cfg_map_get(zoptions, "file", &obj); 1691 if (tresult != ISC_R_SUCCESS && 1692 (ztype == MASTERZONE || ztype == HINTZONE)) { 1693 cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR, 1694 "zone '%s': missing 'file' entry", 1695 znamestr); 1696 result = tresult; 1697 } 1698 } 1699 1700 return (result); 1701} 1702 1703 1704typedef struct keyalgorithms { 1705 const char *name; 1706 isc_uint16_t size; 1707} algorithmtable; 1708 1709isc_result_t 1710bind9_check_key(const cfg_obj_t *key, isc_log_t *logctx) { 1711 const cfg_obj_t *algobj = NULL; 1712 const cfg_obj_t *secretobj = NULL; 1713 const char *keyname = cfg_obj_asstring(cfg_map_getname(key)); 1714 const char *algorithm; 1715 int i; 1716 size_t len = 0; 1717 isc_result_t result; 1718 isc_buffer_t buf; 1719 unsigned char secretbuf[1024]; 1720 static const algorithmtable algorithms[] = { 1721 { "hmac-md5", 128 }, 1722 { "hmac-md5.sig-alg.reg.int", 0 }, 1723 { "hmac-md5.sig-alg.reg.int.", 0 }, 1724 { "hmac-sha1", 160 }, 1725 { "hmac-sha224", 224 }, 1726 { "hmac-sha256", 256 }, 1727 { "hmac-sha384", 384 }, 1728 { "hmac-sha512", 512 }, 1729 { NULL, 0 } 1730 }; 1731 1732 (void)cfg_map_get(key, "algorithm", &algobj); 1733 (void)cfg_map_get(key, "secret", &secretobj); 1734 if (secretobj == NULL || algobj == NULL) { 1735 cfg_obj_log(key, logctx, ISC_LOG_ERROR, 1736 "key '%s' must have both 'secret' and " 1737 "'algorithm' defined", 1738 keyname); 1739 return (ISC_R_FAILURE); 1740 } 1741 1742 isc_buffer_init(&buf, secretbuf, sizeof(secretbuf)); 1743 result = isc_base64_decodestring(cfg_obj_asstring(secretobj), &buf); 1744 if (result != ISC_R_SUCCESS) { 1745 cfg_obj_log(secretobj, logctx, ISC_LOG_ERROR, 1746 "bad secret '%s'", isc_result_totext(result)); 1747 return (result); 1748 } 1749 1750 algorithm = cfg_obj_asstring(algobj); 1751 for (i = 0; algorithms[i].name != NULL; i++) { 1752 len = strlen(algorithms[i].name); 1753 if (strncasecmp(algorithms[i].name, algorithm, len) == 0 && 1754 (algorithm[len] == '\0' || 1755 (algorithms[i].size != 0 && algorithm[len] == '-'))) 1756 break; 1757 } 1758 if (algorithms[i].name == NULL) { 1759 cfg_obj_log(algobj, logctx, ISC_LOG_ERROR, 1760 "unknown algorithm '%s'", algorithm); 1761 return (ISC_R_NOTFOUND); 1762 } 1763 if (algorithm[len] == '-') { 1764 isc_uint16_t digestbits; 1765 isc_result_t result; 1766 result = isc_parse_uint16(&digestbits, algorithm + len + 1, 10); 1767 if (result == ISC_R_SUCCESS || result == ISC_R_RANGE) { 1768 if (result == ISC_R_RANGE || 1769 digestbits > algorithms[i].size) { 1770 cfg_obj_log(algobj, logctx, ISC_LOG_ERROR, 1771 "key '%s' digest-bits too large " 1772 "[%u..%u]", keyname, 1773 algorithms[i].size / 2, 1774 algorithms[i].size); 1775 return (ISC_R_RANGE); 1776 } 1777 if ((digestbits % 8) != 0) { 1778 cfg_obj_log(algobj, logctx, ISC_LOG_ERROR, 1779 "key '%s' digest-bits not multiple" 1780 " of 8", keyname); 1781 return (ISC_R_RANGE); 1782 } 1783 /* 1784 * Recommended minima for hmac algorithms. 1785 */ 1786 if ((digestbits < (algorithms[i].size / 2U) || 1787 (digestbits < 80U))) 1788 cfg_obj_log(algobj, logctx, ISC_LOG_WARNING, 1789 "key '%s' digest-bits too small " 1790 "[<%u]", keyname, 1791 algorithms[i].size/2); 1792 } else { 1793 cfg_obj_log(algobj, logctx, ISC_LOG_ERROR, 1794 "key '%s': unable to parse digest-bits", 1795 keyname); 1796 return (result); 1797 } 1798 } 1799 return (ISC_R_SUCCESS); 1800} 1801 1802/* 1803 * Check key list for duplicates key names and that the key names 1804 * are valid domain names as these keys are used for TSIG. 1805 * 1806 * Check the key contents for validity. 1807 */ 1808static isc_result_t 1809check_keylist(const cfg_obj_t *keys, isc_symtab_t *symtab, 1810 isc_mem_t *mctx, isc_log_t *logctx) 1811{ 1812 char namebuf[DNS_NAME_FORMATSIZE]; 1813 dns_fixedname_t fname; 1814 dns_name_t *name; 1815 isc_result_t result = ISC_R_SUCCESS; 1816 isc_result_t tresult; 1817 const cfg_listelt_t *element; 1818 1819 dns_fixedname_init(&fname); 1820 name = dns_fixedname_name(&fname); 1821 for (element = cfg_list_first(keys); 1822 element != NULL; 1823 element = cfg_list_next(element)) 1824 { 1825 const cfg_obj_t *key = cfg_listelt_value(element); 1826 const char *keyid = cfg_obj_asstring(cfg_map_getname(key)); 1827 isc_symvalue_t symvalue; 1828 isc_buffer_t b; 1829 char *keyname; 1830 1831 isc_buffer_init(&b, keyid, strlen(keyid)); 1832 isc_buffer_add(&b, strlen(keyid)); 1833 tresult = dns_name_fromtext(name, &b, dns_rootname, 1834 0, NULL); 1835 if (tresult != ISC_R_SUCCESS) { 1836 cfg_obj_log(key, logctx, ISC_LOG_ERROR, 1837 "key '%s': bad key name", keyid); 1838 result = tresult; 1839 continue; 1840 } 1841 tresult = bind9_check_key(key, logctx); 1842 if (tresult != ISC_R_SUCCESS) 1843 return (tresult); 1844 1845 dns_name_format(name, namebuf, sizeof(namebuf)); 1846 keyname = isc_mem_strdup(mctx, namebuf); 1847 if (keyname == NULL) 1848 return (ISC_R_NOMEMORY); 1849 symvalue.as_cpointer = key; 1850 tresult = isc_symtab_define(symtab, keyname, 1, symvalue, 1851 isc_symexists_reject); 1852 if (tresult == ISC_R_EXISTS) { 1853 const char *file; 1854 unsigned int line; 1855 1856 RUNTIME_CHECK(isc_symtab_lookup(symtab, keyname, 1857 1, &symvalue) == ISC_R_SUCCESS); 1858 file = cfg_obj_file(symvalue.as_cpointer); 1859 line = cfg_obj_line(symvalue.as_cpointer); 1860 1861 if (file == NULL) 1862 file = "<unknown file>"; 1863 cfg_obj_log(key, logctx, ISC_LOG_ERROR, 1864 "key '%s': already exists " 1865 "previous definition: %s:%u", 1866 keyid, file, line); 1867 isc_mem_free(mctx, keyname); 1868 result = tresult; 1869 } else if (tresult != ISC_R_SUCCESS) { 1870 isc_mem_free(mctx, keyname); 1871 return (tresult); 1872 } 1873 } 1874 return (result); 1875} 1876 1877static struct { 1878 const char *v4; 1879 const char *v6; 1880} sources[] = { 1881 { "transfer-source", "transfer-source-v6" }, 1882 { "notify-source", "notify-source-v6" }, 1883 { "query-source", "query-source-v6" }, 1884 { NULL, NULL } 1885}; 1886 1887/* 1888 * RNDC keys are not normalised unlike TSIG keys. 1889 * 1890 * "foo." is different to "foo". 1891 */ 1892static isc_boolean_t 1893rndckey_exists(const cfg_obj_t *keylist, const char *keyname) { 1894 const cfg_listelt_t *element; 1895 const cfg_obj_t *obj; 1896 const char *str; 1897 1898 if (keylist == NULL) 1899 return (ISC_FALSE); 1900 1901 for (element = cfg_list_first(keylist); 1902 element != NULL; 1903 element = cfg_list_next(element)) 1904 { 1905 obj = cfg_listelt_value(element); 1906 str = cfg_obj_asstring(cfg_map_getname(obj)); 1907 if (!strcasecmp(str, keyname)) 1908 return (ISC_TRUE); 1909 } 1910 return (ISC_FALSE); 1911} 1912 1913static isc_result_t 1914check_servers(const cfg_obj_t *config, const cfg_obj_t *voptions, 1915 isc_symtab_t *symtab, isc_log_t *logctx) 1916{ 1917 dns_fixedname_t fname; 1918 isc_result_t result = ISC_R_SUCCESS; 1919 isc_result_t tresult; 1920 const cfg_listelt_t *e1, *e2; 1921 const cfg_obj_t *v1, *v2, *keys; 1922 const cfg_obj_t *servers; 1923 isc_netaddr_t n1, n2; 1924 unsigned int p1, p2; 1925 const cfg_obj_t *obj; 1926 char buf[ISC_NETADDR_FORMATSIZE]; 1927 char namebuf[DNS_NAME_FORMATSIZE]; 1928 const char *xfr; 1929 const char *keyval; 1930 isc_buffer_t b; 1931 int source; 1932 dns_name_t *keyname; 1933 1934 servers = NULL; 1935 if (voptions != NULL) 1936 (void)cfg_map_get(voptions, "server", &servers); 1937 if (servers == NULL) 1938 (void)cfg_map_get(config, "server", &servers); 1939 if (servers == NULL) 1940 return (ISC_R_SUCCESS); 1941 1942 for (e1 = cfg_list_first(servers); e1 != NULL; e1 = cfg_list_next(e1)) { 1943 v1 = cfg_listelt_value(e1); 1944 cfg_obj_asnetprefix(cfg_map_getname(v1), &n1, &p1); 1945 /* 1946 * Check that unused bits are zero. 1947 */ 1948 tresult = isc_netaddr_prefixok(&n1, p1); 1949 if (tresult != ISC_R_SUCCESS) { 1950 INSIST(tresult == ISC_R_FAILURE); 1951 isc_netaddr_format(&n1, buf, sizeof(buf)); 1952 cfg_obj_log(v1, logctx, ISC_LOG_ERROR, 1953 "server '%s/%u': invalid prefix " 1954 "(extra bits specified)", buf, p1); 1955 result = tresult; 1956 } 1957 source = 0; 1958 do { 1959 obj = NULL; 1960 if (n1.family == AF_INET) 1961 xfr = sources[source].v6; 1962 else 1963 xfr = sources[source].v4; 1964 (void)cfg_map_get(v1, xfr, &obj); 1965 if (obj != NULL) { 1966 isc_netaddr_format(&n1, buf, sizeof(buf)); 1967 cfg_obj_log(v1, logctx, ISC_LOG_ERROR, 1968 "server '%s/%u': %s not legal", 1969 buf, p1, xfr); 1970 result = ISC_R_FAILURE; 1971 } 1972 } while (sources[++source].v4 != NULL); 1973 e2 = e1; 1974 while ((e2 = cfg_list_next(e2)) != NULL) { 1975 v2 = cfg_listelt_value(e2); 1976 cfg_obj_asnetprefix(cfg_map_getname(v2), &n2, &p2); 1977 if (p1 == p2 && isc_netaddr_equal(&n1, &n2)) { 1978 const char *file = cfg_obj_file(v1); 1979 unsigned int line = cfg_obj_line(v1); 1980 1981 if (file == NULL) 1982 file = "<unknown file>"; 1983 1984 isc_netaddr_format(&n2, buf, sizeof(buf)); 1985 cfg_obj_log(v2, logctx, ISC_LOG_ERROR, 1986 "server '%s/%u': already exists " 1987 "previous definition: %s:%u", 1988 buf, p2, file, line); 1989 result = ISC_R_FAILURE; 1990 } 1991 } 1992 keys = NULL; 1993 cfg_map_get(v1, "keys", &keys); 1994 if (keys != NULL) { 1995 /* 1996 * Normalize key name. 1997 */ 1998 keyval = cfg_obj_asstring(keys); 1999 dns_fixedname_init(&fname); 2000 isc_buffer_init(&b, keyval, strlen(keyval)); 2001 isc_buffer_add(&b, strlen(keyval)); 2002 keyname = dns_fixedname_name(&fname); 2003 tresult = dns_name_fromtext(keyname, &b, dns_rootname, 2004 0, NULL); 2005 if (tresult != ISC_R_SUCCESS) { 2006 cfg_obj_log(keys, logctx, ISC_LOG_ERROR, 2007 "bad key name '%s'", keyval); 2008 result = ISC_R_FAILURE; 2009 continue; 2010 } 2011 dns_name_format(keyname, namebuf, sizeof(namebuf)); 2012 tresult = isc_symtab_lookup(symtab, namebuf, 1, NULL); 2013 if (tresult != ISC_R_SUCCESS) { 2014 cfg_obj_log(keys, logctx, ISC_LOG_ERROR, 2015 "unknown key '%s'", keyval); 2016 result = ISC_R_FAILURE; 2017 } 2018 } 2019 } 2020 return (result); 2021} 2022 2023static isc_result_t 2024check_trusted_key(const cfg_obj_t *key, isc_boolean_t managed, 2025 isc_log_t *logctx) 2026{ 2027 const char *keystr, *keynamestr; 2028 dns_fixedname_t fkeyname; 2029 dns_name_t *keyname; 2030 isc_buffer_t b; 2031 isc_region_t r; 2032 isc_result_t result = ISC_R_SUCCESS; 2033 isc_result_t tresult; 2034 isc_uint32_t flags, proto, alg; 2035 unsigned char keydata[4096]; 2036 2037 flags = cfg_obj_asuint32(cfg_tuple_get(key, "flags")); 2038 proto = cfg_obj_asuint32(cfg_tuple_get(key, "protocol")); 2039 alg = cfg_obj_asuint32(cfg_tuple_get(key, "algorithm")); 2040 2041 dns_fixedname_init(&fkeyname); 2042 keyname = dns_fixedname_name(&fkeyname); 2043 keynamestr = cfg_obj_asstring(cfg_tuple_get(key, "name")); 2044 2045 isc_buffer_init(&b, keynamestr, strlen(keynamestr)); 2046 isc_buffer_add(&b, strlen(keynamestr)); 2047 result = dns_name_fromtext(keyname, &b, dns_rootname, 0, NULL); 2048 if (result != ISC_R_SUCCESS) { 2049 cfg_obj_log(key, logctx, ISC_LOG_WARNING, "bad key name: %s\n", 2050 isc_result_totext(result)); 2051 result = ISC_R_FAILURE; 2052 } 2053 2054 if (flags > 0xffff) { 2055 cfg_obj_log(key, logctx, ISC_LOG_WARNING, 2056 "flags too big: %u\n", flags); 2057 result = ISC_R_FAILURE; 2058 } 2059 if (proto > 0xff) { 2060 cfg_obj_log(key, logctx, ISC_LOG_WARNING, 2061 "protocol too big: %u\n", proto); 2062 result = ISC_R_FAILURE; 2063 } 2064 if (alg > 0xff) { 2065 cfg_obj_log(key, logctx, ISC_LOG_WARNING, 2066 "algorithm too big: %u\n", alg); 2067 result = ISC_R_FAILURE; 2068 } 2069 2070 if (managed) { 2071 const char *initmethod; 2072 initmethod = cfg_obj_asstring(cfg_tuple_get(key, "init")); 2073 2074 if (strcasecmp(initmethod, "initial-key") != 0) { 2075 cfg_obj_log(key, logctx, ISC_LOG_ERROR, 2076 "managed key '%s': " 2077 "invalid initialization method '%s'", 2078 keynamestr, initmethod); 2079 result = ISC_R_FAILURE; 2080 } 2081 } 2082 2083 isc_buffer_init(&b, keydata, sizeof(keydata)); 2084 2085 keystr = cfg_obj_asstring(cfg_tuple_get(key, "key")); 2086 tresult = isc_base64_decodestring(keystr, &b); 2087 2088 if (tresult != ISC_R_SUCCESS) { 2089 cfg_obj_log(key, logctx, ISC_LOG_ERROR, 2090 "%s", isc_result_totext(tresult)); 2091 result = ISC_R_FAILURE; 2092 } else { 2093 isc_buffer_usedregion(&b, &r); 2094 2095 if ((alg == DST_ALG_RSASHA1 || alg == DST_ALG_RSAMD5) && 2096 r.length > 1 && r.base[0] == 1 && r.base[1] == 3) 2097 cfg_obj_log(key, logctx, ISC_LOG_WARNING, 2098 "%s key '%s' has a weak exponent", 2099 managed ? "managed" : "trusted", 2100 keynamestr); 2101 } 2102 2103 return (result); 2104} 2105 2106static isc_result_t 2107check_viewconf(const cfg_obj_t *config, const cfg_obj_t *voptions, 2108 const char *viewname, dns_rdataclass_t vclass, 2109 isc_log_t *logctx, isc_mem_t *mctx) 2110{ 2111 const cfg_obj_t *zones = NULL; 2112 const cfg_obj_t *keys = NULL; 2113 const cfg_listelt_t *element, *element2; 2114 isc_symtab_t *symtab = NULL; 2115 isc_result_t result = ISC_R_SUCCESS; 2116 isc_result_t tresult = ISC_R_SUCCESS; 2117 cfg_aclconfctx_t *actx = NULL; 2118 const cfg_obj_t *obj; 2119 const cfg_obj_t *options = NULL; 2120 isc_boolean_t enablednssec, enablevalidation; 2121 const char *valstr = "no"; 2122 2123 /* 2124 * Get global options block 2125 */ 2126 (void)cfg_map_get(config, "options", &options); 2127 2128 /* 2129 * Check that all zone statements are syntactically correct and 2130 * there are no duplicate zones. 2131 */ 2132 tresult = isc_symtab_create(mctx, 1000, freekey, mctx, 2133 ISC_FALSE, &symtab); 2134 if (tresult != ISC_R_SUCCESS) 2135 return (ISC_R_NOMEMORY); 2136 2137 cfg_aclconfctx_create(mctx, &actx); 2138 2139 if (voptions != NULL) 2140 (void)cfg_map_get(voptions, "zone", &zones); 2141 else 2142 (void)cfg_map_get(config, "zone", &zones); 2143 2144 for (element = cfg_list_first(zones); 2145 element != NULL; 2146 element = cfg_list_next(element)) 2147 { 2148 isc_result_t tresult; 2149 const cfg_obj_t *zone = cfg_listelt_value(element); 2150 2151 tresult = check_zoneconf(zone, voptions, config, symtab, 2152 vclass, actx, logctx, mctx); 2153 if (tresult != ISC_R_SUCCESS) 2154 result = ISC_R_FAILURE; 2155 } 2156 2157 isc_symtab_destroy(&symtab); 2158 2159 /* 2160 * Check that forwarding is reasonable. 2161 */ 2162 if (voptions == NULL) { 2163 if (options != NULL) 2164 if (check_forward(options, NULL, 2165 logctx) != ISC_R_SUCCESS) 2166 result = ISC_R_FAILURE; 2167 } else { 2168 if (check_forward(voptions, NULL, logctx) != ISC_R_SUCCESS) 2169 result = ISC_R_FAILURE; 2170 } 2171 2172 /* 2173 * Check that dual-stack-servers is reasonable. 2174 */ 2175 if (voptions == NULL) { 2176 if (options != NULL) 2177 if (check_dual_stack(options, logctx) != ISC_R_SUCCESS) 2178 result = ISC_R_FAILURE; 2179 } else { 2180 if (check_dual_stack(voptions, logctx) != ISC_R_SUCCESS) 2181 result = ISC_R_FAILURE; 2182 } 2183 2184 /* 2185 * Check that rrset-order is reasonable. 2186 */ 2187 if (voptions != NULL) { 2188 if (check_order(voptions, logctx) != ISC_R_SUCCESS) 2189 result = ISC_R_FAILURE; 2190 } 2191 2192 /* 2193 * Check that all key statements are syntactically correct and 2194 * there are no duplicate keys. 2195 */ 2196 tresult = isc_symtab_create(mctx, 1000, freekey, mctx, 2197 ISC_FALSE, &symtab); 2198 if (tresult != ISC_R_SUCCESS) 2199 return (ISC_R_NOMEMORY); 2200 2201 (void)cfg_map_get(config, "key", &keys); 2202 tresult = check_keylist(keys, symtab, mctx, logctx); 2203 if (tresult == ISC_R_EXISTS) 2204 result = ISC_R_FAILURE; 2205 else if (tresult != ISC_R_SUCCESS) { 2206 isc_symtab_destroy(&symtab); 2207 return (tresult); 2208 } 2209 2210 if (voptions != NULL) { 2211 keys = NULL; 2212 (void)cfg_map_get(voptions, "key", &keys); 2213 tresult = check_keylist(keys, symtab, mctx, logctx); 2214 if (tresult == ISC_R_EXISTS) 2215 result = ISC_R_FAILURE; 2216 else if (tresult != ISC_R_SUCCESS) { 2217 isc_symtab_destroy(&symtab); 2218 return (tresult); 2219 } 2220 } 2221 2222 /* 2223 * Global servers can refer to keys in views. 2224 */ 2225 if (check_servers(config, voptions, symtab, logctx) != ISC_R_SUCCESS) 2226 result = ISC_R_FAILURE; 2227 2228 isc_symtab_destroy(&symtab); 2229 2230 /* 2231 * Check that dnssec-enable/dnssec-validation are sensible. 2232 */ 2233 obj = NULL; 2234 if (voptions != NULL) 2235 (void)cfg_map_get(voptions, "dnssec-enable", &obj); 2236 if (obj == NULL && options != NULL) 2237 (void)cfg_map_get(options, "dnssec-enable", &obj); 2238 if (obj == NULL) 2239 enablednssec = ISC_TRUE; 2240 else 2241 enablednssec = cfg_obj_asboolean(obj); 2242 2243 obj = NULL; 2244 if (voptions != NULL) 2245 (void)cfg_map_get(voptions, "dnssec-validation", &obj); 2246 if (obj == NULL && options != NULL) 2247 (void)cfg_map_get(options, "dnssec-validation", &obj); 2248 if (obj == NULL) { 2249 enablevalidation = enablednssec; 2250 valstr = "yes"; 2251 } else if (cfg_obj_isboolean(obj)) { 2252 enablevalidation = cfg_obj_asboolean(obj); 2253 valstr = enablevalidation ? "yes" : "no"; 2254 } else { 2255 enablevalidation = ISC_TRUE; 2256 valstr = "auto"; 2257 } 2258 2259 if (enablevalidation && !enablednssec) 2260 cfg_obj_log(obj, logctx, ISC_LOG_WARNING, 2261 "'dnssec-validation %s;' and 'dnssec-enable no;'", 2262 valstr); 2263 2264 /* 2265 * Check trusted-keys and managed-keys. 2266 */ 2267 keys = NULL; 2268 if (voptions != NULL) 2269 (void)cfg_map_get(voptions, "trusted-keys", &keys); 2270 if (keys == NULL) 2271 (void)cfg_map_get(config, "trusted-keys", &keys); 2272 2273 for (element = cfg_list_first(keys); 2274 element != NULL; 2275 element = cfg_list_next(element)) 2276 { 2277 const cfg_obj_t *keylist = cfg_listelt_value(element); 2278 for (element2 = cfg_list_first(keylist); 2279 element2 != NULL; 2280 element2 = cfg_list_next(element2)) { 2281 obj = cfg_listelt_value(element2); 2282 tresult = check_trusted_key(obj, ISC_FALSE, logctx); 2283 if (tresult != ISC_R_SUCCESS) 2284 result = tresult; 2285 } 2286 } 2287 2288 keys = NULL; 2289 if (voptions != NULL) 2290 (void)cfg_map_get(voptions, "managed-keys", &keys); 2291 if (keys == NULL) 2292 (void)cfg_map_get(config, "managed-keys", &keys); 2293 2294 for (element = cfg_list_first(keys); 2295 element != NULL; 2296 element = cfg_list_next(element)) 2297 { 2298 const cfg_obj_t *keylist = cfg_listelt_value(element); 2299 for (element2 = cfg_list_first(keylist); 2300 element2 != NULL; 2301 element2 = cfg_list_next(element2)) { 2302 obj = cfg_listelt_value(element2); 2303 tresult = check_trusted_key(obj, ISC_TRUE, logctx); 2304 if (tresult != ISC_R_SUCCESS) 2305 result = tresult; 2306 } 2307 } 2308 2309 /* 2310 * Check options. 2311 */ 2312 if (voptions != NULL) 2313 tresult = check_options(voptions, logctx, mctx, 2314 optlevel_view); 2315 else 2316 tresult = check_options(config, logctx, mctx, 2317 optlevel_config); 2318 if (tresult != ISC_R_SUCCESS) 2319 result = tresult; 2320 2321 tresult = check_viewacls(actx, voptions, config, logctx, mctx); 2322 if (tresult != ISC_R_SUCCESS) 2323 result = tresult; 2324 2325 tresult = check_recursionacls(actx, voptions, viewname, 2326 config, logctx, mctx); 2327 if (tresult != ISC_R_SUCCESS) 2328 result = tresult; 2329 2330 tresult = check_filteraaaa(actx, voptions, viewname, config, 2331 logctx, mctx); 2332 if (tresult != ISC_R_SUCCESS) 2333 result = tresult; 2334 2335 tresult = check_dns64(actx, voptions, config, logctx, mctx); 2336 if (tresult != ISC_R_SUCCESS) 2337 result = tresult; 2338 2339 cfg_aclconfctx_detach(&actx); 2340 2341 return (result); 2342} 2343 2344static const char * 2345default_channels[] = { 2346 "default_syslog", 2347 "default_stderr", 2348 "default_debug", 2349 "null", 2350 NULL 2351}; 2352 2353static isc_result_t 2354bind9_check_logging(const cfg_obj_t *config, isc_log_t *logctx, 2355 isc_mem_t *mctx) 2356{ 2357 const cfg_obj_t *categories = NULL; 2358 const cfg_obj_t *category; 2359 const cfg_obj_t *channels = NULL; 2360 const cfg_obj_t *channel; 2361 const cfg_listelt_t *element; 2362 const cfg_listelt_t *delement; 2363 const char *channelname; 2364 const char *catname; 2365 const cfg_obj_t *fileobj = NULL; 2366 const cfg_obj_t *syslogobj = NULL; 2367 const cfg_obj_t *nullobj = NULL; 2368 const cfg_obj_t *stderrobj = NULL; 2369 const cfg_obj_t *logobj = NULL; 2370 isc_result_t result = ISC_R_SUCCESS; 2371 isc_result_t tresult; 2372 isc_symtab_t *symtab = NULL; 2373 isc_symvalue_t symvalue; 2374 int i; 2375 2376 (void)cfg_map_get(config, "logging", &logobj); 2377 if (logobj == NULL) 2378 return (ISC_R_SUCCESS); 2379 2380 result = isc_symtab_create(mctx, 100, NULL, NULL, ISC_FALSE, &symtab); 2381 if (result != ISC_R_SUCCESS) 2382 return (result); 2383 2384 symvalue.as_cpointer = NULL; 2385 for (i = 0; default_channels[i] != NULL; i++) { 2386 tresult = isc_symtab_define(symtab, default_channels[i], 1, 2387 symvalue, isc_symexists_replace); 2388 if (tresult != ISC_R_SUCCESS) 2389 result = tresult; 2390 } 2391 2392 cfg_map_get(logobj, "channel", &channels); 2393 2394 for (element = cfg_list_first(channels); 2395 element != NULL; 2396 element = cfg_list_next(element)) 2397 { 2398 channel = cfg_listelt_value(element); 2399 channelname = cfg_obj_asstring(cfg_map_getname(channel)); 2400 fileobj = syslogobj = nullobj = stderrobj = NULL; 2401 (void)cfg_map_get(channel, "file", &fileobj); 2402 (void)cfg_map_get(channel, "syslog", &syslogobj); 2403 (void)cfg_map_get(channel, "null", &nullobj); 2404 (void)cfg_map_get(channel, "stderr", &stderrobj); 2405 i = 0; 2406 if (fileobj != NULL) 2407 i++; 2408 if (syslogobj != NULL) 2409 i++; 2410 if (nullobj != NULL) 2411 i++; 2412 if (stderrobj != NULL) 2413 i++; 2414 if (i != 1) { 2415 cfg_obj_log(channel, logctx, ISC_LOG_ERROR, 2416 "channel '%s': exactly one of file, syslog, " 2417 "null, and stderr must be present", 2418 channelname); 2419 result = ISC_R_FAILURE; 2420 } 2421 tresult = isc_symtab_define(symtab, channelname, 1, 2422 symvalue, isc_symexists_replace); 2423 if (tresult != ISC_R_SUCCESS) 2424 result = tresult; 2425 } 2426 2427 cfg_map_get(logobj, "category", &categories); 2428 2429 for (element = cfg_list_first(categories); 2430 element != NULL; 2431 element = cfg_list_next(element)) 2432 { 2433 category = cfg_listelt_value(element); 2434 catname = cfg_obj_asstring(cfg_tuple_get(category, "name")); 2435 if (isc_log_categorybyname(logctx, catname) == NULL) { 2436 cfg_obj_log(category, logctx, ISC_LOG_ERROR, 2437 "undefined category: '%s'", catname); 2438 result = ISC_R_FAILURE; 2439 } 2440 channels = cfg_tuple_get(category, "destinations"); 2441 for (delement = cfg_list_first(channels); 2442 delement != NULL; 2443 delement = cfg_list_next(delement)) 2444 { 2445 channel = cfg_listelt_value(delement); 2446 channelname = cfg_obj_asstring(channel); 2447 tresult = isc_symtab_lookup(symtab, channelname, 1, 2448 &symvalue); 2449 if (tresult != ISC_R_SUCCESS) { 2450 cfg_obj_log(channel, logctx, ISC_LOG_ERROR, 2451 "undefined channel: '%s'", 2452 channelname); 2453 result = tresult; 2454 } 2455 } 2456 } 2457 isc_symtab_destroy(&symtab); 2458 return (result); 2459} 2460 2461static isc_result_t 2462bind9_check_controlskeys(const cfg_obj_t *control, const cfg_obj_t *keylist, 2463 isc_log_t *logctx) 2464{ 2465 isc_result_t result = ISC_R_SUCCESS; 2466 const cfg_obj_t *control_keylist; 2467 const cfg_listelt_t *element; 2468 const cfg_obj_t *key; 2469 const char *keyval; 2470 2471 control_keylist = cfg_tuple_get(control, "keys"); 2472 if (cfg_obj_isvoid(control_keylist)) 2473 return (ISC_R_SUCCESS); 2474 2475 for (element = cfg_list_first(control_keylist); 2476 element != NULL; 2477 element = cfg_list_next(element)) 2478 { 2479 key = cfg_listelt_value(element); 2480 keyval = cfg_obj_asstring(key); 2481 2482 if (!rndckey_exists(keylist, keyval)) { 2483 cfg_obj_log(key, logctx, ISC_LOG_ERROR, 2484 "unknown key '%s'", keyval); 2485 result = ISC_R_NOTFOUND; 2486 } 2487 } 2488 return (result); 2489} 2490 2491static isc_result_t 2492bind9_check_controls(const cfg_obj_t *config, isc_log_t *logctx, 2493 isc_mem_t *mctx) 2494{ 2495 isc_result_t result = ISC_R_SUCCESS, tresult; 2496 cfg_aclconfctx_t *actx = NULL; 2497 const cfg_listelt_t *element, *element2; 2498 const cfg_obj_t *allow; 2499 const cfg_obj_t *control; 2500 const cfg_obj_t *controls; 2501 const cfg_obj_t *controlslist = NULL; 2502 const cfg_obj_t *inetcontrols; 2503 const cfg_obj_t *unixcontrols; 2504 const cfg_obj_t *keylist = NULL; 2505 const char *path; 2506 isc_uint32_t perm, mask; 2507 dns_acl_t *acl = NULL; 2508 isc_sockaddr_t addr; 2509 int i; 2510 2511 (void)cfg_map_get(config, "controls", &controlslist); 2512 if (controlslist == NULL) 2513 return (ISC_R_SUCCESS); 2514 2515 (void)cfg_map_get(config, "key", &keylist); 2516 2517 cfg_aclconfctx_create(mctx, &actx); 2518 2519 /* 2520 * INET: Check allow clause. 2521 * UNIX: Check "perm" for sanity, check path length. 2522 */ 2523 for (element = cfg_list_first(controlslist); 2524 element != NULL; 2525 element = cfg_list_next(element)) { 2526 controls = cfg_listelt_value(element); 2527 unixcontrols = NULL; 2528 inetcontrols = NULL; 2529 (void)cfg_map_get(controls, "unix", &unixcontrols); 2530 (void)cfg_map_get(controls, "inet", &inetcontrols); 2531 for (element2 = cfg_list_first(inetcontrols); 2532 element2 != NULL; 2533 element2 = cfg_list_next(element2)) { 2534 control = cfg_listelt_value(element2); 2535 allow = cfg_tuple_get(control, "allow"); 2536 tresult = cfg_acl_fromconfig(allow, config, logctx, 2537 actx, mctx, 0, &acl); 2538 if (acl != NULL) 2539 dns_acl_detach(&acl); 2540 if (tresult != ISC_R_SUCCESS) 2541 result = tresult; 2542 tresult = bind9_check_controlskeys(control, keylist, 2543 logctx); 2544 if (tresult != ISC_R_SUCCESS) 2545 result = tresult; 2546 } 2547 for (element2 = cfg_list_first(unixcontrols); 2548 element2 != NULL; 2549 element2 = cfg_list_next(element2)) { 2550 control = cfg_listelt_value(element2); 2551 path = cfg_obj_asstring(cfg_tuple_get(control, "path")); 2552 tresult = isc_sockaddr_frompath(&addr, path); 2553 if (tresult == ISC_R_NOSPACE) { 2554 cfg_obj_log(control, logctx, ISC_LOG_ERROR, 2555 "unix control '%s': path too long", 2556 path); 2557 result = ISC_R_NOSPACE; 2558 } 2559 perm = cfg_obj_asuint32(cfg_tuple_get(control, "perm")); 2560 for (i = 0; i < 3; i++) { 2561#ifdef NEED_SECURE_DIRECTORY 2562 mask = (0x1 << (i*3)); /* SEARCH */ 2563#else 2564 mask = (0x6 << (i*3)); /* READ + WRITE */ 2565#endif 2566 if ((perm & mask) == mask) 2567 break; 2568 } 2569 if (i == 0) { 2570 cfg_obj_log(control, logctx, ISC_LOG_WARNING, 2571 "unix control '%s' allows access " 2572 "to everyone", path); 2573 } else if (i == 3) { 2574 cfg_obj_log(control, logctx, ISC_LOG_WARNING, 2575 "unix control '%s' allows access " 2576 "to nobody", path); 2577 } 2578 tresult = bind9_check_controlskeys(control, keylist, 2579 logctx); 2580 if (tresult != ISC_R_SUCCESS) 2581 result = tresult; 2582 } 2583 } 2584 cfg_aclconfctx_detach(&actx); 2585 return (result); 2586} 2587 2588isc_result_t 2589bind9_check_namedconf(const cfg_obj_t *config, isc_log_t *logctx, 2590 isc_mem_t *mctx) 2591{ 2592 const cfg_obj_t *options = NULL; 2593 const cfg_obj_t *views = NULL; 2594 const cfg_obj_t *acls = NULL; 2595 const cfg_obj_t *kals = NULL; 2596 const cfg_obj_t *obj; 2597 const cfg_listelt_t *velement; 2598 isc_result_t result = ISC_R_SUCCESS; 2599 isc_result_t tresult; 2600 isc_symtab_t *symtab = NULL; 2601 2602 static const char *builtin[] = { "localhost", "localnets", 2603 "any", "none"}; 2604 2605 (void)cfg_map_get(config, "options", &options); 2606 2607 if (options != NULL && 2608 check_options(options, logctx, mctx, 2609 optlevel_options) != ISC_R_SUCCESS) 2610 result = ISC_R_FAILURE; 2611 2612 if (bind9_check_logging(config, logctx, mctx) != ISC_R_SUCCESS) 2613 result = ISC_R_FAILURE; 2614 2615 if (bind9_check_controls(config, logctx, mctx) != ISC_R_SUCCESS) 2616 result = ISC_R_FAILURE; 2617 2618 if (options != NULL && 2619 check_order(options, logctx) != ISC_R_SUCCESS) 2620 result = ISC_R_FAILURE; 2621 2622 (void)cfg_map_get(config, "view", &views); 2623 2624 if (views != NULL && options != NULL) 2625 if (check_dual_stack(options, logctx) != ISC_R_SUCCESS) 2626 result = ISC_R_FAILURE; 2627 2628 if (views == NULL) { 2629 if (check_viewconf(config, NULL, NULL, dns_rdataclass_in, 2630 logctx, mctx) != ISC_R_SUCCESS) 2631 result = ISC_R_FAILURE; 2632 } else { 2633 const cfg_obj_t *zones = NULL; 2634 2635 (void)cfg_map_get(config, "zone", &zones); 2636 if (zones != NULL) { 2637 cfg_obj_log(zones, logctx, ISC_LOG_ERROR, 2638 "when using 'view' statements, " 2639 "all zones must be in views"); 2640 result = ISC_R_FAILURE; 2641 } 2642 } 2643 2644 tresult = isc_symtab_create(mctx, 100, NULL, NULL, ISC_TRUE, &symtab); 2645 if (tresult != ISC_R_SUCCESS) 2646 result = tresult; 2647 for (velement = cfg_list_first(views); 2648 velement != NULL; 2649 velement = cfg_list_next(velement)) 2650 { 2651 const cfg_obj_t *view = cfg_listelt_value(velement); 2652 const cfg_obj_t *vname = cfg_tuple_get(view, "name"); 2653 const cfg_obj_t *voptions = cfg_tuple_get(view, "options"); 2654 const cfg_obj_t *vclassobj = cfg_tuple_get(view, "class"); 2655 dns_rdataclass_t vclass = dns_rdataclass_in; 2656 isc_result_t tresult = ISC_R_SUCCESS; 2657 const char *key = cfg_obj_asstring(vname); 2658 isc_symvalue_t symvalue; 2659 2660 if (cfg_obj_isstring(vclassobj)) { 2661 isc_textregion_t r; 2662 2663 DE_CONST(cfg_obj_asstring(vclassobj), r.base); 2664 r.length = strlen(r.base); 2665 tresult = dns_rdataclass_fromtext(&vclass, &r); 2666 if (tresult != ISC_R_SUCCESS) 2667 cfg_obj_log(vclassobj, logctx, ISC_LOG_ERROR, 2668 "view '%s': invalid class %s", 2669 cfg_obj_asstring(vname), r.base); 2670 } 2671 if (tresult == ISC_R_SUCCESS && symtab != NULL) { 2672 symvalue.as_cpointer = view; 2673 tresult = isc_symtab_define(symtab, key, vclass, 2674 symvalue, 2675 isc_symexists_reject); 2676 if (tresult == ISC_R_EXISTS) { 2677 const char *file; 2678 unsigned int line; 2679 RUNTIME_CHECK(isc_symtab_lookup(symtab, key, 2680 vclass, &symvalue) == ISC_R_SUCCESS); 2681 file = cfg_obj_file(symvalue.as_cpointer); 2682 line = cfg_obj_line(symvalue.as_cpointer); 2683 cfg_obj_log(view, logctx, ISC_LOG_ERROR, 2684 "view '%s': already exists " 2685 "previous definition: %s:%u", 2686 key, file, line); 2687 result = tresult; 2688 } else if (tresult != ISC_R_SUCCESS) { 2689 result = tresult; 2690 } else if ((strcasecmp(key, "_bind") == 0 && 2691 vclass == dns_rdataclass_ch) || 2692 (strcasecmp(key, "_default") == 0 && 2693 vclass == dns_rdataclass_in)) { 2694 cfg_obj_log(view, logctx, ISC_LOG_ERROR, 2695 "attempt to redefine builtin view " 2696 "'%s'", key); 2697 result = ISC_R_EXISTS; 2698 } 2699 } 2700 if (tresult == ISC_R_SUCCESS) 2701 tresult = check_viewconf(config, voptions, key, 2702 vclass, logctx, mctx); 2703 if (tresult != ISC_R_SUCCESS) 2704 result = ISC_R_FAILURE; 2705 } 2706 if (symtab != NULL) 2707 isc_symtab_destroy(&symtab); 2708 2709 if (views != NULL && options != NULL) { 2710 obj = NULL; 2711 tresult = cfg_map_get(options, "cache-file", &obj); 2712 if (tresult == ISC_R_SUCCESS) { 2713 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, 2714 "'cache-file' cannot be a global " 2715 "option if views are present"); 2716 result = ISC_R_FAILURE; 2717 } 2718 } 2719 2720 cfg_map_get(config, "acl", &acls); 2721 2722 if (acls != NULL) { 2723 const cfg_listelt_t *elt; 2724 const cfg_listelt_t *elt2; 2725 const char *aclname; 2726 2727 for (elt = cfg_list_first(acls); 2728 elt != NULL; 2729 elt = cfg_list_next(elt)) { 2730 const cfg_obj_t *acl = cfg_listelt_value(elt); 2731 unsigned int line = cfg_obj_line(acl); 2732 unsigned int i; 2733 2734 aclname = cfg_obj_asstring(cfg_tuple_get(acl, "name")); 2735 for (i = 0; 2736 i < sizeof(builtin) / sizeof(builtin[0]); 2737 i++) 2738 if (strcasecmp(aclname, builtin[i]) == 0) { 2739 cfg_obj_log(acl, logctx, ISC_LOG_ERROR, 2740 "attempt to redefine " 2741 "builtin acl '%s'", 2742 aclname); 2743 result = ISC_R_FAILURE; 2744 break; 2745 } 2746 2747 for (elt2 = cfg_list_next(elt); 2748 elt2 != NULL; 2749 elt2 = cfg_list_next(elt2)) { 2750 const cfg_obj_t *acl2 = cfg_listelt_value(elt2); 2751 const char *name; 2752 name = cfg_obj_asstring(cfg_tuple_get(acl2, 2753 "name")); 2754 if (strcasecmp(aclname, name) == 0) { 2755 const char *file = cfg_obj_file(acl); 2756 2757 if (file == NULL) 2758 file = "<unknown file>"; 2759 2760 cfg_obj_log(acl2, logctx, ISC_LOG_ERROR, 2761 "attempt to redefine " 2762 "acl '%s' previous " 2763 "definition: %s:%u", 2764 name, file, line); 2765 result = ISC_R_FAILURE; 2766 } 2767 } 2768 } 2769 } 2770 2771 tresult = cfg_map_get(config, "kal", &kals); 2772 if (tresult == ISC_R_SUCCESS) { 2773 const cfg_listelt_t *elt; 2774 const cfg_listelt_t *elt2; 2775 const char *aclname; 2776 2777 for (elt = cfg_list_first(kals); 2778 elt != NULL; 2779 elt = cfg_list_next(elt)) { 2780 const cfg_obj_t *acl = cfg_listelt_value(elt); 2781 2782 aclname = cfg_obj_asstring(cfg_tuple_get(acl, "name")); 2783 2784 for (elt2 = cfg_list_next(elt); 2785 elt2 != NULL; 2786 elt2 = cfg_list_next(elt2)) { 2787 const cfg_obj_t *acl2 = cfg_listelt_value(elt2); 2788 const char *name; 2789 name = cfg_obj_asstring(cfg_tuple_get(acl2, 2790 "name")); 2791 if (strcasecmp(aclname, name) == 0) { 2792 const char *file = cfg_obj_file(acl); 2793 unsigned int line = cfg_obj_line(acl); 2794 2795 if (file == NULL) 2796 file = "<unknown file>"; 2797 2798 cfg_obj_log(acl2, logctx, ISC_LOG_ERROR, 2799 "attempt to redefine " 2800 "kal '%s' previous " 2801 "definition: %s:%u", 2802 name, file, line); 2803 result = ISC_R_FAILURE; 2804 } 2805 } 2806 } 2807 } 2808 2809 return (result); 2810} 2811