2 * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2001-2003 Internet Software Consortium.
5 * Permission to use, copy, modify, and 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.14.2.25 2004/07/29 00:08:17 marka Exp $ */
25 #include <isc/buffer.h>
28 #include <isc/netaddr.h>
29 #include <isc/result.h>
30 #include <isc/sockaddr.h>
31 #include <isc/symtab.h>
34 #include <dns/fixedname.h>
35 #include <dns/rdataclass.h>
37 #include <isccfg/cfg.h>
38 #include <isccfg/check.h>
41 freekey(char *key, unsigned int type, isc_symvalue_t value, void *userarg) {
44 isc_mem_free(userarg, key);
48 check_forward(cfg_obj_t *options, isc_log_t *logctx) {
49 cfg_obj_t *forward = NULL;
50 cfg_obj_t *forwarders = NULL;
52 (void)cfg_map_get(options, "forward", &forward);
53 (void)cfg_map_get(options, "forwarders", &forwarders);
55 if (forward != NULL && forwarders == NULL) {
56 cfg_obj_log(forward, logctx, ISC_LOG_ERROR,
57 "no matching 'forwarders' statement");
58 return (ISC_R_FAILURE);
60 return (ISC_R_SUCCESS);
69 check_options(cfg_obj_t *options, isc_log_t *logctx) {
70 isc_result_t result = ISC_R_SUCCESS;
74 static intervaltable intervals[] = {
75 { "cleaning-interval", 60 },
76 { "heartbeat-interval", 60 },
77 { "interface-interval", 60 },
78 { "max-transfer-idle-in", 60 },
79 { "max-transfer-idle-out", 60 },
80 { "max-transfer-time-in", 60 },
81 { "max-transfer-time-out", 60 },
82 { "sig-validity-interval", 86400},
83 { "statistics-interval", 60 },
87 * Check that fields specified in units of time other than seconds
88 * have reasonable values.
90 for (i = 0; i < sizeof(intervals) / sizeof(intervals[0]); i++) {
92 cfg_obj_t *obj = NULL;
93 (void)cfg_map_get(options, intervals[i].name, &obj);
96 val = cfg_obj_asuint32(obj);
97 if (val > (ISC_UINT32_MAX / intervals[i].scale)) {
98 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
99 "%s '%d' is out of range",
100 intervals[i].name, val);
101 result = ISC_R_RANGE;
106 (void)cfg_map_get(options, "root-delegation-only", &obj);
108 if (!cfg_obj_isvoid(obj)) {
109 cfg_listelt_t *element;
112 dns_fixedname_t fixed;
115 isc_result_t tresult;
117 dns_fixedname_init(&fixed);
118 name = dns_fixedname_name(&fixed);
119 for (element = cfg_list_first(obj);
121 element = cfg_list_next(element)) {
122 exclude = cfg_listelt_value(element);
123 str = cfg_obj_asstring(exclude);
124 isc_buffer_init(&b, str, strlen(str));
125 isc_buffer_add(&b, strlen(str));
126 tresult = dns_name_fromtext(name, &b,
129 if (tresult != ISC_R_SUCCESS) {
130 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
131 "bad domain name '%s'",
145 #define FORWARDZONE 16
146 #define DELEGATIONZONE 32
154 check_zoneconf(cfg_obj_t *zconfig, isc_symtab_t *symtab, isc_log_t *logctx,
161 cfg_obj_t *obj = NULL;
162 cfg_obj_t *addrlist = NULL;
163 isc_symvalue_t symvalue;
164 isc_result_t result = ISC_R_SUCCESS;
165 isc_result_t tresult;
167 dns_fixedname_t fixedname;
170 static optionstable options[] = {
171 { "allow-query", MASTERZONE | SLAVEZONE | STUBZONE },
172 { "allow-notify", SLAVEZONE },
173 { "allow-transfer", MASTERZONE | SLAVEZONE },
174 { "notify", MASTERZONE | SLAVEZONE },
175 { "also-notify", MASTERZONE | SLAVEZONE },
176 { "dialup", MASTERZONE | SLAVEZONE | STUBZONE },
177 { "delegation-only", HINTZONE | STUBZONE },
178 { "forward", MASTERZONE | SLAVEZONE | STUBZONE | FORWARDZONE},
179 { "forwarders", MASTERZONE | SLAVEZONE | STUBZONE | FORWARDZONE},
180 { "maintain-ixfr-base", MASTERZONE | SLAVEZONE },
181 { "max-ixfr-log-size", MASTERZONE | SLAVEZONE },
182 { "notify-source", MASTERZONE | SLAVEZONE },
183 { "notify-source-v6", MASTERZONE | SLAVEZONE },
184 { "transfer-source", SLAVEZONE | STUBZONE },
185 { "transfer-source-v6", SLAVEZONE | STUBZONE },
186 { "max-transfer-time-in", SLAVEZONE | STUBZONE },
187 { "max-transfer-time-out", MASTERZONE | SLAVEZONE },
188 { "max-transfer-idle-in", SLAVEZONE | STUBZONE },
189 { "max-transfer-idle-out", MASTERZONE | SLAVEZONE },
190 { "max-retry-time", SLAVEZONE | STUBZONE },
191 { "min-retry-time", SLAVEZONE | STUBZONE },
192 { "max-refresh-time", SLAVEZONE | STUBZONE },
193 { "min-refresh-time", SLAVEZONE | STUBZONE },
194 { "sig-validity-interval", MASTERZONE },
195 { "zone-statistics", MASTERZONE | SLAVEZONE | STUBZONE },
196 { "allow-update", MASTERZONE },
197 { "allow-update-forwarding", SLAVEZONE },
198 { "file", MASTERZONE | SLAVEZONE | STUBZONE | HINTZONE},
199 { "ixfr-base", MASTERZONE | SLAVEZONE },
200 { "ixfr-tmp-file", MASTERZONE | SLAVEZONE },
201 { "masters", SLAVEZONE | STUBZONE },
202 { "pubkey", MASTERZONE | SLAVEZONE | STUBZONE },
203 { "update-policy", MASTERZONE },
204 { "database", MASTERZONE | SLAVEZONE | STUBZONE },
207 static optionstable dialups[] = {
208 { "notify", MASTERZONE | SLAVEZONE },
209 { "notify-passive", SLAVEZONE },
210 { "refresh", SLAVEZONE | STUBZONE },
211 { "passive", SLAVEZONE | STUBZONE },
214 zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
216 zoptions = cfg_tuple_get(zconfig, "options");
219 (void)cfg_map_get(zoptions, "type", &obj);
221 cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
222 "zone '%s': type not present", zname);
223 return (ISC_R_FAILURE);
226 typestr = cfg_obj_asstring(obj);
227 if (strcasecmp(typestr, "master") == 0)
229 else if (strcasecmp(typestr, "slave") == 0)
231 else if (strcasecmp(typestr, "stub") == 0)
233 else if (strcasecmp(typestr, "forward") == 0)
235 else if (strcasecmp(typestr, "hint") == 0)
237 else if (strcasecmp(typestr, "delegation-only") == 0)
238 ztype = DELEGATIONZONE;
240 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
241 "zone '%s': invalid type %s",
243 return (ISC_R_FAILURE);
247 * Look for an already existing zone.
248 * We need to make this cannonical as isc_symtab_define()
249 * deals with strings.
251 dns_fixedname_init(&fixedname);
252 isc_buffer_init(&b, zname, strlen(zname));
253 isc_buffer_add(&b, strlen(zname));
254 result = dns_name_fromtext(dns_fixedname_name(&fixedname), &b,
255 dns_rootname, ISC_TRUE, NULL);
256 if (result != ISC_R_SUCCESS) {
257 cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
258 "zone '%s': is not a valid name", zname);
259 result = ISC_R_FAILURE;
261 char namebuf[DNS_NAME_FORMATSIZE];
264 dns_name_format(dns_fixedname_name(&fixedname),
265 namebuf, sizeof(namebuf));
266 key = isc_mem_strdup(mctx, namebuf);
268 return (ISC_R_NOMEMORY);
269 symvalue.as_pointer = NULL;
270 tresult = isc_symtab_define(symtab, key,
271 ztype == HINTZONE ? 1 : 2,
272 symvalue, isc_symexists_reject);
273 if (tresult == ISC_R_EXISTS) {
274 isc_mem_free(mctx, key);
275 cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
276 "zone '%s': already exists ", zname);
277 result = ISC_R_FAILURE;
278 } else if (tresult != ISC_R_SUCCESS) {
279 isc_mem_free(mctx, key);
286 * Look for inappropriate options for the given zone type.
288 for (i = 0; i < sizeof(options) / sizeof(options[0]); i++) {
290 if ((options[i].allowed & ztype) == 0 &&
291 cfg_map_get(zoptions, options[i].name, &obj) ==
294 if (strcmp(options[i].name, "allow-update") != 0 ||
295 ztype != SLAVEZONE) {
296 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
297 "option '%s' is not allowed "
299 options[i].name, typestr, zname);
300 result = ISC_R_FAILURE;
302 cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
303 "option '%s' is not allowed "
305 options[i].name, typestr, zname);
310 * Slave & stub zones must have a "masters" field.
312 if (ztype == SLAVEZONE || ztype == STUBZONE) {
314 if (cfg_map_get(zoptions, "masters", &obj) != ISC_R_SUCCESS) {
315 cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR,
316 "zone '%s': missing 'masters' entry",
318 result = ISC_R_FAILURE;
320 addrlist = cfg_tuple_get(obj, "addresses");
321 if (cfg_list_first(addrlist) == NULL) {
322 cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR,
323 "zone '%s': empty 'masters' entry",
325 result = ISC_R_FAILURE;
331 * Master zones can't have both "allow-update" and "update-policy".
333 if (ztype == MASTERZONE) {
334 isc_result_t res1, res2;
336 res1 = cfg_map_get(zoptions, "allow-update", &obj);
338 res2 = cfg_map_get(zoptions, "update-policy", &obj);
339 if (res1 == ISC_R_SUCCESS && res2 == ISC_R_SUCCESS) {
340 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
341 "zone '%s': 'allow-update' is ignored "
342 "when 'update-policy' is present",
344 result = ISC_R_FAILURE;
349 * Check the excessively complicated "dialup" option.
351 if (ztype == MASTERZONE || ztype == SLAVEZONE || ztype == STUBZONE) {
352 cfg_obj_t *dialup = NULL;
353 cfg_map_get(zoptions, "dialup", &dialup);
354 if (dialup != NULL && cfg_obj_isstring(dialup)) {
355 char *str = cfg_obj_asstring(dialup);
357 i < sizeof(dialups) / sizeof(dialups[0]);
360 if (strcasecmp(dialups[i].name, str) != 0)
362 if ((dialups[i].allowed & ztype) == 0) {
363 cfg_obj_log(obj, logctx,
365 "dialup type '%s' is not "
368 str, typestr, zname);
369 result = ISC_R_FAILURE;
373 if (i == sizeof(dialups) / sizeof(dialups[0])) {
374 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
375 "invalid dialup type '%s' in zone "
377 result = ISC_R_FAILURE;
383 * Check that forwarding is reasonable.
385 if (check_forward(zoptions, logctx) != ISC_R_SUCCESS)
386 result = ISC_R_FAILURE;
389 * Check various options.
391 tresult = check_options(zoptions, logctx);
392 if (tresult != ISC_R_SUCCESS)
399 cfg_check_key(cfg_obj_t *key, isc_log_t *logctx) {
400 cfg_obj_t *algobj = NULL;
401 cfg_obj_t *secretobj = NULL;
402 const char *keyname = cfg_obj_asstring(cfg_map_getname(key));
404 cfg_map_get(key, "algorithm", &algobj);
405 cfg_map_get(key, "secret", &secretobj);
406 if (secretobj == NULL || algobj == NULL) {
407 cfg_obj_log(key, logctx, ISC_LOG_ERROR,
408 "key '%s' must have both 'secret' and "
409 "'algorithm' defined",
411 return ISC_R_FAILURE;
413 return ISC_R_SUCCESS;
417 check_keylist(cfg_obj_t *keys, isc_symtab_t *symtab, isc_log_t *logctx) {
418 isc_result_t result = ISC_R_SUCCESS;
419 isc_result_t tresult;
420 cfg_listelt_t *element;
422 for (element = cfg_list_first(keys);
424 element = cfg_list_next(element))
426 cfg_obj_t *key = cfg_listelt_value(element);
427 const char *keyname = cfg_obj_asstring(cfg_map_getname(key));
428 isc_symvalue_t symvalue;
430 symvalue.as_pointer = NULL;
431 tresult = isc_symtab_define(symtab, keyname, 1,
432 symvalue, isc_symexists_reject);
433 if (tresult == ISC_R_EXISTS) {
434 cfg_obj_log(key, logctx, ISC_LOG_ERROR,
435 "key '%s': already exists ", keyname);
437 } else if (tresult != ISC_R_SUCCESS)
440 tresult = cfg_check_key(key, logctx);
441 if (tresult != ISC_R_SUCCESS)
448 check_servers(cfg_obj_t *servers, isc_log_t *logctx) {
449 isc_result_t result = ISC_R_SUCCESS;
450 cfg_listelt_t *e1, *e2;
452 isc_sockaddr_t *s1, *s2;
455 for (e1 = cfg_list_first(servers); e1 != NULL; e1 = cfg_list_next(e1)) {
456 v1 = cfg_listelt_value(e1);
457 s1 = cfg_obj_assockaddr(cfg_map_getname(v1));
459 while ((e2 = cfg_list_next(e2)) != NULL) {
460 v2 = cfg_listelt_value(e2);
461 s2 = cfg_obj_assockaddr(cfg_map_getname(v2));
462 if (isc_sockaddr_eqaddr(s1, s2)) {
466 isc_netaddr_fromsockaddr(&na, s2);
467 isc_buffer_init(&target, buf, sizeof(buf) - 1);
468 INSIST(isc_netaddr_totext(&na, &target)
470 buf[isc_buffer_usedlength(&target)] = '\0';
472 cfg_obj_log(v2, logctx, ISC_LOG_ERROR,
473 "server '%s': already exists",
475 result = ISC_R_FAILURE;
483 check_viewconf(cfg_obj_t *config, cfg_obj_t *vconfig, isc_log_t *logctx, isc_mem_t *mctx)
485 cfg_obj_t *servers = NULL;
486 cfg_obj_t *zones = NULL;
487 cfg_obj_t *keys = NULL;
488 cfg_listelt_t *element;
489 isc_symtab_t *symtab = NULL;
490 isc_result_t result = ISC_R_SUCCESS;
491 isc_result_t tresult = ISC_R_SUCCESS;
494 * Check that all zone statements are syntactically correct and
495 * there are no duplicate zones.
497 tresult = isc_symtab_create(mctx, 100, freekey, mctx,
499 if (tresult != ISC_R_SUCCESS)
500 return (ISC_R_NOMEMORY);
503 (void)cfg_map_get(vconfig, "zone", &zones);
505 (void)cfg_map_get(config, "zone", &zones);
507 for (element = cfg_list_first(zones);
509 element = cfg_list_next(element))
511 cfg_obj_t *zone = cfg_listelt_value(element);
513 if (check_zoneconf(zone, symtab, logctx, mctx) != ISC_R_SUCCESS)
514 result = ISC_R_FAILURE;
517 isc_symtab_destroy(&symtab);
520 * Check that all key statements are syntactically correct and
521 * there are no duplicate keys.
523 tresult = isc_symtab_create(mctx, 100, NULL, NULL, ISC_TRUE, &symtab);
524 if (tresult != ISC_R_SUCCESS)
525 return (ISC_R_NOMEMORY);
527 cfg_map_get(config, "key", &keys);
528 tresult = check_keylist(keys, symtab, logctx);
529 if (tresult == ISC_R_EXISTS)
530 result = ISC_R_FAILURE;
531 else if (tresult != ISC_R_SUCCESS) {
532 isc_symtab_destroy(&symtab);
536 if (vconfig != NULL) {
538 (void)cfg_map_get(vconfig, "key", &keys);
539 tresult = check_keylist(keys, symtab, logctx);
540 if (tresult == ISC_R_EXISTS)
541 result = ISC_R_FAILURE;
542 else if (tresult != ISC_R_SUCCESS) {
543 isc_symtab_destroy(&symtab);
548 isc_symtab_destroy(&symtab);
551 * Check that forwarding is reasonable.
553 if (vconfig == NULL) {
554 cfg_obj_t *options = NULL;
555 cfg_map_get(config, "options", &options);
557 if (check_forward(options, logctx) != ISC_R_SUCCESS)
558 result = ISC_R_FAILURE;
560 if (check_forward(vconfig, logctx) != ISC_R_SUCCESS)
561 result = ISC_R_FAILURE;
565 if (vconfig != NULL) {
566 (void)cfg_map_get(vconfig, "server", &servers);
567 if (servers != NULL &&
568 check_servers(servers, logctx) != ISC_R_SUCCESS)
569 result = ISC_R_FAILURE;
573 tresult = check_options(vconfig, logctx);
575 tresult = check_options(config, logctx);
576 if (tresult != ISC_R_SUCCESS)
584 cfg_check_namedconf(cfg_obj_t *config, isc_log_t *logctx, isc_mem_t *mctx) {
585 cfg_obj_t *options = NULL;
586 cfg_obj_t *servers = NULL;
587 cfg_obj_t *views = NULL;
588 cfg_obj_t *acls = NULL;
590 cfg_listelt_t *velement;
591 isc_result_t result = ISC_R_SUCCESS;
592 isc_result_t tresult;
593 isc_symtab_t *symtab = NULL;
595 static const char *builtin[] = { "localhost", "localnets",
598 (void)cfg_map_get(config, "options", &options);
600 if (options != NULL &&
601 check_options(options, logctx) != ISC_R_SUCCESS)
602 result = ISC_R_FAILURE;
604 (void)cfg_map_get(config, "server", &servers);
605 if (servers != NULL &&
606 check_servers(servers, logctx) != ISC_R_SUCCESS)
607 result = ISC_R_FAILURE;
609 (void)cfg_map_get(config, "view", &views);
612 if (check_viewconf(config, NULL, logctx, mctx)
614 result = ISC_R_FAILURE;
616 cfg_obj_t *zones = NULL;
618 (void)cfg_map_get(config, "zone", &zones);
620 cfg_obj_log(zones, logctx, ISC_LOG_ERROR,
621 "when using 'view' statements, "
622 "all zones must be in views");
623 result = ISC_R_FAILURE;
627 tresult = isc_symtab_create(mctx, 100, NULL, NULL, ISC_TRUE, &symtab);
628 if (tresult != ISC_R_SUCCESS)
630 for (velement = cfg_list_first(views);
632 velement = cfg_list_next(velement))
634 cfg_obj_t *view = cfg_listelt_value(velement);
635 cfg_obj_t *vname = cfg_tuple_get(view, "name");
636 cfg_obj_t *voptions = cfg_tuple_get(view, "options");
637 cfg_obj_t *vclassobj = cfg_tuple_get(view, "class");
638 dns_rdataclass_t vclass = dns_rdataclass_in;
639 isc_result_t tresult = ISC_R_SUCCESS;
640 const char *key = cfg_obj_asstring(vname);
641 isc_symvalue_t symvalue;
643 if (cfg_obj_isstring(vclassobj)) {
646 DE_CONST(cfg_obj_asstring(vclassobj), r.base);
647 r.length = strlen(r.base);
648 tresult = dns_rdataclass_fromtext(&vclass, &r);
649 if (tresult != ISC_R_SUCCESS)
650 cfg_obj_log(vclassobj, logctx, ISC_LOG_ERROR,
651 "view '%s': invalid class %s",
652 cfg_obj_asstring(vname), r.base);
654 if (tresult == ISC_R_SUCCESS && symtab != NULL) {
655 symvalue.as_pointer = view;
656 tresult = isc_symtab_define(symtab, key, vclass,
658 isc_symexists_reject);
659 if (tresult == ISC_R_EXISTS) {
660 cfg_obj_log(view, logctx, ISC_LOG_ERROR,
661 "view '%s': already exists", key);
663 } else if (result != ISC_R_SUCCESS) {
665 } else if ((strcasecmp(key, "_bind") == 0 &&
666 vclass == dns_rdataclass_ch) ||
667 (strcasecmp(key, "_default") == 0 &&
668 vclass == dns_rdataclass_in)) {
669 cfg_obj_log(view, logctx, ISC_LOG_ERROR,
670 "attempt to redefine builtin view "
672 result = ISC_R_EXISTS;
675 if (check_viewconf(config, voptions, logctx, mctx)
677 result = ISC_R_FAILURE;
680 isc_symtab_destroy(&symtab);
682 if (views != NULL && options != NULL) {
684 tresult = cfg_map_get(options, "cache-file", &obj);
685 if (tresult == ISC_R_SUCCESS) {
686 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
687 "'cache-file' cannot be a global "
688 "option if views are present");
689 result = ISC_R_FAILURE;
693 tresult = cfg_map_get(config, "acl", &acls);
694 if (tresult == ISC_R_SUCCESS) {
699 for (elt = cfg_list_first(acls);
701 elt = cfg_list_next(elt)) {
702 cfg_obj_t *acl = cfg_listelt_value(elt);
705 aclname = cfg_obj_asstring(cfg_tuple_get(acl, "name"));
707 i < sizeof(builtin) / sizeof(builtin[0]);
709 if (strcasecmp(aclname, builtin[i]) == 0) {
710 cfg_obj_log(acl, logctx, ISC_LOG_ERROR,
711 "attempt to redefine "
714 result = ISC_R_FAILURE;
718 for (elt2 = cfg_list_next(elt);
720 elt2 = cfg_list_next(elt2)) {
721 cfg_obj_t *acl2 = cfg_listelt_value(elt2);
723 name = cfg_obj_asstring(cfg_tuple_get(acl2,
725 if (strcasecmp(aclname, name) == 0) {
726 cfg_obj_log(acl2, logctx, ISC_LOG_ERROR,
727 "attempt to redefine "
729 result = ISC_R_FAILURE;