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