Try to fixup the mess of mdoc(7)/man(7) mixture as created by the merge.
[netbsd-mini2440.git] / external / bsd / bind / dist / lib / bind9 / check.c
blobccb8682d24077cffb655d6727373bfeae6434efd
1 /* $NetBSD$ */
3 /*
4 * Copyright (C) 2004-2009 Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 2001-2003 Internet Software Consortium.
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
20 /* Id: check.c,v 1.114 2009/12/04 21:09:33 marka Exp */
22 /*! \file */
24 #include <config.h>
26 #include <stdlib.h>
28 #include <isc/base64.h>
29 #include <isc/buffer.h>
30 #include <isc/log.h>
31 #include <isc/mem.h>
32 #include <isc/netaddr.h>
33 #include <isc/parseint.h>
34 #include <isc/region.h>
35 #include <isc/result.h>
36 #include <isc/sockaddr.h>
37 #include <isc/string.h>
38 #include <isc/symtab.h>
39 #include <isc/util.h>
41 #include <dns/acl.h>
42 #include <dns/fixedname.h>
43 #include <dns/rdataclass.h>
44 #include <dns/rdatatype.h>
45 #include <dns/secalg.h>
47 #include <isccfg/aclconf.h>
48 #include <isccfg/cfg.h>
50 #include <bind9/check.h>
52 static void
53 freekey(char *key, unsigned int type, isc_symvalue_t value, void *userarg) {
54 UNUSED(type);
55 UNUSED(value);
56 isc_mem_free(userarg, key);
59 static isc_result_t
60 check_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;
71 dns_fixedname_init(&fixed);
72 obj = cfg_tuple_get(ent, "class");
73 if (cfg_obj_isstring(obj)) {
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;
86 obj = cfg_tuple_get(ent, "type");
87 if (cfg_obj_isstring(obj)) {
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;
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;
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;
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;
140 return (result);
143 static isc_result_t
144 check_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;
150 if (cfg_map_get(options, "rrset-order", &obj) != ISC_R_SUCCESS)
151 return (result);
153 for (element = cfg_list_first(obj);
154 element != NULL;
155 element = cfg_list_next(element))
157 tresult = check_orderent(cfg_listelt_value(element), logctx);
158 if (tresult != ISC_R_SUCCESS)
159 result = tresult;
161 return (result);
164 static isc_result_t
165 check_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;
177 (void)cfg_map_get(options, "dual-stack-servers", &alternates);
179 if (alternates == NULL)
180 return (ISC_R_SUCCESS);
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;
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;
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;
221 return (result);
224 static isc_result_t
225 check_forward(const cfg_obj_t *options, const cfg_obj_t *global,
226 isc_log_t *logctx)
228 const cfg_obj_t *forward = NULL;
229 const cfg_obj_t *forwarders = NULL;
231 (void)cfg_map_get(options, "forward", &forward);
232 (void)cfg_map_get(options, "forwarders", &forwarders);
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);
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);
248 return (ISC_R_SUCCESS);
251 static isc_result_t
252 disabled_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;
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;
275 obj = cfg_tuple_get(disabled, "algorithms");
277 for (element = cfg_list_first(obj);
278 element != NULL;
279 element = cfg_list_next(element))
281 isc_textregion_t r;
282 dns_secalg_t alg;
283 isc_result_t tresult;
285 DE_CONST(cfg_obj_asstring(cfg_listelt_value(element)), r.base);
286 r.length = strlen(r.base);
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);
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;
300 return (result);
303 static isc_result_t
304 nameexist(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)
308 char *key;
309 const char *file;
310 unsigned int line;
311 isc_result_t result;
312 isc_symvalue_t symvalue;
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);
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);
334 return (result);
337 static isc_result_t
338 mustbesecure(const cfg_obj_t *secure, isc_symtab_t *symtab, isc_log_t *logctx,
339 isc_mem_t *mctx)
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;
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);
366 return (result);
369 static isc_result_t
370 checkacl(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)
374 isc_result_t result;
375 const cfg_obj_t *aclobj = NULL;
376 const cfg_obj_t *options;
377 dns_acl_t *acl = NULL;
379 if (zconfig != NULL) {
380 options = cfg_tuple_get(zconfig, "options");
381 cfg_map_get(options, aclname, &aclobj);
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);
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);
400 static isc_result_t
401 check_viewacls(cfg_aclconfctx_t *actx, const cfg_obj_t *voptions,
402 const cfg_obj_t *config, isc_log_t *logctx, isc_mem_t *mctx)
404 isc_result_t result = ISC_R_SUCCESS, tresult;
405 int i = 0;
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", NULL };
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;
418 return (result);
422 * Check allow-recursion and allow-recursion-on acls, and also log a
423 * warning if they're inconsistent with the "recursion" option.
425 static isc_result_t
426 check_recursionacls(cfg_aclconfctx_t *actx, const cfg_obj_t *voptions,
427 const char *viewname, const cfg_obj_t *config,
428 isc_log_t *logctx, isc_mem_t *mctx)
430 const cfg_obj_t *options, *aclobj, *obj = NULL;
431 dns_acl_t *acl = NULL;
432 isc_result_t result = ISC_R_SUCCESS, tresult;
433 isc_boolean_t recursion;
434 const char *forview = " for view ";
435 int i = 0;
437 static const char *acls[] = { "allow-recursion", "allow-recursion-on",
438 NULL };
440 if (voptions != NULL)
441 cfg_map_get(voptions, "recursion", &obj);
442 if (obj == NULL && config != NULL) {
443 options = NULL;
444 cfg_map_get(config, "options", &options);
445 if (options != NULL)
446 cfg_map_get(options, "recursion", &obj);
448 if (obj == NULL)
449 recursion = ISC_TRUE;
450 else
451 recursion = cfg_obj_asboolean(obj);
453 if (viewname == NULL) {
454 viewname = "";
455 forview = "";
458 for (i = 0; acls[i] != NULL; i++) {
459 aclobj = options = NULL;
460 acl = NULL;
462 if (voptions != NULL)
463 cfg_map_get(voptions, acls[i], &aclobj);
464 if (config != NULL && aclobj == NULL) {
465 options = NULL;
466 cfg_map_get(config, "options", &options);
467 if (options != NULL)
468 cfg_map_get(options, acls[i], &aclobj);
470 if (aclobj == NULL)
471 continue;
473 tresult = cfg_acl_fromconfig(aclobj, config, logctx,
474 actx, mctx, 0, &acl);
476 if (tresult != ISC_R_SUCCESS)
477 result = tresult;
479 if (acl == NULL)
480 continue;
482 if (recursion == ISC_FALSE && !dns_acl_isnone(acl)) {
483 cfg_obj_log(aclobj, logctx, ISC_LOG_WARNING,
484 "both \"recursion no;\" and "
485 "\"%s\" active%s%s",
486 acls[i], forview, viewname);
489 if (acl != NULL)
490 dns_acl_detach(&acl);
493 return (result);
496 typedef struct {
497 const char *name;
498 unsigned int scale;
499 unsigned int max;
500 } intervaltable;
502 static isc_result_t
503 check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx) {
504 isc_result_t result = ISC_R_SUCCESS;
505 isc_result_t tresult;
506 unsigned int i;
507 const cfg_obj_t *obj = NULL;
508 const cfg_obj_t *resignobj = NULL;
509 const cfg_listelt_t *element;
510 isc_symtab_t *symtab = NULL;
511 dns_fixedname_t fixed;
512 const char *str;
513 dns_name_t *name;
514 isc_buffer_t b;
516 static intervaltable intervals[] = {
517 { "cleaning-interval", 60, 28 * 24 * 60 }, /* 28 days */
518 { "heartbeat-interval", 60, 28 * 24 * 60 }, /* 28 days */
519 { "interface-interval", 60, 28 * 24 * 60 }, /* 28 days */
520 { "max-transfer-idle-in", 60, 28 * 24 * 60 }, /* 28 days */
521 { "max-transfer-idle-out", 60, 28 * 24 * 60 }, /* 28 days */
522 { "max-transfer-time-in", 60, 28 * 24 * 60 }, /* 28 days */
523 { "max-transfer-time-out", 60, 28 * 24 * 60 }, /* 28 days */
524 { "statistics-interval", 60, 28 * 24 * 60 }, /* 28 days */
528 * Check that fields specified in units of time other than seconds
529 * have reasonable values.
531 for (i = 0; i < sizeof(intervals) / sizeof(intervals[0]); i++) {
532 isc_uint32_t val;
533 obj = NULL;
534 (void)cfg_map_get(options, intervals[i].name, &obj);
535 if (obj == NULL)
536 continue;
537 val = cfg_obj_asuint32(obj);
538 if (val > intervals[i].max) {
539 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
540 "%s '%u' is out of range (0..%u)",
541 intervals[i].name, val,
542 intervals[i].max);
543 result = ISC_R_RANGE;
544 } else if (val > (ISC_UINT32_MAX / intervals[i].scale)) {
545 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
546 "%s '%d' is out of range",
547 intervals[i].name, val);
548 result = ISC_R_RANGE;
552 obj = NULL;
553 cfg_map_get(options, "sig-validity-interval", &obj);
554 if (obj != NULL) {
555 isc_uint32_t validity, resign = 0;
557 validity = cfg_obj_asuint32(cfg_tuple_get(obj, "validity"));
558 resignobj = cfg_tuple_get(obj, "re-sign");
559 if (!cfg_obj_isvoid(resignobj))
560 resign = cfg_obj_asuint32(resignobj);
562 if (validity > 3660 || validity == 0) { /* 10 years */
563 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
564 "%s '%u' is out of range (1..3660)",
565 "sig-validity-interval", validity);
566 result = ISC_R_RANGE;
569 if (!cfg_obj_isvoid(resignobj)) {
570 if (resign > 3660 || resign == 0) { /* 10 years */
571 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
572 "%s '%u' is out of range (1..3660)",
573 "sig-validity-interval (re-sign)",
574 validity);
575 result = ISC_R_RANGE;
576 } else if ((validity > 7 && validity < resign) ||
577 (validity <= 7 && validity * 24 < resign)) {
578 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
579 "validity interval (%u days) "
580 "less than re-signing interval "
581 "(%u %s)", validity, resign,
582 (validity > 7) ? "days" : "hours");
583 result = ISC_R_RANGE;
588 obj = NULL;
589 (void)cfg_map_get(options, "preferred-glue", &obj);
590 if (obj != NULL) {
591 const char *str;
592 str = cfg_obj_asstring(obj);
593 if (strcasecmp(str, "a") != 0 &&
594 strcasecmp(str, "aaaa") != 0 &&
595 strcasecmp(str, "none") != 0)
596 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
597 "preferred-glue unexpected value '%s'",
598 str);
601 obj = NULL;
602 (void)cfg_map_get(options, "root-delegation-only", &obj);
603 if (obj != NULL) {
604 if (!cfg_obj_isvoid(obj)) {
605 const cfg_listelt_t *element;
606 const cfg_obj_t *exclude;
607 const char *str;
608 dns_fixedname_t fixed;
609 dns_name_t *name;
610 isc_buffer_t b;
612 dns_fixedname_init(&fixed);
613 name = dns_fixedname_name(&fixed);
614 for (element = cfg_list_first(obj);
615 element != NULL;
616 element = cfg_list_next(element)) {
617 exclude = cfg_listelt_value(element);
618 str = cfg_obj_asstring(exclude);
619 isc_buffer_init(&b, str, strlen(str));
620 isc_buffer_add(&b, strlen(str));
621 tresult = dns_name_fromtext(name, &b,
622 dns_rootname,
623 0, NULL);
624 if (tresult != ISC_R_SUCCESS) {
625 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
626 "bad domain name '%s'",
627 str);
628 result = tresult;
635 * Set supported DNSSEC algorithms.
637 obj = NULL;
638 (void)cfg_map_get(options, "disable-algorithms", &obj);
639 if (obj != NULL) {
640 for (element = cfg_list_first(obj);
641 element != NULL;
642 element = cfg_list_next(element))
644 obj = cfg_listelt_value(element);
645 tresult = disabled_algorithms(obj, logctx);
646 if (tresult != ISC_R_SUCCESS)
647 result = tresult;
651 dns_fixedname_init(&fixed);
652 name = dns_fixedname_name(&fixed);
655 * Check the DLV zone name.
657 obj = NULL;
658 (void)cfg_map_get(options, "dnssec-lookaside", &obj);
659 if (obj != NULL) {
660 tresult = isc_symtab_create(mctx, 100, freekey, mctx,
661 ISC_FALSE, &symtab);
662 if (tresult != ISC_R_SUCCESS)
663 result = tresult;
664 for (element = cfg_list_first(obj);
665 element != NULL;
666 element = cfg_list_next(element))
668 const char *dlv;
669 const cfg_obj_t *anchor;
671 obj = cfg_listelt_value(element);
673 dlv = cfg_obj_asstring(cfg_tuple_get(obj, "domain"));
674 anchor = cfg_tuple_get(obj, "trust-anchor");
677 * If domain is "auto" and trust anchor is missing,
678 * skip remaining tests
680 if (!strcmp(dlv, "auto") && cfg_obj_isvoid(anchor))
681 continue;
683 isc_buffer_init(&b, dlv, strlen(dlv));
684 isc_buffer_add(&b, strlen(dlv));
685 tresult = dns_name_fromtext(name, &b, dns_rootname,
686 0, NULL);
687 if (tresult != ISC_R_SUCCESS) {
688 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
689 "bad domain name '%s'", dlv);
690 result = tresult;
691 continue;
693 if (symtab != NULL) {
694 tresult = nameexist(obj, dlv, 1, symtab,
695 "dnssec-lookaside '%s': "
696 "already exists previous "
697 "definition: %s:%u",
698 logctx, mctx);
699 if (tresult != ISC_R_SUCCESS &&
700 result == ISC_R_SUCCESS)
701 result = tresult;
704 * XXXMPA to be removed when multiple lookaside
705 * namespaces are supported.
707 if (!dns_name_equal(dns_rootname, name)) {
708 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
709 "dnssec-lookaside '%s': "
710 "non-root not yet supported", dlv);
711 if (result == ISC_R_SUCCESS)
712 result = ISC_R_FAILURE;
715 if (!cfg_obj_isvoid(anchor)) {
716 dlv = cfg_obj_asstring(anchor);
717 isc_buffer_init(&b, dlv, strlen(dlv));
718 isc_buffer_add(&b, strlen(dlv));
719 tresult = dns_name_fromtext(name, &b,
720 dns_rootname,
721 DNS_NAME_DOWNCASE,
722 NULL);
723 if (tresult != ISC_R_SUCCESS) {
724 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
725 "bad domain name '%s'",
726 dlv);
727 if (result == ISC_R_SUCCESS)
728 result = tresult;
730 } else {
731 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
732 "dnssec-lookaside requires "
733 "either 'auto' or a domain and "
734 "trust anchor");
735 if (result == ISC_R_SUCCESS)
736 result = ISC_R_FAILURE;
740 if (symtab != NULL)
741 isc_symtab_destroy(&symtab);
745 * Check dnssec-must-be-secure.
747 obj = NULL;
748 (void)cfg_map_get(options, "dnssec-must-be-secure", &obj);
749 if (obj != NULL) {
750 isc_symtab_t *symtab = NULL;
751 tresult = isc_symtab_create(mctx, 100, freekey, mctx,
752 ISC_FALSE, &symtab);
753 if (tresult != ISC_R_SUCCESS)
754 result = tresult;
755 for (element = cfg_list_first(obj);
756 element != NULL;
757 element = cfg_list_next(element))
759 obj = cfg_listelt_value(element);
760 tresult = mustbesecure(obj, symtab, logctx, mctx);
761 if (tresult != ISC_R_SUCCESS)
762 result = tresult;
764 if (symtab != NULL)
765 isc_symtab_destroy(&symtab);
769 * Check empty zone configuration.
771 obj = NULL;
772 (void)cfg_map_get(options, "empty-server", &obj);
773 if (obj != NULL) {
774 str = cfg_obj_asstring(obj);
775 isc_buffer_init(&b, str, strlen(str));
776 isc_buffer_add(&b, strlen(str));
777 tresult = dns_name_fromtext(dns_fixedname_name(&fixed), &b,
778 dns_rootname, 0, NULL);
779 if (tresult != ISC_R_SUCCESS) {
780 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
781 "empty-server: invalid name '%s'", str);
782 result = ISC_R_FAILURE;
786 obj = NULL;
787 (void)cfg_map_get(options, "empty-contact", &obj);
788 if (obj != NULL) {
789 str = cfg_obj_asstring(obj);
790 isc_buffer_init(&b, str, strlen(str));
791 isc_buffer_add(&b, strlen(str));
792 tresult = dns_name_fromtext(dns_fixedname_name(&fixed), &b,
793 dns_rootname, 0, NULL);
794 if (tresult != ISC_R_SUCCESS) {
795 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
796 "empty-contact: invalid name '%s'", str);
797 result = ISC_R_FAILURE;
801 obj = NULL;
802 (void)cfg_map_get(options, "disable-empty-zone", &obj);
803 for (element = cfg_list_first(obj);
804 element != NULL;
805 element = cfg_list_next(element))
807 obj = cfg_listelt_value(element);
808 str = cfg_obj_asstring(obj);
809 isc_buffer_init(&b, str, strlen(str));
810 isc_buffer_add(&b, strlen(str));
811 tresult = dns_name_fromtext(dns_fixedname_name(&fixed), &b,
812 dns_rootname, 0, NULL);
813 if (tresult != ISC_R_SUCCESS) {
814 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
815 "disable-empty-zone: invalid name '%s'",
816 str);
817 result = ISC_R_FAILURE;
822 * Check that server-id is not too long.
823 * 1024 bytes should be big enough.
825 obj = NULL;
826 (void)cfg_map_get(options, "server-id", &obj);
827 if (obj != NULL && cfg_obj_isstring(obj) &&
828 strlen(cfg_obj_asstring(obj)) > 1024U) {
829 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
830 "'server-id' too big (>1024 bytes)");
831 result = ISC_R_FAILURE;
834 return (result);
837 static isc_result_t
838 get_masters_def(const cfg_obj_t *cctx, const char *name, const cfg_obj_t **ret) {
839 isc_result_t result;
840 const cfg_obj_t *masters = NULL;
841 const cfg_listelt_t *elt;
843 result = cfg_map_get(cctx, "masters", &masters);
844 if (result != ISC_R_SUCCESS)
845 return (result);
846 for (elt = cfg_list_first(masters);
847 elt != NULL;
848 elt = cfg_list_next(elt)) {
849 const cfg_obj_t *list;
850 const char *listname;
852 list = cfg_listelt_value(elt);
853 listname = cfg_obj_asstring(cfg_tuple_get(list, "name"));
855 if (strcasecmp(listname, name) == 0) {
856 *ret = list;
857 return (ISC_R_SUCCESS);
860 return (ISC_R_NOTFOUND);
863 static isc_result_t
864 validate_masters(const cfg_obj_t *obj, const cfg_obj_t *config,
865 isc_uint32_t *countp, isc_log_t *logctx, isc_mem_t *mctx)
867 isc_result_t result = ISC_R_SUCCESS;
868 isc_result_t tresult;
869 isc_uint32_t count = 0;
870 isc_symtab_t *symtab = NULL;
871 isc_symvalue_t symvalue;
872 const cfg_listelt_t *element;
873 const cfg_listelt_t **stack = NULL;
874 isc_uint32_t stackcount = 0, pushed = 0;
875 const cfg_obj_t *list;
877 REQUIRE(countp != NULL);
878 result = isc_symtab_create(mctx, 100, NULL, NULL, ISC_FALSE, &symtab);
879 if (result != ISC_R_SUCCESS) {
880 *countp = count;
881 return (result);
884 newlist:
885 list = cfg_tuple_get(obj, "addresses");
886 element = cfg_list_first(list);
887 resume:
888 for ( ;
889 element != NULL;
890 element = cfg_list_next(element))
892 const char *listname;
893 const cfg_obj_t *addr;
894 const cfg_obj_t *key;
896 addr = cfg_tuple_get(cfg_listelt_value(element),
897 "masterselement");
898 key = cfg_tuple_get(cfg_listelt_value(element), "key");
900 if (cfg_obj_issockaddr(addr)) {
901 count++;
902 continue;
904 if (!cfg_obj_isvoid(key)) {
905 cfg_obj_log(key, logctx, ISC_LOG_ERROR,
906 "unexpected token '%s'",
907 cfg_obj_asstring(key));
908 if (result == ISC_R_SUCCESS)
909 result = ISC_R_FAILURE;
911 listname = cfg_obj_asstring(addr);
912 symvalue.as_cpointer = addr;
913 tresult = isc_symtab_define(symtab, listname, 1, symvalue,
914 isc_symexists_reject);
915 if (tresult == ISC_R_EXISTS)
916 continue;
917 tresult = get_masters_def(config, listname, &obj);
918 if (tresult != ISC_R_SUCCESS) {
919 if (result == ISC_R_SUCCESS)
920 result = tresult;
921 cfg_obj_log(addr, logctx, ISC_LOG_ERROR,
922 "unable to find masters list '%s'",
923 listname);
924 continue;
926 /* Grow stack? */
927 if (stackcount == pushed) {
928 void * new;
929 isc_uint32_t newlen = stackcount + 16;
930 size_t newsize, oldsize;
932 newsize = newlen * sizeof(*stack);
933 oldsize = stackcount * sizeof(*stack);
934 new = isc_mem_get(mctx, newsize);
935 if (new == NULL)
936 goto cleanup;
937 if (stackcount != 0) {
938 void *ptr;
940 DE_CONST(stack, ptr);
941 memcpy(new, stack, oldsize);
942 isc_mem_put(mctx, ptr, oldsize);
944 stack = new;
945 stackcount = newlen;
947 stack[pushed++] = cfg_list_next(element);
948 goto newlist;
950 if (pushed != 0) {
951 element = stack[--pushed];
952 goto resume;
954 cleanup:
955 if (stack != NULL) {
956 void *ptr;
958 DE_CONST(stack, ptr);
959 isc_mem_put(mctx, ptr, stackcount * sizeof(*stack));
961 isc_symtab_destroy(&symtab);
962 *countp = count;
963 return (result);
966 static isc_result_t
967 check_update_policy(const cfg_obj_t *policy, isc_log_t *logctx) {
968 isc_result_t result = ISC_R_SUCCESS;
969 isc_result_t tresult;
970 const cfg_listelt_t *element;
971 const cfg_listelt_t *element2;
972 dns_fixedname_t fixed;
973 const char *str;
974 isc_buffer_t b;
976 /* Check for "update-policy local;" */
977 if (cfg_obj_isstring(policy) &&
978 strcmp("local", cfg_obj_asstring(policy)) == 0)
979 return (ISC_R_SUCCESS);
981 /* Now check the grant policy */
982 for (element = cfg_list_first(policy);
983 element != NULL;
984 element = cfg_list_next(element))
986 const cfg_obj_t *stmt = cfg_listelt_value(element);
987 const cfg_obj_t *identity = cfg_tuple_get(stmt, "identity");
988 const cfg_obj_t *matchtype = cfg_tuple_get(stmt, "matchtype");
989 const cfg_obj_t *dname = cfg_tuple_get(stmt, "name");
990 const cfg_obj_t *typelist = cfg_tuple_get(stmt, "types");
992 dns_fixedname_init(&fixed);
993 str = cfg_obj_asstring(identity);
994 isc_buffer_init(&b, str, strlen(str));
995 isc_buffer_add(&b, strlen(str));
996 tresult = dns_name_fromtext(dns_fixedname_name(&fixed), &b,
997 dns_rootname, 0, NULL);
998 if (tresult != ISC_R_SUCCESS) {
999 cfg_obj_log(identity, logctx, ISC_LOG_ERROR,
1000 "'%s' is not a valid name", str);
1001 result = tresult;
1004 if (tresult == ISC_R_SUCCESS &&
1005 strcasecmp(cfg_obj_asstring(matchtype), "zonesub") != 0) {
1006 dns_fixedname_init(&fixed);
1007 str = cfg_obj_asstring(dname);
1008 isc_buffer_init(&b, str, strlen(str));
1009 isc_buffer_add(&b, strlen(str));
1010 tresult = dns_name_fromtext(dns_fixedname_name(&fixed),
1011 &b, dns_rootname, 0, NULL);
1012 if (tresult != ISC_R_SUCCESS) {
1013 cfg_obj_log(dname, logctx, ISC_LOG_ERROR,
1014 "'%s' is not a valid name", str);
1015 result = tresult;
1019 if (tresult == ISC_R_SUCCESS &&
1020 strcasecmp(cfg_obj_asstring(matchtype), "wildcard") == 0 &&
1021 !dns_name_iswildcard(dns_fixedname_name(&fixed))) {
1022 cfg_obj_log(identity, logctx, ISC_LOG_ERROR,
1023 "'%s' is not a wildcard", str);
1024 result = ISC_R_FAILURE;
1027 for (element2 = cfg_list_first(typelist);
1028 element2 != NULL;
1029 element2 = cfg_list_next(element2))
1031 const cfg_obj_t *typeobj;
1032 isc_textregion_t r;
1033 dns_rdatatype_t type;
1035 typeobj = cfg_listelt_value(element2);
1036 DE_CONST(cfg_obj_asstring(typeobj), r.base);
1037 r.length = strlen(r.base);
1039 tresult = dns_rdatatype_fromtext(&type, &r);
1040 if (tresult != ISC_R_SUCCESS) {
1041 cfg_obj_log(typeobj, logctx, ISC_LOG_ERROR,
1042 "'%s' is not a valid type", r.base);
1043 result = tresult;
1047 return (result);
1050 #define MASTERZONE 1
1051 #define SLAVEZONE 2
1052 #define STUBZONE 4
1053 #define HINTZONE 8
1054 #define FORWARDZONE 16
1055 #define DELEGATIONZONE 32
1056 #define CHECKACL 64
1058 typedef struct {
1059 const char *name;
1060 int allowed;
1061 } optionstable;
1063 static isc_result_t
1064 check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions,
1065 const cfg_obj_t *config, isc_symtab_t *symtab,
1066 dns_rdataclass_t defclass, cfg_aclconfctx_t *actx,
1067 isc_log_t *logctx, isc_mem_t *mctx)
1069 const char *zname;
1070 const char *typestr;
1071 unsigned int ztype;
1072 const cfg_obj_t *zoptions;
1073 const cfg_obj_t *obj = NULL;
1074 isc_result_t result = ISC_R_SUCCESS;
1075 isc_result_t tresult;
1076 unsigned int i;
1077 dns_rdataclass_t zclass;
1078 dns_fixedname_t fixedname;
1079 isc_buffer_t b;
1080 isc_boolean_t root = ISC_FALSE;
1082 static optionstable options[] = {
1083 { "allow-query", MASTERZONE | SLAVEZONE | STUBZONE | CHECKACL },
1084 { "allow-notify", SLAVEZONE | CHECKACL },
1085 { "allow-transfer", MASTERZONE | SLAVEZONE | CHECKACL },
1086 { "notify", MASTERZONE | SLAVEZONE },
1087 { "also-notify", MASTERZONE | SLAVEZONE },
1088 { "dialup", MASTERZONE | SLAVEZONE | STUBZONE },
1089 { "delegation-only", HINTZONE | STUBZONE | DELEGATIONZONE },
1090 { "forward", MASTERZONE | SLAVEZONE | STUBZONE | FORWARDZONE },
1091 { "forwarders", MASTERZONE | SLAVEZONE | STUBZONE | FORWARDZONE },
1092 { "maintain-ixfr-base", MASTERZONE | SLAVEZONE },
1093 { "max-ixfr-log-size", MASTERZONE | SLAVEZONE },
1094 { "notify-source", MASTERZONE | SLAVEZONE },
1095 { "notify-source-v6", MASTERZONE | SLAVEZONE },
1096 { "transfer-source", SLAVEZONE | STUBZONE },
1097 { "transfer-source-v6", SLAVEZONE | STUBZONE },
1098 { "max-transfer-time-in", SLAVEZONE | STUBZONE },
1099 { "max-transfer-time-out", MASTERZONE | SLAVEZONE },
1100 { "max-transfer-idle-in", SLAVEZONE | STUBZONE },
1101 { "max-transfer-idle-out", MASTERZONE | SLAVEZONE },
1102 { "max-retry-time", SLAVEZONE | STUBZONE },
1103 { "min-retry-time", SLAVEZONE | STUBZONE },
1104 { "max-refresh-time", SLAVEZONE | STUBZONE },
1105 { "min-refresh-time", SLAVEZONE | STUBZONE },
1106 { "dnssec-secure-to-insecure", MASTERZONE },
1107 { "sig-validity-interval", MASTERZONE },
1108 { "sig-re-signing-interval", MASTERZONE },
1109 { "sig-signing-nodes", MASTERZONE },
1110 { "sig-signing-type", MASTERZONE },
1111 { "sig-signing-signatures", MASTERZONE },
1112 { "zone-statistics", MASTERZONE | SLAVEZONE | STUBZONE },
1113 { "allow-update", MASTERZONE | CHECKACL },
1114 { "allow-update-forwarding", SLAVEZONE | CHECKACL },
1115 { "file", MASTERZONE | SLAVEZONE | STUBZONE | HINTZONE },
1116 { "journal", MASTERZONE | SLAVEZONE },
1117 { "ixfr-base", MASTERZONE | SLAVEZONE },
1118 { "ixfr-tmp-file", MASTERZONE | SLAVEZONE },
1119 { "masters", SLAVEZONE | STUBZONE },
1120 { "pubkey", MASTERZONE | SLAVEZONE | STUBZONE },
1121 { "update-policy", MASTERZONE },
1122 { "database", MASTERZONE | SLAVEZONE | STUBZONE },
1123 { "key-directory", MASTERZONE },
1124 { "check-wildcard", MASTERZONE },
1125 { "check-mx", MASTERZONE },
1126 { "check-dup-records", MASTERZONE },
1127 { "integrity-check", MASTERZONE },
1128 { "check-mx-cname", MASTERZONE },
1129 { "check-srv-cname", MASTERZONE },
1130 { "masterfile-format", MASTERZONE | SLAVEZONE | STUBZONE | HINTZONE },
1131 { "update-check-ksk", MASTERZONE },
1132 { "dnssec-dnskey-kskonly", MASTERZONE },
1133 { "auto-dnssec", MASTERZONE },
1134 { "try-tcp-refresh", SLAVEZONE },
1137 static optionstable dialups[] = {
1138 { "notify", MASTERZONE | SLAVEZONE },
1139 { "notify-passive", SLAVEZONE },
1140 { "refresh", SLAVEZONE | STUBZONE },
1141 { "passive", SLAVEZONE | STUBZONE },
1144 zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
1146 zoptions = cfg_tuple_get(zconfig, "options");
1148 obj = NULL;
1149 (void)cfg_map_get(zoptions, "type", &obj);
1150 if (obj == NULL) {
1151 cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
1152 "zone '%s': type not present", zname);
1153 return (ISC_R_FAILURE);
1156 typestr = cfg_obj_asstring(obj);
1157 if (strcasecmp(typestr, "master") == 0)
1158 ztype = MASTERZONE;
1159 else if (strcasecmp(typestr, "slave") == 0)
1160 ztype = SLAVEZONE;
1161 else if (strcasecmp(typestr, "stub") == 0)
1162 ztype = STUBZONE;
1163 else if (strcasecmp(typestr, "forward") == 0)
1164 ztype = FORWARDZONE;
1165 else if (strcasecmp(typestr, "hint") == 0)
1166 ztype = HINTZONE;
1167 else if (strcasecmp(typestr, "delegation-only") == 0)
1168 ztype = DELEGATIONZONE;
1169 else {
1170 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1171 "zone '%s': invalid type %s",
1172 zname, typestr);
1173 return (ISC_R_FAILURE);
1176 obj = cfg_tuple_get(zconfig, "class");
1177 if (cfg_obj_isstring(obj)) {
1178 isc_textregion_t r;
1180 DE_CONST(cfg_obj_asstring(obj), r.base);
1181 r.length = strlen(r.base);
1182 result = dns_rdataclass_fromtext(&zclass, &r);
1183 if (result != ISC_R_SUCCESS) {
1184 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1185 "zone '%s': invalid class %s",
1186 zname, r.base);
1187 return (ISC_R_FAILURE);
1189 if (zclass != defclass) {
1190 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1191 "zone '%s': class '%s' does not "
1192 "match view/default class",
1193 zname, r.base);
1194 return (ISC_R_FAILURE);
1199 * Look for an already existing zone.
1200 * We need to make this canonical as isc_symtab_define()
1201 * deals with strings.
1203 dns_fixedname_init(&fixedname);
1204 isc_buffer_init(&b, zname, strlen(zname));
1205 isc_buffer_add(&b, strlen(zname));
1206 tresult = dns_name_fromtext(dns_fixedname_name(&fixedname), &b,
1207 dns_rootname, DNS_NAME_DOWNCASE, NULL);
1208 if (tresult != ISC_R_SUCCESS) {
1209 cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
1210 "zone '%s': is not a valid name", zname);
1211 result = ISC_R_FAILURE;
1212 } else {
1213 char namebuf[DNS_NAME_FORMATSIZE];
1215 dns_name_format(dns_fixedname_name(&fixedname),
1216 namebuf, sizeof(namebuf));
1217 tresult = nameexist(zconfig, namebuf, ztype == HINTZONE ? 1 : 2,
1218 symtab, "zone '%s': already exists "
1219 "previous definition: %s:%u", logctx, mctx);
1220 if (tresult != ISC_R_SUCCESS)
1221 result = tresult;
1222 if (dns_name_equal(dns_fixedname_name(&fixedname),
1223 dns_rootname))
1224 root = ISC_TRUE;
1228 * Look for inappropriate options for the given zone type.
1229 * Check that ACLs expand correctly.
1231 for (i = 0; i < sizeof(options) / sizeof(options[0]); i++) {
1232 obj = NULL;
1233 if ((options[i].allowed & ztype) == 0 &&
1234 cfg_map_get(zoptions, options[i].name, &obj) ==
1235 ISC_R_SUCCESS)
1237 if (strcmp(options[i].name, "allow-update") != 0 ||
1238 ztype != SLAVEZONE) {
1239 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1240 "option '%s' is not allowed "
1241 "in '%s' zone '%s'",
1242 options[i].name, typestr, zname);
1243 result = ISC_R_FAILURE;
1244 } else
1245 cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
1246 "option '%s' is not allowed "
1247 "in '%s' zone '%s'",
1248 options[i].name, typestr, zname);
1250 obj = NULL;
1251 if ((options[i].allowed & ztype) != 0 &&
1252 (options[i].allowed & CHECKACL) != 0) {
1254 tresult = checkacl(options[i].name, actx, zconfig,
1255 voptions, config, logctx, mctx);
1256 if (tresult != ISC_R_SUCCESS)
1257 result = tresult;
1263 * Slave & stub zones must have a "masters" field.
1265 if (ztype == SLAVEZONE || ztype == STUBZONE) {
1266 obj = NULL;
1267 if (cfg_map_get(zoptions, "masters", &obj) != ISC_R_SUCCESS) {
1268 cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR,
1269 "zone '%s': missing 'masters' entry",
1270 zname);
1271 result = ISC_R_FAILURE;
1272 } else {
1273 isc_uint32_t count;
1274 tresult = validate_masters(obj, config, &count,
1275 logctx, mctx);
1276 if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS)
1277 result = tresult;
1278 if (tresult == ISC_R_SUCCESS && count == 0) {
1279 cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR,
1280 "zone '%s': empty 'masters' entry",
1281 zname);
1282 result = ISC_R_FAILURE;
1288 * Master zones can't have both "allow-update" and "update-policy".
1290 if (ztype == MASTERZONE) {
1291 isc_result_t res1, res2, res3;
1292 const char *arg;
1293 isc_boolean_t ddns;
1295 obj = NULL;
1296 res1 = cfg_map_get(zoptions, "allow-update", &obj);
1297 obj = NULL;
1298 res2 = cfg_map_get(zoptions, "update-policy", &obj);
1299 if (res1 == ISC_R_SUCCESS && res2 == ISC_R_SUCCESS) {
1300 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1301 "zone '%s': 'allow-update' is ignored "
1302 "when 'update-policy' is present",
1303 zname);
1304 result = ISC_R_FAILURE;
1305 } else if (res2 == ISC_R_SUCCESS &&
1306 check_update_policy(obj, logctx) != ISC_R_SUCCESS)
1307 result = ISC_R_FAILURE;
1308 ddns = ISC_TF(res1 == ISC_R_SUCCESS || res2 == ISC_R_SUCCESS);
1310 obj = NULL;
1311 arg = "off";
1312 res3 = cfg_map_get(zoptions, "auto-dnssec", &obj);
1313 if (res3 == ISC_R_SUCCESS)
1314 arg = cfg_obj_asstring(obj);
1315 if (strcasecmp(arg, "off") != 0 && !ddns) {
1316 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1317 "'auto-dnssec %s;' requires "
1318 "dynamic DNS to be configured in the zone",
1319 arg);
1320 result = ISC_R_FAILURE;
1322 if (strcasecmp(arg, "create") == 0) {
1323 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1324 "'auto-dnssec create;' is not "
1325 "yet implemented");
1326 result = ISC_R_FAILURE;
1329 obj = NULL;
1330 res1 = cfg_map_get(zoptions, "sig-signing-type", &obj);
1331 if (res1 == ISC_R_SUCCESS) {
1332 isc_uint32_t type = cfg_obj_asuint32(obj);
1333 if (type < 0xff00U || type > 0xffffU)
1334 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1335 "sig-signing-type: %u out of "
1336 "range [%u..%u]", type,
1337 0xff00U, 0xffffU);
1338 result = ISC_R_FAILURE;
1343 * Check the excessively complicated "dialup" option.
1345 if (ztype == MASTERZONE || ztype == SLAVEZONE || ztype == STUBZONE) {
1346 const cfg_obj_t *dialup = NULL;
1347 (void)cfg_map_get(zoptions, "dialup", &dialup);
1348 if (dialup != NULL && cfg_obj_isstring(dialup)) {
1349 const char *str = cfg_obj_asstring(dialup);
1350 for (i = 0;
1351 i < sizeof(dialups) / sizeof(dialups[0]);
1352 i++)
1354 if (strcasecmp(dialups[i].name, str) != 0)
1355 continue;
1356 if ((dialups[i].allowed & ztype) == 0) {
1357 cfg_obj_log(obj, logctx,
1358 ISC_LOG_ERROR,
1359 "dialup type '%s' is not "
1360 "allowed in '%s' "
1361 "zone '%s'",
1362 str, typestr, zname);
1363 result = ISC_R_FAILURE;
1365 break;
1367 if (i == sizeof(dialups) / sizeof(dialups[0])) {
1368 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1369 "invalid dialup type '%s' in zone "
1370 "'%s'", str, zname);
1371 result = ISC_R_FAILURE;
1377 * Check that forwarding is reasonable.
1379 obj = NULL;
1380 if (root) {
1381 if (voptions != NULL)
1382 (void)cfg_map_get(voptions, "forwarders", &obj);
1383 if (obj == NULL) {
1384 const cfg_obj_t *options = NULL;
1385 (void)cfg_map_get(config, "options", &options);
1386 if (options != NULL)
1387 (void)cfg_map_get(options, "forwarders", &obj);
1390 if (check_forward(zoptions, obj, logctx) != ISC_R_SUCCESS)
1391 result = ISC_R_FAILURE;
1394 * Check various options.
1396 tresult = check_options(zoptions, logctx, mctx);
1397 if (tresult != ISC_R_SUCCESS)
1398 result = tresult;
1401 * If the zone type is rbt/rbt64 then master/hint zones
1402 * require file clauses.
1404 obj = NULL;
1405 tresult = cfg_map_get(zoptions, "database", &obj);
1406 if (tresult == ISC_R_NOTFOUND ||
1407 (tresult == ISC_R_SUCCESS &&
1408 (strcmp("rbt", cfg_obj_asstring(obj)) == 0 ||
1409 strcmp("rbt64", cfg_obj_asstring(obj)) == 0))) {
1410 obj = NULL;
1411 tresult = cfg_map_get(zoptions, "file", &obj);
1412 if (tresult != ISC_R_SUCCESS &&
1413 (ztype == MASTERZONE || ztype == HINTZONE)) {
1414 cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
1415 "zone '%s': missing 'file' entry",
1416 zname);
1417 result = tresult;
1421 return (result);
1425 typedef struct keyalgorithms {
1426 const char *name;
1427 isc_uint16_t size;
1428 } algorithmtable;
1430 isc_result_t
1431 bind9_check_key(const cfg_obj_t *key, isc_log_t *logctx) {
1432 const cfg_obj_t *algobj = NULL;
1433 const cfg_obj_t *secretobj = NULL;
1434 const char *keyname = cfg_obj_asstring(cfg_map_getname(key));
1435 const char *algorithm;
1436 int i;
1437 size_t len = 0;
1438 isc_result_t result;
1439 isc_buffer_t buf;
1440 unsigned char secretbuf[1024];
1441 static const algorithmtable algorithms[] = {
1442 { "hmac-md5", 128 },
1443 { "hmac-md5.sig-alg.reg.int", 0 },
1444 { "hmac-md5.sig-alg.reg.int.", 0 },
1445 { "hmac-sha1", 160 },
1446 { "hmac-sha224", 224 },
1447 { "hmac-sha256", 256 },
1448 { "hmac-sha384", 384 },
1449 { "hmac-sha512", 512 },
1450 { NULL, 0 }
1453 (void)cfg_map_get(key, "algorithm", &algobj);
1454 (void)cfg_map_get(key, "secret", &secretobj);
1455 if (secretobj == NULL || algobj == NULL) {
1456 cfg_obj_log(key, logctx, ISC_LOG_ERROR,
1457 "key '%s' must have both 'secret' and "
1458 "'algorithm' defined",
1459 keyname);
1460 return (ISC_R_FAILURE);
1463 isc_buffer_init(&buf, secretbuf, sizeof(secretbuf));
1464 result = isc_base64_decodestring(cfg_obj_asstring(secretobj), &buf);
1465 if (result != ISC_R_SUCCESS) {
1466 cfg_obj_log(secretobj, logctx, ISC_LOG_ERROR,
1467 "bad secret '%s'", isc_result_totext(result));
1468 return (result);
1471 algorithm = cfg_obj_asstring(algobj);
1472 for (i = 0; algorithms[i].name != NULL; i++) {
1473 len = strlen(algorithms[i].name);
1474 if (strncasecmp(algorithms[i].name, algorithm, len) == 0 &&
1475 (algorithm[len] == '\0' ||
1476 (algorithms[i].size != 0 && algorithm[len] == '-')))
1477 break;
1479 if (algorithms[i].name == NULL) {
1480 cfg_obj_log(algobj, logctx, ISC_LOG_ERROR,
1481 "unknown algorithm '%s'", algorithm);
1482 return (ISC_R_NOTFOUND);
1484 if (algorithm[len] == '-') {
1485 isc_uint16_t digestbits;
1486 isc_result_t result;
1487 result = isc_parse_uint16(&digestbits, algorithm + len + 1, 10);
1488 if (result == ISC_R_SUCCESS || result == ISC_R_RANGE) {
1489 if (result == ISC_R_RANGE ||
1490 digestbits > algorithms[i].size) {
1491 cfg_obj_log(algobj, logctx, ISC_LOG_ERROR,
1492 "key '%s' digest-bits too large "
1493 "[%u..%u]", keyname,
1494 algorithms[i].size / 2,
1495 algorithms[i].size);
1496 return (ISC_R_RANGE);
1498 if ((digestbits % 8) != 0) {
1499 cfg_obj_log(algobj, logctx, ISC_LOG_ERROR,
1500 "key '%s' digest-bits not multiple"
1501 " of 8", keyname);
1502 return (ISC_R_RANGE);
1505 * Recommended minima for hmac algorithms.
1507 if ((digestbits < (algorithms[i].size / 2U) ||
1508 (digestbits < 80U)))
1509 cfg_obj_log(algobj, logctx, ISC_LOG_WARNING,
1510 "key '%s' digest-bits too small "
1511 "[<%u]", keyname,
1512 algorithms[i].size/2);
1513 } else {
1514 cfg_obj_log(algobj, logctx, ISC_LOG_ERROR,
1515 "key '%s': unable to parse digest-bits",
1516 keyname);
1517 return (result);
1520 return (ISC_R_SUCCESS);
1524 * Check key list for duplicates key names and that the key names
1525 * are valid domain names as these keys are used for TSIG.
1527 * Check the key contents for validity.
1529 static isc_result_t
1530 check_keylist(const cfg_obj_t *keys, isc_symtab_t *symtab,
1531 isc_mem_t *mctx, isc_log_t *logctx)
1533 char namebuf[DNS_NAME_FORMATSIZE];
1534 dns_fixedname_t fname;
1535 dns_name_t *name;
1536 isc_result_t result = ISC_R_SUCCESS;
1537 isc_result_t tresult;
1538 const cfg_listelt_t *element;
1540 dns_fixedname_init(&fname);
1541 name = dns_fixedname_name(&fname);
1542 for (element = cfg_list_first(keys);
1543 element != NULL;
1544 element = cfg_list_next(element))
1546 const cfg_obj_t *key = cfg_listelt_value(element);
1547 const char *keyid = cfg_obj_asstring(cfg_map_getname(key));
1548 isc_symvalue_t symvalue;
1549 isc_buffer_t b;
1550 char *keyname;
1552 isc_buffer_init(&b, keyid, strlen(keyid));
1553 isc_buffer_add(&b, strlen(keyid));
1554 tresult = dns_name_fromtext(name, &b, dns_rootname,
1555 0, NULL);
1556 if (tresult != ISC_R_SUCCESS) {
1557 cfg_obj_log(key, logctx, ISC_LOG_ERROR,
1558 "key '%s': bad key name", keyid);
1559 result = tresult;
1560 continue;
1562 tresult = bind9_check_key(key, logctx);
1563 if (tresult != ISC_R_SUCCESS)
1564 return (tresult);
1566 dns_name_format(name, namebuf, sizeof(namebuf));
1567 keyname = isc_mem_strdup(mctx, namebuf);
1568 if (keyname == NULL)
1569 return (ISC_R_NOMEMORY);
1570 symvalue.as_cpointer = key;
1571 tresult = isc_symtab_define(symtab, keyname, 1, symvalue,
1572 isc_symexists_reject);
1573 if (tresult == ISC_R_EXISTS) {
1574 const char *file;
1575 unsigned int line;
1577 RUNTIME_CHECK(isc_symtab_lookup(symtab, keyname,
1578 1, &symvalue) == ISC_R_SUCCESS);
1579 file = cfg_obj_file(symvalue.as_cpointer);
1580 line = cfg_obj_line(symvalue.as_cpointer);
1582 if (file == NULL)
1583 file = "<unknown file>";
1584 cfg_obj_log(key, logctx, ISC_LOG_ERROR,
1585 "key '%s': already exists "
1586 "previous definition: %s:%u",
1587 keyid, file, line);
1588 isc_mem_free(mctx, keyname);
1589 result = tresult;
1590 } else if (tresult != ISC_R_SUCCESS) {
1591 isc_mem_free(mctx, keyname);
1592 return (tresult);
1595 return (result);
1598 static struct {
1599 const char *v4;
1600 const char *v6;
1601 } sources[] = {
1602 { "transfer-source", "transfer-source-v6" },
1603 { "notify-source", "notify-source-v6" },
1604 { "query-source", "query-source-v6" },
1605 { NULL, NULL }
1609 * RNDC keys are not normalised unlike TSIG keys.
1611 * "foo." is different to "foo".
1613 static isc_boolean_t
1614 rndckey_exists(const cfg_obj_t *keylist, const char *keyname) {
1615 const cfg_listelt_t *element;
1616 const cfg_obj_t *obj;
1617 const char *str;
1619 if (keylist == NULL)
1620 return (ISC_FALSE);
1622 for (element = cfg_list_first(keylist);
1623 element != NULL;
1624 element = cfg_list_next(element))
1626 obj = cfg_listelt_value(element);
1627 str = cfg_obj_asstring(cfg_map_getname(obj));
1628 if (!strcasecmp(str, keyname))
1629 return (ISC_TRUE);
1631 return (ISC_FALSE);
1634 static isc_result_t
1635 check_servers(const cfg_obj_t *config, const cfg_obj_t *voptions,
1636 isc_symtab_t *symtab, isc_log_t *logctx)
1638 dns_fixedname_t fname;
1639 isc_result_t result = ISC_R_SUCCESS;
1640 isc_result_t tresult;
1641 const cfg_listelt_t *e1, *e2;
1642 const cfg_obj_t *v1, *v2, *keys;
1643 const cfg_obj_t *servers;
1644 isc_netaddr_t n1, n2;
1645 unsigned int p1, p2;
1646 const cfg_obj_t *obj;
1647 char buf[ISC_NETADDR_FORMATSIZE];
1648 char namebuf[DNS_NAME_FORMATSIZE];
1649 const char *xfr;
1650 const char *keyval;
1651 isc_buffer_t b;
1652 int source;
1653 dns_name_t *keyname;
1655 servers = NULL;
1656 if (voptions != NULL)
1657 (void)cfg_map_get(voptions, "server", &servers);
1658 if (servers == NULL)
1659 (void)cfg_map_get(config, "server", &servers);
1660 if (servers == NULL)
1661 return (ISC_R_SUCCESS);
1663 for (e1 = cfg_list_first(servers); e1 != NULL; e1 = cfg_list_next(e1)) {
1664 v1 = cfg_listelt_value(e1);
1665 cfg_obj_asnetprefix(cfg_map_getname(v1), &n1, &p1);
1667 * Check that unused bits are zero.
1669 tresult = isc_netaddr_prefixok(&n1, p1);
1670 if (tresult != ISC_R_SUCCESS) {
1671 INSIST(tresult == ISC_R_FAILURE);
1672 isc_netaddr_format(&n1, buf, sizeof(buf));
1673 cfg_obj_log(v1, logctx, ISC_LOG_ERROR,
1674 "server '%s/%u': invalid prefix "
1675 "(extra bits specified)", buf, p1);
1676 result = tresult;
1678 source = 0;
1679 do {
1680 obj = NULL;
1681 if (n1.family == AF_INET)
1682 xfr = sources[source].v6;
1683 else
1684 xfr = sources[source].v4;
1685 (void)cfg_map_get(v1, xfr, &obj);
1686 if (obj != NULL) {
1687 isc_netaddr_format(&n1, buf, sizeof(buf));
1688 cfg_obj_log(v1, logctx, ISC_LOG_ERROR,
1689 "server '%s/%u': %s not legal",
1690 buf, p1, xfr);
1691 result = ISC_R_FAILURE;
1693 } while (sources[++source].v4 != NULL);
1694 e2 = e1;
1695 while ((e2 = cfg_list_next(e2)) != NULL) {
1696 v2 = cfg_listelt_value(e2);
1697 cfg_obj_asnetprefix(cfg_map_getname(v2), &n2, &p2);
1698 if (p1 == p2 && isc_netaddr_equal(&n1, &n2)) {
1699 const char *file = cfg_obj_file(v1);
1700 unsigned int line = cfg_obj_line(v1);
1702 if (file == NULL)
1703 file = "<unknown file>";
1705 isc_netaddr_format(&n2, buf, sizeof(buf));
1706 cfg_obj_log(v2, logctx, ISC_LOG_ERROR,
1707 "server '%s/%u': already exists "
1708 "previous definition: %s:%u",
1709 buf, p2, file, line);
1710 result = ISC_R_FAILURE;
1713 keys = NULL;
1714 cfg_map_get(v1, "keys", &keys);
1715 if (keys != NULL) {
1717 * Normalize key name.
1719 keyval = cfg_obj_asstring(keys);
1720 dns_fixedname_init(&fname);
1721 isc_buffer_init(&b, keyval, strlen(keyval));
1722 isc_buffer_add(&b, strlen(keyval));
1723 keyname = dns_fixedname_name(&fname);
1724 tresult = dns_name_fromtext(keyname, &b, dns_rootname,
1725 0, NULL);
1726 if (tresult != ISC_R_SUCCESS) {
1727 cfg_obj_log(keys, logctx, ISC_LOG_ERROR,
1728 "bad key name '%s'", keyval);
1729 result = ISC_R_FAILURE;
1730 continue;
1732 dns_name_format(keyname, namebuf, sizeof(namebuf));
1733 tresult = isc_symtab_lookup(symtab, namebuf, 1, NULL);
1734 if (tresult != ISC_R_SUCCESS) {
1735 cfg_obj_log(keys, logctx, ISC_LOG_ERROR,
1736 "unknown key '%s'", keyval);
1737 result = ISC_R_FAILURE;
1741 return (result);
1744 static isc_result_t
1745 check_viewconf(const cfg_obj_t *config, const cfg_obj_t *voptions,
1746 const char *viewname, dns_rdataclass_t vclass,
1747 isc_log_t *logctx, isc_mem_t *mctx)
1749 const cfg_obj_t *zones = NULL;
1750 const cfg_obj_t *keys = NULL;
1751 const cfg_listelt_t *element;
1752 isc_symtab_t *symtab = NULL;
1753 isc_result_t result = ISC_R_SUCCESS;
1754 isc_result_t tresult = ISC_R_SUCCESS;
1755 cfg_aclconfctx_t actx;
1756 const cfg_obj_t *obj;
1757 isc_boolean_t enablednssec, enablevalidation;
1760 * Check that all zone statements are syntactically correct and
1761 * there are no duplicate zones.
1763 tresult = isc_symtab_create(mctx, 100, freekey, mctx,
1764 ISC_FALSE, &symtab);
1765 if (tresult != ISC_R_SUCCESS)
1766 return (ISC_R_NOMEMORY);
1768 cfg_aclconfctx_init(&actx);
1770 if (voptions != NULL)
1771 (void)cfg_map_get(voptions, "zone", &zones);
1772 else
1773 (void)cfg_map_get(config, "zone", &zones);
1775 for (element = cfg_list_first(zones);
1776 element != NULL;
1777 element = cfg_list_next(element))
1779 isc_result_t tresult;
1780 const cfg_obj_t *zone = cfg_listelt_value(element);
1782 tresult = check_zoneconf(zone, voptions, config, symtab,
1783 vclass, &actx, logctx, mctx);
1784 if (tresult != ISC_R_SUCCESS)
1785 result = ISC_R_FAILURE;
1788 isc_symtab_destroy(&symtab);
1791 * Check that forwarding is reasonable.
1793 if (voptions == NULL) {
1794 const cfg_obj_t *options = NULL;
1795 (void)cfg_map_get(config, "options", &options);
1796 if (options != NULL)
1797 if (check_forward(options, NULL,
1798 logctx) != ISC_R_SUCCESS)
1799 result = ISC_R_FAILURE;
1800 } else {
1801 if (check_forward(voptions, NULL, logctx) != ISC_R_SUCCESS)
1802 result = ISC_R_FAILURE;
1806 * Check that dual-stack-servers is reasonable.
1808 if (voptions == NULL) {
1809 const cfg_obj_t *options = NULL;
1810 (void)cfg_map_get(config, "options", &options);
1811 if (options != NULL)
1812 if (check_dual_stack(options, logctx) != ISC_R_SUCCESS)
1813 result = ISC_R_FAILURE;
1814 } else {
1815 if (check_dual_stack(voptions, logctx) != ISC_R_SUCCESS)
1816 result = ISC_R_FAILURE;
1820 * Check that rrset-order is reasonable.
1822 if (voptions != NULL) {
1823 if (check_order(voptions, logctx) != ISC_R_SUCCESS)
1824 result = ISC_R_FAILURE;
1828 * Check that all key statements are syntactically correct and
1829 * there are no duplicate keys.
1831 tresult = isc_symtab_create(mctx, 100, freekey, mctx,
1832 ISC_FALSE, &symtab);
1833 if (tresult != ISC_R_SUCCESS)
1834 return (ISC_R_NOMEMORY);
1836 (void)cfg_map_get(config, "key", &keys);
1837 tresult = check_keylist(keys, symtab, mctx, logctx);
1838 if (tresult == ISC_R_EXISTS)
1839 result = ISC_R_FAILURE;
1840 else if (tresult != ISC_R_SUCCESS) {
1841 isc_symtab_destroy(&symtab);
1842 return (tresult);
1845 if (voptions != NULL) {
1846 keys = NULL;
1847 (void)cfg_map_get(voptions, "key", &keys);
1848 tresult = check_keylist(keys, symtab, mctx, logctx);
1849 if (tresult == ISC_R_EXISTS)
1850 result = ISC_R_FAILURE;
1851 else if (tresult != ISC_R_SUCCESS) {
1852 isc_symtab_destroy(&symtab);
1853 return (tresult);
1858 * Global servers can refer to keys in views.
1860 if (check_servers(config, voptions, symtab, logctx) != ISC_R_SUCCESS)
1861 result = ISC_R_FAILURE;
1863 isc_symtab_destroy(&symtab);
1866 * Check that dnssec-enable/dnssec-validation are sensible.
1868 obj = NULL;
1869 if (voptions != NULL)
1870 (void)cfg_map_get(voptions, "dnssec-enable", &obj);
1871 if (obj == NULL)
1872 (void)cfg_map_get(config, "dnssec-enable", &obj);
1873 if (obj == NULL)
1874 enablednssec = ISC_TRUE;
1875 else
1876 enablednssec = cfg_obj_asboolean(obj);
1878 obj = NULL;
1879 if (voptions != NULL)
1880 (void)cfg_map_get(voptions, "dnssec-validation", &obj);
1881 if (obj == NULL)
1882 (void)cfg_map_get(config, "dnssec-validation", &obj);
1883 if (obj == NULL)
1884 enablevalidation = ISC_FALSE; /* XXXMPA Change for 9.5. */
1885 else
1886 enablevalidation = cfg_obj_asboolean(obj);
1888 if (enablevalidation && !enablednssec)
1889 cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
1890 "'dnssec-validation yes;' and 'dnssec-enable no;'");
1892 if (voptions != NULL)
1893 tresult = check_options(voptions, logctx, mctx);
1894 else
1895 tresult = check_options(config, logctx, mctx);
1896 if (tresult != ISC_R_SUCCESS)
1897 result = tresult;
1899 tresult = check_viewacls(&actx, voptions, config, logctx, mctx);
1900 if (tresult != ISC_R_SUCCESS)
1901 result = tresult;
1903 tresult = check_recursionacls(&actx, voptions, viewname,
1904 config, logctx, mctx);
1905 if (tresult != ISC_R_SUCCESS)
1906 result = tresult;
1908 cfg_aclconfctx_destroy(&actx);
1910 return (result);
1913 static const char *
1914 default_channels[] = {
1915 "default_syslog",
1916 "default_stderr",
1917 "default_debug",
1918 "null",
1919 NULL
1922 static isc_result_t
1923 bind9_check_logging(const cfg_obj_t *config, isc_log_t *logctx,
1924 isc_mem_t *mctx)
1926 const cfg_obj_t *categories = NULL;
1927 const cfg_obj_t *category;
1928 const cfg_obj_t *channels = NULL;
1929 const cfg_obj_t *channel;
1930 const cfg_listelt_t *element;
1931 const cfg_listelt_t *delement;
1932 const char *channelname;
1933 const char *catname;
1934 const cfg_obj_t *fileobj = NULL;
1935 const cfg_obj_t *syslogobj = NULL;
1936 const cfg_obj_t *nullobj = NULL;
1937 const cfg_obj_t *stderrobj = NULL;
1938 const cfg_obj_t *logobj = NULL;
1939 isc_result_t result = ISC_R_SUCCESS;
1940 isc_result_t tresult;
1941 isc_symtab_t *symtab = NULL;
1942 isc_symvalue_t symvalue;
1943 int i;
1945 (void)cfg_map_get(config, "logging", &logobj);
1946 if (logobj == NULL)
1947 return (ISC_R_SUCCESS);
1949 result = isc_symtab_create(mctx, 100, NULL, NULL, ISC_FALSE, &symtab);
1950 if (result != ISC_R_SUCCESS)
1951 return (result);
1953 symvalue.as_cpointer = NULL;
1954 for (i = 0; default_channels[i] != NULL; i++) {
1955 tresult = isc_symtab_define(symtab, default_channels[i], 1,
1956 symvalue, isc_symexists_replace);
1957 if (tresult != ISC_R_SUCCESS)
1958 result = tresult;
1961 cfg_map_get(logobj, "channel", &channels);
1963 for (element = cfg_list_first(channels);
1964 element != NULL;
1965 element = cfg_list_next(element))
1967 channel = cfg_listelt_value(element);
1968 channelname = cfg_obj_asstring(cfg_map_getname(channel));
1969 fileobj = syslogobj = nullobj = stderrobj = NULL;
1970 (void)cfg_map_get(channel, "file", &fileobj);
1971 (void)cfg_map_get(channel, "syslog", &syslogobj);
1972 (void)cfg_map_get(channel, "null", &nullobj);
1973 (void)cfg_map_get(channel, "stderr", &stderrobj);
1974 i = 0;
1975 if (fileobj != NULL)
1976 i++;
1977 if (syslogobj != NULL)
1978 i++;
1979 if (nullobj != NULL)
1980 i++;
1981 if (stderrobj != NULL)
1982 i++;
1983 if (i != 1) {
1984 cfg_obj_log(channel, logctx, ISC_LOG_ERROR,
1985 "channel '%s': exactly one of file, syslog, "
1986 "null, and stderr must be present",
1987 channelname);
1988 result = ISC_R_FAILURE;
1990 tresult = isc_symtab_define(symtab, channelname, 1,
1991 symvalue, isc_symexists_replace);
1992 if (tresult != ISC_R_SUCCESS)
1993 result = tresult;
1996 cfg_map_get(logobj, "category", &categories);
1998 for (element = cfg_list_first(categories);
1999 element != NULL;
2000 element = cfg_list_next(element))
2002 category = cfg_listelt_value(element);
2003 catname = cfg_obj_asstring(cfg_tuple_get(category, "name"));
2004 if (isc_log_categorybyname(logctx, catname) == NULL) {
2005 cfg_obj_log(category, logctx, ISC_LOG_ERROR,
2006 "undefined category: '%s'", catname);
2007 result = ISC_R_FAILURE;
2009 channels = cfg_tuple_get(category, "destinations");
2010 for (delement = cfg_list_first(channels);
2011 delement != NULL;
2012 delement = cfg_list_next(delement))
2014 channel = cfg_listelt_value(delement);
2015 channelname = cfg_obj_asstring(channel);
2016 tresult = isc_symtab_lookup(symtab, channelname, 1,
2017 &symvalue);
2018 if (tresult != ISC_R_SUCCESS) {
2019 cfg_obj_log(channel, logctx, ISC_LOG_ERROR,
2020 "undefined channel: '%s'",
2021 channelname);
2022 result = tresult;
2026 isc_symtab_destroy(&symtab);
2027 return (result);
2030 static isc_result_t
2031 bind9_check_controlskeys(const cfg_obj_t *control, const cfg_obj_t *keylist,
2032 isc_log_t *logctx)
2034 isc_result_t result = ISC_R_SUCCESS;
2035 const cfg_obj_t *control_keylist;
2036 const cfg_listelt_t *element;
2037 const cfg_obj_t *key;
2038 const char *keyval;
2040 control_keylist = cfg_tuple_get(control, "keys");
2041 if (cfg_obj_isvoid(control_keylist))
2042 return (ISC_R_SUCCESS);
2044 for (element = cfg_list_first(control_keylist);
2045 element != NULL;
2046 element = cfg_list_next(element))
2048 key = cfg_listelt_value(element);
2049 keyval = cfg_obj_asstring(key);
2051 if (!rndckey_exists(keylist, keyval)) {
2052 cfg_obj_log(key, logctx, ISC_LOG_ERROR,
2053 "unknown key '%s'", keyval);
2054 result = ISC_R_NOTFOUND;
2057 return (result);
2060 static isc_result_t
2061 bind9_check_controls(const cfg_obj_t *config, isc_log_t *logctx,
2062 isc_mem_t *mctx)
2064 isc_result_t result = ISC_R_SUCCESS, tresult;
2065 cfg_aclconfctx_t actx;
2066 const cfg_listelt_t *element, *element2;
2067 const cfg_obj_t *allow;
2068 const cfg_obj_t *control;
2069 const cfg_obj_t *controls;
2070 const cfg_obj_t *controlslist = NULL;
2071 const cfg_obj_t *inetcontrols;
2072 const cfg_obj_t *unixcontrols;
2073 const cfg_obj_t *keylist = NULL;
2074 const char *path;
2075 isc_uint32_t perm, mask;
2076 dns_acl_t *acl = NULL;
2077 isc_sockaddr_t addr;
2078 int i;
2080 (void)cfg_map_get(config, "controls", &controlslist);
2081 if (controlslist == NULL)
2082 return (ISC_R_SUCCESS);
2084 (void)cfg_map_get(config, "key", &keylist);
2086 cfg_aclconfctx_init(&actx);
2089 * INET: Check allow clause.
2090 * UNIX: Check "perm" for sanity, check path length.
2092 for (element = cfg_list_first(controlslist);
2093 element != NULL;
2094 element = cfg_list_next(element)) {
2095 controls = cfg_listelt_value(element);
2096 unixcontrols = NULL;
2097 inetcontrols = NULL;
2098 (void)cfg_map_get(controls, "unix", &unixcontrols);
2099 (void)cfg_map_get(controls, "inet", &inetcontrols);
2100 for (element2 = cfg_list_first(inetcontrols);
2101 element2 != NULL;
2102 element2 = cfg_list_next(element2)) {
2103 control = cfg_listelt_value(element2);
2104 allow = cfg_tuple_get(control, "allow");
2105 tresult = cfg_acl_fromconfig(allow, config, logctx,
2106 &actx, mctx, 0, &acl);
2107 if (acl != NULL)
2108 dns_acl_detach(&acl);
2109 if (tresult != ISC_R_SUCCESS)
2110 result = tresult;
2111 tresult = bind9_check_controlskeys(control, keylist,
2112 logctx);
2113 if (tresult != ISC_R_SUCCESS)
2114 result = tresult;
2116 for (element2 = cfg_list_first(unixcontrols);
2117 element2 != NULL;
2118 element2 = cfg_list_next(element2)) {
2119 control = cfg_listelt_value(element2);
2120 path = cfg_obj_asstring(cfg_tuple_get(control, "path"));
2121 tresult = isc_sockaddr_frompath(&addr, path);
2122 if (tresult == ISC_R_NOSPACE) {
2123 cfg_obj_log(control, logctx, ISC_LOG_ERROR,
2124 "unix control '%s': path too long",
2125 path);
2126 result = ISC_R_NOSPACE;
2128 perm = cfg_obj_asuint32(cfg_tuple_get(control, "perm"));
2129 for (i = 0; i < 3; i++) {
2130 #ifdef NEED_SECURE_DIRECTORY
2131 mask = (0x1 << (i*3)); /* SEARCH */
2132 #else
2133 mask = (0x6 << (i*3)); /* READ + WRITE */
2134 #endif
2135 if ((perm & mask) == mask)
2136 break;
2138 if (i == 0) {
2139 cfg_obj_log(control, logctx, ISC_LOG_WARNING,
2140 "unix control '%s' allows access "
2141 "to everyone", path);
2142 } else if (i == 3) {
2143 cfg_obj_log(control, logctx, ISC_LOG_WARNING,
2144 "unix control '%s' allows access "
2145 "to nobody", path);
2147 tresult = bind9_check_controlskeys(control, keylist,
2148 logctx);
2149 if (tresult != ISC_R_SUCCESS)
2150 result = tresult;
2153 cfg_aclconfctx_destroy(&actx);
2154 return (result);
2157 isc_result_t
2158 bind9_check_namedconf(const cfg_obj_t *config, isc_log_t *logctx,
2159 isc_mem_t *mctx)
2161 const cfg_obj_t *options = NULL;
2162 const cfg_obj_t *views = NULL;
2163 const cfg_obj_t *acls = NULL;
2164 const cfg_obj_t *kals = NULL;
2165 const cfg_obj_t *obj;
2166 const cfg_listelt_t *velement;
2167 isc_result_t result = ISC_R_SUCCESS;
2168 isc_result_t tresult;
2169 isc_symtab_t *symtab = NULL;
2171 static const char *builtin[] = { "localhost", "localnets",
2172 "any", "none"};
2174 (void)cfg_map_get(config, "options", &options);
2176 if (options != NULL &&
2177 check_options(options, logctx, mctx) != ISC_R_SUCCESS)
2178 result = ISC_R_FAILURE;
2180 if (bind9_check_logging(config, logctx, mctx) != ISC_R_SUCCESS)
2181 result = ISC_R_FAILURE;
2183 if (bind9_check_controls(config, logctx, mctx) != ISC_R_SUCCESS)
2184 result = ISC_R_FAILURE;
2186 if (options != NULL &&
2187 check_order(options, logctx) != ISC_R_SUCCESS)
2188 result = ISC_R_FAILURE;
2190 (void)cfg_map_get(config, "view", &views);
2192 if (views != NULL && options != NULL)
2193 if (check_dual_stack(options, logctx) != ISC_R_SUCCESS)
2194 result = ISC_R_FAILURE;
2196 if (views == NULL) {
2197 if (check_viewconf(config, NULL, NULL, dns_rdataclass_in,
2198 logctx, mctx) != ISC_R_SUCCESS)
2199 result = ISC_R_FAILURE;
2200 } else {
2201 const cfg_obj_t *zones = NULL;
2203 (void)cfg_map_get(config, "zone", &zones);
2204 if (zones != NULL) {
2205 cfg_obj_log(zones, logctx, ISC_LOG_ERROR,
2206 "when using 'view' statements, "
2207 "all zones must be in views");
2208 result = ISC_R_FAILURE;
2212 tresult = isc_symtab_create(mctx, 100, NULL, NULL, ISC_TRUE, &symtab);
2213 if (tresult != ISC_R_SUCCESS)
2214 result = tresult;
2215 for (velement = cfg_list_first(views);
2216 velement != NULL;
2217 velement = cfg_list_next(velement))
2219 const cfg_obj_t *view = cfg_listelt_value(velement);
2220 const cfg_obj_t *vname = cfg_tuple_get(view, "name");
2221 const cfg_obj_t *voptions = cfg_tuple_get(view, "options");
2222 const cfg_obj_t *vclassobj = cfg_tuple_get(view, "class");
2223 dns_rdataclass_t vclass = dns_rdataclass_in;
2224 isc_result_t tresult = ISC_R_SUCCESS;
2225 const char *key = cfg_obj_asstring(vname);
2226 isc_symvalue_t symvalue;
2228 if (cfg_obj_isstring(vclassobj)) {
2229 isc_textregion_t r;
2231 DE_CONST(cfg_obj_asstring(vclassobj), r.base);
2232 r.length = strlen(r.base);
2233 tresult = dns_rdataclass_fromtext(&vclass, &r);
2234 if (tresult != ISC_R_SUCCESS)
2235 cfg_obj_log(vclassobj, logctx, ISC_LOG_ERROR,
2236 "view '%s': invalid class %s",
2237 cfg_obj_asstring(vname), r.base);
2239 if (tresult == ISC_R_SUCCESS && symtab != NULL) {
2240 symvalue.as_cpointer = view;
2241 tresult = isc_symtab_define(symtab, key, vclass,
2242 symvalue,
2243 isc_symexists_reject);
2244 if (tresult == ISC_R_EXISTS) {
2245 const char *file;
2246 unsigned int line;
2247 RUNTIME_CHECK(isc_symtab_lookup(symtab, key,
2248 vclass, &symvalue) == ISC_R_SUCCESS);
2249 file = cfg_obj_file(symvalue.as_cpointer);
2250 line = cfg_obj_line(symvalue.as_cpointer);
2251 cfg_obj_log(view, logctx, ISC_LOG_ERROR,
2252 "view '%s': already exists "
2253 "previous definition: %s:%u",
2254 key, file, line);
2255 result = tresult;
2256 } else if (tresult != ISC_R_SUCCESS) {
2257 result = tresult;
2258 } else if ((strcasecmp(key, "_bind") == 0 &&
2259 vclass == dns_rdataclass_ch) ||
2260 (strcasecmp(key, "_default") == 0 &&
2261 vclass == dns_rdataclass_in)) {
2262 cfg_obj_log(view, logctx, ISC_LOG_ERROR,
2263 "attempt to redefine builtin view "
2264 "'%s'", key);
2265 result = ISC_R_EXISTS;
2268 if (tresult == ISC_R_SUCCESS)
2269 tresult = check_viewconf(config, voptions, key,
2270 vclass, logctx, mctx);
2271 if (tresult != ISC_R_SUCCESS)
2272 result = ISC_R_FAILURE;
2274 if (symtab != NULL)
2275 isc_symtab_destroy(&symtab);
2277 if (views != NULL && options != NULL) {
2278 obj = NULL;
2279 tresult = cfg_map_get(options, "cache-file", &obj);
2280 if (tresult == ISC_R_SUCCESS) {
2281 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
2282 "'cache-file' cannot be a global "
2283 "option if views are present");
2284 result = ISC_R_FAILURE;
2288 cfg_map_get(config, "acl", &acls);
2290 if (acls != NULL) {
2291 const cfg_listelt_t *elt;
2292 const cfg_listelt_t *elt2;
2293 const char *aclname;
2295 for (elt = cfg_list_first(acls);
2296 elt != NULL;
2297 elt = cfg_list_next(elt)) {
2298 const cfg_obj_t *acl = cfg_listelt_value(elt);
2299 unsigned int line = cfg_obj_line(acl);
2300 unsigned int i;
2302 aclname = cfg_obj_asstring(cfg_tuple_get(acl, "name"));
2303 for (i = 0;
2304 i < sizeof(builtin) / sizeof(builtin[0]);
2305 i++)
2306 if (strcasecmp(aclname, builtin[i]) == 0) {
2307 cfg_obj_log(acl, logctx, ISC_LOG_ERROR,
2308 "attempt to redefine "
2309 "builtin acl '%s'",
2310 aclname);
2311 result = ISC_R_FAILURE;
2312 break;
2315 for (elt2 = cfg_list_next(elt);
2316 elt2 != NULL;
2317 elt2 = cfg_list_next(elt2)) {
2318 const cfg_obj_t *acl2 = cfg_listelt_value(elt2);
2319 const char *name;
2320 name = cfg_obj_asstring(cfg_tuple_get(acl2,
2321 "name"));
2322 if (strcasecmp(aclname, name) == 0) {
2323 const char *file = cfg_obj_file(acl);
2325 if (file == NULL)
2326 file = "<unknown file>";
2328 cfg_obj_log(acl2, logctx, ISC_LOG_ERROR,
2329 "attempt to redefine "
2330 "acl '%s' previous "
2331 "definition: %s:%u",
2332 name, file, line);
2333 result = ISC_R_FAILURE;
2339 tresult = cfg_map_get(config, "kal", &kals);
2340 if (tresult == ISC_R_SUCCESS) {
2341 const cfg_listelt_t *elt;
2342 const cfg_listelt_t *elt2;
2343 const char *aclname;
2345 for (elt = cfg_list_first(kals);
2346 elt != NULL;
2347 elt = cfg_list_next(elt)) {
2348 const cfg_obj_t *acl = cfg_listelt_value(elt);
2350 aclname = cfg_obj_asstring(cfg_tuple_get(acl, "name"));
2352 for (elt2 = cfg_list_next(elt);
2353 elt2 != NULL;
2354 elt2 = cfg_list_next(elt2)) {
2355 const cfg_obj_t *acl2 = cfg_listelt_value(elt2);
2356 const char *name;
2357 name = cfg_obj_asstring(cfg_tuple_get(acl2,
2358 "name"));
2359 if (strcasecmp(aclname, name) == 0) {
2360 const char *file = cfg_obj_file(acl);
2361 unsigned int line = cfg_obj_line(acl);
2363 if (file == NULL)
2364 file = "<unknown file>";
2366 cfg_obj_log(acl2, logctx, ISC_LOG_ERROR,
2367 "attempt to redefine "
2368 "kal '%s' previous "
2369 "definition: %s:%u",
2370 name, file, line);
2371 result = ISC_R_FAILURE;
2377 return (result);