Import bind 9.5.2 vendor sources.
[dragonfly.git] / contrib / bind-9.5.2 / lib / bind9 / check.c
blob4dd7200b4692718e073e064aaf2e8ef8d4ca8d7e
1 /*
2 * Copyright (C) 2004-2009 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2001-2003 Internet Software Consortium.
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.
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.
18 /* $Id: check.c,v 1.86.10.11 2009/06/03 07:08:20 marka Exp $ */
20 /*! \file */
22 #include <config.h>
24 #include <stdlib.h>
26 #include <isc/buffer.h>
27 #include <isc/log.h>
28 #include <isc/mem.h>
29 #include <isc/netaddr.h>
30 #include <isc/parseint.h>
31 #include <isc/region.h>
32 #include <isc/result.h>
33 #include <isc/sockaddr.h>
34 #include <isc/string.h>
35 #include <isc/symtab.h>
36 #include <isc/util.h>
38 #include <dns/acl.h>
39 #include <dns/fixedname.h>
40 #include <dns/rdataclass.h>
41 #include <dns/rdatatype.h>
42 #include <dns/secalg.h>
44 #include <isccfg/aclconf.h>
45 #include <isccfg/cfg.h>
47 #include <bind9/check.h>
49 static void
50 freekey(char *key, unsigned int type, isc_symvalue_t value, void *userarg) {
51 UNUSED(type);
52 UNUSED(value);
53 isc_mem_free(userarg, key);
56 static isc_result_t
57 check_orderent(const cfg_obj_t *ent, isc_log_t *logctx) {
58 isc_result_t result = ISC_R_SUCCESS;
59 isc_result_t tresult;
60 isc_textregion_t r;
61 dns_fixedname_t fixed;
62 const cfg_obj_t *obj;
63 dns_rdataclass_t rdclass;
64 dns_rdatatype_t rdtype;
65 isc_buffer_t b;
66 const char *str;
68 dns_fixedname_init(&fixed);
69 obj = cfg_tuple_get(ent, "class");
70 if (cfg_obj_isstring(obj)) {
72 DE_CONST(cfg_obj_asstring(obj), r.base);
73 r.length = strlen(r.base);
74 tresult = dns_rdataclass_fromtext(&rdclass, &r);
75 if (tresult != ISC_R_SUCCESS) {
76 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
77 "rrset-order: invalid class '%s'",
78 r.base);
79 result = ISC_R_FAILURE;
83 obj = cfg_tuple_get(ent, "type");
84 if (cfg_obj_isstring(obj)) {
86 DE_CONST(cfg_obj_asstring(obj), r.base);
87 r.length = strlen(r.base);
88 tresult = dns_rdatatype_fromtext(&rdtype, &r);
89 if (tresult != ISC_R_SUCCESS) {
90 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
91 "rrset-order: invalid type '%s'",
92 r.base);
93 result = ISC_R_FAILURE;
97 obj = cfg_tuple_get(ent, "name");
98 if (cfg_obj_isstring(obj)) {
99 str = cfg_obj_asstring(obj);
100 isc_buffer_init(&b, str, strlen(str));
101 isc_buffer_add(&b, strlen(str));
102 tresult = dns_name_fromtext(dns_fixedname_name(&fixed), &b,
103 dns_rootname, ISC_FALSE, NULL);
104 if (tresult != ISC_R_SUCCESS) {
105 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
106 "rrset-order: invalid name '%s'", str);
107 result = ISC_R_FAILURE;
111 obj = cfg_tuple_get(ent, "order");
112 if (!cfg_obj_isstring(obj) ||
113 strcasecmp("order", cfg_obj_asstring(obj)) != 0) {
114 cfg_obj_log(ent, logctx, ISC_LOG_ERROR,
115 "rrset-order: keyword 'order' missing");
116 result = ISC_R_FAILURE;
119 obj = cfg_tuple_get(ent, "ordering");
120 if (!cfg_obj_isstring(obj)) {
121 cfg_obj_log(ent, logctx, ISC_LOG_ERROR,
122 "rrset-order: missing ordering");
123 result = ISC_R_FAILURE;
124 } else if (strcasecmp(cfg_obj_asstring(obj), "fixed") == 0) {
125 #if !DNS_RDATASET_FIXED
126 cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
127 "rrset-order: order 'fixed' was disabled at "
128 "compilation time");
129 #endif
130 } else if (strcasecmp(cfg_obj_asstring(obj), "random") != 0 &&
131 strcasecmp(cfg_obj_asstring(obj), "cyclic") != 0) {
132 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
133 "rrset-order: invalid order '%s'",
134 cfg_obj_asstring(obj));
135 result = ISC_R_FAILURE;
137 return (result);
140 static isc_result_t
141 check_order(const cfg_obj_t *options, isc_log_t *logctx) {
142 isc_result_t result = ISC_R_SUCCESS;
143 isc_result_t tresult;
144 const cfg_listelt_t *element;
145 const cfg_obj_t *obj = NULL;
147 if (cfg_map_get(options, "rrset-order", &obj) != ISC_R_SUCCESS)
148 return (result);
150 for (element = cfg_list_first(obj);
151 element != NULL;
152 element = cfg_list_next(element))
154 tresult = check_orderent(cfg_listelt_value(element), logctx);
155 if (tresult != ISC_R_SUCCESS)
156 result = tresult;
158 return (result);
161 static isc_result_t
162 check_dual_stack(const cfg_obj_t *options, isc_log_t *logctx) {
163 const cfg_listelt_t *element;
164 const cfg_obj_t *alternates = NULL;
165 const cfg_obj_t *value;
166 const cfg_obj_t *obj;
167 const char *str;
168 dns_fixedname_t fixed;
169 dns_name_t *name;
170 isc_buffer_t buffer;
171 isc_result_t result = ISC_R_SUCCESS;
172 isc_result_t tresult;
174 (void)cfg_map_get(options, "dual-stack-servers", &alternates);
176 if (alternates == NULL)
177 return (ISC_R_SUCCESS);
179 obj = cfg_tuple_get(alternates, "port");
180 if (cfg_obj_isuint32(obj)) {
181 isc_uint32_t val = cfg_obj_asuint32(obj);
182 if (val > ISC_UINT16_MAX) {
183 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
184 "port '%u' out of range", val);
185 result = ISC_R_FAILURE;
188 obj = cfg_tuple_get(alternates, "addresses");
189 for (element = cfg_list_first(obj);
190 element != NULL;
191 element = cfg_list_next(element)) {
192 value = cfg_listelt_value(element);
193 if (cfg_obj_issockaddr(value))
194 continue;
195 obj = cfg_tuple_get(value, "name");
196 str = cfg_obj_asstring(obj);
197 isc_buffer_init(&buffer, str, strlen(str));
198 isc_buffer_add(&buffer, strlen(str));
199 dns_fixedname_init(&fixed);
200 name = dns_fixedname_name(&fixed);
201 tresult = dns_name_fromtext(name, &buffer, dns_rootname,
202 ISC_FALSE, NULL);
203 if (tresult != ISC_R_SUCCESS) {
204 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
205 "bad name '%s'", str);
206 result = ISC_R_FAILURE;
208 obj = cfg_tuple_get(value, "port");
209 if (cfg_obj_isuint32(obj)) {
210 isc_uint32_t val = cfg_obj_asuint32(obj);
211 if (val > ISC_UINT16_MAX) {
212 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
213 "port '%u' out of range", val);
214 result = ISC_R_FAILURE;
218 return (result);
221 static isc_result_t
222 check_forward(const cfg_obj_t *options, const cfg_obj_t *global,
223 isc_log_t *logctx)
225 const cfg_obj_t *forward = NULL;
226 const cfg_obj_t *forwarders = NULL;
228 (void)cfg_map_get(options, "forward", &forward);
229 (void)cfg_map_get(options, "forwarders", &forwarders);
231 if (forwarders != NULL && global != NULL) {
232 const char *file = cfg_obj_file(global);
233 unsigned int line = cfg_obj_line(global);
234 cfg_obj_log(forwarders, logctx, ISC_LOG_ERROR,
235 "forwarders declared in root zone and "
236 "in general configuration: %s:%u",
237 file, line);
238 return (ISC_R_FAILURE);
240 if (forward != NULL && forwarders == NULL) {
241 cfg_obj_log(forward, logctx, ISC_LOG_ERROR,
242 "no matching 'forwarders' statement");
243 return (ISC_R_FAILURE);
245 return (ISC_R_SUCCESS);
248 static isc_result_t
249 disabled_algorithms(const cfg_obj_t *disabled, isc_log_t *logctx) {
250 isc_result_t result = ISC_R_SUCCESS;
251 isc_result_t tresult;
252 const cfg_listelt_t *element;
253 const char *str;
254 isc_buffer_t b;
255 dns_fixedname_t fixed;
256 dns_name_t *name;
257 const cfg_obj_t *obj;
259 dns_fixedname_init(&fixed);
260 name = dns_fixedname_name(&fixed);
261 obj = cfg_tuple_get(disabled, "name");
262 str = cfg_obj_asstring(obj);
263 isc_buffer_init(&b, str, strlen(str));
264 isc_buffer_add(&b, strlen(str));
265 tresult = dns_name_fromtext(name, &b, dns_rootname, ISC_FALSE, NULL);
266 if (tresult != ISC_R_SUCCESS) {
267 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
268 "bad domain name '%s'", str);
269 result = tresult;
272 obj = cfg_tuple_get(disabled, "algorithms");
274 for (element = cfg_list_first(obj);
275 element != NULL;
276 element = cfg_list_next(element))
278 isc_textregion_t r;
279 dns_secalg_t alg;
280 isc_result_t tresult;
282 DE_CONST(cfg_obj_asstring(cfg_listelt_value(element)), r.base);
283 r.length = strlen(r.base);
285 tresult = dns_secalg_fromtext(&alg, &r);
286 if (tresult != ISC_R_SUCCESS) {
287 isc_uint8_t ui;
288 result = isc_parse_uint8(&ui, r.base, 10);
290 if (tresult != ISC_R_SUCCESS) {
291 cfg_obj_log(cfg_listelt_value(element), logctx,
292 ISC_LOG_ERROR, "invalid algorithm '%s'",
293 r.base);
294 result = tresult;
297 return (result);
300 static isc_result_t
301 nameexist(const cfg_obj_t *obj, const char *name, int value,
302 isc_symtab_t *symtab, const char *fmt, isc_log_t *logctx,
303 isc_mem_t *mctx)
305 char *key;
306 const char *file;
307 unsigned int line;
308 isc_result_t result;
309 isc_symvalue_t symvalue;
311 key = isc_mem_strdup(mctx, name);
312 if (key == NULL)
313 return (ISC_R_NOMEMORY);
314 symvalue.as_cpointer = obj;
315 result = isc_symtab_define(symtab, key, value, symvalue,
316 isc_symexists_reject);
317 if (result == ISC_R_EXISTS) {
318 RUNTIME_CHECK(isc_symtab_lookup(symtab, key, value,
319 &symvalue) == ISC_R_SUCCESS);
320 file = cfg_obj_file(symvalue.as_cpointer);
321 line = cfg_obj_line(symvalue.as_cpointer);
323 if (file == NULL)
324 file = "<unknown file>";
325 cfg_obj_log(obj, logctx, ISC_LOG_ERROR, fmt, key, file, line);
326 isc_mem_free(mctx, key);
327 result = ISC_R_EXISTS;
328 } else if (result != ISC_R_SUCCESS) {
329 isc_mem_free(mctx, key);
331 return (result);
334 static isc_result_t
335 mustbesecure(const cfg_obj_t *secure, isc_symtab_t *symtab, isc_log_t *logctx,
336 isc_mem_t *mctx)
338 const cfg_obj_t *obj;
339 char namebuf[DNS_NAME_FORMATSIZE];
340 const char *str;
341 dns_fixedname_t fixed;
342 dns_name_t *name;
343 isc_buffer_t b;
344 isc_result_t result = ISC_R_SUCCESS;
346 dns_fixedname_init(&fixed);
347 name = dns_fixedname_name(&fixed);
348 obj = cfg_tuple_get(secure, "name");
349 str = cfg_obj_asstring(obj);
350 isc_buffer_init(&b, str, strlen(str));
351 isc_buffer_add(&b, strlen(str));
352 result = dns_name_fromtext(name, &b, dns_rootname, ISC_FALSE, NULL);
353 if (result != ISC_R_SUCCESS) {
354 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
355 "bad domain name '%s'", str);
356 } else {
357 dns_name_format(name, namebuf, sizeof(namebuf));
358 result = nameexist(secure, namebuf, 1, symtab,
359 "dnssec-must-be-secure '%s': already "
360 "exists previous definition: %s:%u",
361 logctx, mctx);
363 return (result);
366 static isc_result_t
367 checkacl(const char *aclname, cfg_aclconfctx_t *actx, const cfg_obj_t *zconfig,
368 const cfg_obj_t *voptions, const cfg_obj_t *config,
369 isc_log_t *logctx, isc_mem_t *mctx)
371 isc_result_t result;
372 const cfg_obj_t *aclobj = NULL;
373 const cfg_obj_t *options;
374 dns_acl_t *acl = NULL;
376 if (zconfig != NULL) {
377 options = cfg_tuple_get(zconfig, "options");
378 cfg_map_get(options, aclname, &aclobj);
380 if (voptions != NULL && aclobj == NULL)
381 cfg_map_get(voptions, aclname, &aclobj);
382 if (config != NULL && aclobj == NULL) {
383 options = NULL;
384 cfg_map_get(config, "options", &options);
385 if (options != NULL)
386 cfg_map_get(options, aclname, &aclobj);
388 if (aclobj == NULL)
389 return (ISC_R_SUCCESS);
390 result = cfg_acl_fromconfig(aclobj, config, logctx,
391 actx, mctx, 0, &acl);
392 if (acl != NULL)
393 dns_acl_detach(&acl);
394 return (result);
397 static isc_result_t
398 check_viewacls(cfg_aclconfctx_t *actx, const cfg_obj_t *voptions,
399 const cfg_obj_t *config, isc_log_t *logctx, isc_mem_t *mctx)
401 isc_result_t result = ISC_R_SUCCESS, tresult;
402 int i = 0;
404 static const char *acls[] = { "allow-query", "allow-query-on",
405 "allow-query-cache", "allow-query-cache-on",
406 "blackhole", "match-clients", "match-destinations",
407 "sortlist", NULL };
409 while (acls[i] != NULL) {
410 tresult = checkacl(acls[i++], actx, NULL, voptions, config,
411 logctx, mctx);
412 if (tresult != ISC_R_SUCCESS)
413 result = tresult;
415 return (result);
419 * Check allow-recursion and allow-recursion-on acls, and also log a
420 * warning if they're inconsistent with the "recursion" option.
422 static isc_result_t
423 check_recursionacls(cfg_aclconfctx_t *actx, const cfg_obj_t *voptions,
424 const char *viewname, const cfg_obj_t *config,
425 isc_log_t *logctx, isc_mem_t *mctx)
427 const cfg_obj_t *options, *aclobj, *obj = NULL;
428 dns_acl_t *acl = NULL;
429 isc_result_t result = ISC_R_SUCCESS, tresult;
430 isc_boolean_t recursion;
431 const char *forview = " for view ";
432 int i = 0;
434 static const char *acls[] = { "allow-recursion", "allow-recursion-on",
435 NULL };
437 if (voptions != NULL)
438 cfg_map_get(voptions, "recursion", &obj);
439 if (obj == NULL && config != NULL) {
440 options = NULL;
441 cfg_map_get(config, "options", &options);
442 if (options != NULL)
443 cfg_map_get(options, "recursion", &obj);
445 if (obj == NULL)
446 recursion = ISC_TRUE;
447 else
448 recursion = cfg_obj_asboolean(obj);
450 if (viewname == NULL) {
451 viewname = "";
452 forview = "";
455 for (i = 0; acls[i] != NULL; i++) {
456 aclobj = options = NULL;
457 acl = NULL;
459 if (voptions != NULL)
460 cfg_map_get(voptions, acls[i], &aclobj);
461 if (config != NULL && aclobj == NULL) {
462 options = NULL;
463 cfg_map_get(config, "options", &options);
464 if (options != NULL)
465 cfg_map_get(options, acls[i], &aclobj);
467 if (aclobj == NULL)
468 continue;
470 tresult = cfg_acl_fromconfig(aclobj, config, logctx,
471 actx, mctx, 0, &acl);
473 if (tresult != ISC_R_SUCCESS)
474 result = tresult;
476 if (acl == NULL)
477 continue;
479 if (recursion == ISC_FALSE && !dns_acl_isnone(acl)) {
480 cfg_obj_log(aclobj, logctx, ISC_LOG_WARNING,
481 "both \"recursion no;\" and "
482 "\"%s\" active%s%s",
483 acls[i], forview, viewname);
486 if (acl != NULL)
487 dns_acl_detach(&acl);
490 return (result);
493 typedef struct {
494 const char *name;
495 unsigned int scale;
496 unsigned int max;
497 } intervaltable;
499 static isc_result_t
500 check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx) {
501 isc_result_t result = ISC_R_SUCCESS;
502 isc_result_t tresult;
503 unsigned int i;
504 const cfg_obj_t *obj = NULL;
505 const cfg_listelt_t *element;
506 isc_symtab_t *symtab = NULL;
507 dns_fixedname_t fixed;
508 const char *str;
509 dns_name_t *name;
510 isc_buffer_t b;
512 static intervaltable intervals[] = {
513 { "cleaning-interval", 60, 28 * 24 * 60 }, /* 28 days */
514 { "heartbeat-interval", 60, 28 * 24 * 60 }, /* 28 days */
515 { "interface-interval", 60, 28 * 24 * 60 }, /* 28 days */
516 { "max-transfer-idle-in", 60, 28 * 24 * 60 }, /* 28 days */
517 { "max-transfer-idle-out", 60, 28 * 24 * 60 }, /* 28 days */
518 { "max-transfer-time-in", 60, 28 * 24 * 60 }, /* 28 days */
519 { "max-transfer-time-out", 60, 28 * 24 * 60 }, /* 28 days */
520 { "sig-validity-interval", 86400, 10 * 366 }, /* 10 years */
521 { "statistics-interval", 60, 28 * 24 * 60 }, /* 28 days */
525 * Check that fields specified in units of time other than seconds
526 * have reasonable values.
528 for (i = 0; i < sizeof(intervals) / sizeof(intervals[0]); i++) {
529 isc_uint32_t val;
530 obj = NULL;
531 (void)cfg_map_get(options, intervals[i].name, &obj);
532 if (obj == NULL)
533 continue;
534 val = cfg_obj_asuint32(obj);
535 if (val > intervals[i].max) {
536 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
537 "%s '%u' is out of range (0..%u)",
538 intervals[i].name, val,
539 intervals[i].max);
540 result = ISC_R_RANGE;
541 } else if (val > (ISC_UINT32_MAX / intervals[i].scale)) {
542 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
543 "%s '%d' is out of range",
544 intervals[i].name, val);
545 result = ISC_R_RANGE;
548 obj = NULL;
549 (void)cfg_map_get(options, "preferred-glue", &obj);
550 if (obj != NULL) {
551 const char *str;
552 str = cfg_obj_asstring(obj);
553 if (strcasecmp(str, "a") != 0 &&
554 strcasecmp(str, "aaaa") != 0 &&
555 strcasecmp(str, "none") != 0)
556 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
557 "preferred-glue unexpected value '%s'",
558 str);
560 obj = NULL;
561 (void)cfg_map_get(options, "root-delegation-only", &obj);
562 if (obj != NULL) {
563 if (!cfg_obj_isvoid(obj)) {
564 const cfg_listelt_t *element;
565 const cfg_obj_t *exclude;
566 const char *str;
567 dns_fixedname_t fixed;
568 dns_name_t *name;
569 isc_buffer_t b;
571 dns_fixedname_init(&fixed);
572 name = dns_fixedname_name(&fixed);
573 for (element = cfg_list_first(obj);
574 element != NULL;
575 element = cfg_list_next(element)) {
576 exclude = cfg_listelt_value(element);
577 str = cfg_obj_asstring(exclude);
578 isc_buffer_init(&b, str, strlen(str));
579 isc_buffer_add(&b, strlen(str));
580 tresult = dns_name_fromtext(name, &b,
581 dns_rootname,
582 ISC_FALSE, NULL);
583 if (tresult != ISC_R_SUCCESS) {
584 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
585 "bad domain name '%s'",
586 str);
587 result = tresult;
594 * Set supported DNSSEC algorithms.
596 obj = NULL;
597 (void)cfg_map_get(options, "disable-algorithms", &obj);
598 if (obj != NULL) {
599 for (element = cfg_list_first(obj);
600 element != NULL;
601 element = cfg_list_next(element))
603 obj = cfg_listelt_value(element);
604 tresult = disabled_algorithms(obj, logctx);
605 if (tresult != ISC_R_SUCCESS)
606 result = tresult;
610 dns_fixedname_init(&fixed);
611 name = dns_fixedname_name(&fixed);
614 * Check the DLV zone name.
616 obj = NULL;
617 (void)cfg_map_get(options, "dnssec-lookaside", &obj);
618 if (obj != NULL) {
619 tresult = isc_symtab_create(mctx, 100, freekey, mctx,
620 ISC_FALSE, &symtab);
621 if (tresult != ISC_R_SUCCESS)
622 result = tresult;
623 for (element = cfg_list_first(obj);
624 element != NULL;
625 element = cfg_list_next(element))
627 const char *dlv;
629 obj = cfg_listelt_value(element);
631 dlv = cfg_obj_asstring(cfg_tuple_get(obj, "domain"));
632 isc_buffer_init(&b, dlv, strlen(dlv));
633 isc_buffer_add(&b, strlen(dlv));
634 tresult = dns_name_fromtext(name, &b, dns_rootname,
635 ISC_TRUE, NULL);
636 if (tresult != ISC_R_SUCCESS) {
637 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
638 "bad domain name '%s'", dlv);
639 result = tresult;
640 continue;
642 if (symtab != NULL) {
643 tresult = nameexist(obj, dlv, 1, symtab,
644 "dnssec-lookaside '%s': "
645 "already exists previous "
646 "definition: %s:%u",
647 logctx, mctx);
648 if (tresult != ISC_R_SUCCESS &&
649 result == ISC_R_SUCCESS)
650 result = tresult;
653 * XXXMPA to be removed when multiple lookaside
654 * namespaces are supported.
656 if (!dns_name_equal(dns_rootname, name)) {
657 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
658 "dnssec-lookaside '%s': "
659 "non-root not yet supported", dlv);
660 if (result == ISC_R_SUCCESS)
661 result = ISC_R_FAILURE;
663 dlv = cfg_obj_asstring(cfg_tuple_get(obj,
664 "trust-anchor"));
665 isc_buffer_init(&b, dlv, strlen(dlv));
666 isc_buffer_add(&b, strlen(dlv));
667 tresult = dns_name_fromtext(name, &b, dns_rootname,
668 ISC_TRUE, NULL);
669 if (tresult != ISC_R_SUCCESS) {
670 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
671 "bad domain name '%s'", dlv);
672 if (result == ISC_R_SUCCESS)
673 result = tresult;
676 if (symtab != NULL)
677 isc_symtab_destroy(&symtab);
681 * Check dnssec-must-be-secure.
683 obj = NULL;
684 (void)cfg_map_get(options, "dnssec-must-be-secure", &obj);
685 if (obj != NULL) {
686 isc_symtab_t *symtab = NULL;
687 tresult = isc_symtab_create(mctx, 100, freekey, mctx,
688 ISC_FALSE, &symtab);
689 if (tresult != ISC_R_SUCCESS)
690 result = tresult;
691 for (element = cfg_list_first(obj);
692 element != NULL;
693 element = cfg_list_next(element))
695 obj = cfg_listelt_value(element);
696 tresult = mustbesecure(obj, symtab, logctx, mctx);
697 if (tresult != ISC_R_SUCCESS)
698 result = tresult;
700 if (symtab != NULL)
701 isc_symtab_destroy(&symtab);
705 * Check empty zone configuration.
707 obj = NULL;
708 (void)cfg_map_get(options, "empty-server", &obj);
709 if (obj != NULL) {
710 str = cfg_obj_asstring(obj);
711 isc_buffer_init(&b, str, strlen(str));
712 isc_buffer_add(&b, strlen(str));
713 tresult = dns_name_fromtext(dns_fixedname_name(&fixed), &b,
714 dns_rootname, ISC_FALSE, NULL);
715 if (tresult != ISC_R_SUCCESS) {
716 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
717 "empty-server: invalid name '%s'", str);
718 result = ISC_R_FAILURE;
722 obj = NULL;
723 (void)cfg_map_get(options, "empty-contact", &obj);
724 if (obj != NULL) {
725 str = cfg_obj_asstring(obj);
726 isc_buffer_init(&b, str, strlen(str));
727 isc_buffer_add(&b, strlen(str));
728 tresult = dns_name_fromtext(dns_fixedname_name(&fixed), &b,
729 dns_rootname, ISC_FALSE, NULL);
730 if (tresult != ISC_R_SUCCESS) {
731 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
732 "empty-contact: invalid name '%s'", str);
733 result = ISC_R_FAILURE;
737 obj = NULL;
738 (void)cfg_map_get(options, "disable-empty-zone", &obj);
739 for (element = cfg_list_first(obj);
740 element != NULL;
741 element = cfg_list_next(element))
743 obj = cfg_listelt_value(element);
744 str = cfg_obj_asstring(obj);
745 isc_buffer_init(&b, str, strlen(str));
746 isc_buffer_add(&b, strlen(str));
747 tresult = dns_name_fromtext(dns_fixedname_name(&fixed), &b,
748 dns_rootname, ISC_FALSE, NULL);
749 if (tresult != ISC_R_SUCCESS) {
750 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
751 "disable-empty-zone: invalid name '%s'",
752 str);
753 result = ISC_R_FAILURE;
758 * Check that server-id is not too long.
759 * 1024 bytes should be big enough.
761 obj = NULL;
762 (void)cfg_map_get(options, "server-id", &obj);
763 if (obj != NULL && cfg_obj_isstring(obj) &&
764 strlen(cfg_obj_asstring(obj)) > 1024U) {
765 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
766 "'server-id' too big (>1024 bytes)");
767 result = ISC_R_FAILURE;
770 return (result);
773 static isc_result_t
774 get_masters_def(const cfg_obj_t *cctx, const char *name, const cfg_obj_t **ret) {
775 isc_result_t result;
776 const cfg_obj_t *masters = NULL;
777 const cfg_listelt_t *elt;
779 result = cfg_map_get(cctx, "masters", &masters);
780 if (result != ISC_R_SUCCESS)
781 return (result);
782 for (elt = cfg_list_first(masters);
783 elt != NULL;
784 elt = cfg_list_next(elt)) {
785 const cfg_obj_t *list;
786 const char *listname;
788 list = cfg_listelt_value(elt);
789 listname = cfg_obj_asstring(cfg_tuple_get(list, "name"));
791 if (strcasecmp(listname, name) == 0) {
792 *ret = list;
793 return (ISC_R_SUCCESS);
796 return (ISC_R_NOTFOUND);
799 static isc_result_t
800 validate_masters(const cfg_obj_t *obj, const cfg_obj_t *config,
801 isc_uint32_t *countp, isc_log_t *logctx, isc_mem_t *mctx)
803 isc_result_t result = ISC_R_SUCCESS;
804 isc_result_t tresult;
805 isc_uint32_t count = 0;
806 isc_symtab_t *symtab = NULL;
807 isc_symvalue_t symvalue;
808 const cfg_listelt_t *element;
809 const cfg_listelt_t **stack = NULL;
810 isc_uint32_t stackcount = 0, pushed = 0;
811 const cfg_obj_t *list;
813 REQUIRE(countp != NULL);
814 result = isc_symtab_create(mctx, 100, NULL, NULL, ISC_FALSE, &symtab);
815 if (result != ISC_R_SUCCESS) {
816 *countp = count;
817 return (result);
820 newlist:
821 list = cfg_tuple_get(obj, "addresses");
822 element = cfg_list_first(list);
823 resume:
824 for ( ;
825 element != NULL;
826 element = cfg_list_next(element))
828 const char *listname;
829 const cfg_obj_t *addr;
830 const cfg_obj_t *key;
832 addr = cfg_tuple_get(cfg_listelt_value(element),
833 "masterselement");
834 key = cfg_tuple_get(cfg_listelt_value(element), "key");
836 if (cfg_obj_issockaddr(addr)) {
837 count++;
838 continue;
840 if (!cfg_obj_isvoid(key)) {
841 cfg_obj_log(key, logctx, ISC_LOG_ERROR,
842 "unexpected token '%s'",
843 cfg_obj_asstring(key));
844 if (result == ISC_R_SUCCESS)
845 result = ISC_R_FAILURE;
847 listname = cfg_obj_asstring(addr);
848 symvalue.as_cpointer = addr;
849 tresult = isc_symtab_define(symtab, listname, 1, symvalue,
850 isc_symexists_reject);
851 if (tresult == ISC_R_EXISTS)
852 continue;
853 tresult = get_masters_def(config, listname, &obj);
854 if (tresult != ISC_R_SUCCESS) {
855 if (result == ISC_R_SUCCESS)
856 result = tresult;
857 cfg_obj_log(addr, logctx, ISC_LOG_ERROR,
858 "unable to find masters list '%s'",
859 listname);
860 continue;
862 /* Grow stack? */
863 if (stackcount == pushed) {
864 void * new;
865 isc_uint32_t newlen = stackcount + 16;
866 size_t newsize, oldsize;
868 newsize = newlen * sizeof(*stack);
869 oldsize = stackcount * sizeof(*stack);
870 new = isc_mem_get(mctx, newsize);
871 if (new == NULL)
872 goto cleanup;
873 if (stackcount != 0) {
874 void *ptr;
876 DE_CONST(stack, ptr);
877 memcpy(new, stack, oldsize);
878 isc_mem_put(mctx, ptr, oldsize);
880 stack = new;
881 stackcount = newlen;
883 stack[pushed++] = cfg_list_next(element);
884 goto newlist;
886 if (pushed != 0) {
887 element = stack[--pushed];
888 goto resume;
890 cleanup:
891 if (stack != NULL) {
892 void *ptr;
894 DE_CONST(stack, ptr);
895 isc_mem_put(mctx, ptr, stackcount * sizeof(*stack));
897 isc_symtab_destroy(&symtab);
898 *countp = count;
899 return (result);
902 static isc_result_t
903 check_update_policy(const cfg_obj_t *policy, isc_log_t *logctx) {
904 isc_result_t result = ISC_R_SUCCESS;
905 isc_result_t tresult;
906 const cfg_listelt_t *element;
907 const cfg_listelt_t *element2;
908 dns_fixedname_t fixed;
909 const char *str;
910 isc_buffer_t b;
912 for (element = cfg_list_first(policy);
913 element != NULL;
914 element = cfg_list_next(element))
916 const cfg_obj_t *stmt = cfg_listelt_value(element);
917 const cfg_obj_t *identity = cfg_tuple_get(stmt, "identity");
918 const cfg_obj_t *matchtype = cfg_tuple_get(stmt, "matchtype");
919 const cfg_obj_t *dname = cfg_tuple_get(stmt, "name");
920 const cfg_obj_t *typelist = cfg_tuple_get(stmt, "types");
922 dns_fixedname_init(&fixed);
923 str = cfg_obj_asstring(identity);
924 isc_buffer_init(&b, str, strlen(str));
925 isc_buffer_add(&b, strlen(str));
926 tresult = dns_name_fromtext(dns_fixedname_name(&fixed), &b,
927 dns_rootname, ISC_FALSE, NULL);
928 if (tresult != ISC_R_SUCCESS) {
929 cfg_obj_log(identity, logctx, ISC_LOG_ERROR,
930 "'%s' is not a valid name", str);
931 result = tresult;
934 dns_fixedname_init(&fixed);
935 str = cfg_obj_asstring(dname);
936 isc_buffer_init(&b, str, strlen(str));
937 isc_buffer_add(&b, strlen(str));
938 tresult = dns_name_fromtext(dns_fixedname_name(&fixed), &b,
939 dns_rootname, ISC_FALSE, NULL);
940 if (tresult != ISC_R_SUCCESS) {
941 cfg_obj_log(dname, logctx, ISC_LOG_ERROR,
942 "'%s' is not a valid name", str);
943 result = tresult;
945 if (tresult == ISC_R_SUCCESS &&
946 strcasecmp(cfg_obj_asstring(matchtype), "wildcard") == 0 &&
947 !dns_name_iswildcard(dns_fixedname_name(&fixed))) {
948 cfg_obj_log(identity, logctx, ISC_LOG_ERROR,
949 "'%s' is not a wildcard", str);
950 result = ISC_R_FAILURE;
953 for (element2 = cfg_list_first(typelist);
954 element2 != NULL;
955 element2 = cfg_list_next(element2))
957 const cfg_obj_t *typeobj;
958 isc_textregion_t r;
959 dns_rdatatype_t type;
961 typeobj = cfg_listelt_value(element2);
962 DE_CONST(cfg_obj_asstring(typeobj), r.base);
963 r.length = strlen(r.base);
965 tresult = dns_rdatatype_fromtext(&type, &r);
966 if (tresult != ISC_R_SUCCESS) {
967 cfg_obj_log(typeobj, logctx, ISC_LOG_ERROR,
968 "'%s' is not a valid type", r.base);
969 result = tresult;
973 return (result);
976 #define MASTERZONE 1
977 #define SLAVEZONE 2
978 #define STUBZONE 4
979 #define HINTZONE 8
980 #define FORWARDZONE 16
981 #define DELEGATIONZONE 32
982 #define CHECKACL 64
984 typedef struct {
985 const char *name;
986 int allowed;
987 } optionstable;
989 static isc_result_t
990 check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions,
991 const cfg_obj_t *config, isc_symtab_t *symtab,
992 dns_rdataclass_t defclass, cfg_aclconfctx_t *actx,
993 isc_log_t *logctx, isc_mem_t *mctx)
995 const char *zname;
996 const char *typestr;
997 unsigned int ztype;
998 const cfg_obj_t *zoptions;
999 const cfg_obj_t *obj = NULL;
1000 isc_result_t result = ISC_R_SUCCESS;
1001 isc_result_t tresult;
1002 unsigned int i;
1003 dns_rdataclass_t zclass;
1004 dns_fixedname_t fixedname;
1005 isc_buffer_t b;
1006 isc_boolean_t root = ISC_FALSE;
1008 static optionstable options[] = {
1009 { "allow-query", MASTERZONE | SLAVEZONE | STUBZONE | CHECKACL },
1010 { "allow-notify", SLAVEZONE | CHECKACL },
1011 { "allow-transfer", MASTERZONE | SLAVEZONE | CHECKACL },
1012 { "notify", MASTERZONE | SLAVEZONE },
1013 { "also-notify", MASTERZONE | SLAVEZONE },
1014 { "dialup", MASTERZONE | SLAVEZONE | STUBZONE },
1015 { "delegation-only", HINTZONE | STUBZONE | DELEGATIONZONE },
1016 { "forward", MASTERZONE | SLAVEZONE | STUBZONE | FORWARDZONE },
1017 { "forwarders", MASTERZONE | SLAVEZONE | STUBZONE | FORWARDZONE },
1018 { "maintain-ixfr-base", MASTERZONE | SLAVEZONE },
1019 { "max-ixfr-log-size", MASTERZONE | SLAVEZONE },
1020 { "notify-source", MASTERZONE | SLAVEZONE },
1021 { "notify-source-v6", MASTERZONE | SLAVEZONE },
1022 { "transfer-source", SLAVEZONE | STUBZONE },
1023 { "transfer-source-v6", SLAVEZONE | STUBZONE },
1024 { "max-transfer-time-in", SLAVEZONE | STUBZONE },
1025 { "max-transfer-time-out", MASTERZONE | SLAVEZONE },
1026 { "max-transfer-idle-in", SLAVEZONE | STUBZONE },
1027 { "max-transfer-idle-out", MASTERZONE | SLAVEZONE },
1028 { "max-retry-time", SLAVEZONE | STUBZONE },
1029 { "min-retry-time", SLAVEZONE | STUBZONE },
1030 { "max-refresh-time", SLAVEZONE | STUBZONE },
1031 { "min-refresh-time", SLAVEZONE | STUBZONE },
1032 { "sig-validity-interval", MASTERZONE },
1033 { "zone-statistics", MASTERZONE | SLAVEZONE | STUBZONE },
1034 { "allow-update", MASTERZONE | CHECKACL },
1035 { "allow-update-forwarding", SLAVEZONE | CHECKACL },
1036 { "file", MASTERZONE | SLAVEZONE | STUBZONE | HINTZONE },
1037 { "journal", MASTERZONE | SLAVEZONE },
1038 { "ixfr-base", MASTERZONE | SLAVEZONE },
1039 { "ixfr-tmp-file", MASTERZONE | SLAVEZONE },
1040 { "masters", SLAVEZONE | STUBZONE },
1041 { "pubkey", MASTERZONE | SLAVEZONE | STUBZONE },
1042 { "update-policy", MASTERZONE },
1043 { "database", MASTERZONE | SLAVEZONE | STUBZONE },
1044 { "key-directory", MASTERZONE },
1045 { "check-wildcard", MASTERZONE },
1046 { "check-mx", MASTERZONE },
1047 { "integrity-check", MASTERZONE },
1048 { "check-mx-cname", MASTERZONE },
1049 { "check-srv-cname", MASTERZONE },
1050 { "masterfile-format", MASTERZONE | SLAVEZONE | STUBZONE | HINTZONE },
1051 { "update-check-ksk", MASTERZONE },
1052 { "try-tcp-refresh", SLAVEZONE },
1055 static optionstable dialups[] = {
1056 { "notify", MASTERZONE | SLAVEZONE },
1057 { "notify-passive", SLAVEZONE },
1058 { "refresh", SLAVEZONE | STUBZONE },
1059 { "passive", SLAVEZONE | STUBZONE },
1062 zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
1064 zoptions = cfg_tuple_get(zconfig, "options");
1066 obj = NULL;
1067 (void)cfg_map_get(zoptions, "type", &obj);
1068 if (obj == NULL) {
1069 cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
1070 "zone '%s': type not present", zname);
1071 return (ISC_R_FAILURE);
1074 typestr = cfg_obj_asstring(obj);
1075 if (strcasecmp(typestr, "master") == 0)
1076 ztype = MASTERZONE;
1077 else if (strcasecmp(typestr, "slave") == 0)
1078 ztype = SLAVEZONE;
1079 else if (strcasecmp(typestr, "stub") == 0)
1080 ztype = STUBZONE;
1081 else if (strcasecmp(typestr, "forward") == 0)
1082 ztype = FORWARDZONE;
1083 else if (strcasecmp(typestr, "hint") == 0)
1084 ztype = HINTZONE;
1085 else if (strcasecmp(typestr, "delegation-only") == 0)
1086 ztype = DELEGATIONZONE;
1087 else {
1088 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1089 "zone '%s': invalid type %s",
1090 zname, typestr);
1091 return (ISC_R_FAILURE);
1094 obj = cfg_tuple_get(zconfig, "class");
1095 if (cfg_obj_isstring(obj)) {
1096 isc_textregion_t r;
1098 DE_CONST(cfg_obj_asstring(obj), r.base);
1099 r.length = strlen(r.base);
1100 result = dns_rdataclass_fromtext(&zclass, &r);
1101 if (result != ISC_R_SUCCESS) {
1102 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1103 "zone '%s': invalid class %s",
1104 zname, r.base);
1105 return (ISC_R_FAILURE);
1107 if (zclass != defclass) {
1108 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1109 "zone '%s': class '%s' does not "
1110 "match view/default class",
1111 zname, r.base);
1112 return (ISC_R_FAILURE);
1117 * Look for an already existing zone.
1118 * We need to make this canonical as isc_symtab_define()
1119 * deals with strings.
1121 dns_fixedname_init(&fixedname);
1122 isc_buffer_init(&b, zname, strlen(zname));
1123 isc_buffer_add(&b, strlen(zname));
1124 tresult = dns_name_fromtext(dns_fixedname_name(&fixedname), &b,
1125 dns_rootname, ISC_TRUE, NULL);
1126 if (tresult != ISC_R_SUCCESS) {
1127 cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
1128 "zone '%s': is not a valid name", zname);
1129 result = ISC_R_FAILURE;
1130 } else {
1131 char namebuf[DNS_NAME_FORMATSIZE];
1133 dns_name_format(dns_fixedname_name(&fixedname),
1134 namebuf, sizeof(namebuf));
1135 tresult = nameexist(zconfig, namebuf, ztype == HINTZONE ? 1 : 2,
1136 symtab, "zone '%s': already exists "
1137 "previous definition: %s:%u", logctx, mctx);
1138 if (tresult != ISC_R_SUCCESS)
1139 result = tresult;
1140 if (dns_name_equal(dns_fixedname_name(&fixedname),
1141 dns_rootname))
1142 root = ISC_TRUE;
1146 * Look for inappropriate options for the given zone type.
1147 * Check that ACLs expand correctly.
1149 for (i = 0; i < sizeof(options) / sizeof(options[0]); i++) {
1150 obj = NULL;
1151 if ((options[i].allowed & ztype) == 0 &&
1152 cfg_map_get(zoptions, options[i].name, &obj) ==
1153 ISC_R_SUCCESS)
1155 if (strcmp(options[i].name, "allow-update") != 0 ||
1156 ztype != SLAVEZONE) {
1157 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1158 "option '%s' is not allowed "
1159 "in '%s' zone '%s'",
1160 options[i].name, typestr, zname);
1161 result = ISC_R_FAILURE;
1162 } else
1163 cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
1164 "option '%s' is not allowed "
1165 "in '%s' zone '%s'",
1166 options[i].name, typestr, zname);
1168 obj = NULL;
1169 if ((options[i].allowed & ztype) != 0 &&
1170 (options[i].allowed & CHECKACL) != 0) {
1172 tresult = checkacl(options[i].name, actx, zconfig,
1173 voptions, config, logctx, mctx);
1174 if (tresult != ISC_R_SUCCESS)
1175 result = tresult;
1181 * Slave & stub zones must have a "masters" field.
1183 if (ztype == SLAVEZONE || ztype == STUBZONE) {
1184 obj = NULL;
1185 if (cfg_map_get(zoptions, "masters", &obj) != ISC_R_SUCCESS) {
1186 cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR,
1187 "zone '%s': missing 'masters' entry",
1188 zname);
1189 result = ISC_R_FAILURE;
1190 } else {
1191 isc_uint32_t count;
1192 tresult = validate_masters(obj, config, &count,
1193 logctx, mctx);
1194 if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS)
1195 result = tresult;
1196 if (tresult == ISC_R_SUCCESS && count == 0) {
1197 cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR,
1198 "zone '%s': empty 'masters' entry",
1199 zname);
1200 result = ISC_R_FAILURE;
1206 * Master zones can't have both "allow-update" and "update-policy".
1208 if (ztype == MASTERZONE) {
1209 isc_result_t res1, res2;
1210 obj = NULL;
1211 res1 = cfg_map_get(zoptions, "allow-update", &obj);
1212 obj = NULL;
1213 res2 = cfg_map_get(zoptions, "update-policy", &obj);
1214 if (res1 == ISC_R_SUCCESS && res2 == ISC_R_SUCCESS) {
1215 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1216 "zone '%s': 'allow-update' is ignored "
1217 "when 'update-policy' is present",
1218 zname);
1219 result = ISC_R_FAILURE;
1220 } else if (res2 == ISC_R_SUCCESS &&
1221 check_update_policy(obj, logctx) != ISC_R_SUCCESS)
1222 result = ISC_R_FAILURE;
1226 * Check the excessively complicated "dialup" option.
1228 if (ztype == MASTERZONE || ztype == SLAVEZONE || ztype == STUBZONE) {
1229 const cfg_obj_t *dialup = NULL;
1230 (void)cfg_map_get(zoptions, "dialup", &dialup);
1231 if (dialup != NULL && cfg_obj_isstring(dialup)) {
1232 const char *str = cfg_obj_asstring(dialup);
1233 for (i = 0;
1234 i < sizeof(dialups) / sizeof(dialups[0]);
1235 i++)
1237 if (strcasecmp(dialups[i].name, str) != 0)
1238 continue;
1239 if ((dialups[i].allowed & ztype) == 0) {
1240 cfg_obj_log(obj, logctx,
1241 ISC_LOG_ERROR,
1242 "dialup type '%s' is not "
1243 "allowed in '%s' "
1244 "zone '%s'",
1245 str, typestr, zname);
1246 result = ISC_R_FAILURE;
1248 break;
1250 if (i == sizeof(dialups) / sizeof(dialups[0])) {
1251 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1252 "invalid dialup type '%s' in zone "
1253 "'%s'", str, zname);
1254 result = ISC_R_FAILURE;
1260 * Check that forwarding is reasonable.
1262 obj = NULL;
1263 if (root) {
1264 if (voptions != NULL)
1265 (void)cfg_map_get(voptions, "forwarders", &obj);
1266 if (obj == NULL) {
1267 const cfg_obj_t *options = NULL;
1268 (void)cfg_map_get(config, "options", &options);
1269 if (options != NULL)
1270 (void)cfg_map_get(options, "forwarders", &obj);
1273 if (check_forward(zoptions, obj, logctx) != ISC_R_SUCCESS)
1274 result = ISC_R_FAILURE;
1277 * Check various options.
1279 tresult = check_options(zoptions, logctx, mctx);
1280 if (tresult != ISC_R_SUCCESS)
1281 result = tresult;
1284 * If the zone type is rbt/rbt64 then master/hint zones
1285 * require file clauses.
1287 obj = NULL;
1288 tresult = cfg_map_get(zoptions, "database", &obj);
1289 if (tresult == ISC_R_NOTFOUND ||
1290 (tresult == ISC_R_SUCCESS &&
1291 (strcmp("rbt", cfg_obj_asstring(obj)) == 0 ||
1292 strcmp("rbt64", cfg_obj_asstring(obj)) == 0))) {
1293 obj = NULL;
1294 tresult = cfg_map_get(zoptions, "file", &obj);
1295 if (tresult != ISC_R_SUCCESS &&
1296 (ztype == MASTERZONE || ztype == HINTZONE)) {
1297 cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
1298 "zone '%s': missing 'file' entry",
1299 zname);
1300 result = tresult;
1304 return (result);
1308 typedef struct keyalgorithms {
1309 const char *name;
1310 isc_uint16_t size;
1311 } algorithmtable;
1313 isc_result_t
1314 bind9_check_key(const cfg_obj_t *key, isc_log_t *logctx) {
1315 const cfg_obj_t *algobj = NULL;
1316 const cfg_obj_t *secretobj = NULL;
1317 const char *keyname = cfg_obj_asstring(cfg_map_getname(key));
1318 const char *algorithm;
1319 int i;
1320 size_t len = 0;
1321 static const algorithmtable algorithms[] = {
1322 { "hmac-md5", 128 },
1323 { "hmac-md5.sig-alg.reg.int", 0 },
1324 { "hmac-md5.sig-alg.reg.int.", 0 },
1325 { "hmac-sha1", 160 },
1326 { "hmac-sha224", 224 },
1327 { "hmac-sha256", 256 },
1328 { "hmac-sha384", 384 },
1329 { "hmac-sha512", 512 },
1330 { NULL, 0 }
1333 (void)cfg_map_get(key, "algorithm", &algobj);
1334 (void)cfg_map_get(key, "secret", &secretobj);
1335 if (secretobj == NULL || algobj == NULL) {
1336 cfg_obj_log(key, logctx, ISC_LOG_ERROR,
1337 "key '%s' must have both 'secret' and "
1338 "'algorithm' defined",
1339 keyname);
1340 return (ISC_R_FAILURE);
1343 algorithm = cfg_obj_asstring(algobj);
1344 for (i = 0; algorithms[i].name != NULL; i++) {
1345 len = strlen(algorithms[i].name);
1346 if (strncasecmp(algorithms[i].name, algorithm, len) == 0 &&
1347 (algorithm[len] == '\0' ||
1348 (algorithms[i].size != 0 && algorithm[len] == '-')))
1349 break;
1351 if (algorithms[i].name == NULL) {
1352 cfg_obj_log(algobj, logctx, ISC_LOG_ERROR,
1353 "unknown algorithm '%s'", algorithm);
1354 return (ISC_R_NOTFOUND);
1356 if (algorithm[len] == '-') {
1357 isc_uint16_t digestbits;
1358 isc_result_t result;
1359 result = isc_parse_uint16(&digestbits, algorithm + len + 1, 10);
1360 if (result == ISC_R_SUCCESS || result == ISC_R_RANGE) {
1361 if (result == ISC_R_RANGE ||
1362 digestbits > algorithms[i].size) {
1363 cfg_obj_log(algobj, logctx, ISC_LOG_ERROR,
1364 "key '%s' digest-bits too large "
1365 "[%u..%u]", keyname,
1366 algorithms[i].size / 2,
1367 algorithms[i].size);
1368 return (ISC_R_RANGE);
1370 if ((digestbits % 8) != 0) {
1371 cfg_obj_log(algobj, logctx, ISC_LOG_ERROR,
1372 "key '%s' digest-bits not multiple"
1373 " of 8", keyname);
1374 return (ISC_R_RANGE);
1377 * Recommended minima for hmac algorithms.
1379 if ((digestbits < (algorithms[i].size / 2U) ||
1380 (digestbits < 80U)))
1381 cfg_obj_log(algobj, logctx, ISC_LOG_WARNING,
1382 "key '%s' digest-bits too small "
1383 "[<%u]", keyname,
1384 algorithms[i].size/2);
1385 } else {
1386 cfg_obj_log(algobj, logctx, ISC_LOG_ERROR,
1387 "key '%s': unable to parse digest-bits",
1388 keyname);
1389 return (result);
1392 return (ISC_R_SUCCESS);
1396 * Check key list for duplicates key names and that the key names
1397 * are valid domain names as these keys are used for TSIG.
1399 * Check the key contents for validity.
1401 static isc_result_t
1402 check_keylist(const cfg_obj_t *keys, isc_symtab_t *symtab,
1403 isc_mem_t *mctx, isc_log_t *logctx)
1405 char namebuf[DNS_NAME_FORMATSIZE];
1406 dns_fixedname_t fname;
1407 dns_name_t *name;
1408 isc_result_t result = ISC_R_SUCCESS;
1409 isc_result_t tresult;
1410 const cfg_listelt_t *element;
1412 dns_fixedname_init(&fname);
1413 name = dns_fixedname_name(&fname);
1414 for (element = cfg_list_first(keys);
1415 element != NULL;
1416 element = cfg_list_next(element))
1418 const cfg_obj_t *key = cfg_listelt_value(element);
1419 const char *keyid = cfg_obj_asstring(cfg_map_getname(key));
1420 isc_symvalue_t symvalue;
1421 isc_buffer_t b;
1422 char *keyname;
1424 isc_buffer_init(&b, keyid, strlen(keyid));
1425 isc_buffer_add(&b, strlen(keyid));
1426 tresult = dns_name_fromtext(name, &b, dns_rootname,
1427 ISC_FALSE, NULL);
1428 if (tresult != ISC_R_SUCCESS) {
1429 cfg_obj_log(key, logctx, ISC_LOG_ERROR,
1430 "key '%s': bad key name", keyid);
1431 result = tresult;
1432 continue;
1434 tresult = bind9_check_key(key, logctx);
1435 if (tresult != ISC_R_SUCCESS)
1436 return (tresult);
1438 dns_name_format(name, namebuf, sizeof(namebuf));
1439 keyname = isc_mem_strdup(mctx, namebuf);
1440 if (keyname == NULL)
1441 return (ISC_R_NOMEMORY);
1442 symvalue.as_cpointer = key;
1443 tresult = isc_symtab_define(symtab, keyname, 1, symvalue,
1444 isc_symexists_reject);
1445 if (tresult == ISC_R_EXISTS) {
1446 const char *file;
1447 unsigned int line;
1449 RUNTIME_CHECK(isc_symtab_lookup(symtab, keyname,
1450 1, &symvalue) == ISC_R_SUCCESS);
1451 file = cfg_obj_file(symvalue.as_cpointer);
1452 line = cfg_obj_line(symvalue.as_cpointer);
1454 if (file == NULL)
1455 file = "<unknown file>";
1456 cfg_obj_log(key, logctx, ISC_LOG_ERROR,
1457 "key '%s': already exists "
1458 "previous definition: %s:%u",
1459 keyid, file, line);
1460 isc_mem_free(mctx, keyname);
1461 result = tresult;
1462 } else if (tresult != ISC_R_SUCCESS) {
1463 isc_mem_free(mctx, keyname);
1464 return (tresult);
1467 return (result);
1470 static struct {
1471 const char *v4;
1472 const char *v6;
1473 } sources[] = {
1474 { "transfer-source", "transfer-source-v6" },
1475 { "notify-source", "notify-source-v6" },
1476 { "query-source", "query-source-v6" },
1477 { NULL, NULL }
1481 * RNDC keys are not normalised unlike TSIG keys.
1483 * "foo." is different to "foo".
1485 static isc_boolean_t
1486 rndckey_exists(const cfg_obj_t *keylist, const char *keyname) {
1487 const cfg_listelt_t *element;
1488 const cfg_obj_t *obj;
1489 const char *str;
1491 if (keylist == NULL)
1492 return (ISC_FALSE);
1494 for (element = cfg_list_first(keylist);
1495 element != NULL;
1496 element = cfg_list_next(element))
1498 obj = cfg_listelt_value(element);
1499 str = cfg_obj_asstring(cfg_map_getname(obj));
1500 if (!strcasecmp(str, keyname))
1501 return (ISC_TRUE);
1503 return (ISC_FALSE);
1506 static isc_result_t
1507 check_servers(const cfg_obj_t *config, const cfg_obj_t *voptions,
1508 isc_symtab_t *symtab, isc_log_t *logctx)
1510 dns_fixedname_t fname;
1511 isc_result_t result = ISC_R_SUCCESS;
1512 isc_result_t tresult;
1513 const cfg_listelt_t *e1, *e2;
1514 const cfg_obj_t *v1, *v2, *keys;
1515 const cfg_obj_t *servers;
1516 isc_netaddr_t n1, n2;
1517 unsigned int p1, p2;
1518 const cfg_obj_t *obj;
1519 char buf[ISC_NETADDR_FORMATSIZE];
1520 char namebuf[DNS_NAME_FORMATSIZE];
1521 const char *xfr;
1522 const char *keyval;
1523 isc_buffer_t b;
1524 int source;
1525 dns_name_t *keyname;
1527 servers = NULL;
1528 if (voptions != NULL)
1529 (void)cfg_map_get(voptions, "server", &servers);
1530 if (servers == NULL)
1531 (void)cfg_map_get(config, "server", &servers);
1532 if (servers == NULL)
1533 return (ISC_R_SUCCESS);
1535 for (e1 = cfg_list_first(servers); e1 != NULL; e1 = cfg_list_next(e1)) {
1536 v1 = cfg_listelt_value(e1);
1537 cfg_obj_asnetprefix(cfg_map_getname(v1), &n1, &p1);
1539 * Check that unused bits are zero.
1541 tresult = isc_netaddr_prefixok(&n1, p1);
1542 if (tresult != ISC_R_SUCCESS) {
1543 INSIST(tresult == ISC_R_FAILURE);
1544 isc_netaddr_format(&n1, buf, sizeof(buf));
1545 cfg_obj_log(v1, logctx, ISC_LOG_ERROR,
1546 "server '%s/%u': invalid prefix "
1547 "(extra bits specified)", buf, p1);
1548 result = tresult;
1550 source = 0;
1551 do {
1552 obj = NULL;
1553 if (n1.family == AF_INET)
1554 xfr = sources[source].v6;
1555 else
1556 xfr = sources[source].v4;
1557 (void)cfg_map_get(v1, xfr, &obj);
1558 if (obj != NULL) {
1559 isc_netaddr_format(&n1, buf, sizeof(buf));
1560 cfg_obj_log(v1, logctx, ISC_LOG_ERROR,
1561 "server '%s/%u': %s not legal",
1562 buf, p1, xfr);
1563 result = ISC_R_FAILURE;
1565 } while (sources[++source].v4 != NULL);
1566 e2 = e1;
1567 while ((e2 = cfg_list_next(e2)) != NULL) {
1568 v2 = cfg_listelt_value(e2);
1569 cfg_obj_asnetprefix(cfg_map_getname(v2), &n2, &p2);
1570 if (p1 == p2 && isc_netaddr_equal(&n1, &n2)) {
1571 const char *file = cfg_obj_file(v1);
1572 unsigned int line = cfg_obj_line(v1);
1574 if (file == NULL)
1575 file = "<unknown file>";
1577 isc_netaddr_format(&n2, buf, sizeof(buf));
1578 cfg_obj_log(v2, logctx, ISC_LOG_ERROR,
1579 "server '%s/%u': already exists "
1580 "previous definition: %s:%u",
1581 buf, p2, file, line);
1582 result = ISC_R_FAILURE;
1585 keys = NULL;
1586 cfg_map_get(v1, "keys", &keys);
1587 if (keys != NULL) {
1589 * Normalize key name.
1591 keyval = cfg_obj_asstring(keys);
1592 dns_fixedname_init(&fname);
1593 isc_buffer_init(&b, keyval, strlen(keyval));
1594 isc_buffer_add(&b, strlen(keyval));
1595 keyname = dns_fixedname_name(&fname);
1596 tresult = dns_name_fromtext(keyname, &b, dns_rootname,
1597 ISC_FALSE, NULL);
1598 if (tresult != ISC_R_SUCCESS) {
1599 cfg_obj_log(keys, logctx, ISC_LOG_ERROR,
1600 "bad key name '%s'", keyval);
1601 result = ISC_R_FAILURE;
1602 continue;
1604 dns_name_format(keyname, namebuf, sizeof(namebuf));
1605 tresult = isc_symtab_lookup(symtab, namebuf, 1, NULL);
1606 if (tresult != ISC_R_SUCCESS) {
1607 cfg_obj_log(keys, logctx, ISC_LOG_ERROR,
1608 "unknown key '%s'", keyval);
1609 result = ISC_R_FAILURE;
1613 return (result);
1616 static isc_result_t
1617 check_viewconf(const cfg_obj_t *config, const cfg_obj_t *voptions,
1618 const char *viewname, dns_rdataclass_t vclass,
1619 isc_log_t *logctx, isc_mem_t *mctx)
1621 const cfg_obj_t *zones = NULL;
1622 const cfg_obj_t *keys = NULL;
1623 const cfg_listelt_t *element;
1624 isc_symtab_t *symtab = NULL;
1625 isc_result_t result = ISC_R_SUCCESS;
1626 isc_result_t tresult = ISC_R_SUCCESS;
1627 cfg_aclconfctx_t actx;
1628 const cfg_obj_t *obj;
1629 isc_boolean_t enablednssec, enablevalidation;
1632 * Check that all zone statements are syntactically correct and
1633 * there are no duplicate zones.
1635 tresult = isc_symtab_create(mctx, 100, freekey, mctx,
1636 ISC_FALSE, &symtab);
1637 if (tresult != ISC_R_SUCCESS)
1638 return (ISC_R_NOMEMORY);
1640 cfg_aclconfctx_init(&actx);
1642 if (voptions != NULL)
1643 (void)cfg_map_get(voptions, "zone", &zones);
1644 else
1645 (void)cfg_map_get(config, "zone", &zones);
1647 for (element = cfg_list_first(zones);
1648 element != NULL;
1649 element = cfg_list_next(element))
1651 isc_result_t tresult;
1652 const cfg_obj_t *zone = cfg_listelt_value(element);
1654 tresult = check_zoneconf(zone, voptions, config, symtab,
1655 vclass, &actx, logctx, mctx);
1656 if (tresult != ISC_R_SUCCESS)
1657 result = ISC_R_FAILURE;
1660 isc_symtab_destroy(&symtab);
1663 * Check that forwarding is reasonable.
1665 if (voptions == NULL) {
1666 const cfg_obj_t *options = NULL;
1667 (void)cfg_map_get(config, "options", &options);
1668 if (options != NULL)
1669 if (check_forward(options, NULL,
1670 logctx) != ISC_R_SUCCESS)
1671 result = ISC_R_FAILURE;
1672 } else {
1673 if (check_forward(voptions, NULL, logctx) != ISC_R_SUCCESS)
1674 result = ISC_R_FAILURE;
1678 * Check that dual-stack-servers is reasonable.
1680 if (voptions == NULL) {
1681 const cfg_obj_t *options = NULL;
1682 (void)cfg_map_get(config, "options", &options);
1683 if (options != NULL)
1684 if (check_dual_stack(options, logctx) != ISC_R_SUCCESS)
1685 result = ISC_R_FAILURE;
1686 } else {
1687 if (check_dual_stack(voptions, logctx) != ISC_R_SUCCESS)
1688 result = ISC_R_FAILURE;
1692 * Check that rrset-order is reasonable.
1694 if (voptions != NULL) {
1695 if (check_order(voptions, logctx) != ISC_R_SUCCESS)
1696 result = ISC_R_FAILURE;
1700 * Check that all key statements are syntactically correct and
1701 * there are no duplicate keys.
1703 tresult = isc_symtab_create(mctx, 100, freekey, mctx,
1704 ISC_FALSE, &symtab);
1705 if (tresult != ISC_R_SUCCESS)
1706 return (ISC_R_NOMEMORY);
1708 (void)cfg_map_get(config, "key", &keys);
1709 tresult = check_keylist(keys, symtab, mctx, logctx);
1710 if (tresult == ISC_R_EXISTS)
1711 result = ISC_R_FAILURE;
1712 else if (tresult != ISC_R_SUCCESS) {
1713 isc_symtab_destroy(&symtab);
1714 return (tresult);
1717 if (voptions != NULL) {
1718 keys = NULL;
1719 (void)cfg_map_get(voptions, "key", &keys);
1720 tresult = check_keylist(keys, symtab, mctx, logctx);
1721 if (tresult == ISC_R_EXISTS)
1722 result = ISC_R_FAILURE;
1723 else if (tresult != ISC_R_SUCCESS) {
1724 isc_symtab_destroy(&symtab);
1725 return (tresult);
1730 * Global servers can refer to keys in views.
1732 if (check_servers(config, voptions, symtab, logctx) != ISC_R_SUCCESS)
1733 result = ISC_R_FAILURE;
1735 isc_symtab_destroy(&symtab);
1738 * Check that dnssec-enable/dnssec-validation are sensible.
1740 obj = NULL;
1741 if (voptions != NULL)
1742 (void)cfg_map_get(voptions, "dnssec-enable", &obj);
1743 if (obj == NULL)
1744 (void)cfg_map_get(config, "dnssec-enable", &obj);
1745 if (obj == NULL)
1746 enablednssec = ISC_TRUE;
1747 else
1748 enablednssec = cfg_obj_asboolean(obj);
1750 obj = NULL;
1751 if (voptions != NULL)
1752 (void)cfg_map_get(voptions, "dnssec-validation", &obj);
1753 if (obj == NULL)
1754 (void)cfg_map_get(config, "dnssec-validation", &obj);
1755 if (obj == NULL)
1756 enablevalidation = ISC_FALSE; /* XXXMPA Change for 9.5. */
1757 else
1758 enablevalidation = cfg_obj_asboolean(obj);
1760 if (enablevalidation && !enablednssec)
1761 cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
1762 "'dnssec-validation yes;' and 'dnssec-enable no;'");
1764 if (voptions != NULL)
1765 tresult = check_options(voptions, logctx, mctx);
1766 else
1767 tresult = check_options(config, logctx, mctx);
1768 if (tresult != ISC_R_SUCCESS)
1769 result = tresult;
1771 tresult = check_viewacls(&actx, voptions, config, logctx, mctx);
1772 if (tresult != ISC_R_SUCCESS)
1773 result = tresult;
1775 tresult = check_recursionacls(&actx, voptions, viewname,
1776 config, logctx, mctx);
1777 if (tresult != ISC_R_SUCCESS)
1778 result = tresult;
1780 cfg_aclconfctx_destroy(&actx);
1782 return (result);
1785 static const char *
1786 default_channels[] = {
1787 "default_syslog",
1788 "default_stderr",
1789 "default_debug",
1790 "null",
1791 NULL
1794 static isc_result_t
1795 bind9_check_logging(const cfg_obj_t *config, isc_log_t *logctx,
1796 isc_mem_t *mctx)
1798 const cfg_obj_t *categories = NULL;
1799 const cfg_obj_t *category;
1800 const cfg_obj_t *channels = NULL;
1801 const cfg_obj_t *channel;
1802 const cfg_listelt_t *element;
1803 const cfg_listelt_t *delement;
1804 const char *channelname;
1805 const char *catname;
1806 const cfg_obj_t *fileobj = NULL;
1807 const cfg_obj_t *syslogobj = NULL;
1808 const cfg_obj_t *nullobj = NULL;
1809 const cfg_obj_t *stderrobj = NULL;
1810 const cfg_obj_t *logobj = NULL;
1811 isc_result_t result = ISC_R_SUCCESS;
1812 isc_result_t tresult;
1813 isc_symtab_t *symtab = NULL;
1814 isc_symvalue_t symvalue;
1815 int i;
1817 (void)cfg_map_get(config, "logging", &logobj);
1818 if (logobj == NULL)
1819 return (ISC_R_SUCCESS);
1821 result = isc_symtab_create(mctx, 100, NULL, NULL, ISC_FALSE, &symtab);
1822 if (result != ISC_R_SUCCESS)
1823 return (result);
1825 symvalue.as_cpointer = NULL;
1826 for (i = 0; default_channels[i] != NULL; i++) {
1827 tresult = isc_symtab_define(symtab, default_channels[i], 1,
1828 symvalue, isc_symexists_replace);
1829 if (tresult != ISC_R_SUCCESS)
1830 result = tresult;
1833 cfg_map_get(logobj, "channel", &channels);
1835 for (element = cfg_list_first(channels);
1836 element != NULL;
1837 element = cfg_list_next(element))
1839 channel = cfg_listelt_value(element);
1840 channelname = cfg_obj_asstring(cfg_map_getname(channel));
1841 fileobj = syslogobj = nullobj = stderrobj = NULL;
1842 (void)cfg_map_get(channel, "file", &fileobj);
1843 (void)cfg_map_get(channel, "syslog", &syslogobj);
1844 (void)cfg_map_get(channel, "null", &nullobj);
1845 (void)cfg_map_get(channel, "stderr", &stderrobj);
1846 i = 0;
1847 if (fileobj != NULL)
1848 i++;
1849 if (syslogobj != NULL)
1850 i++;
1851 if (nullobj != NULL)
1852 i++;
1853 if (stderrobj != NULL)
1854 i++;
1855 if (i != 1) {
1856 cfg_obj_log(channel, logctx, ISC_LOG_ERROR,
1857 "channel '%s': exactly one of file, syslog, "
1858 "null, and stderr must be present",
1859 channelname);
1860 result = ISC_R_FAILURE;
1862 tresult = isc_symtab_define(symtab, channelname, 1,
1863 symvalue, isc_symexists_replace);
1864 if (tresult != ISC_R_SUCCESS)
1865 result = tresult;
1868 cfg_map_get(logobj, "category", &categories);
1870 for (element = cfg_list_first(categories);
1871 element != NULL;
1872 element = cfg_list_next(element))
1874 category = cfg_listelt_value(element);
1875 catname = cfg_obj_asstring(cfg_tuple_get(category, "name"));
1876 if (isc_log_categorybyname(logctx, catname) == NULL) {
1877 cfg_obj_log(category, logctx, ISC_LOG_ERROR,
1878 "undefined category: '%s'", catname);
1879 result = ISC_R_FAILURE;
1881 channels = cfg_tuple_get(category, "destinations");
1882 for (delement = cfg_list_first(channels);
1883 delement != NULL;
1884 delement = cfg_list_next(delement))
1886 channel = cfg_listelt_value(delement);
1887 channelname = cfg_obj_asstring(channel);
1888 tresult = isc_symtab_lookup(symtab, channelname, 1,
1889 &symvalue);
1890 if (tresult != ISC_R_SUCCESS) {
1891 cfg_obj_log(channel, logctx, ISC_LOG_ERROR,
1892 "undefined channel: '%s'",
1893 channelname);
1894 result = tresult;
1898 isc_symtab_destroy(&symtab);
1899 return (result);
1902 static isc_result_t
1903 bind9_check_controlskeys(const cfg_obj_t *control, const cfg_obj_t *keylist,
1904 isc_log_t *logctx)
1906 isc_result_t result = ISC_R_SUCCESS;
1907 const cfg_obj_t *control_keylist;
1908 const cfg_listelt_t *element;
1909 const cfg_obj_t *key;
1910 const char *keyval;
1912 control_keylist = cfg_tuple_get(control, "keys");
1913 if (cfg_obj_isvoid(control_keylist))
1914 return (ISC_R_SUCCESS);
1916 for (element = cfg_list_first(control_keylist);
1917 element != NULL;
1918 element = cfg_list_next(element))
1920 key = cfg_listelt_value(element);
1921 keyval = cfg_obj_asstring(key);
1923 if (!rndckey_exists(keylist, keyval)) {
1924 cfg_obj_log(key, logctx, ISC_LOG_ERROR,
1925 "unknown key '%s'", keyval);
1926 result = ISC_R_NOTFOUND;
1929 return (result);
1932 static isc_result_t
1933 bind9_check_controls(const cfg_obj_t *config, isc_log_t *logctx,
1934 isc_mem_t *mctx)
1936 isc_result_t result = ISC_R_SUCCESS, tresult;
1937 cfg_aclconfctx_t actx;
1938 const cfg_listelt_t *element, *element2;
1939 const cfg_obj_t *allow;
1940 const cfg_obj_t *control;
1941 const cfg_obj_t *controls;
1942 const cfg_obj_t *controlslist = NULL;
1943 const cfg_obj_t *inetcontrols;
1944 const cfg_obj_t *unixcontrols;
1945 const cfg_obj_t *keylist = NULL;
1946 const char *path;
1947 isc_uint32_t perm, mask;
1948 dns_acl_t *acl = NULL;
1949 isc_sockaddr_t addr;
1950 int i;
1952 (void)cfg_map_get(config, "controls", &controlslist);
1953 if (controlslist == NULL)
1954 return (ISC_R_SUCCESS);
1956 (void)cfg_map_get(config, "key", &keylist);
1958 cfg_aclconfctx_init(&actx);
1961 * INET: Check allow clause.
1962 * UNIX: Check "perm" for sanity, check path length.
1964 for (element = cfg_list_first(controlslist);
1965 element != NULL;
1966 element = cfg_list_next(element)) {
1967 controls = cfg_listelt_value(element);
1968 unixcontrols = NULL;
1969 inetcontrols = NULL;
1970 (void)cfg_map_get(controls, "unix", &unixcontrols);
1971 (void)cfg_map_get(controls, "inet", &inetcontrols);
1972 for (element2 = cfg_list_first(inetcontrols);
1973 element2 != NULL;
1974 element2 = cfg_list_next(element2)) {
1975 control = cfg_listelt_value(element2);
1976 allow = cfg_tuple_get(control, "allow");
1977 tresult = cfg_acl_fromconfig(allow, config, logctx,
1978 &actx, mctx, 0, &acl);
1979 if (acl != NULL)
1980 dns_acl_detach(&acl);
1981 if (tresult != ISC_R_SUCCESS)
1982 result = tresult;
1983 tresult = bind9_check_controlskeys(control, keylist,
1984 logctx);
1985 if (tresult != ISC_R_SUCCESS)
1986 result = tresult;
1988 for (element2 = cfg_list_first(unixcontrols);
1989 element2 != NULL;
1990 element2 = cfg_list_next(element2)) {
1991 control = cfg_listelt_value(element2);
1992 path = cfg_obj_asstring(cfg_tuple_get(control, "path"));
1993 tresult = isc_sockaddr_frompath(&addr, path);
1994 if (tresult == ISC_R_NOSPACE) {
1995 cfg_obj_log(control, logctx, ISC_LOG_ERROR,
1996 "unix control '%s': path too long",
1997 path);
1998 result = ISC_R_NOSPACE;
2000 perm = cfg_obj_asuint32(cfg_tuple_get(control, "perm"));
2001 for (i = 0; i < 3; i++) {
2002 #ifdef NEED_SECURE_DIRECTORY
2003 mask = (0x1 << (i*3)); /* SEARCH */
2004 #else
2005 mask = (0x6 << (i*3)); /* READ + WRITE */
2006 #endif
2007 if ((perm & mask) == mask)
2008 break;
2010 if (i == 0) {
2011 cfg_obj_log(control, logctx, ISC_LOG_WARNING,
2012 "unix control '%s' allows access "
2013 "to everyone", path);
2014 } else if (i == 3) {
2015 cfg_obj_log(control, logctx, ISC_LOG_WARNING,
2016 "unix control '%s' allows access "
2017 "to nobody", path);
2019 tresult = bind9_check_controlskeys(control, keylist,
2020 logctx);
2021 if (tresult != ISC_R_SUCCESS)
2022 result = tresult;
2025 cfg_aclconfctx_destroy(&actx);
2026 return (result);
2029 isc_result_t
2030 bind9_check_namedconf(const cfg_obj_t *config, isc_log_t *logctx,
2031 isc_mem_t *mctx)
2033 const cfg_obj_t *options = NULL;
2034 const cfg_obj_t *views = NULL;
2035 const cfg_obj_t *acls = NULL;
2036 const cfg_obj_t *kals = NULL;
2037 const cfg_obj_t *obj;
2038 const cfg_listelt_t *velement;
2039 isc_result_t result = ISC_R_SUCCESS;
2040 isc_result_t tresult;
2041 isc_symtab_t *symtab = NULL;
2043 static const char *builtin[] = { "localhost", "localnets",
2044 "any", "none"};
2046 (void)cfg_map_get(config, "options", &options);
2048 if (options != NULL &&
2049 check_options(options, logctx, mctx) != ISC_R_SUCCESS)
2050 result = ISC_R_FAILURE;
2052 if (bind9_check_logging(config, logctx, mctx) != ISC_R_SUCCESS)
2053 result = ISC_R_FAILURE;
2055 if (bind9_check_controls(config, logctx, mctx) != ISC_R_SUCCESS)
2056 result = ISC_R_FAILURE;
2058 if (options != NULL &&
2059 check_order(options, logctx) != ISC_R_SUCCESS)
2060 result = ISC_R_FAILURE;
2062 (void)cfg_map_get(config, "view", &views);
2064 if (views != NULL && options != NULL)
2065 if (check_dual_stack(options, logctx) != ISC_R_SUCCESS)
2066 result = ISC_R_FAILURE;
2068 if (views == NULL) {
2069 if (check_viewconf(config, NULL, NULL, dns_rdataclass_in,
2070 logctx, mctx) != ISC_R_SUCCESS)
2071 result = ISC_R_FAILURE;
2072 } else {
2073 const cfg_obj_t *zones = NULL;
2075 (void)cfg_map_get(config, "zone", &zones);
2076 if (zones != NULL) {
2077 cfg_obj_log(zones, logctx, ISC_LOG_ERROR,
2078 "when using 'view' statements, "
2079 "all zones must be in views");
2080 result = ISC_R_FAILURE;
2084 tresult = isc_symtab_create(mctx, 100, NULL, NULL, ISC_TRUE, &symtab);
2085 if (tresult != ISC_R_SUCCESS)
2086 result = tresult;
2087 for (velement = cfg_list_first(views);
2088 velement != NULL;
2089 velement = cfg_list_next(velement))
2091 const cfg_obj_t *view = cfg_listelt_value(velement);
2092 const cfg_obj_t *vname = cfg_tuple_get(view, "name");
2093 const cfg_obj_t *voptions = cfg_tuple_get(view, "options");
2094 const cfg_obj_t *vclassobj = cfg_tuple_get(view, "class");
2095 dns_rdataclass_t vclass = dns_rdataclass_in;
2096 isc_result_t tresult = ISC_R_SUCCESS;
2097 const char *key = cfg_obj_asstring(vname);
2098 isc_symvalue_t symvalue;
2100 if (cfg_obj_isstring(vclassobj)) {
2101 isc_textregion_t r;
2103 DE_CONST(cfg_obj_asstring(vclassobj), r.base);
2104 r.length = strlen(r.base);
2105 tresult = dns_rdataclass_fromtext(&vclass, &r);
2106 if (tresult != ISC_R_SUCCESS)
2107 cfg_obj_log(vclassobj, logctx, ISC_LOG_ERROR,
2108 "view '%s': invalid class %s",
2109 cfg_obj_asstring(vname), r.base);
2111 if (tresult == ISC_R_SUCCESS && symtab != NULL) {
2112 symvalue.as_cpointer = view;
2113 tresult = isc_symtab_define(symtab, key, vclass,
2114 symvalue,
2115 isc_symexists_reject);
2116 if (tresult == ISC_R_EXISTS) {
2117 const char *file;
2118 unsigned int line;
2119 RUNTIME_CHECK(isc_symtab_lookup(symtab, key,
2120 vclass, &symvalue) == ISC_R_SUCCESS);
2121 file = cfg_obj_file(symvalue.as_cpointer);
2122 line = cfg_obj_line(symvalue.as_cpointer);
2123 cfg_obj_log(view, logctx, ISC_LOG_ERROR,
2124 "view '%s': already exists "
2125 "previous definition: %s:%u",
2126 key, file, line);
2127 result = tresult;
2128 } else if (tresult != ISC_R_SUCCESS) {
2129 result = tresult;
2130 } else if ((strcasecmp(key, "_bind") == 0 &&
2131 vclass == dns_rdataclass_ch) ||
2132 (strcasecmp(key, "_default") == 0 &&
2133 vclass == dns_rdataclass_in)) {
2134 cfg_obj_log(view, logctx, ISC_LOG_ERROR,
2135 "attempt to redefine builtin view "
2136 "'%s'", key);
2137 result = ISC_R_EXISTS;
2140 if (tresult == ISC_R_SUCCESS)
2141 tresult = check_viewconf(config, voptions, key,
2142 vclass, logctx, mctx);
2143 if (tresult != ISC_R_SUCCESS)
2144 result = ISC_R_FAILURE;
2146 if (symtab != NULL)
2147 isc_symtab_destroy(&symtab);
2149 if (views != NULL && options != NULL) {
2150 obj = NULL;
2151 tresult = cfg_map_get(options, "cache-file", &obj);
2152 if (tresult == ISC_R_SUCCESS) {
2153 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
2154 "'cache-file' cannot be a global "
2155 "option if views are present");
2156 result = ISC_R_FAILURE;
2160 cfg_map_get(config, "acl", &acls);
2162 if (acls != NULL) {
2163 const cfg_listelt_t *elt;
2164 const cfg_listelt_t *elt2;
2165 const char *aclname;
2167 for (elt = cfg_list_first(acls);
2168 elt != NULL;
2169 elt = cfg_list_next(elt)) {
2170 const cfg_obj_t *acl = cfg_listelt_value(elt);
2171 unsigned int line = cfg_obj_line(acl);
2172 unsigned int i;
2174 aclname = cfg_obj_asstring(cfg_tuple_get(acl, "name"));
2175 for (i = 0;
2176 i < sizeof(builtin) / sizeof(builtin[0]);
2177 i++)
2178 if (strcasecmp(aclname, builtin[i]) == 0) {
2179 cfg_obj_log(acl, logctx, ISC_LOG_ERROR,
2180 "attempt to redefine "
2181 "builtin acl '%s'",
2182 aclname);
2183 result = ISC_R_FAILURE;
2184 break;
2187 for (elt2 = cfg_list_next(elt);
2188 elt2 != NULL;
2189 elt2 = cfg_list_next(elt2)) {
2190 const cfg_obj_t *acl2 = cfg_listelt_value(elt2);
2191 const char *name;
2192 name = cfg_obj_asstring(cfg_tuple_get(acl2,
2193 "name"));
2194 if (strcasecmp(aclname, name) == 0) {
2195 const char *file = cfg_obj_file(acl);
2197 if (file == NULL)
2198 file = "<unknown file>";
2200 cfg_obj_log(acl2, logctx, ISC_LOG_ERROR,
2201 "attempt to redefine "
2202 "acl '%s' previous "
2203 "definition: %s:%u",
2204 name, file, line);
2205 result = ISC_R_FAILURE;
2211 tresult = cfg_map_get(config, "kal", &kals);
2212 if (tresult == ISC_R_SUCCESS) {
2213 const cfg_listelt_t *elt;
2214 const cfg_listelt_t *elt2;
2215 const char *aclname;
2217 for (elt = cfg_list_first(kals);
2218 elt != NULL;
2219 elt = cfg_list_next(elt)) {
2220 const cfg_obj_t *acl = cfg_listelt_value(elt);
2222 aclname = cfg_obj_asstring(cfg_tuple_get(acl, "name"));
2224 for (elt2 = cfg_list_next(elt);
2225 elt2 != NULL;
2226 elt2 = cfg_list_next(elt2)) {
2227 const cfg_obj_t *acl2 = cfg_listelt_value(elt2);
2228 const char *name;
2229 name = cfg_obj_asstring(cfg_tuple_get(acl2,
2230 "name"));
2231 if (strcasecmp(aclname, name) == 0) {
2232 const char *file = cfg_obj_file(acl);
2233 unsigned int line = cfg_obj_line(acl);
2235 if (file == NULL)
2236 file = "<unknown file>";
2238 cfg_obj_log(acl2, logctx, ISC_LOG_ERROR,
2239 "attempt to redefine "
2240 "kal '%s' previous "
2241 "definition: %s:%u",
2242 name, file, line);
2243 result = ISC_R_FAILURE;
2249 return (result);