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