1/*	$NetBSD: check.c,v 1.16 2024/02/21 22:52:04 christos Exp $	*/
2
3/*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * SPDX-License-Identifier: MPL-2.0
7 *
8 * This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this
10 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11 *
12 * See the COPYRIGHT file distributed with this work for additional
13 * information regarding copyright ownership.
14 */
15
16/*! \file */
17
18#include <ctype.h>
19#include <inttypes.h>
20#include <stdbool.h>
21#include <stdlib.h>
22
23#include <openssl/opensslv.h>
24
25#ifdef HAVE_DNSTAP
26#include <fstrm.h>
27#endif
28
29#include <isc/aes.h>
30#include <isc/base64.h>
31#include <isc/buffer.h>
32#include <isc/dir.h>
33#include <isc/file.h>
34#include <isc/hex.h>
35#include <isc/log.h>
36#include <isc/md.h>
37#include <isc/mem.h>
38#include <isc/netaddr.h>
39#include <isc/parseint.h>
40#include <isc/print.h>
41#include <isc/region.h>
42#include <isc/result.h>
43#include <isc/siphash.h>
44#include <isc/sockaddr.h>
45#include <isc/string.h>
46#include <isc/symtab.h>
47#include <isc/util.h>
48
49#include <dns/acl.h>
50#include <dns/dnstap.h>
51#include <dns/fixedname.h>
52#include <dns/kasp.h>
53#include <dns/keyvalues.h>
54#include <dns/peer.h>
55#include <dns/rbt.h>
56#include <dns/rdataclass.h>
57#include <dns/rdatatype.h>
58#include <dns/rrl.h>
59#include <dns/secalg.h>
60#include <dns/ssu.h>
61
62#include <dst/dst.h>
63
64#include <isccfg/aclconf.h>
65#include <isccfg/cfg.h>
66#include <isccfg/grammar.h>
67#include <isccfg/kaspconf.h>
68#include <isccfg/namedconf.h>
69
70#include <ns/hooks.h>
71
72#include <bind9/check.h>
73
74static in_port_t dnsport = 53;
75
76static isc_result_t
77fileexist(const cfg_obj_t *obj, isc_symtab_t *symtab, bool writeable,
78	  isc_log_t *logctxlogc);
79
80static isc_result_t
81keydirexist(const cfg_obj_t *zcgf, const char *dir, const char *kaspnamestr,
82	    isc_symtab_t *symtab, isc_log_t *logctx, isc_mem_t *mctx);
83static void
84freekey(char *key, unsigned int type, isc_symvalue_t value, void *userarg) {
85	UNUSED(type);
86	UNUSED(value);
87	isc_mem_free(userarg, key);
88}
89
90static isc_result_t
91check_orderent(const cfg_obj_t *ent, isc_log_t *logctx) {
92	isc_result_t result = ISC_R_SUCCESS;
93	isc_result_t tresult;
94	isc_textregion_t r;
95	dns_fixedname_t fixed;
96	const cfg_obj_t *obj;
97	dns_rdataclass_t rdclass;
98	dns_rdatatype_t rdtype;
99	isc_buffer_t b;
100	const char *str;
101
102	dns_fixedname_init(&fixed);
103	obj = cfg_tuple_get(ent, "class");
104	if (cfg_obj_isstring(obj)) {
105		DE_CONST(cfg_obj_asstring(obj), r.base);
106		r.length = strlen(r.base);
107		tresult = dns_rdataclass_fromtext(&rdclass, &r);
108		if (tresult != ISC_R_SUCCESS) {
109			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
110				    "rrset-order: invalid class '%s'", r.base);
111			if (result == ISC_R_SUCCESS) {
112				result = ISC_R_FAILURE;
113			}
114		}
115	}
116
117	obj = cfg_tuple_get(ent, "type");
118	if (cfg_obj_isstring(obj)) {
119		DE_CONST(cfg_obj_asstring(obj), r.base);
120		r.length = strlen(r.base);
121		tresult = dns_rdatatype_fromtext(&rdtype, &r);
122		if (tresult != ISC_R_SUCCESS) {
123			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
124				    "rrset-order: invalid type '%s'", r.base);
125			if (result == ISC_R_SUCCESS) {
126				result = ISC_R_FAILURE;
127			}
128		}
129	}
130
131	obj = cfg_tuple_get(ent, "name");
132	if (cfg_obj_isstring(obj)) {
133		str = cfg_obj_asstring(obj);
134		isc_buffer_constinit(&b, str, strlen(str));
135		isc_buffer_add(&b, strlen(str));
136		tresult = dns_name_fromtext(dns_fixedname_name(&fixed), &b,
137					    dns_rootname, 0, NULL);
138		if (tresult != ISC_R_SUCCESS) {
139			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
140				    "rrset-order: invalid name '%s'", str);
141			if (result == ISC_R_SUCCESS) {
142				result = ISC_R_FAILURE;
143			}
144		}
145	}
146
147	obj = cfg_tuple_get(ent, "order");
148	if (!cfg_obj_isstring(obj) ||
149	    strcasecmp("order", cfg_obj_asstring(obj)) != 0)
150	{
151		cfg_obj_log(ent, logctx, ISC_LOG_ERROR,
152			    "rrset-order: keyword 'order' missing");
153		if (result == ISC_R_SUCCESS) {
154			result = ISC_R_FAILURE;
155		}
156	}
157
158	obj = cfg_tuple_get(ent, "ordering");
159	if (!cfg_obj_isstring(obj)) {
160		cfg_obj_log(ent, logctx, ISC_LOG_ERROR,
161			    "rrset-order: missing ordering");
162		if (result == ISC_R_SUCCESS) {
163			result = ISC_R_FAILURE;
164		}
165	} else if (strcasecmp(cfg_obj_asstring(obj), "fixed") == 0) {
166#if !DNS_RDATASET_FIXED
167		cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
168			    "rrset-order: order 'fixed' was disabled at "
169			    "compilation time");
170#endif /* if !DNS_RDATASET_FIXED */
171	} else if (strcasecmp(cfg_obj_asstring(obj), "random") != 0 &&
172		   strcasecmp(cfg_obj_asstring(obj), "cyclic") != 0 &&
173		   strcasecmp(cfg_obj_asstring(obj), "none") != 0)
174	{
175		cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
176			    "rrset-order: invalid order '%s'",
177			    cfg_obj_asstring(obj));
178		if (result == ISC_R_SUCCESS) {
179			result = ISC_R_FAILURE;
180		}
181	}
182	return (result);
183}
184
185static isc_result_t
186check_order(const cfg_obj_t *options, isc_log_t *logctx) {
187	isc_result_t result = ISC_R_SUCCESS;
188	isc_result_t tresult;
189	const cfg_listelt_t *element;
190	const cfg_obj_t *obj = NULL;
191
192	if (cfg_map_get(options, "rrset-order", &obj) != ISC_R_SUCCESS) {
193		return (result);
194	}
195
196	for (element = cfg_list_first(obj); element != NULL;
197	     element = cfg_list_next(element))
198	{
199		tresult = check_orderent(cfg_listelt_value(element), logctx);
200		if (result == ISC_R_SUCCESS && tresult != ISC_R_SUCCESS) {
201			result = tresult;
202		}
203	}
204	return (result);
205}
206
207static isc_result_t
208check_dual_stack(const cfg_obj_t *options, isc_log_t *logctx) {
209	const cfg_listelt_t *element;
210	const cfg_obj_t *alternates = NULL;
211	const cfg_obj_t *value;
212	const cfg_obj_t *obj;
213	const char *str;
214	dns_fixedname_t fixed;
215	dns_name_t *name;
216	isc_buffer_t buffer;
217	isc_result_t result = ISC_R_SUCCESS;
218	isc_result_t tresult;
219
220	(void)cfg_map_get(options, "dual-stack-servers", &alternates);
221
222	if (alternates == NULL) {
223		return (ISC_R_SUCCESS);
224	}
225
226	obj = cfg_tuple_get(alternates, "port");
227	if (cfg_obj_isuint32(obj)) {
228		uint32_t val = cfg_obj_asuint32(obj);
229		if (val > UINT16_MAX) {
230			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
231				    "port '%u' out of range", val);
232			if (result == ISC_R_SUCCESS) {
233				result = ISC_R_RANGE;
234			}
235		}
236	}
237	obj = cfg_tuple_get(alternates, "addresses");
238	for (element = cfg_list_first(obj); element != NULL;
239	     element = cfg_list_next(element))
240	{
241		value = cfg_listelt_value(element);
242		if (cfg_obj_issockaddr(value)) {
243			continue;
244		}
245		obj = cfg_tuple_get(value, "name");
246		str = cfg_obj_asstring(obj);
247		isc_buffer_constinit(&buffer, str, strlen(str));
248		isc_buffer_add(&buffer, strlen(str));
249		name = dns_fixedname_initname(&fixed);
250		tresult = dns_name_fromtext(name, &buffer, dns_rootname, 0,
251					    NULL);
252		if (tresult != ISC_R_SUCCESS) {
253			cfg_obj_log(obj, logctx, ISC_LOG_ERROR, "bad name '%s'",
254				    str);
255			if (result == ISC_R_SUCCESS) {
256				result = tresult;
257			}
258		}
259		obj = cfg_tuple_get(value, "port");
260		if (cfg_obj_isuint32(obj)) {
261			uint32_t val = cfg_obj_asuint32(obj);
262			if (val > UINT16_MAX) {
263				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
264					    "port '%u' out of range", val);
265				if (result == ISC_R_SUCCESS) {
266					result = ISC_R_RANGE;
267				}
268			}
269		}
270	}
271	return (result);
272}
273
274static isc_result_t
275check_forward(const cfg_obj_t *options, const cfg_obj_t *global,
276	      isc_log_t *logctx) {
277	const cfg_obj_t *forward = NULL;
278	const cfg_obj_t *forwarders = NULL;
279
280	(void)cfg_map_get(options, "forward", &forward);
281	(void)cfg_map_get(options, "forwarders", &forwarders);
282
283	if (forwarders != NULL && global != NULL) {
284		const char *file = cfg_obj_file(global);
285		unsigned int line = cfg_obj_line(global);
286		cfg_obj_log(forwarders, logctx, ISC_LOG_ERROR,
287			    "forwarders declared in root zone and "
288			    "in general configuration: %s:%u",
289			    file, line);
290		return (ISC_R_FAILURE);
291	}
292	if (forward != NULL && forwarders == NULL) {
293		cfg_obj_log(forward, logctx, ISC_LOG_ERROR,
294			    "no matching 'forwarders' statement");
295		return (ISC_R_FAILURE);
296	}
297	return (ISC_R_SUCCESS);
298}
299
300static isc_result_t
301disabled_algorithms(const cfg_obj_t *disabled, isc_log_t *logctx) {
302	isc_result_t result = ISC_R_SUCCESS;
303	isc_result_t tresult;
304	const cfg_listelt_t *element;
305	const char *str;
306	isc_buffer_t b;
307	dns_fixedname_t fixed;
308	dns_name_t *name;
309	const cfg_obj_t *obj;
310
311	name = dns_fixedname_initname(&fixed);
312	obj = cfg_tuple_get(disabled, "name");
313	str = cfg_obj_asstring(obj);
314	isc_buffer_constinit(&b, str, strlen(str));
315	isc_buffer_add(&b, strlen(str));
316	tresult = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
317	if (tresult != ISC_R_SUCCESS) {
318		cfg_obj_log(obj, logctx, ISC_LOG_ERROR, "bad domain name '%s'",
319			    str);
320		result = tresult;
321	}
322
323	obj = cfg_tuple_get(disabled, "algorithms");
324
325	for (element = cfg_list_first(obj); element != NULL;
326	     element = cfg_list_next(element))
327	{
328		isc_textregion_t r;
329		dns_secalg_t alg;
330
331		DE_CONST(cfg_obj_asstring(cfg_listelt_value(element)), r.base);
332		r.length = strlen(r.base);
333
334		tresult = dns_secalg_fromtext(&alg, &r);
335		if (tresult != ISC_R_SUCCESS) {
336			cfg_obj_log(cfg_listelt_value(element), logctx,
337				    ISC_LOG_ERROR, "invalid algorithm '%s'",
338				    r.base);
339			result = tresult;
340		}
341	}
342	return (result);
343}
344
345static isc_result_t
346disabled_ds_digests(const cfg_obj_t *disabled, isc_log_t *logctx) {
347	isc_result_t result = ISC_R_SUCCESS;
348	isc_result_t tresult;
349	const cfg_listelt_t *element;
350	const char *str;
351	isc_buffer_t b;
352	dns_fixedname_t fixed;
353	dns_name_t *name;
354	const cfg_obj_t *obj;
355
356	name = dns_fixedname_initname(&fixed);
357	obj = cfg_tuple_get(disabled, "name");
358	str = cfg_obj_asstring(obj);
359	isc_buffer_constinit(&b, str, strlen(str));
360	isc_buffer_add(&b, strlen(str));
361	tresult = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
362	if (tresult != ISC_R_SUCCESS) {
363		cfg_obj_log(obj, logctx, ISC_LOG_ERROR, "bad domain name '%s'",
364			    str);
365		result = tresult;
366	}
367
368	obj = cfg_tuple_get(disabled, "digests");
369
370	for (element = cfg_list_first(obj); element != NULL;
371	     element = cfg_list_next(element))
372	{
373		isc_textregion_t r;
374		dns_dsdigest_t digest;
375
376		DE_CONST(cfg_obj_asstring(cfg_listelt_value(element)), r.base);
377		r.length = strlen(r.base);
378
379		/* works with a numeric argument too */
380		tresult = dns_dsdigest_fromtext(&digest, &r);
381		if (tresult != ISC_R_SUCCESS) {
382			cfg_obj_log(cfg_listelt_value(element), logctx,
383				    ISC_LOG_ERROR, "invalid digest type '%s'",
384				    r.base);
385			result = tresult;
386		}
387	}
388	return (result);
389}
390
391static isc_result_t
392nameexist(const cfg_obj_t *obj, const char *name, int value,
393	  isc_symtab_t *symtab, const char *fmt, isc_log_t *logctx,
394	  isc_mem_t *mctx) {
395	char *key;
396	const char *file;
397	unsigned int line;
398	isc_result_t result;
399	isc_symvalue_t symvalue;
400
401	key = isc_mem_strdup(mctx, name);
402	symvalue.as_cpointer = obj;
403	result = isc_symtab_define(symtab, key, value, symvalue,
404				   isc_symexists_reject);
405	if (result == ISC_R_EXISTS) {
406		RUNTIME_CHECK(isc_symtab_lookup(symtab, key, value,
407						&symvalue) == ISC_R_SUCCESS);
408		file = cfg_obj_file(symvalue.as_cpointer);
409		line = cfg_obj_line(symvalue.as_cpointer);
410
411		if (file == NULL) {
412			file = "<unknown file>";
413		}
414		cfg_obj_log(obj, logctx, ISC_LOG_ERROR, fmt, key, file, line);
415		isc_mem_free(mctx, key);
416		result = ISC_R_EXISTS;
417	} else if (result != ISC_R_SUCCESS) {
418		isc_mem_free(mctx, key);
419	}
420	return (result);
421}
422
423static isc_result_t
424mustbesecure(const cfg_obj_t *secure, isc_symtab_t *symtab, isc_log_t *logctx,
425	     isc_mem_t *mctx) {
426	const cfg_obj_t *obj;
427	char namebuf[DNS_NAME_FORMATSIZE];
428	const char *str;
429	dns_fixedname_t fixed;
430	dns_name_t *name;
431	isc_buffer_t b;
432	isc_result_t result = ISC_R_SUCCESS;
433
434	name = dns_fixedname_initname(&fixed);
435	obj = cfg_tuple_get(secure, "name");
436	str = cfg_obj_asstring(obj);
437	isc_buffer_constinit(&b, str, strlen(str));
438	isc_buffer_add(&b, strlen(str));
439	result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
440	if (result != ISC_R_SUCCESS) {
441		cfg_obj_log(obj, logctx, ISC_LOG_ERROR, "bad domain name '%s'",
442			    str);
443	} else {
444		dns_name_format(name, namebuf, sizeof(namebuf));
445		result = nameexist(secure, namebuf, 1, symtab,
446				   "dnssec-must-be-secure '%s': already "
447				   "exists previous definition: %s:%u",
448				   logctx, mctx);
449	}
450	return (result);
451}
452
453static isc_result_t
454checkacl(const char *aclname, cfg_aclconfctx_t *actx, const cfg_obj_t *zconfig,
455	 const cfg_obj_t *voptions, const cfg_obj_t *config, isc_log_t *logctx,
456	 isc_mem_t *mctx) {
457	isc_result_t result;
458	const cfg_obj_t *aclobj = NULL;
459	const cfg_obj_t *options;
460	dns_acl_t *acl = NULL;
461
462	if (zconfig != NULL) {
463		options = cfg_tuple_get(zconfig, "options");
464		cfg_map_get(options, aclname, &aclobj);
465	}
466	if (voptions != NULL && aclobj == NULL) {
467		cfg_map_get(voptions, aclname, &aclobj);
468	}
469	if (config != NULL && aclobj == NULL) {
470		options = NULL;
471		cfg_map_get(config, "options", &options);
472		if (options != NULL) {
473			cfg_map_get(options, aclname, &aclobj);
474		}
475	}
476	if (aclobj == NULL) {
477		return (ISC_R_SUCCESS);
478	}
479	result = cfg_acl_fromconfig(aclobj, config, logctx, actx, mctx, 0,
480				    &acl);
481	if (acl != NULL) {
482		dns_acl_detach(&acl);
483	}
484
485	if (strcasecmp(aclname, "allow-transfer") == 0 &&
486	    cfg_obj_istuple(aclobj))
487	{
488		const cfg_obj_t *obj_port = cfg_tuple_get(
489			cfg_tuple_get(aclobj, "port-transport"), "port");
490		const cfg_obj_t *obj_proto = cfg_tuple_get(
491			cfg_tuple_get(aclobj, "port-transport"), "transport");
492
493		if (cfg_obj_isuint32(obj_port) &&
494		    cfg_obj_asuint32(obj_port) >= UINT16_MAX)
495		{
496			cfg_obj_log(obj_port, logctx, ISC_LOG_ERROR,
497				    "port value '%u' is out of range",
498
499				    cfg_obj_asuint32(obj_port));
500			if (result == ISC_R_SUCCESS) {
501				result = ISC_R_RANGE;
502			}
503		}
504
505		if (cfg_obj_isstring(obj_proto)) {
506			const char *allowed[] = { "tcp", "tls" };
507			const char *transport = cfg_obj_asstring(obj_proto);
508			bool found = false;
509			for (size_t i = 0; i < ARRAY_SIZE(allowed); i++) {
510				if (strcasecmp(transport, allowed[i]) == 0) {
511					found = true;
512				}
513			}
514
515			if (!found) {
516				cfg_obj_log(obj_proto, logctx, ISC_LOG_ERROR,
517					    "'%s' is not a valid transport "
518					    "protocol for "
519					    "zone "
520					    "transfers. Please specify either "
521					    "'tcp' or 'tls'",
522					    transport);
523				result = ISC_R_FAILURE;
524			}
525		}
526	}
527	return (result);
528}
529
530static isc_result_t
531check_viewacls(cfg_aclconfctx_t *actx, const cfg_obj_t *voptions,
532	       const cfg_obj_t *config, isc_log_t *logctx, isc_mem_t *mctx) {
533	isc_result_t result = ISC_R_SUCCESS, tresult;
534	int i = 0;
535
536	static const char *acls[] = {
537		"allow-query",	     "allow-query-on",
538		"allow-query-cache", "allow-query-cache-on",
539		"blackhole",	     "keep-response-order",
540		"match-clients",     "match-destinations",
541		"sortlist",	     NULL
542	};
543
544	while (acls[i] != NULL) {
545		tresult = checkacl(acls[i++], actx, NULL, voptions, config,
546				   logctx, mctx);
547		if (tresult != ISC_R_SUCCESS) {
548			result = tresult;
549		}
550	}
551	return (result);
552}
553
554static void
555dns64_error(const cfg_obj_t *obj, isc_log_t *logctx, isc_netaddr_t *netaddr,
556	    unsigned int prefixlen, const char *message) {
557	char buf[ISC_NETADDR_FORMATSIZE + 1];
558	isc_netaddr_format(netaddr, buf, sizeof(buf));
559	cfg_obj_log(obj, logctx, ISC_LOG_ERROR, "dns64 prefix %s/%u %s", buf,
560		    prefixlen, message);
561}
562
563static isc_result_t
564check_dns64(cfg_aclconfctx_t *actx, const cfg_obj_t *voptions,
565	    const cfg_obj_t *config, isc_log_t *logctx, isc_mem_t *mctx) {
566	isc_result_t result = ISC_R_SUCCESS;
567	const cfg_obj_t *dns64 = NULL;
568	const cfg_obj_t *options;
569	const cfg_listelt_t *element;
570	const cfg_obj_t *map, *obj;
571	isc_netaddr_t na, sa;
572	unsigned int prefixlen;
573	int nbytes;
574	int i;
575
576	static const char *acls[] = { "clients", "exclude", "mapped", NULL };
577
578	if (voptions != NULL) {
579		cfg_map_get(voptions, "dns64", &dns64);
580	}
581	if (config != NULL && dns64 == NULL) {
582		options = NULL;
583		cfg_map_get(config, "options", &options);
584		if (options != NULL) {
585			cfg_map_get(options, "dns64", &dns64);
586		}
587	}
588	if (dns64 == NULL) {
589		return (ISC_R_SUCCESS);
590	}
591
592	for (element = cfg_list_first(dns64); element != NULL;
593	     element = cfg_list_next(element))
594	{
595		map = cfg_listelt_value(element);
596		obj = cfg_map_getname(map);
597
598		cfg_obj_asnetprefix(obj, &na, &prefixlen);
599		if (na.family != AF_INET6) {
600			dns64_error(map, logctx, &na, prefixlen,
601				    "must be IPv6");
602			result = ISC_R_FAILURE;
603			continue;
604		}
605
606		if (na.type.in6.s6_addr[8] != 0) {
607			dns64_error(map, logctx, &na, prefixlen,
608				    "bits [64..71] must be zero");
609			result = ISC_R_FAILURE;
610			continue;
611		}
612
613		if (prefixlen != 32 && prefixlen != 40 && prefixlen != 48 &&
614		    prefixlen != 56 && prefixlen != 64 && prefixlen != 96)
615		{
616			dns64_error(map, logctx, &na, prefixlen,
617				    "length is not 32/40/48/56/64/96");
618			result = ISC_R_FAILURE;
619			continue;
620		}
621
622		for (i = 0; acls[i] != NULL; i++) {
623			obj = NULL;
624			(void)cfg_map_get(map, acls[i], &obj);
625			if (obj != NULL) {
626				dns_acl_t *acl = NULL;
627				isc_result_t tresult;
628
629				tresult = cfg_acl_fromconfig(obj, config,
630							     logctx, actx, mctx,
631							     0, &acl);
632				if (acl != NULL) {
633					dns_acl_detach(&acl);
634				}
635				if (tresult != ISC_R_SUCCESS) {
636					result = tresult;
637				}
638			}
639		}
640
641		obj = NULL;
642		(void)cfg_map_get(map, "suffix", &obj);
643		if (obj != NULL) {
644			static const unsigned char zeros[16];
645			isc_netaddr_fromsockaddr(&sa, cfg_obj_assockaddr(obj));
646			if (sa.family != AF_INET6) {
647				cfg_obj_log(map, logctx, ISC_LOG_ERROR,
648					    "dns64 requires a IPv6 suffix");
649				result = ISC_R_FAILURE;
650				continue;
651			}
652			nbytes = prefixlen / 8 + 4;
653			if (prefixlen <= 64) {
654				nbytes++;
655			}
656			if (memcmp(sa.type.in6.s6_addr, zeros, nbytes) != 0) {
657				char netaddrbuf[ISC_NETADDR_FORMATSIZE];
658				isc_netaddr_format(&sa, netaddrbuf,
659						   sizeof(netaddrbuf));
660				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
661					    "bad suffix '%s' leading "
662					    "%u octets not zeros",
663					    netaddrbuf, nbytes);
664				result = ISC_R_FAILURE;
665			}
666		}
667	}
668
669	return (result);
670}
671
672#define CHECK_RRL(cond, pat, val1, val2)                                   \
673	do {                                                               \
674		if (!(cond)) {                                             \
675			cfg_obj_log(obj, logctx, ISC_LOG_ERROR, pat, val1, \
676				    val2);                                 \
677			if (result == ISC_R_SUCCESS)                       \
678				result = ISC_R_RANGE;                      \
679		}                                                          \
680	} while (0)
681
682#define CHECK_RRL_RATE(rate, def, max_rate, name)                          \
683	do {                                                               \
684		obj = NULL;                                                \
685		mresult = cfg_map_get(map, name, &obj);                    \
686		if (mresult == ISC_R_SUCCESS) {                            \
687			rate = cfg_obj_asuint32(obj);                      \
688			CHECK_RRL(rate <= max_rate, name " %d > %d", rate, \
689				  max_rate);                               \
690		}                                                          \
691	} while (0)
692
693static isc_result_t
694check_ratelimit(cfg_aclconfctx_t *actx, const cfg_obj_t *voptions,
695		const cfg_obj_t *config, isc_log_t *logctx, isc_mem_t *mctx) {
696	isc_result_t result = ISC_R_SUCCESS;
697	isc_result_t mresult;
698	const cfg_obj_t *map = NULL;
699	const cfg_obj_t *options;
700	const cfg_obj_t *obj;
701	int min_entries, i;
702	int all_per_second;
703	int errors_per_second;
704	int nodata_per_second;
705	int nxdomains_per_second;
706	int referrals_per_second;
707	int responses_per_second;
708	int slip;
709
710	if (voptions != NULL) {
711		cfg_map_get(voptions, "rate-limit", &map);
712	}
713	if (config != NULL && map == NULL) {
714		options = NULL;
715		cfg_map_get(config, "options", &options);
716		if (options != NULL) {
717			cfg_map_get(options, "rate-limit", &map);
718		}
719	}
720	if (map == NULL) {
721		return (ISC_R_SUCCESS);
722	}
723
724	min_entries = 500;
725	obj = NULL;
726	mresult = cfg_map_get(map, "min-table-size", &obj);
727	if (mresult == ISC_R_SUCCESS) {
728		min_entries = cfg_obj_asuint32(obj);
729		if (min_entries < 1) {
730			min_entries = 1;
731		}
732	}
733
734	obj = NULL;
735	mresult = cfg_map_get(map, "max-table-size", &obj);
736	if (mresult == ISC_R_SUCCESS) {
737		i = cfg_obj_asuint32(obj);
738		CHECK_RRL(i >= min_entries,
739			  "max-table-size %d < min-table-size %d", i,
740			  min_entries);
741	}
742
743	CHECK_RRL_RATE(responses_per_second, 0, DNS_RRL_MAX_RATE,
744		       "responses-per-second");
745
746	CHECK_RRL_RATE(referrals_per_second, responses_per_second,
747		       DNS_RRL_MAX_RATE, "referrals-per-second");
748	CHECK_RRL_RATE(nodata_per_second, responses_per_second,
749		       DNS_RRL_MAX_RATE, "nodata-per-second");
750	CHECK_RRL_RATE(nxdomains_per_second, responses_per_second,
751		       DNS_RRL_MAX_RATE, "nxdomains-per-second");
752	CHECK_RRL_RATE(errors_per_second, responses_per_second,
753		       DNS_RRL_MAX_RATE, "errors-per-second");
754
755	CHECK_RRL_RATE(all_per_second, 0, DNS_RRL_MAX_RATE, "all-per-second");
756
757	CHECK_RRL_RATE(slip, 2, DNS_RRL_MAX_SLIP, "slip");
758
759	obj = NULL;
760	mresult = cfg_map_get(map, "window", &obj);
761	if (mresult == ISC_R_SUCCESS) {
762		i = cfg_obj_asuint32(obj);
763		CHECK_RRL(i >= 1 && i <= DNS_RRL_MAX_WINDOW,
764			  "window %d < 1 or > %d", i, DNS_RRL_MAX_WINDOW);
765	}
766
767	obj = NULL;
768	mresult = cfg_map_get(map, "qps-scale", &obj);
769	if (mresult == ISC_R_SUCCESS) {
770		i = cfg_obj_asuint32(obj);
771		CHECK_RRL(i >= 1, "invalid 'qps-scale %d'%s", i, "");
772	}
773
774	obj = NULL;
775	mresult = cfg_map_get(map, "ipv4-prefix-length", &obj);
776	if (mresult == ISC_R_SUCCESS) {
777		i = cfg_obj_asuint32(obj);
778		CHECK_RRL(i >= 8 && i <= 32,
779			  "invalid 'ipv4-prefix-length %d'%s", i, "");
780	}
781
782	obj = NULL;
783	mresult = cfg_map_get(map, "ipv6-prefix-length", &obj);
784	if (mresult == ISC_R_SUCCESS) {
785		i = cfg_obj_asuint32(obj);
786		CHECK_RRL(i >= 16 && i <= DNS_RRL_MAX_PREFIX,
787			  "ipv6-prefix-length %d < 16 or > %d", i,
788			  DNS_RRL_MAX_PREFIX);
789	}
790
791	obj = NULL;
792	(void)cfg_map_get(map, "exempt-clients", &obj);
793	if (obj != NULL) {
794		dns_acl_t *acl = NULL;
795		isc_result_t tresult;
796
797		tresult = cfg_acl_fromconfig(obj, config, logctx, actx, mctx, 0,
798					     &acl);
799		if (acl != NULL) {
800			dns_acl_detach(&acl);
801		}
802		if (result == ISC_R_SUCCESS) {
803			result = tresult;
804		}
805	}
806
807	return (result);
808}
809
810/*
811 * Check allow-recursion and allow-recursion-on acls, and also log a
812 * warning if they're inconsistent with the "recursion" option.
813 */
814static isc_result_t
815check_recursionacls(cfg_aclconfctx_t *actx, const cfg_obj_t *voptions,
816		    const char *viewname, const cfg_obj_t *config,
817		    isc_log_t *logctx, isc_mem_t *mctx) {
818	const cfg_obj_t *options, *aclobj, *obj = NULL;
819	dns_acl_t *acl = NULL;
820	isc_result_t result = ISC_R_SUCCESS, tresult;
821	bool recursion;
822	const char *forview = " for view ";
823	int i = 0;
824
825	static const char *acls[] = { "allow-recursion", "allow-recursion-on",
826				      NULL };
827
828	if (voptions != NULL) {
829		cfg_map_get(voptions, "recursion", &obj);
830	}
831	if (obj == NULL && config != NULL) {
832		options = NULL;
833		cfg_map_get(config, "options", &options);
834		if (options != NULL) {
835			cfg_map_get(options, "recursion", &obj);
836		}
837	}
838	if (obj == NULL) {
839		recursion = true;
840	} else {
841		recursion = cfg_obj_asboolean(obj);
842	}
843
844	if (viewname == NULL) {
845		viewname = "";
846		forview = "";
847	}
848
849	for (i = 0; acls[i] != NULL; i++) {
850		aclobj = options = NULL;
851		acl = NULL;
852
853		if (voptions != NULL) {
854			cfg_map_get(voptions, acls[i], &aclobj);
855		}
856		if (config != NULL && aclobj == NULL) {
857			options = NULL;
858			cfg_map_get(config, "options", &options);
859			if (options != NULL) {
860				cfg_map_get(options, acls[i], &aclobj);
861			}
862		}
863		if (aclobj == NULL) {
864			continue;
865		}
866
867		tresult = cfg_acl_fromconfig(aclobj, config, logctx, actx, mctx,
868					     0, &acl);
869
870		if (tresult != ISC_R_SUCCESS) {
871			result = tresult;
872		}
873
874		if (acl == NULL) {
875			continue;
876		}
877
878		if (!recursion && !dns_acl_isnone(acl)) {
879			cfg_obj_log(aclobj, logctx, ISC_LOG_WARNING,
880				    "both \"recursion no;\" and "
881				    "\"%s\" active%s%s",
882				    acls[i], forview, viewname);
883		}
884
885		if (acl != NULL) {
886			dns_acl_detach(&acl);
887		}
888	}
889
890	return (result);
891}
892
893typedef struct {
894	const char *name;
895	unsigned int scale;
896	unsigned int max;
897} intervaltable;
898
899#ifdef HAVE_DNSTAP
900typedef struct {
901	const char *name;
902	unsigned int min;
903	unsigned int max;
904} fstrmtable;
905#endif /* ifdef HAVE_DNSTAP */
906
907typedef enum {
908	optlevel_config,
909	optlevel_options,
910	optlevel_view,
911	optlevel_zone
912} optlevel_t;
913
914static isc_result_t
915check_name(const char *str) {
916	dns_fixedname_t fixed;
917
918	dns_fixedname_init(&fixed);
919	return (dns_name_fromstring(dns_fixedname_name(&fixed), str, 0, NULL));
920}
921
922static bool
923kasp_name_allowed(const cfg_listelt_t *element) {
924	const char *name = cfg_obj_asstring(
925		cfg_tuple_get(cfg_listelt_value(element), "name"));
926
927	if (strcmp("none", name) == 0) {
928		return (false);
929	}
930	if (strcmp("default", name) == 0) {
931		return (false);
932	}
933	if (strcmp("insecure", name) == 0) {
934		return (false);
935	}
936	return (true);
937}
938
939static const cfg_obj_t *
940find_maplist(const cfg_obj_t *config, const char *listname, const char *name) {
941	isc_result_t result;
942	const cfg_obj_t *maplist = NULL;
943	const cfg_listelt_t *elt = NULL;
944
945	REQUIRE(config != NULL);
946	REQUIRE(name != NULL);
947
948	result = cfg_map_get(config, listname, &maplist);
949	if (result != ISC_R_SUCCESS) {
950		return (NULL);
951	}
952
953	for (elt = cfg_list_first(maplist); elt != NULL;
954	     elt = cfg_list_next(elt))
955	{
956		const cfg_obj_t *map = cfg_listelt_value(elt);
957		if (strcasecmp(cfg_obj_asstring(cfg_map_getname(map)), name) ==
958		    0)
959		{
960			return (map);
961		}
962	}
963
964	return (NULL);
965}
966
967static isc_result_t
968check_listener(const cfg_obj_t *listener, const cfg_obj_t *config,
969	       cfg_aclconfctx_t *actx, isc_log_t *logctx, isc_mem_t *mctx) {
970	isc_result_t tresult, result = ISC_R_SUCCESS;
971	const cfg_obj_t *ltup = NULL;
972	const cfg_obj_t *tlsobj = NULL, *httpobj = NULL;
973	const cfg_obj_t *portobj = NULL;
974	const cfg_obj_t *http_server = NULL;
975	bool do_tls = false, no_tls = false;
976	dns_acl_t *acl = NULL;
977
978	ltup = cfg_tuple_get(listener, "tuple");
979	RUNTIME_CHECK(ltup != NULL);
980
981	tlsobj = cfg_tuple_get(ltup, "tls");
982	if (tlsobj != NULL && cfg_obj_isstring(tlsobj)) {
983		const char *tlsname = cfg_obj_asstring(tlsobj);
984
985		if (strcasecmp(tlsname, "none") == 0) {
986			no_tls = true;
987		} else if (strcasecmp(tlsname, "ephemeral") == 0) {
988			do_tls = true;
989		} else {
990			const cfg_obj_t *tlsmap = NULL;
991
992			do_tls = true;
993
994			tlsmap = find_maplist(config, "tls", tlsname);
995			if (tlsmap == NULL) {
996				cfg_obj_log(tlsobj, logctx, ISC_LOG_ERROR,
997					    "tls '%s' is not defined",
998					    cfg_obj_asstring(tlsobj));
999				result = ISC_R_FAILURE;
1000			}
1001		}
1002	}
1003
1004	httpobj = cfg_tuple_get(ltup, "http");
1005	if (httpobj != NULL && cfg_obj_isstring(httpobj)) {
1006		const char *httpname = cfg_obj_asstring(httpobj);
1007
1008		if (!do_tls && !no_tls) {
1009			cfg_obj_log(httpobj, logctx, ISC_LOG_ERROR,
1010				    "http must specify a 'tls' "
1011				    "statement, 'tls ephemeral', or "
1012				    "'tls none'");
1013			result = ISC_R_FAILURE;
1014		}
1015
1016		http_server = find_maplist(config, "http", httpname);
1017		if (http_server == NULL && strcasecmp(httpname, "default") != 0)
1018		{
1019			cfg_obj_log(httpobj, logctx, ISC_LOG_ERROR,
1020				    "http '%s' is not defined",
1021				    cfg_obj_asstring(httpobj));
1022			result = ISC_R_FAILURE;
1023		}
1024	}
1025
1026	portobj = cfg_tuple_get(ltup, "port");
1027	if (cfg_obj_isuint32(portobj) &&
1028	    cfg_obj_asuint32(portobj) >= UINT16_MAX)
1029	{
1030		cfg_obj_log(portobj, logctx, ISC_LOG_ERROR,
1031			    "port value '%u' is out of range",
1032
1033			    cfg_obj_asuint32(portobj));
1034		if (result == ISC_R_SUCCESS) {
1035			result = ISC_R_RANGE;
1036		}
1037	}
1038
1039	tresult = cfg_acl_fromconfig(cfg_tuple_get(listener, "acl"), config,
1040				     logctx, actx, mctx, 0, &acl);
1041	if (result == ISC_R_SUCCESS) {
1042		result = tresult;
1043	}
1044
1045	if (acl != NULL) {
1046		dns_acl_detach(&acl);
1047	}
1048
1049	return (result);
1050}
1051
1052static isc_result_t
1053check_listeners(const cfg_obj_t *list, const cfg_obj_t *config,
1054		cfg_aclconfctx_t *actx, isc_log_t *logctx, isc_mem_t *mctx) {
1055	isc_result_t tresult, result = ISC_R_SUCCESS;
1056	const cfg_listelt_t *elt = NULL;
1057
1058	for (elt = cfg_list_first(list); elt != NULL; elt = cfg_list_next(elt))
1059	{
1060		const cfg_obj_t *obj = cfg_listelt_value(elt);
1061		tresult = check_listener(obj, config, actx, logctx, mctx);
1062		if (result == ISC_R_SUCCESS) {
1063			result = tresult;
1064		}
1065	}
1066
1067	return (result);
1068}
1069
1070static isc_result_t
1071check_port(const cfg_obj_t *options, isc_log_t *logctx, const char *type,
1072	   in_port_t *portp) {
1073	const cfg_obj_t *portobj = NULL;
1074	isc_result_t result;
1075
1076	result = cfg_map_get(options, type, &portobj);
1077	if (result != ISC_R_SUCCESS) {
1078		return (ISC_R_SUCCESS);
1079	}
1080
1081	if (cfg_obj_asuint32(portobj) >= UINT16_MAX) {
1082		cfg_obj_log(portobj, logctx, ISC_LOG_ERROR,
1083			    "port '%u' out of range",
1084			    cfg_obj_asuint32(portobj));
1085		return (ISC_R_RANGE);
1086	}
1087
1088	if (portp != NULL) {
1089		*portp = (in_port_t)cfg_obj_asuint32(portobj);
1090	}
1091	return (ISC_R_SUCCESS);
1092}
1093
1094static isc_result_t
1095check_options(const cfg_obj_t *options, const cfg_obj_t *config,
1096	      isc_log_t *logctx, isc_mem_t *mctx, optlevel_t optlevel) {
1097	isc_result_t result = ISC_R_SUCCESS;
1098	isc_result_t tresult;
1099	unsigned int i;
1100	const cfg_obj_t *obj = NULL;
1101	const cfg_obj_t *resignobj = NULL;
1102	const cfg_listelt_t *element;
1103	isc_symtab_t *symtab = NULL;
1104	const char *str;
1105	isc_buffer_t b;
1106	uint32_t lifetime = 3600;
1107	bool has_dnssecpolicy = false;
1108	const char *ccalg = "siphash24";
1109	cfg_aclconfctx_t *actx = NULL;
1110	static const char *sources[] = {
1111		"query-source",
1112		"query-source-v6",
1113	};
1114
1115	/*
1116	 * { "name", scale, value }
1117	 * (scale * value) <= UINT32_MAX
1118	 */
1119	static intervaltable intervals[] = {
1120		{ "heartbeat-interval", 60, 28 * 24 * 60 },    /* 28 days */
1121		{ "interface-interval", 60, 28 * 24 * 60 },    /* 28 days */
1122		{ "max-transfer-idle-in", 60, 28 * 24 * 60 },  /* 28 days */
1123		{ "max-transfer-idle-out", 60, 28 * 24 * 60 }, /* 28 days */
1124		{ "max-transfer-time-in", 60, 28 * 24 * 60 },  /* 28 days */
1125		{ "max-transfer-time-out", 60, 28 * 24 * 60 }, /* 28 days */
1126
1127		/* minimum and maximum cache and negative cache TTLs */
1128		{ "min-cache-ttl", 1, MAX_MIN_CACHE_TTL },   /* 90 secs */
1129		{ "max-cache-ttl", 1, UINT32_MAX },	     /* no limit */
1130		{ "min-ncache-ttl", 1, MAX_MIN_NCACHE_TTL }, /* 90 secs */
1131		{ "max-ncache-ttl", 1, MAX_MAX_NCACHE_TTL }, /*  7 days */
1132	};
1133
1134	static const char *server_contact[] = { "empty-server", "empty-contact",
1135						"dns64-server", "dns64-contact",
1136						NULL };
1137
1138#ifdef HAVE_DNSTAP
1139	static fstrmtable fstrm[] = {
1140		{ "fstrm-set-buffer-hint", FSTRM_IOTHR_BUFFER_HINT_MIN,
1141		  FSTRM_IOTHR_BUFFER_HINT_MAX },
1142		{ "fstrm-set-flush-timeout", FSTRM_IOTHR_FLUSH_TIMEOUT_MIN,
1143		  FSTRM_IOTHR_FLUSH_TIMEOUT_MAX },
1144		{ "fstrm-set-input-queue-size",
1145		  FSTRM_IOTHR_INPUT_QUEUE_SIZE_MIN,
1146		  FSTRM_IOTHR_INPUT_QUEUE_SIZE_MAX },
1147		{ "fstrm-set-output-notify-threshold",
1148		  FSTRM_IOTHR_QUEUE_NOTIFY_THRESHOLD_MIN, 0 },
1149		{ "fstrm-set-output-queue-size",
1150		  FSTRM_IOTHR_OUTPUT_QUEUE_SIZE_MIN,
1151		  FSTRM_IOTHR_OUTPUT_QUEUE_SIZE_MAX },
1152		{ "fstrm-set-reopen-interval", FSTRM_IOTHR_REOPEN_INTERVAL_MIN,
1153		  FSTRM_IOTHR_REOPEN_INTERVAL_MAX }
1154	};
1155#endif /* ifdef HAVE_DNSTAP */
1156
1157	if (optlevel == optlevel_options) {
1158		/*
1159		 * Check port values, and record "port" for later use.
1160		 */
1161		tresult = check_port(options, logctx, "port", &dnsport);
1162		if (tresult != ISC_R_SUCCESS) {
1163			result = tresult;
1164		}
1165		tresult = check_port(options, logctx, "tls-port", NULL);
1166		if (tresult != ISC_R_SUCCESS) {
1167			result = tresult;
1168		}
1169		tresult = check_port(options, logctx, "http-port", NULL);
1170		if (tresult != ISC_R_SUCCESS) {
1171			result = tresult;
1172		}
1173		tresult = check_port(options, logctx, "https-port", NULL);
1174		if (tresult != ISC_R_SUCCESS) {
1175			result = tresult;
1176		}
1177	}
1178
1179	if (optlevel == optlevel_options || optlevel == optlevel_view) {
1180		/*
1181		 * Warn if query-source or query-source-v6 options specify
1182		 * a port, and fail if they specify the DNS port.
1183		 */
1184		for (i = 0; i < ARRAY_SIZE(sources); i++) {
1185			obj = NULL;
1186			(void)cfg_map_get(options, sources[i], &obj);
1187			if (obj != NULL) {
1188				const isc_sockaddr_t *sa =
1189					cfg_obj_assockaddr(obj);
1190				in_port_t port = isc_sockaddr_getport(sa);
1191				if (port == dnsport) {
1192					cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1193						    "'%s' cannot specify the "
1194						    "DNS listener port (%d)",
1195						    sources[i], port);
1196					result = ISC_R_FAILURE;
1197				} else if (port != 0) {
1198					cfg_obj_log(obj, logctx,
1199						    ISC_LOG_WARNING,
1200						    "'%s': specifying a port "
1201						    "is not recommended",
1202						    sources[i]);
1203				}
1204			}
1205		}
1206	}
1207
1208	/*
1209	 * Check that fields specified in units of time other than seconds
1210	 * have reasonable values.
1211	 */
1212	for (i = 0; i < sizeof(intervals) / sizeof(intervals[0]); i++) {
1213		uint32_t val;
1214		obj = NULL;
1215		(void)cfg_map_get(options, intervals[i].name, &obj);
1216		if (obj == NULL) {
1217			continue;
1218		}
1219		if (cfg_obj_isduration(obj)) {
1220			val = cfg_obj_asduration(obj);
1221		} else {
1222			val = cfg_obj_asuint32(obj);
1223		}
1224		if (val > intervals[i].max) {
1225			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1226				    "%s '%u' is out of range (0..%u)",
1227				    intervals[i].name, val, intervals[i].max);
1228			result = ISC_R_RANGE;
1229		} else if (val > (UINT32_MAX / intervals[i].scale)) {
1230			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1231				    "%s '%d' is out of range",
1232				    intervals[i].name, val);
1233			result = ISC_R_RANGE;
1234		}
1235	}
1236
1237	/*
1238	 * Check dnssec-policy.
1239	 */
1240	obj = NULL;
1241	(void)cfg_map_get(options, "dnssec-policy", &obj);
1242	if (obj != NULL) {
1243		bool bad_kasp = false;
1244		bool bad_name = false;
1245
1246		if (optlevel != optlevel_config && !cfg_obj_isstring(obj)) {
1247			bad_kasp = true;
1248		} else if (optlevel == optlevel_config) {
1249			dns_kasplist_t list;
1250			dns_kasp_t *kasp = NULL, *kasp_next = NULL;
1251
1252			ISC_LIST_INIT(list);
1253
1254			if (cfg_obj_islist(obj)) {
1255				for (element = cfg_list_first(obj);
1256				     element != NULL;
1257				     element = cfg_list_next(element))
1258				{
1259					isc_result_t ret;
1260					cfg_obj_t *kconfig =
1261						cfg_listelt_value(element);
1262
1263					if (!cfg_obj_istuple(kconfig)) {
1264						bad_kasp = true;
1265						continue;
1266					}
1267					if (!kasp_name_allowed(element)) {
1268						bad_name = true;
1269						continue;
1270					}
1271
1272					ret = cfg_kasp_fromconfig(kconfig, NULL,
1273								  mctx, logctx,
1274								  &list, &kasp);
1275					if (ret != ISC_R_SUCCESS) {
1276						if (result == ISC_R_SUCCESS) {
1277							result = ret;
1278						}
1279					}
1280
1281					if (kasp != NULL) {
1282						dns_kasp_detach(&kasp);
1283					}
1284				}
1285			}
1286
1287			for (kasp = ISC_LIST_HEAD(list); kasp != NULL;
1288			     kasp = kasp_next)
1289			{
1290				kasp_next = ISC_LIST_NEXT(kasp, link);
1291				ISC_LIST_UNLINK(list, kasp, link);
1292				dns_kasp_detach(&kasp);
1293			}
1294		}
1295
1296		if (bad_kasp) {
1297			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1298				    "dnssec-policy may only be configured at "
1299				    "the top level, please use name reference "
1300				    "at the zone level");
1301			if (result == ISC_R_SUCCESS) {
1302				result = ISC_R_FAILURE;
1303			}
1304		} else if (bad_name) {
1305			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1306				    "dnssec-policy name may not be 'insecure', "
1307				    "'none', or 'default' (which are built-in "
1308				    "policies)");
1309			if (result == ISC_R_SUCCESS) {
1310				result = ISC_R_FAILURE;
1311			}
1312		} else {
1313			has_dnssecpolicy = true;
1314		}
1315	}
1316
1317	obj = NULL;
1318	cfg_map_get(options, "max-rsa-exponent-size", &obj);
1319	if (obj != NULL) {
1320		uint32_t val;
1321
1322		val = cfg_obj_asuint32(obj);
1323		if (val != 0 && (val < 35 || val > 4096)) {
1324			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1325				    "max-rsa-exponent-size '%u' is out of "
1326				    "range (35..4096)",
1327				    val);
1328			result = ISC_R_RANGE;
1329		}
1330	}
1331
1332	obj = NULL;
1333	cfg_map_get(options, "sig-validity-interval", &obj);
1334	if (obj != NULL) {
1335		uint32_t validity, resign = 0;
1336
1337		validity = cfg_obj_asuint32(cfg_tuple_get(obj, "validity"));
1338		resignobj = cfg_tuple_get(obj, "re-sign");
1339		if (!cfg_obj_isvoid(resignobj)) {
1340			resign = cfg_obj_asuint32(resignobj);
1341		}
1342
1343		if (validity > 3660 || validity == 0) { /* 10 years */
1344			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1345				    "%s '%u' is out of range (1..3660)",
1346				    "sig-validity-interval", validity);
1347			result = ISC_R_RANGE;
1348		}
1349
1350		if (!cfg_obj_isvoid(resignobj)) {
1351			if (resign > 3660 || resign == 0) { /* 10 years */
1352				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1353					    "%s '%u' is out of range (1..3660)",
1354					    "sig-validity-interval (re-sign)",
1355					    validity);
1356				result = ISC_R_RANGE;
1357			} else if ((validity > 7 && validity < resign) ||
1358				   (validity <= 7 && validity * 24 < resign))
1359			{
1360				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1361					    "validity interval (%u days) "
1362					    "less than re-signing interval "
1363					    "(%u %s)",
1364					    validity, resign,
1365					    (validity > 7) ? "days" : "hours");
1366				result = ISC_R_RANGE;
1367			}
1368		}
1369
1370		if (has_dnssecpolicy) {
1371			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1372				    "sig-validity-interval: cannot be "
1373				    "configured if dnssec-policy is also set");
1374			result = ISC_R_FAILURE;
1375		}
1376	}
1377
1378	obj = NULL;
1379	cfg_map_get(options, "dnskey-sig-validity", &obj);
1380	if (obj != NULL) {
1381		uint32_t keyvalidity;
1382
1383		keyvalidity = cfg_obj_asuint32(obj);
1384		if (keyvalidity > 3660) { /* 10 years */
1385			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1386				    "%s '%u' is out of range (0..3660)",
1387				    "dnskey-sig-validity", keyvalidity);
1388			result = ISC_R_RANGE;
1389		}
1390
1391		if (has_dnssecpolicy) {
1392			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1393				    "dnskey-sig-validity: cannot be "
1394				    "configured if dnssec-policy is also set");
1395			result = ISC_R_FAILURE;
1396		}
1397	}
1398
1399	obj = NULL;
1400	(void)cfg_map_get(options, "preferred-glue", &obj);
1401	if (obj != NULL) {
1402		str = cfg_obj_asstring(obj);
1403		if (strcasecmp(str, "a") != 0 && strcasecmp(str, "aaaa") != 0 &&
1404		    strcasecmp(str, "none") != 0)
1405		{
1406			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1407				    "preferred-glue unexpected value '%s'",
1408				    str);
1409		}
1410	}
1411
1412	obj = NULL;
1413	(void)cfg_map_get(options, "root-delegation-only", &obj);
1414	if (obj != NULL) {
1415		if (!cfg_obj_isvoid(obj)) {
1416			for (element = cfg_list_first(obj); element != NULL;
1417			     element = cfg_list_next(element))
1418			{
1419				const cfg_obj_t *exclude;
1420
1421				exclude = cfg_listelt_value(element);
1422				str = cfg_obj_asstring(exclude);
1423				tresult = check_name(str);
1424				if (tresult != ISC_R_SUCCESS) {
1425					cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1426						    "bad domain name '%s'",
1427						    str);
1428					result = tresult;
1429				}
1430			}
1431		}
1432	}
1433
1434	/*
1435	 * Set supported DNSSEC algorithms.
1436	 */
1437	obj = NULL;
1438	(void)cfg_map_get(options, "disable-algorithms", &obj);
1439	if (obj != NULL) {
1440		for (element = cfg_list_first(obj); element != NULL;
1441		     element = cfg_list_next(element))
1442		{
1443			obj = cfg_listelt_value(element);
1444			tresult = disabled_algorithms(obj, logctx);
1445			if (tresult != ISC_R_SUCCESS) {
1446				result = tresult;
1447			}
1448		}
1449	}
1450
1451	/*
1452	 * Set supported DS digest types.
1453	 */
1454	obj = NULL;
1455	(void)cfg_map_get(options, "disable-ds-digests", &obj);
1456	if (obj != NULL) {
1457		for (element = cfg_list_first(obj); element != NULL;
1458		     element = cfg_list_next(element))
1459		{
1460			obj = cfg_listelt_value(element);
1461			tresult = disabled_ds_digests(obj, logctx);
1462			if (tresult != ISC_R_SUCCESS) {
1463				result = tresult;
1464			}
1465		}
1466	}
1467
1468	/*
1469	 * Check auto-dnssec at the view/options level
1470	 */
1471	obj = NULL;
1472	(void)cfg_map_get(options, "auto-dnssec", &obj);
1473	if (obj != NULL) {
1474		const char *arg = cfg_obj_asstring(obj);
1475		if (optlevel != optlevel_zone && strcasecmp(arg, "off") != 0) {
1476			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1477				    "auto-dnssec may only be activated at the "
1478				    "zone level");
1479			if (result == ISC_R_SUCCESS) {
1480				result = ISC_R_FAILURE;
1481			}
1482		}
1483	}
1484
1485	/*
1486	 * Check dnssec-must-be-secure.
1487	 */
1488	obj = NULL;
1489	(void)cfg_map_get(options, "dnssec-must-be-secure", &obj);
1490	if (obj != NULL) {
1491		tresult = isc_symtab_create(mctx, 100, freekey, mctx, false,
1492					    &symtab);
1493		if (tresult != ISC_R_SUCCESS) {
1494			result = tresult;
1495		} else {
1496			for (element = cfg_list_first(obj); element != NULL;
1497			     element = cfg_list_next(element))
1498			{
1499				obj = cfg_listelt_value(element);
1500				tresult = mustbesecure(obj, symtab, logctx,
1501						       mctx);
1502				if (result == ISC_R_SUCCESS &&
1503				    tresult != ISC_R_SUCCESS)
1504				{
1505					result = tresult;
1506				}
1507			}
1508		}
1509		if (symtab != NULL) {
1510			isc_symtab_destroy(&symtab);
1511		}
1512	}
1513
1514	/*
1515	 * Check server/contacts for syntactic validity.
1516	 */
1517	for (i = 0; server_contact[i] != NULL; i++) {
1518		obj = NULL;
1519		(void)cfg_map_get(options, server_contact[i], &obj);
1520		if (obj != NULL) {
1521			str = cfg_obj_asstring(obj);
1522			if (check_name(str) != ISC_R_SUCCESS) {
1523				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1524					    "%s: invalid name '%s'",
1525					    server_contact[i], str);
1526				if (result == ISC_R_SUCCESS) {
1527					result = ISC_R_FAILURE;
1528				}
1529			}
1530		}
1531	}
1532
1533	/*
1534	 * Check empty zone configuration.
1535	 */
1536	obj = NULL;
1537	(void)cfg_map_get(options, "disable-empty-zone", &obj);
1538	for (element = cfg_list_first(obj); element != NULL;
1539	     element = cfg_list_next(element))
1540	{
1541		obj = cfg_listelt_value(element);
1542		str = cfg_obj_asstring(obj);
1543		if (check_name(str) != ISC_R_SUCCESS) {
1544			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1545				    "disable-empty-zone: invalid name '%s'",
1546				    str);
1547			if (result == ISC_R_SUCCESS) {
1548				result = ISC_R_FAILURE;
1549			}
1550		}
1551	}
1552
1553	/*
1554	 * Check that server-id is not too long.
1555	 * 1024 bytes should be big enough.
1556	 */
1557	obj = NULL;
1558	(void)cfg_map_get(options, "server-id", &obj);
1559	if (obj != NULL && cfg_obj_isstring(obj) &&
1560	    strlen(cfg_obj_asstring(obj)) > 1024U)
1561	{
1562		cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1563			    "'server-id' too big (>1024 bytes)");
1564		if (result == ISC_R_SUCCESS) {
1565			result = ISC_R_FAILURE;
1566		}
1567	}
1568
1569	obj = NULL;
1570	(void)cfg_map_get(options, "nta-lifetime", &obj);
1571	if (obj != NULL) {
1572		lifetime = cfg_obj_asduration(obj);
1573		if (lifetime > 604800) { /* 7 days */
1574			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1575				    "'nta-lifetime' cannot exceed one week");
1576			if (result == ISC_R_SUCCESS) {
1577				result = ISC_R_RANGE;
1578			}
1579		} else if (lifetime == 0) {
1580			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1581				    "'nta-lifetime' may not be zero");
1582			if (result == ISC_R_SUCCESS) {
1583				result = ISC_R_RANGE;
1584			}
1585		}
1586	}
1587
1588	obj = NULL;
1589	(void)cfg_map_get(options, "nta-recheck", &obj);
1590	if (obj != NULL) {
1591		uint32_t recheck = cfg_obj_asduration(obj);
1592		if (recheck > 604800) { /* 7 days */
1593			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1594				    "'nta-recheck' cannot exceed one week");
1595			if (result == ISC_R_SUCCESS) {
1596				result = ISC_R_RANGE;
1597			}
1598		}
1599
1600		if (recheck > lifetime) {
1601			cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
1602				    "'nta-recheck' (%d seconds) is "
1603				    "greater than 'nta-lifetime' "
1604				    "(%d seconds)",
1605				    recheck, lifetime);
1606		}
1607	}
1608
1609	obj = NULL;
1610	(void)cfg_map_get(options, "cookie-algorithm", &obj);
1611	if (obj != NULL) {
1612		ccalg = cfg_obj_asstring(obj);
1613		if (strcasecmp(ccalg, "aes") == 0) {
1614			cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
1615				    "cookie-algorithm 'aes' is deprecated");
1616		}
1617	}
1618
1619	obj = NULL;
1620	(void)cfg_map_get(options, "cookie-secret", &obj);
1621	if (obj != NULL) {
1622		unsigned char secret[32];
1623
1624		for (element = cfg_list_first(obj); element != NULL;
1625		     element = cfg_list_next(element))
1626		{
1627			unsigned int usedlength;
1628
1629			obj = cfg_listelt_value(element);
1630			str = cfg_obj_asstring(obj);
1631
1632			memset(secret, 0, sizeof(secret));
1633			isc_buffer_init(&b, secret, sizeof(secret));
1634			tresult = isc_hex_decodestring(str, &b);
1635			if (tresult == ISC_R_NOSPACE) {
1636				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1637					    "cookie-secret: too long");
1638			} else if (tresult != ISC_R_SUCCESS) {
1639				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1640					    "cookie-secret: invalid hex "
1641					    "string");
1642			}
1643			if (tresult != ISC_R_SUCCESS) {
1644				if (result == ISC_R_SUCCESS) {
1645					result = tresult;
1646				}
1647				continue;
1648			}
1649
1650			usedlength = isc_buffer_usedlength(&b);
1651			if (strcasecmp(ccalg, "aes") == 0 &&
1652			    usedlength != ISC_AES128_KEYLENGTH)
1653			{
1654				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1655					    "AES cookie-secret must be 128 "
1656					    "bits");
1657				if (result == ISC_R_SUCCESS) {
1658					result = ISC_R_RANGE;
1659				}
1660			}
1661			if (strcasecmp(ccalg, "siphash24") == 0 &&
1662			    usedlength != ISC_SIPHASH24_KEY_LENGTH)
1663			{
1664				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1665					    "SipHash-2-4 cookie-secret must be "
1666					    "128 bits");
1667				if (result == ISC_R_SUCCESS) {
1668					result = ISC_R_RANGE;
1669				}
1670			}
1671		}
1672	}
1673
1674#ifdef HAVE_DNSTAP
1675	for (i = 0; i < sizeof(fstrm) / sizeof(fstrm[0]); i++) {
1676		uint32_t value;
1677
1678		obj = NULL;
1679		(void)cfg_map_get(options, fstrm[i].name, &obj);
1680		if (obj == NULL) {
1681			continue;
1682		}
1683
1684		if (cfg_obj_isduration(obj)) {
1685			value = cfg_obj_asduration(obj);
1686		} else {
1687			value = cfg_obj_asuint32(obj);
1688		}
1689		if (value < fstrm[i].min ||
1690		    (fstrm[i].max != 0U && value > fstrm[i].max))
1691		{
1692			if (fstrm[i].max != 0U) {
1693				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1694					    "%s '%u' out of range (%u..%u)",
1695					    fstrm[i].name, value, fstrm[i].min,
1696					    fstrm[i].max);
1697			} else {
1698				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1699					    "%s out of range (%u < %u)",
1700					    fstrm[i].name, value, fstrm[i].min);
1701			}
1702			if (result == ISC_R_SUCCESS) {
1703				result = ISC_R_RANGE;
1704			}
1705		}
1706
1707		if (strcmp(fstrm[i].name, "fstrm-set-input-queue-size") == 0) {
1708			int bits = 0;
1709			do {
1710				bits += value & 0x1;
1711				value >>= 1;
1712			} while (value != 0U);
1713			if (bits != 1) {
1714				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1715					    "%s '%u' not a power-of-2",
1716					    fstrm[i].name,
1717					    cfg_obj_asuint32(obj));
1718				if (result == ISC_R_SUCCESS) {
1719					result = ISC_R_RANGE;
1720				}
1721			}
1722		}
1723	}
1724
1725	/* Check that dnstap-ouput values are consistent */
1726	obj = NULL;
1727	(void)cfg_map_get(options, "dnstap-output", &obj);
1728	if (obj != NULL) {
1729		const cfg_obj_t *obj2;
1730		dns_dtmode_t dmode;
1731
1732		obj2 = cfg_tuple_get(obj, "mode");
1733		if (obj2 == NULL) {
1734			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1735				    "dnstap-output mode not found");
1736			if (result == ISC_R_SUCCESS) {
1737				result = ISC_R_FAILURE;
1738			}
1739		} else {
1740			if (strcasecmp(cfg_obj_asstring(obj2), "file") == 0) {
1741				dmode = dns_dtmode_file;
1742			} else {
1743				dmode = dns_dtmode_unix;
1744			}
1745
1746			obj2 = cfg_tuple_get(obj, "size");
1747			if (obj2 != NULL && !cfg_obj_isvoid(obj2) &&
1748			    dmode == dns_dtmode_unix)
1749			{
1750				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1751					    "dnstap-output size "
1752					    "cannot be set with mode unix");
1753				if (result == ISC_R_SUCCESS) {
1754					result = ISC_R_FAILURE;
1755				}
1756			}
1757
1758			obj2 = cfg_tuple_get(obj, "versions");
1759			if (obj2 != NULL && !cfg_obj_isvoid(obj2) &&
1760			    dmode == dns_dtmode_unix)
1761			{
1762				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1763					    "dnstap-output versions "
1764					    "cannot be set with mode unix");
1765				if (result == ISC_R_SUCCESS) {
1766					result = ISC_R_FAILURE;
1767				}
1768			}
1769
1770			obj2 = cfg_tuple_get(obj, "suffix");
1771			if (obj2 != NULL && !cfg_obj_isvoid(obj2) &&
1772			    dmode == dns_dtmode_unix)
1773			{
1774				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1775					    "dnstap-output suffix "
1776					    "cannot be set with mode unix");
1777				if (result == ISC_R_SUCCESS) {
1778					result = ISC_R_FAILURE;
1779				}
1780			}
1781		}
1782	}
1783#endif /* ifdef HAVE_DNSTAP */
1784
1785	obj = NULL;
1786	(void)cfg_map_get(options, "lmdb-mapsize", &obj);
1787	if (obj != NULL) {
1788		uint64_t mapsize = cfg_obj_asuint64(obj);
1789
1790		if (mapsize < (1ULL << 20)) { /* 1 megabyte */
1791			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1792				    "'lmdb-mapsize "
1793				    "%" PRId64 "' "
1794				    "is too small",
1795				    mapsize);
1796			if (result == ISC_R_SUCCESS) {
1797				result = ISC_R_RANGE;
1798			}
1799		} else if (mapsize > (1ULL << 40)) { /* 1 terabyte */
1800			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1801				    "'lmdb-mapsize "
1802				    "%" PRId64 "' "
1803				    "is too large",
1804				    mapsize);
1805			if (result == ISC_R_SUCCESS) {
1806				result = ISC_R_RANGE;
1807			}
1808		}
1809	}
1810
1811	obj = NULL;
1812	(void)cfg_map_get(options, "resolver-nonbackoff-tries", &obj);
1813	if (obj != NULL && cfg_obj_asuint32(obj) == 0U) {
1814		cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1815			    "'resolver-nonbackoff-tries' must be >= 1");
1816		if (result == ISC_R_SUCCESS) {
1817			result = ISC_R_RANGE;
1818		}
1819	}
1820
1821	obj = NULL;
1822	(void)cfg_map_get(options, "max-ixfr-ratio", &obj);
1823	if (obj != NULL && cfg_obj_ispercentage(obj)) {
1824		uint32_t percent = cfg_obj_aspercentage(obj);
1825		if (percent == 0) {
1826			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1827				    "'ixfr-max-ratio' must be a nonzero "
1828				    "percentage or 'unlimited')");
1829			if (result == ISC_R_SUCCESS) {
1830				result = ISC_R_RANGE;
1831			}
1832		} else if (percent > 100) {
1833			cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
1834				    "'ixfr-max-ratio %d%%' exceeds 100%%",
1835				    percent);
1836		}
1837	}
1838
1839	obj = NULL;
1840	(void)cfg_map_get(options, "check-names", &obj);
1841	if (obj != NULL && !cfg_obj_islist(obj)) {
1842		obj = NULL;
1843	}
1844	if (obj != NULL) {
1845		/* Note: SEC is defined in <sys/time.h> on some platforms. */
1846		enum { MAS = 1, PRI = 2, SLA = 4, SCN = 8 } values = 0;
1847		for (const cfg_listelt_t *el = cfg_list_first(obj); el != NULL;
1848		     el = cfg_list_next(el))
1849		{
1850			const cfg_obj_t *tuple = cfg_listelt_value(el);
1851			const cfg_obj_t *type = cfg_tuple_get(tuple, "type");
1852			const char *keyword = cfg_obj_asstring(type);
1853			if (strcasecmp(keyword, "primary") == 0) {
1854				if ((values & PRI) == PRI) {
1855					cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1856						    "'check-names primary' "
1857						    "duplicated");
1858					if (result == ISC_R_SUCCESS) {
1859						result = ISC_R_FAILURE;
1860					}
1861				}
1862				values |= PRI;
1863			} else if (strcasecmp(keyword, "master") == 0) {
1864				if ((values & MAS) == MAS) {
1865					cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1866						    "'check-names master' "
1867						    "duplicated");
1868					if (result == ISC_R_SUCCESS) {
1869						result = ISC_R_FAILURE;
1870					}
1871				}
1872				values |= MAS;
1873			} else if (strcasecmp(keyword, "secondary") == 0) {
1874				if ((values & SCN) == SCN) {
1875					cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1876						    "'check-names secondary' "
1877						    "duplicated");
1878					if (result == ISC_R_SUCCESS) {
1879						result = ISC_R_FAILURE;
1880					}
1881				}
1882				values |= SCN;
1883			} else if (strcasecmp(keyword, "slave") == 0) {
1884				if ((values & SLA) == SLA) {
1885					cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1886						    "'check-names slave' "
1887						    "duplicated");
1888					if (result == ISC_R_SUCCESS) {
1889						result = ISC_R_FAILURE;
1890					}
1891				}
1892				values |= SLA;
1893			}
1894		}
1895
1896		if ((values & (PRI | MAS)) == (PRI | MAS)) {
1897			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1898				    "'check-names' cannot take both "
1899				    "'primary' and 'master'");
1900			if (result == ISC_R_SUCCESS) {
1901				result = ISC_R_FAILURE;
1902			}
1903		}
1904
1905		if ((values & (SCN | SLA)) == (SCN | SLA)) {
1906			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1907				    "'check-names' cannot take both "
1908				    "'secondary' and 'slave'");
1909			if (result == ISC_R_SUCCESS) {
1910				result = ISC_R_FAILURE;
1911			}
1912		}
1913	}
1914
1915	obj = NULL;
1916	(void)cfg_map_get(options, "stale-refresh-time", &obj);
1917	if (obj != NULL) {
1918		uint32_t refresh_time = cfg_obj_asduration(obj);
1919		if (refresh_time > 0 && refresh_time < 30) {
1920			cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
1921				    "'stale-refresh-time' should either be 0 "
1922				    "or otherwise 30 seconds or higher");
1923		}
1924	}
1925
1926	cfg_aclconfctx_create(mctx, &actx);
1927
1928	obj = NULL;
1929	(void)cfg_map_get(options, "listen-on", &obj);
1930	if (obj != NULL) {
1931		INSIST(config != NULL);
1932		tresult = check_listeners(obj, config, actx, logctx, mctx);
1933		if (result == ISC_R_SUCCESS) {
1934			result = tresult;
1935		}
1936	}
1937
1938	obj = NULL;
1939	(void)cfg_map_get(options, "listen-on-v6", &obj);
1940	if (obj != NULL) {
1941		INSIST(config != NULL);
1942		tresult = check_listeners(obj, config, actx, logctx, mctx);
1943		if (result == ISC_R_SUCCESS) {
1944			result = tresult;
1945		}
1946	}
1947
1948	if (actx != NULL) {
1949		cfg_aclconfctx_detach(&actx);
1950	}
1951
1952	return (result);
1953}
1954
1955/*
1956 * Check "remote-servers" style list.
1957 */
1958static isc_result_t
1959bind9_check_remoteserverlist(const cfg_obj_t *cctx, const char *list,
1960			     isc_log_t *logctx, isc_symtab_t *symtab,
1961			     isc_mem_t *mctx) {
1962	isc_symvalue_t symvalue;
1963	isc_result_t result, tresult;
1964	const cfg_obj_t *obj = NULL;
1965	const cfg_listelt_t *elt;
1966
1967	result = cfg_map_get(cctx, list, &obj);
1968	if (result != ISC_R_SUCCESS) {
1969		return (ISC_R_SUCCESS);
1970	}
1971
1972	elt = cfg_list_first(obj);
1973	while (elt != NULL) {
1974		char *tmp;
1975		const char *name;
1976
1977		obj = cfg_listelt_value(elt);
1978		name = cfg_obj_asstring(cfg_tuple_get(obj, "name"));
1979
1980		tmp = isc_mem_strdup(mctx, name);
1981		symvalue.as_cpointer = obj;
1982		tresult = isc_symtab_define(symtab, tmp, 1, symvalue,
1983					    isc_symexists_reject);
1984		if (tresult == ISC_R_EXISTS) {
1985			const char *file = NULL;
1986			unsigned int line;
1987
1988			RUNTIME_CHECK(
1989				isc_symtab_lookup(symtab, tmp, 1, &symvalue) ==
1990				ISC_R_SUCCESS);
1991			file = cfg_obj_file(symvalue.as_cpointer);
1992			line = cfg_obj_line(symvalue.as_cpointer);
1993
1994			if (file == NULL) {
1995				file = "<unknown file>";
1996			}
1997			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1998				    "%s list '%s' is duplicated: "
1999				    "also defined at %s:%u",
2000				    list, name, file, line);
2001			isc_mem_free(mctx, tmp);
2002			result = tresult;
2003			break;
2004		} else if (tresult != ISC_R_SUCCESS) {
2005			isc_mem_free(mctx, tmp);
2006			result = tresult;
2007			break;
2008		}
2009
2010		elt = cfg_list_next(elt);
2011	}
2012	return (result);
2013}
2014
2015/*
2016 * Check primaries lists for duplicates.
2017 */
2018static isc_result_t
2019bind9_check_primarylists(const cfg_obj_t *cctx, isc_log_t *logctx,
2020			 isc_mem_t *mctx) {
2021	isc_result_t result, tresult;
2022	isc_symtab_t *symtab = NULL;
2023
2024	result = isc_symtab_create(mctx, 100, freekey, mctx, false, &symtab);
2025	if (result != ISC_R_SUCCESS) {
2026		return (result);
2027	}
2028	tresult = bind9_check_remoteserverlist(cctx, "primaries", logctx,
2029					       symtab, mctx);
2030	if (tresult != ISC_R_SUCCESS) {
2031		result = tresult;
2032	}
2033	tresult = bind9_check_remoteserverlist(cctx, "masters", logctx, symtab,
2034					       mctx);
2035	if (tresult != ISC_R_SUCCESS) {
2036		result = tresult;
2037	}
2038	isc_symtab_destroy(&symtab);
2039	return (result);
2040}
2041
2042/*
2043 * Check parental-agents lists for duplicates.
2044 */
2045static isc_result_t
2046bind9_check_parentalagentlists(const cfg_obj_t *cctx, isc_log_t *logctx,
2047			       isc_mem_t *mctx) {
2048	isc_result_t result, tresult;
2049	isc_symtab_t *symtab = NULL;
2050
2051	result = isc_symtab_create(mctx, 100, freekey, mctx, false, &symtab);
2052	if (result != ISC_R_SUCCESS) {
2053		return (result);
2054	}
2055	tresult = bind9_check_remoteserverlist(cctx, "parental-agents", logctx,
2056					       symtab, mctx);
2057	if (tresult != ISC_R_SUCCESS) {
2058		result = tresult;
2059	}
2060	isc_symtab_destroy(&symtab);
2061	return (result);
2062}
2063
2064#if HAVE_LIBNGHTTP2
2065static isc_result_t
2066bind9_check_httpserver(const cfg_obj_t *http, isc_log_t *logctx,
2067		       isc_symtab_t *symtab) {
2068	isc_result_t result, tresult;
2069	const char *name = cfg_obj_asstring(cfg_map_getname(http));
2070	const cfg_obj_t *eps = NULL;
2071	const cfg_listelt_t *elt = NULL;
2072	isc_symvalue_t symvalue;
2073
2074	if (strcasecmp(name, "default") == 0) {
2075		cfg_obj_log(http, logctx, ISC_LOG_ERROR,
2076			    "'http' name cannot be '%s' (which is a "
2077			    "built-in configuration)",
2078			    name);
2079		result = ISC_R_FAILURE;
2080	} else {
2081		/* Check for duplicates */
2082		symvalue.as_cpointer = http;
2083		result = isc_symtab_define(symtab, name, 1, symvalue,
2084					   isc_symexists_reject);
2085		if (result == ISC_R_EXISTS) {
2086			const char *file = NULL;
2087			unsigned int line;
2088
2089			tresult = isc_symtab_lookup(symtab, name, 1, &symvalue);
2090			RUNTIME_CHECK(tresult == ISC_R_SUCCESS);
2091
2092			line = cfg_obj_line(symvalue.as_cpointer);
2093			file = cfg_obj_file(symvalue.as_cpointer);
2094			if (file == NULL) {
2095				file = "<unknown file>";
2096			}
2097
2098			cfg_obj_log(http, logctx, ISC_LOG_ERROR,
2099				    "http '%s' is duplicated: "
2100				    "also defined at %s:%u",
2101				    name, file, line);
2102		}
2103	}
2104
2105	/* Check endpoints are valid */
2106	tresult = cfg_map_get(http, "endpoints", &eps);
2107	if (tresult == ISC_R_SUCCESS) {
2108		for (elt = cfg_list_first(eps); elt != NULL;
2109		     elt = cfg_list_next(elt))
2110		{
2111			const cfg_obj_t *ep = cfg_listelt_value(elt);
2112			const char *path = cfg_obj_asstring(ep);
2113			if (!isc_nm_http_path_isvalid(path)) {
2114				cfg_obj_log(eps, logctx, ISC_LOG_ERROR,
2115					    "endpoint '%s' is not a "
2116					    "valid absolute HTTP path",
2117					    path);
2118				if (result == ISC_R_SUCCESS) {
2119					result = ISC_R_FAILURE;
2120				}
2121			}
2122		}
2123	}
2124
2125	return (result);
2126}
2127
2128static isc_result_t
2129bind9_check_httpservers(const cfg_obj_t *config, isc_log_t *logctx,
2130			isc_mem_t *mctx) {
2131	isc_result_t result, tresult;
2132	const cfg_obj_t *obj = NULL;
2133	const cfg_listelt_t *elt = NULL;
2134	isc_symtab_t *symtab = NULL;
2135
2136	result = isc_symtab_create(mctx, 100, NULL, NULL, false, &symtab);
2137	if (result != ISC_R_SUCCESS) {
2138		return (result);
2139	}
2140
2141	result = cfg_map_get(config, "http", &obj);
2142	if (result != ISC_R_SUCCESS) {
2143		result = ISC_R_SUCCESS;
2144		goto done;
2145	}
2146
2147	for (elt = cfg_list_first(obj); elt != NULL; elt = cfg_list_next(elt)) {
2148		obj = cfg_listelt_value(elt);
2149		tresult = bind9_check_httpserver(obj, logctx, symtab);
2150		if (result == ISC_R_SUCCESS) {
2151			result = tresult;
2152		}
2153	}
2154
2155done:
2156	isc_symtab_destroy(&symtab);
2157	return (result);
2158}
2159#endif /* HAVE_LIBNGHTTP2 */
2160
2161static isc_result_t
2162bind9_check_tls_defintion(const cfg_obj_t *tlsobj, const char *name,
2163			  isc_log_t *logctx, isc_symtab_t *symtab) {
2164	isc_result_t result, tresult;
2165	const cfg_obj_t *tls_proto_list = NULL, *tls_key = NULL,
2166			*tls_cert = NULL, *tls_ciphers = NULL;
2167	uint32_t tls_protos = 0;
2168	isc_symvalue_t symvalue;
2169
2170	if (strcasecmp(name, "ephemeral") == 0 || strcasecmp(name, "none") == 0)
2171	{
2172		cfg_obj_log(tlsobj, logctx, ISC_LOG_ERROR,
2173			    "tls clause name '%s' is reserved for internal use",
2174			    name);
2175		result = ISC_R_FAILURE;
2176	} else {
2177		/* Check for duplicates */
2178		symvalue.as_cpointer = tlsobj;
2179		result = isc_symtab_define(symtab, name, 1, symvalue,
2180					   isc_symexists_reject);
2181		if (result == ISC_R_EXISTS) {
2182			const char *file = NULL;
2183			unsigned int line;
2184
2185			tresult = isc_symtab_lookup(symtab, name, 1, &symvalue);
2186			RUNTIME_CHECK(tresult == ISC_R_SUCCESS);
2187
2188			line = cfg_obj_line(symvalue.as_cpointer);
2189			file = cfg_obj_file(symvalue.as_cpointer);
2190			if (file == NULL) {
2191				file = "<unknown file>";
2192			}
2193
2194			cfg_obj_log(tlsobj, logctx, ISC_LOG_ERROR,
2195				    "tls clause '%s' is duplicated: "
2196				    "also defined at %s:%u",
2197				    name, file, line);
2198		}
2199	}
2200
2201	(void)cfg_map_get(tlsobj, "key-file", &tls_key);
2202	(void)cfg_map_get(tlsobj, "cert-file", &tls_cert);
2203	if ((tls_key == NULL && tls_cert != NULL) ||
2204	    (tls_cert == NULL && tls_key != NULL))
2205	{
2206		cfg_obj_log(tlsobj, logctx, ISC_LOG_ERROR,
2207			    "tls '%s': 'cert-file' and 'key-file' must "
2208			    "both be specified, or both omitted",
2209			    name);
2210		result = ISC_R_FAILURE;
2211	}
2212
2213	/* Check protocols are valid */
2214	tresult = cfg_map_get(tlsobj, "protocols", &tls_proto_list);
2215	if (tresult == ISC_R_SUCCESS) {
2216		const cfg_listelt_t *proto = NULL;
2217		INSIST(tls_proto_list != NULL);
2218		for (proto = cfg_list_first(tls_proto_list); proto != 0;
2219		     proto = cfg_list_next(proto))
2220		{
2221			const cfg_obj_t *tls_proto_obj =
2222				cfg_listelt_value(proto);
2223			const char *tls_sver = cfg_obj_asstring(tls_proto_obj);
2224			const isc_tls_protocol_version_t ver =
2225				isc_tls_protocol_name_to_version(tls_sver);
2226
2227			if (ver == ISC_TLS_PROTO_VER_UNDEFINED) {
2228				cfg_obj_log(tls_proto_obj, logctx,
2229					    ISC_LOG_ERROR,
2230					    "'%s' is not a valid "
2231					    "TLS protocol version",
2232					    tls_sver);
2233				result = ISC_R_FAILURE;
2234				continue;
2235			} else if (!isc_tls_protocol_supported(ver)) {
2236				cfg_obj_log(tls_proto_obj, logctx,
2237					    ISC_LOG_ERROR,
2238					    "'%s' is not "
2239					    "supported by the "
2240					    "cryptographic library version in "
2241					    "use (%s)",
2242					    tls_sver, OPENSSL_VERSION_TEXT);
2243				result = ISC_R_FAILURE;
2244			}
2245
2246			if ((tls_protos & ver) != 0) {
2247				cfg_obj_log(tls_proto_obj, logctx,
2248					    ISC_LOG_WARNING,
2249					    "'%s' is specified more than once "
2250					    "in '%s'",
2251					    tls_sver, name);
2252				result = ISC_R_FAILURE;
2253			}
2254
2255			tls_protos |= ver;
2256		}
2257
2258		if (tls_protos == 0) {
2259			cfg_obj_log(tlsobj, logctx, ISC_LOG_ERROR,
2260				    "tls '%s' does not contain any valid "
2261				    "TLS protocol versions definitions",
2262				    name);
2263			result = ISC_R_FAILURE;
2264		}
2265	}
2266
2267	/* Check cipher list string is valid */
2268	tresult = cfg_map_get(tlsobj, "ciphers", &tls_ciphers);
2269	if (tresult == ISC_R_SUCCESS) {
2270		const char *ciphers = cfg_obj_asstring(tls_ciphers);
2271		if (!isc_tls_cipherlist_valid(ciphers)) {
2272			cfg_obj_log(tls_ciphers, logctx, ISC_LOG_ERROR,
2273				    "'ciphers' in the 'tls' clause '%s' is "
2274				    "not a "
2275				    "valid cipher list string",
2276				    name);
2277			result = ISC_R_FAILURE;
2278		}
2279	}
2280
2281	return (result);
2282}
2283
2284static isc_result_t
2285bind9_check_tls_definitions(const cfg_obj_t *config, isc_log_t *logctx,
2286			    isc_mem_t *mctx) {
2287	isc_result_t result, tresult;
2288	const cfg_obj_t *obj = NULL;
2289	const cfg_listelt_t *elt = NULL;
2290	isc_symtab_t *symtab = NULL;
2291
2292	result = cfg_map_get(config, "tls", &obj);
2293	if (result != ISC_R_SUCCESS) {
2294		result = ISC_R_SUCCESS;
2295		return (result);
2296	}
2297
2298	result = isc_symtab_create(mctx, 100, NULL, NULL, false, &symtab);
2299	if (result != ISC_R_SUCCESS) {
2300		return (result);
2301	}
2302
2303	for (elt = cfg_list_first(obj); elt != NULL; elt = cfg_list_next(elt)) {
2304		const char *name;
2305		obj = cfg_listelt_value(elt);
2306		name = cfg_obj_asstring(cfg_map_getname(obj));
2307		tresult = bind9_check_tls_defintion(obj, name, logctx, symtab);
2308		if (result == ISC_R_SUCCESS) {
2309			result = tresult;
2310		}
2311	}
2312
2313	isc_symtab_destroy(&symtab);
2314
2315	return (result);
2316}
2317
2318static isc_result_t
2319get_remotes(const cfg_obj_t *cctx, const char *list, const char *name,
2320	    const cfg_obj_t **ret) {
2321	isc_result_t result;
2322	const cfg_obj_t *obj = NULL;
2323	const cfg_listelt_t *elt = NULL;
2324
2325	result = cfg_map_get(cctx, list, &obj);
2326	if (result != ISC_R_SUCCESS) {
2327		return (result);
2328	}
2329
2330	elt = cfg_list_first(obj);
2331	while (elt != NULL) {
2332		const char *listname;
2333
2334		obj = cfg_listelt_value(elt);
2335		listname = cfg_obj_asstring(cfg_tuple_get(obj, "name"));
2336
2337		if (strcasecmp(listname, name) == 0) {
2338			*ret = obj;
2339			return (ISC_R_SUCCESS);
2340		}
2341
2342		elt = cfg_list_next(elt);
2343	}
2344
2345	return (ISC_R_NOTFOUND);
2346}
2347
2348static isc_result_t
2349get_remoteservers_def(const char *list, const char *name, const cfg_obj_t *cctx,
2350		      const cfg_obj_t **ret) {
2351	isc_result_t result = ISC_R_NOTFOUND;
2352
2353	if (strcmp(list, "primaries") == 0) {
2354		result = get_remotes(cctx, "primaries", name, ret);
2355		if (result != ISC_R_SUCCESS) {
2356			result = get_remotes(cctx, "masters", name, ret);
2357		}
2358	} else if (strcmp(list, "parental-agents") == 0) {
2359		result = get_remotes(cctx, "parental-agents", name, ret);
2360	}
2361	return (result);
2362}
2363
2364static isc_result_t
2365validate_remotes(const char *list, const cfg_obj_t *obj,
2366		 const cfg_obj_t *config, uint32_t *countp, isc_log_t *logctx,
2367		 isc_mem_t *mctx) {
2368	isc_result_t result = ISC_R_SUCCESS;
2369	isc_result_t tresult;
2370	uint32_t count = 0;
2371	isc_symtab_t *symtab = NULL;
2372	isc_symvalue_t symvalue;
2373	const cfg_listelt_t *element;
2374	const cfg_listelt_t **stack = NULL;
2375	uint32_t stackcount = 0, pushed = 0;
2376	const cfg_obj_t *listobj;
2377
2378	REQUIRE(countp != NULL);
2379	result = isc_symtab_create(mctx, 100, NULL, NULL, false, &symtab);
2380	if (result != ISC_R_SUCCESS) {
2381		*countp = count;
2382		return (result);
2383	}
2384
2385newlist:
2386	listobj = cfg_tuple_get(obj, "addresses");
2387	element = cfg_list_first(listobj);
2388resume:
2389	for (; element != NULL; element = cfg_list_next(element)) {
2390		const char *listname;
2391		const cfg_obj_t *addr;
2392		const cfg_obj_t *key;
2393		const cfg_obj_t *tls;
2394
2395		addr = cfg_tuple_get(cfg_listelt_value(element),
2396				     "remoteselement");
2397		key = cfg_tuple_get(cfg_listelt_value(element), "key");
2398		tls = cfg_tuple_get(cfg_listelt_value(element), "tls");
2399
2400		if (cfg_obj_issockaddr(addr)) {
2401			count++;
2402			if (cfg_obj_isstring(key)) {
2403				const char *str = cfg_obj_asstring(key);
2404				dns_fixedname_t fname;
2405				dns_name_t *nm = dns_fixedname_initname(&fname);
2406				tresult = dns_name_fromstring(nm, str, 0, NULL);
2407				if (tresult != ISC_R_SUCCESS) {
2408					cfg_obj_log(key, logctx, ISC_LOG_ERROR,
2409						    "'%s' is not a valid name",
2410						    str);
2411					if (result == ISC_R_SUCCESS) {
2412						result = tresult;
2413					}
2414				}
2415			}
2416			if (cfg_obj_isstring(tls)) {
2417				const char *str = cfg_obj_asstring(tls);
2418				dns_fixedname_t fname;
2419				dns_name_t *nm = dns_fixedname_initname(&fname);
2420				tresult = dns_name_fromstring(nm, str, 0, NULL);
2421				if (tresult != ISC_R_SUCCESS) {
2422					cfg_obj_log(tls, logctx, ISC_LOG_ERROR,
2423						    "'%s' is not a valid name",
2424						    str);
2425					if (result == ISC_R_SUCCESS) {
2426						result = tresult;
2427					}
2428				}
2429
2430				if (strcasecmp(str, "ephemeral") != 0) {
2431					const cfg_obj_t *tlsmap = NULL;
2432
2433					tlsmap = find_maplist(config, "tls",
2434							      str);
2435					if (tlsmap == NULL) {
2436						cfg_obj_log(
2437							tls, logctx,
2438							ISC_LOG_ERROR,
2439							"tls '%s' is not "
2440							"defined",
2441							cfg_obj_asstring(tls));
2442						result = ISC_R_FAILURE;
2443					}
2444				}
2445			}
2446			continue;
2447		}
2448		if (!cfg_obj_isvoid(key)) {
2449			cfg_obj_log(key, logctx, ISC_LOG_ERROR,
2450				    "unexpected token '%s'",
2451				    cfg_obj_asstring(key));
2452			if (result == ISC_R_SUCCESS) {
2453				result = ISC_R_FAILURE;
2454			}
2455		}
2456		if (!cfg_obj_isvoid(tls)) {
2457			cfg_obj_log(key, logctx, ISC_LOG_ERROR,
2458				    "unexpected token '%s'",
2459				    cfg_obj_asstring(tls));
2460			if (result == ISC_R_SUCCESS) {
2461				result = ISC_R_FAILURE;
2462			}
2463		}
2464		listname = cfg_obj_asstring(addr);
2465		symvalue.as_cpointer = addr;
2466		tresult = isc_symtab_define(symtab, listname, 1, symvalue,
2467					    isc_symexists_reject);
2468		if (tresult == ISC_R_EXISTS) {
2469			continue;
2470		}
2471		tresult = get_remoteservers_def(list, listname, config, &obj);
2472		if (tresult != ISC_R_SUCCESS) {
2473			if (result == ISC_R_SUCCESS) {
2474				result = tresult;
2475			}
2476			cfg_obj_log(addr, logctx, ISC_LOG_ERROR,
2477				    "unable to find %s list '%s'", list,
2478				    listname);
2479			continue;
2480		}
2481		/* Grow stack? */
2482		if (stackcount == pushed) {
2483			void *newstack;
2484			uint32_t newlen = stackcount + 16;
2485			size_t newsize, oldsize;
2486
2487			newsize = newlen * sizeof(*stack);
2488			oldsize = stackcount * sizeof(*stack);
2489			newstack = isc_mem_get(mctx, newsize);
2490			if (stackcount != 0) {
2491				void *ptr;
2492
2493				DE_CONST(stack, ptr);
2494				memmove(newstack, stack, oldsize);
2495				isc_mem_put(mctx, ptr, oldsize);
2496			}
2497			stack = newstack;
2498			stackcount = newlen;
2499		}
2500		stack[pushed++] = cfg_list_next(element);
2501		goto newlist;
2502	}
2503	if (pushed != 0) {
2504		element = stack[--pushed];
2505		goto resume;
2506	}
2507	if (stack != NULL) {
2508		void *ptr;
2509
2510		DE_CONST(stack, ptr);
2511		isc_mem_put(mctx, ptr, stackcount * sizeof(*stack));
2512	}
2513	isc_symtab_destroy(&symtab);
2514	*countp = count;
2515	return (result);
2516}
2517
2518static isc_result_t
2519check_update_policy(const cfg_obj_t *policy, isc_log_t *logctx) {
2520	isc_result_t result = ISC_R_SUCCESS;
2521	isc_result_t tresult;
2522	const cfg_listelt_t *element;
2523	const cfg_listelt_t *element2;
2524	dns_fixedname_t fixed_id, fixed_name;
2525	dns_name_t *id, *name;
2526	const char *str;
2527	isc_textregion_t r;
2528	dns_rdatatype_t type;
2529
2530	/* Check for "update-policy local;" */
2531	if (cfg_obj_isstring(policy) &&
2532	    strcmp("local", cfg_obj_asstring(policy)) == 0)
2533	{
2534		return (ISC_R_SUCCESS);
2535	}
2536
2537	/* Now check the grant policy */
2538	for (element = cfg_list_first(policy); element != NULL;
2539	     element = cfg_list_next(element))
2540	{
2541		const cfg_obj_t *stmt = cfg_listelt_value(element);
2542		const cfg_obj_t *identity = cfg_tuple_get(stmt, "identity");
2543		const cfg_obj_t *matchtype = cfg_tuple_get(stmt, "matchtype");
2544		const cfg_obj_t *dname = cfg_tuple_get(stmt, "name");
2545		const cfg_obj_t *typelist = cfg_tuple_get(stmt, "types");
2546		dns_ssumatchtype_t mtype;
2547
2548		id = dns_fixedname_initname(&fixed_id);
2549		name = dns_fixedname_initname(&fixed_name);
2550
2551		tresult = dns_ssu_mtypefromstring(cfg_obj_asstring(matchtype),
2552						  &mtype);
2553		if (tresult != ISC_R_SUCCESS) {
2554			cfg_obj_log(identity, logctx, ISC_LOG_ERROR,
2555				    "has a bad match-type");
2556		}
2557
2558		str = cfg_obj_asstring(identity);
2559		tresult = dns_name_fromstring(id, str, 1, NULL);
2560		if (tresult != ISC_R_SUCCESS) {
2561			cfg_obj_log(identity, logctx, ISC_LOG_ERROR,
2562				    "'%s' is not a valid name", str);
2563			result = tresult;
2564		}
2565
2566		/*
2567		 * There is no name field for subzone and dname is void
2568		 */
2569		if (mtype == dns_ssumatchtype_subdomain &&
2570		    cfg_obj_isvoid(dname))
2571		{
2572			str = "."; /* Use "." as a replacement. */
2573		} else {
2574			str = cfg_obj_asstring(dname);
2575		}
2576		if (tresult == ISC_R_SUCCESS) {
2577			tresult = dns_name_fromstring(name, str, 0, NULL);
2578			if (tresult != ISC_R_SUCCESS) {
2579				cfg_obj_log(dname, logctx, ISC_LOG_ERROR,
2580					    "'%s' is not a valid name", str);
2581				result = tresult;
2582			}
2583		}
2584
2585		if (tresult == ISC_R_SUCCESS &&
2586		    mtype == dns_ssumatchtype_wildcard &&
2587		    !dns_name_iswildcard(name))
2588		{
2589			cfg_obj_log(identity, logctx, ISC_LOG_ERROR,
2590				    "'%s' is not a wildcard", str);
2591			result = ISC_R_FAILURE;
2592		}
2593
2594		/*
2595		 * For some match types, the name should be a placeholder
2596		 * value, either "." or the same as identity.
2597		 */
2598		switch (mtype) {
2599		case dns_ssumatchtype_self:
2600		case dns_ssumatchtype_selfsub:
2601		case dns_ssumatchtype_selfwild:
2602			if (tresult == ISC_R_SUCCESS &&
2603			    (!dns_name_equal(id, name) &&
2604			     !dns_name_equal(dns_rootname, name)))
2605			{
2606				cfg_obj_log(identity, logctx, ISC_LOG_ERROR,
2607					    "identity and name fields are not "
2608					    "the same");
2609				result = ISC_R_FAILURE;
2610			}
2611			break;
2612		case dns_ssumatchtype_selfkrb5:
2613		case dns_ssumatchtype_selfms:
2614		case dns_ssumatchtype_selfsubkrb5:
2615		case dns_ssumatchtype_selfsubms:
2616		case dns_ssumatchtype_tcpself:
2617		case dns_ssumatchtype_6to4self:
2618			if (tresult == ISC_R_SUCCESS &&
2619			    !dns_name_equal(dns_rootname, name))
2620			{
2621				cfg_obj_log(identity, logctx, ISC_LOG_ERROR,
2622					    "name field not set to "
2623					    "placeholder value '.'");
2624				result = ISC_R_FAILURE;
2625			}
2626			break;
2627		case dns_ssumatchtype_name:
2628		case dns_ssumatchtype_subdomain: /* also zonesub */
2629		case dns_ssumatchtype_subdomainms:
2630		case dns_ssumatchtype_subdomainselfmsrhs:
2631		case dns_ssumatchtype_subdomainkrb5:
2632		case dns_ssumatchtype_subdomainselfkrb5rhs:
2633		case dns_ssumatchtype_wildcard:
2634		case dns_ssumatchtype_external:
2635		case dns_ssumatchtype_local:
2636			if (tresult == ISC_R_SUCCESS) {
2637				DE_CONST(str, r.base);
2638				r.length = strlen(str);
2639				tresult = dns_rdatatype_fromtext(&type, &r);
2640			}
2641			if (tresult == ISC_R_SUCCESS) {
2642				cfg_obj_log(identity, logctx, ISC_LOG_ERROR,
2643					    "missing name field type '%s' "
2644					    "found",
2645					    str);
2646				result = ISC_R_FAILURE;
2647				break;
2648			}
2649			break;
2650		default:
2651			UNREACHABLE();
2652		}
2653
2654		for (element2 = cfg_list_first(typelist); element2 != NULL;
2655		     element2 = cfg_list_next(element2))
2656		{
2657			const cfg_obj_t *typeobj;
2658			const char *bracket;
2659
2660			typeobj = cfg_listelt_value(element2);
2661			DE_CONST(cfg_obj_asstring(typeobj), r.base);
2662
2663			bracket = strchr(r.base, '(' /*)*/);
2664			if (bracket != NULL) {
2665				char *end = NULL;
2666				unsigned long max;
2667
2668				r.length = bracket - r.base;
2669				max = strtoul(bracket + 1, &end, 10);
2670				if (max > 0xffff || end[0] != /*(*/ ')' ||
2671				    end[1] != 0)
2672				{
2673					cfg_obj_log(typeobj, logctx,
2674						    ISC_LOG_ERROR,
2675						    "'%s' is not a valid count",
2676						    bracket);
2677					result = DNS_R_SYNTAX;
2678				}
2679			} else {
2680				r.length = strlen(r.base);
2681			}
2682
2683			tresult = dns_rdatatype_fromtext(&type, &r);
2684			if (tresult != ISC_R_SUCCESS) {
2685				cfg_obj_log(typeobj, logctx, ISC_LOG_ERROR,
2686					    "'%.*s' is not a valid type",
2687					    (int)r.length, r.base);
2688				result = tresult;
2689			}
2690		}
2691	}
2692	return (result);
2693}
2694
2695typedef struct {
2696	const char *name;
2697	unsigned int allowed;
2698} optionstable;
2699
2700static isc_result_t
2701check_nonzero(const cfg_obj_t *options, isc_log_t *logctx) {
2702	isc_result_t result = ISC_R_SUCCESS;
2703	const cfg_obj_t *obj = NULL;
2704	unsigned int i;
2705
2706	static const char *nonzero[] = { "max-retry-time", "min-retry-time",
2707					 "max-refresh-time",
2708					 "min-refresh-time" };
2709	/*
2710	 * Check if value is zero.
2711	 */
2712	for (i = 0; i < sizeof(nonzero) / sizeof(nonzero[0]); i++) {
2713		obj = NULL;
2714		if (cfg_map_get(options, nonzero[i], &obj) == ISC_R_SUCCESS &&
2715		    cfg_obj_asuint32(obj) == 0)
2716		{
2717			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
2718				    "'%s' must not be zero", nonzero[i]);
2719			result = ISC_R_FAILURE;
2720		}
2721	}
2722	return (result);
2723}
2724
2725/*%
2726 * Check whether NOTIFY configuration at the zone level is acceptable for a
2727 * mirror zone.  Return true if it is; return false otherwise.
2728 */
2729static bool
2730check_mirror_zone_notify(const cfg_obj_t *zoptions, const char *znamestr,
2731			 isc_log_t *logctx) {
2732	bool notify_configuration_ok = true;
2733	const cfg_obj_t *obj = NULL;
2734
2735	(void)cfg_map_get(zoptions, "notify", &obj);
2736	if (obj == NULL) {
2737		/*
2738		 * "notify" not set at zone level.  This is fine.
2739		 */
2740		return (true);
2741	}
2742
2743	if (cfg_obj_isboolean(obj)) {
2744		if (cfg_obj_asboolean(obj)) {
2745			/*
2746			 * "notify yes;" set at zone level.  This is an error.
2747			 */
2748			notify_configuration_ok = false;
2749		}
2750	} else {
2751		const char *notifystr = cfg_obj_asstring(obj);
2752		if (strcasecmp(notifystr, "explicit") != 0) {
2753			/*
2754			 * Something else than "notify explicit;" set at zone
2755			 * level.  This is an error.
2756			 */
2757			notify_configuration_ok = false;
2758		}
2759	}
2760
2761	if (!notify_configuration_ok) {
2762		cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR,
2763			    "zone '%s': mirror zones can only be used with "
2764			    "'notify no;' or 'notify explicit;'",
2765			    znamestr);
2766	}
2767
2768	return (notify_configuration_ok);
2769}
2770
2771/*%
2772 * Try to determine whether recursion is available in a view without resorting
2773 * to extraordinary measures: just check the "recursion" and "allow-recursion"
2774 * settings.  The point is to prevent accidental mirror zone misuse rather than
2775 * to enforce some sort of policy.  Recursion is assumed to be allowed by
2776 * default if it is not explicitly disabled.
2777 */
2778static bool
2779check_recursion(const cfg_obj_t *config, const cfg_obj_t *voptions,
2780		const cfg_obj_t *goptions, isc_log_t *logctx,
2781		cfg_aclconfctx_t *actx, isc_mem_t *mctx) {
2782	dns_acl_t *acl = NULL;
2783	const cfg_obj_t *obj;
2784	isc_result_t result;
2785	bool retval = true;
2786
2787	/*
2788	 * Check the "recursion" option first.
2789	 */
2790	obj = NULL;
2791	result = ISC_R_NOTFOUND;
2792	if (voptions != NULL) {
2793		result = cfg_map_get(voptions, "recursion", &obj);
2794	}
2795	if (result != ISC_R_SUCCESS && goptions != NULL) {
2796		result = cfg_map_get(goptions, "recursion", &obj);
2797	}
2798	if (result == ISC_R_SUCCESS && !cfg_obj_asboolean(obj)) {
2799		retval = false;
2800		goto cleanup;
2801	}
2802
2803	/*
2804	 * If recursion is not disabled by the "recursion" option, check
2805	 * whether it is disabled by the "allow-recursion" ACL.
2806	 */
2807	obj = NULL;
2808	result = ISC_R_NOTFOUND;
2809	if (voptions != NULL) {
2810		result = cfg_map_get(voptions, "allow-recursion", &obj);
2811	}
2812	if (result != ISC_R_SUCCESS && goptions != NULL) {
2813		result = cfg_map_get(goptions, "allow-recursion", &obj);
2814	}
2815	if (result == ISC_R_SUCCESS) {
2816		result = cfg_acl_fromconfig(obj, config, logctx, actx, mctx, 0,
2817					    &acl);
2818		if (result != ISC_R_SUCCESS) {
2819			goto cleanup;
2820		}
2821		retval = !dns_acl_isnone(acl);
2822	}
2823
2824cleanup:
2825	if (acl != NULL) {
2826		dns_acl_detach(&acl);
2827	}
2828
2829	return (retval);
2830}
2831
2832static isc_result_t
2833check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions,
2834	       const cfg_obj_t *config, isc_symtab_t *symtab,
2835	       isc_symtab_t *files, isc_symtab_t *keydirs, isc_symtab_t *inview,
2836	       const char *viewname, dns_rdataclass_t defclass,
2837	       bool nodeprecate, cfg_aclconfctx_t *actx, isc_log_t *logctx,
2838	       isc_mem_t *mctx) {
2839	const char *znamestr;
2840	const char *typestr = NULL;
2841	const char *target = NULL;
2842	unsigned int ztype;
2843	const cfg_obj_t *zoptions, *goptions = NULL;
2844	const cfg_obj_t *obj = NULL, *kasp = NULL;
2845	const cfg_obj_t *inviewobj = NULL;
2846	isc_result_t result = ISC_R_SUCCESS;
2847	isc_result_t tresult;
2848	unsigned int i;
2849	dns_rdataclass_t zclass;
2850	dns_fixedname_t fixedname;
2851	dns_name_t *zname = NULL; /* NULL if parsing of zone name fails. */
2852	isc_buffer_t b;
2853	bool root = false;
2854	bool rfc1918 = false;
2855	bool ula = false;
2856	const cfg_listelt_t *element;
2857	bool dlz;
2858	bool ddns = false;
2859	bool has_dnssecpolicy = false;
2860	const void *clauses = NULL;
2861	const char *option = NULL;
2862	const char *kaspname = NULL;
2863	const char *dir = NULL;
2864	static const char *acls[] = {
2865		"allow-notify",
2866		"allow-transfer",
2867		"allow-update",
2868		"allow-update-forwarding",
2869	};
2870	static optionstable dialups[] = {
2871		{ "notify", CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY },
2872		{ "notify-passive", CFG_ZONE_SECONDARY },
2873		{ "passive", CFG_ZONE_SECONDARY | CFG_ZONE_STUB },
2874		{ "refresh", CFG_ZONE_SECONDARY | CFG_ZONE_STUB },
2875	};
2876	static const char *sources[] = {
2877		"transfer-source",  "transfer-source-v6", "notify-source",
2878		"notify-source-v6", "parental-source",	  "parental-source-v6",
2879	};
2880
2881	znamestr = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
2882
2883	zoptions = cfg_tuple_get(zconfig, "options");
2884
2885	if (config != NULL) {
2886		cfg_map_get(config, "options", &goptions);
2887	}
2888
2889	inviewobj = NULL;
2890	(void)cfg_map_get(zoptions, "in-view", &inviewobj);
2891	if (inviewobj != NULL) {
2892		target = cfg_obj_asstring(inviewobj);
2893		ztype = CFG_ZONE_INVIEW;
2894	} else {
2895		obj = NULL;
2896		(void)cfg_map_get(zoptions, "type", &obj);
2897		if (obj == NULL) {
2898			cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
2899				    "zone '%s': type not present", znamestr);
2900			return (ISC_R_FAILURE);
2901		}
2902
2903		typestr = cfg_obj_asstring(obj);
2904		if (strcasecmp(typestr, "master") == 0 ||
2905		    strcasecmp(typestr, "primary") == 0)
2906		{
2907			ztype = CFG_ZONE_PRIMARY;
2908		} else if (strcasecmp(typestr, "slave") == 0 ||
2909			   strcasecmp(typestr, "secondary") == 0)
2910		{
2911			ztype = CFG_ZONE_SECONDARY;
2912		} else if (strcasecmp(typestr, "mirror") == 0) {
2913			ztype = CFG_ZONE_MIRROR;
2914		} else if (strcasecmp(typestr, "stub") == 0) {
2915			ztype = CFG_ZONE_STUB;
2916		} else if (strcasecmp(typestr, "static-stub") == 0) {
2917			ztype = CFG_ZONE_STATICSTUB;
2918		} else if (strcasecmp(typestr, "forward") == 0) {
2919			ztype = CFG_ZONE_FORWARD;
2920		} else if (strcasecmp(typestr, "hint") == 0) {
2921			ztype = CFG_ZONE_HINT;
2922		} else if (strcasecmp(typestr, "delegation-only") == 0) {
2923			ztype = CFG_ZONE_DELEGATION;
2924			if (!nodeprecate) {
2925				cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
2926					    "'type delegation-only' is "
2927					    "deprecated");
2928			}
2929		} else if (strcasecmp(typestr, "redirect") == 0) {
2930			ztype = CFG_ZONE_REDIRECT;
2931		} else {
2932			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
2933				    "zone '%s': invalid type %s", znamestr,
2934				    typestr);
2935			return (ISC_R_FAILURE);
2936		}
2937
2938		if (ztype == CFG_ZONE_REDIRECT && strcmp(znamestr, ".") != 0) {
2939			cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
2940				    "redirect zones must be called \".\"");
2941			return (ISC_R_FAILURE);
2942		}
2943	}
2944
2945	obj = cfg_tuple_get(zconfig, "class");
2946	if (cfg_obj_isstring(obj)) {
2947		isc_textregion_t r;
2948
2949		DE_CONST(cfg_obj_asstring(obj), r.base);
2950		r.length = strlen(r.base);
2951		result = dns_rdataclass_fromtext(&zclass, &r);
2952		if (result != ISC_R_SUCCESS) {
2953			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
2954				    "zone '%s': invalid class %s", znamestr,
2955				    r.base);
2956			return (ISC_R_FAILURE);
2957		}
2958		if (zclass != defclass) {
2959			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
2960				    "zone '%s': class '%s' does not "
2961				    "match view/default class",
2962				    znamestr, r.base);
2963			return (ISC_R_FAILURE);
2964		}
2965	} else {
2966		zclass = defclass;
2967	}
2968
2969	/*
2970	 * Look for an already existing zone.
2971	 * We need to make this canonical as isc_symtab_define()
2972	 * deals with strings.
2973	 */
2974	dns_fixedname_init(&fixedname);
2975	isc_buffer_constinit(&b, znamestr, strlen(znamestr));
2976	isc_buffer_add(&b, strlen(znamestr));
2977	tresult = dns_name_fromtext(dns_fixedname_name(&fixedname), &b,
2978				    dns_rootname, DNS_NAME_DOWNCASE, NULL);
2979	if (tresult != ISC_R_SUCCESS) {
2980		cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
2981			    "zone '%s': is not a valid name", znamestr);
2982		result = ISC_R_FAILURE;
2983	} else {
2984		char namebuf[DNS_NAME_FORMATSIZE + 128];
2985		char *tmp = namebuf;
2986		size_t len = sizeof(namebuf);
2987
2988		zname = dns_fixedname_name(&fixedname);
2989		dns_name_format(zname, namebuf, sizeof(namebuf));
2990		tresult = nameexist(zconfig, namebuf,
2991				    ztype == CFG_ZONE_HINT	 ? 1
2992				    : ztype == CFG_ZONE_REDIRECT ? 2
2993								 : 3,
2994				    symtab,
2995				    "zone '%s': already exists "
2996				    "previous definition: %s:%u",
2997				    logctx, mctx);
2998		if (tresult != ISC_R_SUCCESS) {
2999			result = tresult;
3000		}
3001		if (dns_name_equal(zname, dns_rootname)) {
3002			root = true;
3003		} else if (dns_name_isrfc1918(zname)) {
3004			rfc1918 = true;
3005		} else if (dns_name_isula(zname)) {
3006			ula = true;
3007		}
3008		len -= strlen(tmp);
3009		tmp += strlen(tmp);
3010		(void)snprintf(tmp, len, "%u/%s", zclass,
3011			       (ztype == CFG_ZONE_INVIEW) ? target
3012			       : (viewname != NULL)	  ? viewname
3013							  : "_default");
3014		switch (ztype) {
3015		case CFG_ZONE_INVIEW:
3016			tresult = isc_symtab_lookup(inview, namebuf, 0, NULL);
3017			if (tresult != ISC_R_SUCCESS) {
3018				cfg_obj_log(inviewobj, logctx, ISC_LOG_ERROR,
3019					    "'in-view' zone '%s' "
3020					    "does not exist in view '%s', "
3021					    "or view '%s' is not yet defined",
3022					    znamestr, target, target);
3023				if (result == ISC_R_SUCCESS) {
3024					result = tresult;
3025				}
3026			}
3027			break;
3028
3029		case CFG_ZONE_FORWARD:
3030		case CFG_ZONE_REDIRECT:
3031		case CFG_ZONE_DELEGATION:
3032			break;
3033
3034		case CFG_ZONE_PRIMARY:
3035		case CFG_ZONE_SECONDARY:
3036		case CFG_ZONE_MIRROR:
3037		case CFG_ZONE_HINT:
3038		case CFG_ZONE_STUB:
3039		case CFG_ZONE_STATICSTUB:
3040			tmp = isc_mem_strdup(mctx, namebuf);
3041			{
3042				isc_symvalue_t symvalue;
3043				symvalue.as_cpointer = NULL;
3044				tresult = isc_symtab_define(
3045					inview, tmp, 1, symvalue,
3046					isc_symexists_replace);
3047				if (tresult == ISC_R_NOMEMORY) {
3048					isc_mem_free(mctx, tmp);
3049				}
3050				if (result == ISC_R_SUCCESS &&
3051				    tresult != ISC_R_SUCCESS)
3052				{
3053					result = tresult;
3054				}
3055			}
3056			break;
3057
3058		default:
3059			UNREACHABLE();
3060		}
3061	}
3062
3063	if (ztype == CFG_ZONE_INVIEW) {
3064		const cfg_obj_t *fwd = NULL;
3065		unsigned int maxopts = 1;
3066
3067		(void)cfg_map_get(zoptions, "forward", &fwd);
3068		if (fwd != NULL) {
3069			maxopts++;
3070		}
3071		fwd = NULL;
3072		(void)cfg_map_get(zoptions, "forwarders", &fwd);
3073		if (fwd != NULL) {
3074			maxopts++;
3075		}
3076		if (cfg_map_count(zoptions) > maxopts) {
3077			cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
3078				    "zone '%s': 'in-view' used "
3079				    "with incompatible zone options",
3080				    znamestr);
3081			if (result == ISC_R_SUCCESS) {
3082				result = ISC_R_FAILURE;
3083			}
3084		}
3085		return (result);
3086	}
3087
3088	/*
3089	 * Check if value is zero.
3090	 */
3091	if (check_nonzero(zoptions, logctx) != ISC_R_SUCCESS) {
3092		result = ISC_R_FAILURE;
3093	}
3094
3095	/*
3096	 * Check if a dnssec-policy is set.
3097	 */
3098	obj = NULL;
3099	(void)cfg_map_get(zoptions, "dnssec-policy", &obj);
3100	if (obj == NULL && voptions != NULL) {
3101		(void)cfg_map_get(voptions, "dnssec-policy", &obj);
3102	}
3103	if (obj == NULL && goptions != NULL) {
3104		(void)cfg_map_get(goptions, "dnssec-policy", &obj);
3105	}
3106	if (obj != NULL) {
3107		const cfg_obj_t *kasps = NULL;
3108
3109		kaspname = cfg_obj_asstring(obj);
3110		if (strcmp(kaspname, "default") == 0) {
3111			has_dnssecpolicy = true;
3112		} else if (strcmp(kaspname, "insecure") == 0) {
3113			has_dnssecpolicy = true;
3114		} else if (strcmp(kaspname, "none") == 0) {
3115			has_dnssecpolicy = false;
3116		} else {
3117			(void)cfg_map_get(config, "dnssec-policy", &kasps);
3118			for (element = cfg_list_first(kasps); element != NULL;
3119			     element = cfg_list_next(element))
3120			{
3121				const cfg_obj_t *kobj = cfg_tuple_get(
3122					cfg_listelt_value(element), "name");
3123				if (strcmp(kaspname, cfg_obj_asstring(kobj)) ==
3124				    0)
3125				{
3126					has_dnssecpolicy = true;
3127				}
3128			}
3129
3130			if (!has_dnssecpolicy) {
3131				cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
3132					    "zone '%s': option "
3133					    "'dnssec-policy %s' has no "
3134					    "matching dnssec-policy config",
3135					    znamestr, kaspname);
3136				if (result == ISC_R_SUCCESS) {
3137					result = ISC_R_FAILURE;
3138				}
3139			}
3140		}
3141		if (has_dnssecpolicy) {
3142			kasp = obj;
3143		}
3144	}
3145
3146	/*
3147	 * Warn about zones with both dnssec-policy and max-zone-ttl
3148	 */
3149	if (has_dnssecpolicy) {
3150		obj = NULL;
3151		(void)cfg_map_get(zoptions, "max-zone-ttl", &obj);
3152		if (obj == NULL && voptions != NULL) {
3153			(void)cfg_map_get(voptions, "max-zone-ttl", &obj);
3154		}
3155		if (obj == NULL && goptions != NULL) {
3156			(void)cfg_map_get(goptions, "max-zone-ttl", &obj);
3157		}
3158		if (obj != NULL) {
3159			cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
3160				    "zone '%s': option 'max-zone-ttl' "
3161				    "is ignored when used together with "
3162				    "'dnssec-policy'",
3163				    znamestr);
3164		}
3165	}
3166
3167	/*
3168	 * Check validity of the zone options.
3169	 */
3170	option = cfg_map_firstclause(&cfg_type_zoneopts, &clauses, &i);
3171	while (option != NULL) {
3172		obj = NULL;
3173		if (cfg_map_get(zoptions, option, &obj) == ISC_R_SUCCESS &&
3174		    obj != NULL && !cfg_clause_validforzone(option, ztype))
3175		{
3176			cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
3177				    "option '%s' is not allowed "
3178				    "in '%s' zone '%s'",
3179				    option, typestr, znamestr);
3180			result = ISC_R_FAILURE;
3181		}
3182		option = cfg_map_nextclause(&cfg_type_zoneopts, &clauses, &i);
3183	}
3184
3185	/*
3186	 * Check that ACLs expand correctly.
3187	 */
3188	for (i = 0; i < ARRAY_SIZE(acls); i++) {
3189		tresult = checkacl(acls[i], actx, zconfig, voptions, config,
3190				   logctx, mctx);
3191		if (tresult != ISC_R_SUCCESS) {
3192			result = tresult;
3193		}
3194	}
3195
3196	/*
3197	 * Only a limited subset of all possible "notify" settings can be used
3198	 * at the zone level for mirror zones.
3199	 */
3200	if (ztype == CFG_ZONE_MIRROR &&
3201	    !check_mirror_zone_notify(zoptions, znamestr, logctx))
3202	{
3203		result = ISC_R_FAILURE;
3204	}
3205
3206	/*
3207	 * Primary, secondary, and mirror zones may have an "also-notify"
3208	 * field, but shouldn't if notify is disabled.
3209	 */
3210	if (ztype == CFG_ZONE_PRIMARY || ztype == CFG_ZONE_SECONDARY ||
3211	    ztype == CFG_ZONE_MIRROR)
3212	{
3213		bool donotify = true;
3214
3215		obj = NULL;
3216		tresult = cfg_map_get(zoptions, "notify", &obj);
3217		if (tresult != ISC_R_SUCCESS && voptions != NULL) {
3218			tresult = cfg_map_get(voptions, "notify", &obj);
3219		}
3220		if (tresult != ISC_R_SUCCESS && goptions != NULL) {
3221			tresult = cfg_map_get(goptions, "notify", &obj);
3222		}
3223		if (tresult == ISC_R_SUCCESS) {
3224			if (cfg_obj_isboolean(obj)) {
3225				donotify = cfg_obj_asboolean(obj);
3226			} else {
3227				const char *str = cfg_obj_asstring(obj);
3228				if (ztype != CFG_ZONE_PRIMARY &&
3229				    (strcasecmp(str, "master-only") == 0 ||
3230				     strcasecmp(str, "primary-only") == 0))
3231				{
3232					donotify = false;
3233				}
3234			}
3235		}
3236
3237		obj = NULL;
3238		tresult = cfg_map_get(zoptions, "also-notify", &obj);
3239		if (tresult == ISC_R_SUCCESS && !donotify) {
3240			cfg_obj_log(zoptions, logctx, ISC_LOG_WARNING,
3241				    "zone '%s': 'also-notify' set but "
3242				    "'notify' is disabled",
3243				    znamestr);
3244		}
3245		if (tresult != ISC_R_SUCCESS && voptions != NULL) {
3246			tresult = cfg_map_get(voptions, "also-notify", &obj);
3247		}
3248		if (tresult != ISC_R_SUCCESS && goptions != NULL) {
3249			tresult = cfg_map_get(goptions, "also-notify", &obj);
3250		}
3251		if (tresult == ISC_R_SUCCESS && donotify) {
3252			uint32_t count;
3253			tresult = validate_remotes("primaries", obj, config,
3254						   &count, logctx, mctx);
3255			if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS)
3256			{
3257				result = tresult;
3258			}
3259		}
3260	}
3261
3262	/*
3263	 * Secondary, mirror, and stub zones must have a "primaries" field,
3264	 * with one exception: when mirroring the root zone, a default,
3265	 * built-in primary server list is used in the absence of one
3266	 * explicitly specified.
3267	 */
3268	if (ztype == CFG_ZONE_SECONDARY || ztype == CFG_ZONE_STUB ||
3269	    (ztype == CFG_ZONE_MIRROR && zname != NULL &&
3270	     !dns_name_equal(zname, dns_rootname)))
3271	{
3272		obj = NULL;
3273		(void)cfg_map_get(zoptions, "primaries", &obj);
3274		if (obj == NULL) {
3275			/* If "primaries" was unset, check for "masters" */
3276			(void)cfg_map_get(zoptions, "masters", &obj);
3277		} else {
3278			const cfg_obj_t *obj2 = NULL;
3279
3280			/* ...bug if it was set, "masters" must not be. */
3281			(void)cfg_map_get(zoptions, "masters", &obj2);
3282			if (obj2 != NULL) {
3283				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
3284					    "'primaries' and 'masters' cannot "
3285					    "both be used in the same zone");
3286				result = ISC_R_FAILURE;
3287			}
3288		}
3289		if (obj == NULL) {
3290			cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR,
3291				    "zone '%s': missing 'primaries' entry",
3292				    znamestr);
3293			result = ISC_R_FAILURE;
3294		} else {
3295			uint32_t count;
3296			tresult = validate_remotes("primaries", obj, config,
3297						   &count, logctx, mctx);
3298			if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS)
3299			{
3300				result = tresult;
3301			}
3302			if (tresult == ISC_R_SUCCESS && count == 0) {
3303				cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR,
3304					    "zone '%s': "
3305					    "empty 'primaries' entry",
3306					    znamestr);
3307				result = ISC_R_FAILURE;
3308			}
3309		}
3310	}
3311
3312	/*
3313	 * Warn if *-source and *-source-v6 options specify a port,
3314	 * and fail if they specify the default listener port.
3315	 */
3316	for (i = 0; i < ARRAY_SIZE(sources); i++) {
3317		obj = NULL;
3318		(void)cfg_map_get(zoptions, sources[i], &obj);
3319		if (obj == NULL && goptions != NULL) {
3320			(void)cfg_map_get(goptions, sources[i], &obj);
3321		}
3322		if (obj != NULL) {
3323			in_port_t port =
3324				isc_sockaddr_getport(cfg_obj_assockaddr(obj));
3325			if (port == dnsport) {
3326				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
3327					    "'%s' cannot specify the "
3328					    "DNS listener port (%d)",
3329					    sources[i], port);
3330				result = ISC_R_FAILURE;
3331			} else if (port != 0) {
3332				cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
3333					    "'%s': specifying a port is "
3334					    "not recommended",
3335					    sources[i]);
3336			}
3337		}
3338	}
3339
3340	/*
3341	 * Primary and secondary zones that have a "parental-agents" field,
3342	 * must have a corresponding "parental-agents" clause.
3343	 */
3344	if (ztype == CFG_ZONE_PRIMARY || ztype == CFG_ZONE_SECONDARY) {
3345		obj = NULL;
3346		(void)cfg_map_get(zoptions, "parental-agents", &obj);
3347		if (obj != NULL) {
3348			uint32_t count;
3349			tresult = validate_remotes("parental-agents", obj,
3350						   config, &count, logctx,
3351						   mctx);
3352			if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS)
3353			{
3354				result = tresult;
3355			}
3356			if (tresult == ISC_R_SUCCESS && count == 0) {
3357				cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR,
3358					    "zone '%s': "
3359					    "empty 'parental-agents' entry",
3360					    znamestr);
3361				result = ISC_R_FAILURE;
3362			}
3363		}
3364	}
3365
3366	/*
3367	 * Configuring a mirror zone and disabling recursion at the same time
3368	 * contradicts the purpose of the former.
3369	 */
3370	if (ztype == CFG_ZONE_MIRROR &&
3371	    !check_recursion(config, voptions, goptions, logctx, actx, mctx))
3372	{
3373		cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR,
3374			    "zone '%s': mirror zones cannot be used if "
3375			    "recursion is disabled",
3376			    znamestr);
3377		result = ISC_R_FAILURE;
3378	}
3379
3380	/*
3381	 * Primary zones can't have both "allow-update" and "update-policy".
3382	 */
3383	if (ztype == CFG_ZONE_PRIMARY || ztype == CFG_ZONE_SECONDARY) {
3384		bool signing = false;
3385		isc_result_t res1, res2, res3;
3386		const cfg_obj_t *au = NULL;
3387		const char *arg;
3388
3389		obj = NULL;
3390		res1 = cfg_map_get(zoptions, "allow-update", &au);
3391		obj = NULL;
3392		res2 = cfg_map_get(zoptions, "update-policy", &obj);
3393		if (res1 == ISC_R_SUCCESS && res2 == ISC_R_SUCCESS) {
3394			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
3395				    "zone '%s': 'allow-update' is ignored "
3396				    "when 'update-policy' is present",
3397				    znamestr);
3398			result = ISC_R_FAILURE;
3399		} else if (res2 == ISC_R_SUCCESS) {
3400			res3 = check_update_policy(obj, logctx);
3401			if (res3 != ISC_R_SUCCESS) {
3402				result = ISC_R_FAILURE;
3403			}
3404		}
3405
3406		/*
3407		 * To determine whether auto-dnssec is allowed,
3408		 * we should also check for allow-update at the
3409		 * view and options levels.
3410		 */
3411		if (res1 != ISC_R_SUCCESS && voptions != NULL) {
3412			res1 = cfg_map_get(voptions, "allow-update", &au);
3413		}
3414		if (res1 != ISC_R_SUCCESS && goptions != NULL) {
3415			res1 = cfg_map_get(goptions, "allow-update", &au);
3416		}
3417
3418		if (res2 == ISC_R_SUCCESS) {
3419			ddns = true;
3420		} else if (res1 == ISC_R_SUCCESS) {
3421			dns_acl_t *acl = NULL;
3422			res1 = cfg_acl_fromconfig(au, config, logctx, actx,
3423						  mctx, 0, &acl);
3424			if (res1 != ISC_R_SUCCESS) {
3425				cfg_obj_log(au, logctx, ISC_LOG_ERROR,
3426					    "acl expansion failed: %s",
3427					    isc_result_totext(result));
3428				result = ISC_R_FAILURE;
3429			} else if (acl != NULL) {
3430				if (!dns_acl_isnone(acl)) {
3431					ddns = true;
3432				}
3433				dns_acl_detach(&acl);
3434			}
3435		}
3436
3437		obj = NULL;
3438		res1 = cfg_map_get(zoptions, "inline-signing", &obj);
3439		if (res1 == ISC_R_SUCCESS) {
3440			signing = cfg_obj_asboolean(obj);
3441		}
3442
3443		if (has_dnssecpolicy) {
3444			if (!ddns && !signing) {
3445				cfg_obj_log(kasp, logctx, ISC_LOG_ERROR,
3446					    "'inline-signing yes;' must also "
3447					    "be configured explicitly for "
3448					    "zones using dnssec-policy%s. See "
3449					    "https://kb.isc.org/docs/"
3450					    "dnssec-policy-requires-dynamic-"
3451					    "dns-or-inline-signing",
3452					    (ztype == CFG_ZONE_PRIMARY)
3453						    ? " without a configured "
3454						      "'allow-update' or "
3455						      "'update-policy'"
3456						    : "");
3457				result = ISC_R_FAILURE;
3458			}
3459		}
3460
3461		obj = NULL;
3462		arg = "off";
3463		res3 = cfg_map_get(zoptions, "auto-dnssec", &obj);
3464		if (res3 == ISC_R_SUCCESS) {
3465			arg = cfg_obj_asstring(obj);
3466			cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
3467				    "'auto-dnssec' option is deprecated and "
3468				    "will be removed in BIND 9.19. Please "
3469				    "migrate to dnssec-policy");
3470		}
3471		if (strcasecmp(arg, "off") != 0) {
3472			if (!ddns && !signing && !has_dnssecpolicy) {
3473				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
3474					    "'auto-dnssec %s;' requires%s "
3475					    "inline-signing to be configured "
3476					    "for the zone",
3477					    arg,
3478					    (ztype == CFG_ZONE_PRIMARY)
3479						    ? " dynamic DNS or"
3480						    : "");
3481				result = ISC_R_FAILURE;
3482			}
3483
3484			if (has_dnssecpolicy) {
3485				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
3486					    "'auto-dnssec %s;' cannot be "
3487					    "configured if dnssec-policy is "
3488					    "also set",
3489					    arg);
3490				result = ISC_R_FAILURE;
3491			}
3492		}
3493
3494		obj = NULL;
3495		res1 = cfg_map_get(zoptions, "sig-signing-type", &obj);
3496		if (res1 == ISC_R_SUCCESS) {
3497			uint32_t type = cfg_obj_asuint32(obj);
3498			if (type < 0xff00U || type > 0xffffU) {
3499				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
3500					    "sig-signing-type: %u out of "
3501					    "range [%u..%u]",
3502					    type, 0xff00U, 0xffffU);
3503				result = ISC_R_FAILURE;
3504			}
3505		}
3506
3507		obj = NULL;
3508		res1 = cfg_map_get(zoptions, "dnssec-dnskey-kskonly", &obj);
3509		if (res1 == ISC_R_SUCCESS && ztype == CFG_ZONE_SECONDARY &&
3510		    !signing)
3511		{
3512			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
3513				    "dnssec-dnskey-kskonly: requires "
3514				    "inline-signing when used in secondary "
3515				    "zone");
3516			result = ISC_R_FAILURE;
3517		}
3518		if (res1 == ISC_R_SUCCESS && has_dnssecpolicy) {
3519			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
3520				    "dnssec-dnskey-kskonly: cannot be "
3521				    "configured if dnssec-policy is also set");
3522			result = ISC_R_FAILURE;
3523		}
3524
3525		obj = NULL;
3526		res1 = cfg_map_get(zoptions, "dnssec-secure-to-insecure", &obj);
3527		if (res1 == ISC_R_SUCCESS && has_dnssecpolicy) {
3528			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
3529				    "dnssec-secure-to-insecure: cannot be "
3530				    "configured if dnssec-policy is also set");
3531			result = ISC_R_FAILURE;
3532		}
3533
3534		obj = NULL;
3535		res1 = cfg_map_get(zoptions, "dnssec-loadkeys-interval", &obj);
3536		if (res1 == ISC_R_SUCCESS && ztype == CFG_ZONE_SECONDARY &&
3537		    !signing)
3538		{
3539			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
3540				    "dnssec-loadkeys-interval: requires "
3541				    "inline-signing when used in secondary "
3542				    "zone");
3543			result = ISC_R_FAILURE;
3544		}
3545
3546		obj = NULL;
3547		res1 = cfg_map_get(zoptions, "update-check-ksk", &obj);
3548		if (res1 == ISC_R_SUCCESS && ztype == CFG_ZONE_SECONDARY &&
3549		    !signing)
3550		{
3551			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
3552				    "update-check-ksk: requires "
3553				    "inline-signing when used in secondary "
3554				    "zone");
3555			result = ISC_R_FAILURE;
3556		}
3557		if (res1 == ISC_R_SUCCESS && has_dnssecpolicy) {
3558			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
3559				    "update-check-ksk: cannot be configured "
3560				    "if dnssec-policy is also set");
3561			result = ISC_R_FAILURE;
3562		}
3563
3564		obj = NULL;
3565		res1 = cfg_map_get(zoptions, "dnssec-update-mode", &obj);
3566		if (res1 == ISC_R_SUCCESS && has_dnssecpolicy) {
3567			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
3568				    "dnssec-update-mode: cannot be configured "
3569				    "if dnssec-policy is also set");
3570			result = ISC_R_FAILURE;
3571		}
3572	}
3573
3574	/*
3575	 * Check the excessively complicated "dialup" option.
3576	 */
3577	if (ztype == CFG_ZONE_PRIMARY || ztype == CFG_ZONE_SECONDARY ||
3578	    ztype == CFG_ZONE_STUB)
3579	{
3580		obj = NULL;
3581		(void)cfg_map_get(zoptions, "dialup", &obj);
3582		if (obj != NULL && cfg_obj_isstring(obj)) {
3583			const char *str = cfg_obj_asstring(obj);
3584			for (i = 0; i < sizeof(dialups) / sizeof(dialups[0]);
3585			     i++)
3586			{
3587				if (strcasecmp(dialups[i].name, str) != 0) {
3588					continue;
3589				}
3590				if ((dialups[i].allowed & ztype) == 0) {
3591					cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
3592						    "dialup type '%s' is not "
3593						    "allowed in '%s' "
3594						    "zone '%s'",
3595						    str, typestr, znamestr);
3596					result = ISC_R_FAILURE;
3597				}
3598				break;
3599			}
3600			if (i == sizeof(dialups) / sizeof(dialups[0])) {
3601				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
3602					    "invalid dialup type '%s' in zone "
3603					    "'%s'",
3604					    str, znamestr);
3605				result = ISC_R_FAILURE;
3606			}
3607		}
3608	}
3609
3610	/*
3611	 * Check that forwarding is reasonable.
3612	 */
3613	obj = NULL;
3614	if (root) {
3615		if (voptions != NULL) {
3616			(void)cfg_map_get(voptions, "forwarders", &obj);
3617		}
3618		if (obj == NULL && goptions != NULL) {
3619			(void)cfg_map_get(goptions, "forwarders", &obj);
3620		}
3621	}
3622	if (check_forward(zoptions, obj, logctx) != ISC_R_SUCCESS) {
3623		result = ISC_R_FAILURE;
3624	}
3625
3626	/*
3627	 * Check that a RFC 1918 / ULA reverse zone is not forward first
3628	 * unless explicitly configured to be so.
3629	 */
3630	if (ztype == CFG_ZONE_FORWARD && (rfc1918 || ula)) {
3631		obj = NULL;
3632		(void)cfg_map_get(zoptions, "forward", &obj);
3633		if (obj == NULL) {
3634			/*
3635			 * Forward mode not explicitly configured.
3636			 */
3637			if (voptions != NULL) {
3638				cfg_map_get(voptions, "forward", &obj);
3639			}
3640			if (obj == NULL && goptions != NULL) {
3641				cfg_map_get(goptions, "forward", &obj);
3642			}
3643			if (obj == NULL ||
3644			    strcasecmp(cfg_obj_asstring(obj), "first") == 0)
3645			{
3646				cfg_obj_log(zconfig, logctx, ISC_LOG_WARNING,
3647					    "inherited 'forward first;' for "
3648					    "%s zone '%s' - did you want "
3649					    "'forward only;'?",
3650					    rfc1918 ? "rfc1918" : "ula",
3651					    znamestr);
3652			}
3653		}
3654	}
3655
3656	/*
3657	 * Check validity of static stub server addresses.
3658	 */
3659	obj = NULL;
3660	(void)cfg_map_get(zoptions, "server-addresses", &obj);
3661	if (ztype == CFG_ZONE_STATICSTUB && obj != NULL) {
3662		for (element = cfg_list_first(obj); element != NULL;
3663		     element = cfg_list_next(element))
3664		{
3665			isc_sockaddr_t sa;
3666			isc_netaddr_t na;
3667			obj = cfg_listelt_value(element);
3668			sa = *cfg_obj_assockaddr(obj);
3669
3670			isc_netaddr_fromsockaddr(&na, &sa);
3671			if (isc_netaddr_getzone(&na) != 0) {
3672				result = ISC_R_FAILURE;
3673				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
3674					    "scoped address is not allowed "
3675					    "for static stub "
3676					    "server-addresses");
3677			}
3678		}
3679	}
3680
3681	/*
3682	 * Check validity of static stub server names.
3683	 */
3684	obj = NULL;
3685	(void)cfg_map_get(zoptions, "server-names", &obj);
3686	if (zname != NULL && ztype == CFG_ZONE_STATICSTUB && obj != NULL) {
3687		for (element = cfg_list_first(obj); element != NULL;
3688		     element = cfg_list_next(element))
3689		{
3690			const char *snamestr;
3691			dns_fixedname_t fixed_sname;
3692			isc_buffer_t b2;
3693			dns_name_t *sname;
3694
3695			obj = cfg_listelt_value(element);
3696			snamestr = cfg_obj_asstring(obj);
3697
3698			isc_buffer_constinit(&b2, snamestr, strlen(snamestr));
3699			isc_buffer_add(&b2, strlen(snamestr));
3700			sname = dns_fixedname_initname(&fixed_sname);
3701			tresult = dns_name_fromtext(sname, &b2, dns_rootname, 0,
3702						    NULL);
3703			if (tresult != ISC_R_SUCCESS) {
3704				cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
3705					    "server-name '%s' is not a valid "
3706					    "name",
3707					    snamestr);
3708				result = ISC_R_FAILURE;
3709			} else if (dns_name_issubdomain(sname, zname)) {
3710				cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
3711					    "server-name '%s' must not be a "
3712					    "subdomain of zone name '%s'",
3713					    snamestr, znamestr);
3714				result = ISC_R_FAILURE;
3715			}
3716		}
3717	}
3718
3719	/*
3720	 * Warn if key-directory doesn't exist
3721	 */
3722	obj = NULL;
3723	(void)cfg_map_get(zoptions, "key-directory", &obj);
3724	if (obj == NULL && voptions != NULL) {
3725		(void)cfg_map_get(voptions, "key-directory", &obj);
3726	}
3727	if (obj == NULL && goptions != NULL) {
3728		(void)cfg_map_get(goptions, "key-directory", &obj);
3729	}
3730	if (obj != NULL) {
3731		dir = cfg_obj_asstring(obj);
3732
3733		tresult = isc_file_isdirectory(dir);
3734		switch (tresult) {
3735		case ISC_R_SUCCESS:
3736			break;
3737		case ISC_R_FILENOTFOUND:
3738			cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
3739				    "key-directory: '%s' does not exist", dir);
3740			break;
3741		case ISC_R_INVALIDFILE:
3742			cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
3743				    "key-directory: '%s' is not a directory",
3744				    dir);
3745			break;
3746		default:
3747			cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
3748				    "key-directory: '%s' %s", dir,
3749				    isc_result_totext(tresult));
3750			result = tresult;
3751		}
3752	}
3753
3754	/*
3755	 * Make sure there is no other zone with the same
3756	 * key-directory and a different dnssec-policy.
3757	 */
3758	if (zname != NULL) {
3759		char keydirbuf[DNS_NAME_FORMATSIZE + 128];
3760		char *tmp = keydirbuf;
3761		size_t len = sizeof(keydirbuf);
3762		dns_name_format(zname, keydirbuf, sizeof(keydirbuf));
3763		len -= strlen(tmp);
3764		tmp += strlen(tmp);
3765		(void)snprintf(tmp, len, "/%s", (dir == NULL) ? "(null)" : dir);
3766		tresult = keydirexist(zconfig, (const char *)keydirbuf,
3767				      kaspname, keydirs, logctx, mctx);
3768		if (tresult != ISC_R_SUCCESS) {
3769			result = tresult;
3770		}
3771	}
3772
3773	/*
3774	 * Check various options.
3775	 */
3776	tresult = check_options(zoptions, config, logctx, mctx, optlevel_zone);
3777	if (tresult != ISC_R_SUCCESS) {
3778		result = tresult;
3779	}
3780
3781	/*
3782	 * If the zone type is rbt/rbt64 then primary/hint zones require file
3783	 * clauses. If inline-signing is used, then secondary zones require a
3784	 * file clause as well.
3785	 */
3786	obj = NULL;
3787	dlz = false;
3788	tresult = cfg_map_get(zoptions, "dlz", &obj);
3789	if (tresult == ISC_R_SUCCESS) {
3790		dlz = true;
3791	}
3792
3793	obj = NULL;
3794	tresult = cfg_map_get(zoptions, "database", &obj);
3795	if (dlz && tresult == ISC_R_SUCCESS) {
3796		cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
3797			    "zone '%s': cannot specify both 'dlz' "
3798			    "and 'database'",
3799			    znamestr);
3800		result = ISC_R_FAILURE;
3801	} else if (!dlz && (tresult == ISC_R_NOTFOUND ||
3802			    (tresult == ISC_R_SUCCESS &&
3803			     (strcmp("rbt", cfg_obj_asstring(obj)) == 0 ||
3804			      strcmp("rbt64", cfg_obj_asstring(obj)) == 0))))
3805	{
3806		isc_result_t res1;
3807		const cfg_obj_t *fileobj = NULL;
3808		tresult = cfg_map_get(zoptions, "file", &fileobj);
3809		obj = NULL;
3810		res1 = cfg_map_get(zoptions, "inline-signing", &obj);
3811		if ((tresult != ISC_R_SUCCESS &&
3812		     (ztype == CFG_ZONE_PRIMARY || ztype == CFG_ZONE_HINT ||
3813		      (ztype == CFG_ZONE_SECONDARY && res1 == ISC_R_SUCCESS &&
3814		       cfg_obj_asboolean(obj)))))
3815		{
3816			cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
3817				    "zone '%s': missing 'file' entry",
3818				    znamestr);
3819			result = tresult;
3820		} else if (tresult == ISC_R_SUCCESS &&
3821			   (ztype == CFG_ZONE_SECONDARY ||
3822			    ztype == CFG_ZONE_MIRROR || ddns ||
3823			    has_dnssecpolicy))
3824		{
3825			tresult = fileexist(fileobj, files, true, logctx);
3826			if (tresult != ISC_R_SUCCESS) {
3827				result = tresult;
3828			}
3829		} else if (tresult == ISC_R_SUCCESS &&
3830			   (ztype == CFG_ZONE_PRIMARY ||
3831			    ztype == CFG_ZONE_HINT))
3832		{
3833			tresult = fileexist(fileobj, files, false, logctx);
3834			if (tresult != ISC_R_SUCCESS) {
3835				result = tresult;
3836			}
3837		}
3838	}
3839
3840	return (result);
3841}
3842
3843typedef struct keyalgorithms {
3844	const char *name;
3845	uint16_t size;
3846} algorithmtable;
3847
3848isc_result_t
3849bind9_check_key(const cfg_obj_t *key, isc_log_t *logctx) {
3850	const cfg_obj_t *algobj = NULL;
3851	const cfg_obj_t *secretobj = NULL;
3852	const char *keyname = cfg_obj_asstring(cfg_map_getname(key));
3853	const char *algorithm;
3854	int i;
3855	size_t len = 0;
3856	isc_result_t result;
3857	isc_buffer_t buf;
3858	unsigned char secretbuf[1024];
3859	static const algorithmtable algorithms[] = {
3860		{ "hmac-md5", 128 },
3861		{ "hmac-md5.sig-alg.reg.int", 0 },
3862		{ "hmac-md5.sig-alg.reg.int.", 0 },
3863		{ "hmac-sha1", 160 },
3864		{ "hmac-sha224", 224 },
3865		{ "hmac-sha256", 256 },
3866		{ "hmac-sha384", 384 },
3867		{ "hmac-sha512", 512 },
3868		{ NULL, 0 }
3869	};
3870
3871	(void)cfg_map_get(key, "algorithm", &algobj);
3872	(void)cfg_map_get(key, "secret", &secretobj);
3873	if (secretobj == NULL || algobj == NULL) {
3874		cfg_obj_log(key, logctx, ISC_LOG_ERROR,
3875			    "key '%s' must have both 'secret' and "
3876			    "'algorithm' defined",
3877			    keyname);
3878		return (ISC_R_FAILURE);
3879	}
3880
3881	isc_buffer_init(&buf, secretbuf, sizeof(secretbuf));
3882	result = isc_base64_decodestring(cfg_obj_asstring(secretobj), &buf);
3883	if (result != ISC_R_SUCCESS) {
3884		cfg_obj_log(secretobj, logctx, ISC_LOG_ERROR, "bad secret '%s'",
3885			    isc_result_totext(result));
3886		return (result);
3887	}
3888
3889	algorithm = cfg_obj_asstring(algobj);
3890	for (i = 0; algorithms[i].name != NULL; i++) {
3891		len = strlen(algorithms[i].name);
3892		if (strncasecmp(algorithms[i].name, algorithm, len) == 0 &&
3893		    (algorithm[len] == '\0' ||
3894		     (algorithms[i].size != 0 && algorithm[len] == '-')))
3895		{
3896			break;
3897		}
3898	}
3899	if (algorithms[i].name == NULL) {
3900		cfg_obj_log(algobj, logctx, ISC_LOG_ERROR,
3901			    "unknown algorithm '%s'", algorithm);
3902		return (ISC_R_NOTFOUND);
3903	}
3904	if (algorithm[len] == '-') {
3905		uint16_t digestbits;
3906		result = isc_parse_uint16(&digestbits, algorithm + len + 1, 10);
3907		if (result == ISC_R_SUCCESS || result == ISC_R_RANGE) {
3908			if (result == ISC_R_RANGE ||
3909			    digestbits > algorithms[i].size)
3910			{
3911				cfg_obj_log(algobj, logctx, ISC_LOG_ERROR,
3912					    "key '%s' digest-bits too large "
3913					    "[%u..%u]",
3914					    keyname, algorithms[i].size / 2,
3915					    algorithms[i].size);
3916				return (ISC_R_RANGE);
3917			}
3918			if ((digestbits % 8) != 0) {
3919				cfg_obj_log(algobj, logctx, ISC_LOG_ERROR,
3920					    "key '%s' digest-bits not multiple"
3921					    " of 8",
3922					    keyname);
3923				return (ISC_R_RANGE);
3924			}
3925			/*
3926			 * Recommended minima for hmac algorithms.
3927			 */
3928			if ((digestbits < (algorithms[i].size / 2U) ||
3929			     (digestbits < 80U)))
3930			{
3931				cfg_obj_log(algobj, logctx, ISC_LOG_WARNING,
3932					    "key '%s' digest-bits too small "
3933					    "[<%u]",
3934					    keyname, algorithms[i].size / 2);
3935			}
3936		} else {
3937			cfg_obj_log(algobj, logctx, ISC_LOG_ERROR,
3938				    "key '%s': unable to parse digest-bits",
3939				    keyname);
3940			return (result);
3941		}
3942	}
3943	return (ISC_R_SUCCESS);
3944}
3945
3946static isc_result_t
3947fileexist(const cfg_obj_t *obj, isc_symtab_t *symtab, bool writeable,
3948	  isc_log_t *logctx) {
3949	isc_result_t result;
3950	isc_symvalue_t symvalue;
3951	unsigned int line;
3952	const char *file;
3953
3954	result = isc_symtab_lookup(symtab, cfg_obj_asstring(obj), 0, &symvalue);
3955	if (result == ISC_R_SUCCESS) {
3956		if (writeable) {
3957			file = cfg_obj_file(symvalue.as_cpointer);
3958			line = cfg_obj_line(symvalue.as_cpointer);
3959			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
3960				    "writeable file '%s': already in use: "
3961				    "%s:%u",
3962				    cfg_obj_asstring(obj), file, line);
3963			return (ISC_R_EXISTS);
3964		}
3965		result = isc_symtab_lookup(symtab, cfg_obj_asstring(obj), 2,
3966					   &symvalue);
3967		if (result == ISC_R_SUCCESS) {
3968			file = cfg_obj_file(symvalue.as_cpointer);
3969			line = cfg_obj_line(symvalue.as_cpointer);
3970			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
3971				    "writeable file '%s': already in use: "
3972				    "%s:%u",
3973				    cfg_obj_asstring(obj), file, line);
3974			return (ISC_R_EXISTS);
3975		}
3976		return (ISC_R_SUCCESS);
3977	}
3978
3979	symvalue.as_cpointer = obj;
3980	result = isc_symtab_define(symtab, cfg_obj_asstring(obj),
3981				   writeable ? 2 : 1, symvalue,
3982				   isc_symexists_reject);
3983	return (result);
3984}
3985
3986static isc_result_t
3987keydirexist(const cfg_obj_t *zcfg, const char *keydir, const char *kaspnamestr,
3988	    isc_symtab_t *symtab, isc_log_t *logctx, isc_mem_t *mctx) {
3989	isc_result_t result;
3990	isc_symvalue_t symvalue;
3991	char *symkey;
3992
3993	if (kaspnamestr == NULL || strcmp(kaspnamestr, "none") == 0) {
3994		return (ISC_R_SUCCESS);
3995	}
3996
3997	result = isc_symtab_lookup(symtab, keydir, 0, &symvalue);
3998	if (result == ISC_R_SUCCESS) {
3999		const cfg_obj_t *kasp = NULL;
4000		const cfg_obj_t *exist = symvalue.as_cpointer;
4001		const char *file = cfg_obj_file(exist);
4002		unsigned int line = cfg_obj_line(exist);
4003
4004		/*
4005		 * Having the same key-directory for the same zone is fine
4006		 * iff the zone is using the same policy, or has no policy.
4007		 */
4008		(void)cfg_map_get(cfg_tuple_get(exist, "options"),
4009				  "dnssec-policy", &kasp);
4010		if (kasp == NULL ||
4011		    strcmp(cfg_obj_asstring(kasp), "none") == 0 ||
4012		    strcmp(cfg_obj_asstring(kasp), kaspnamestr) == 0)
4013		{
4014			return (ISC_R_SUCCESS);
4015		}
4016
4017		cfg_obj_log(zcfg, logctx, ISC_LOG_ERROR,
4018			    "key-directory '%s' already in use by zone %s with "
4019			    "policy %s: %s:%u",
4020			    keydir,
4021			    cfg_obj_asstring(cfg_tuple_get(exist, "name")),
4022			    cfg_obj_asstring(kasp), file, line);
4023		return (ISC_R_EXISTS);
4024	}
4025
4026	/*
4027	 * Add the new zone plus key-directory.
4028	 */
4029	symkey = isc_mem_strdup(mctx, keydir);
4030	symvalue.as_cpointer = zcfg;
4031	result = isc_symtab_define(symtab, symkey, 2, symvalue,
4032				   isc_symexists_reject);
4033	return (result);
4034}
4035
4036/*
4037 * Check key list for duplicates key names and that the key names
4038 * are valid domain names as these keys are used for TSIG.
4039 *
4040 * Check the key contents for validity.
4041 */
4042static isc_result_t
4043check_keylist(const cfg_obj_t *keys, isc_symtab_t *symtab, isc_mem_t *mctx,
4044	      isc_log_t *logctx) {
4045	char namebuf[DNS_NAME_FORMATSIZE];
4046	dns_fixedname_t fname;
4047	dns_name_t *name;
4048	isc_result_t result = ISC_R_SUCCESS;
4049	isc_result_t tresult;
4050	const cfg_listelt_t *element;
4051
4052	name = dns_fixedname_initname(&fname);
4053	for (element = cfg_list_first(keys); element != NULL;
4054	     element = cfg_list_next(element))
4055	{
4056		const cfg_obj_t *key = cfg_listelt_value(element);
4057		const char *keyid = cfg_obj_asstring(cfg_map_getname(key));
4058		isc_symvalue_t symvalue;
4059		isc_buffer_t b;
4060		char *keyname;
4061
4062		isc_buffer_constinit(&b, keyid, strlen(keyid));
4063		isc_buffer_add(&b, strlen(keyid));
4064		tresult = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
4065		if (tresult != ISC_R_SUCCESS) {
4066			cfg_obj_log(key, logctx, ISC_LOG_ERROR,
4067				    "key '%s': bad key name", keyid);
4068			result = tresult;
4069			continue;
4070		}
4071		tresult = bind9_check_key(key, logctx);
4072		if (tresult != ISC_R_SUCCESS) {
4073			return (tresult);
4074		}
4075
4076		dns_name_format(name, namebuf, sizeof(namebuf));
4077		keyname = isc_mem_strdup(mctx, namebuf);
4078		symvalue.as_cpointer = key;
4079		tresult = isc_symtab_define(symtab, keyname, 1, symvalue,
4080					    isc_symexists_reject);
4081		if (tresult == ISC_R_EXISTS) {
4082			const char *file;
4083			unsigned int line;
4084
4085			RUNTIME_CHECK(isc_symtab_lookup(symtab, keyname, 1,
4086							&symvalue) ==
4087				      ISC_R_SUCCESS);
4088			file = cfg_obj_file(symvalue.as_cpointer);
4089			line = cfg_obj_line(symvalue.as_cpointer);
4090
4091			if (file == NULL) {
4092				file = "<unknown file>";
4093			}
4094			cfg_obj_log(key, logctx, ISC_LOG_ERROR,
4095				    "key '%s': already exists "
4096				    "previous definition: %s:%u",
4097				    keyid, file, line);
4098			isc_mem_free(mctx, keyname);
4099			result = tresult;
4100		} else if (tresult != ISC_R_SUCCESS) {
4101			isc_mem_free(mctx, keyname);
4102			return (tresult);
4103		}
4104	}
4105	return (result);
4106}
4107
4108/*
4109 * RNDC keys are not normalised unlike TSIG keys.
4110 *
4111 * 	"foo." is different to "foo".
4112 */
4113static bool
4114rndckey_exists(const cfg_obj_t *keylist, const char *keyname) {
4115	const cfg_listelt_t *element;
4116	const cfg_obj_t *obj;
4117	const char *str;
4118
4119	if (keylist == NULL) {
4120		return (false);
4121	}
4122
4123	for (element = cfg_list_first(keylist); element != NULL;
4124	     element = cfg_list_next(element))
4125	{
4126		obj = cfg_listelt_value(element);
4127		str = cfg_obj_asstring(cfg_map_getname(obj));
4128		if (!strcasecmp(str, keyname)) {
4129			return (true);
4130		}
4131	}
4132	return (false);
4133}
4134
4135static struct {
4136	const char *v4;
4137	const char *v6;
4138} sources[] = { { "transfer-source", "transfer-source-v6" },
4139		{ "notify-source", "notify-source-v6" },
4140		{ "parental-source", "parental-source-v6" },
4141		{ "query-source", "query-source-v6" },
4142		{ NULL, NULL } };
4143
4144static struct {
4145	const char *name;
4146	isc_result_t (*set)(dns_peer_t *peer, bool newval);
4147} bools[] = {
4148	{ "bogus", dns_peer_setbogus },
4149	{ "edns", dns_peer_setsupportedns },
4150	{ "provide-ixfr", dns_peer_setprovideixfr },
4151	{ "request-expire", dns_peer_setrequestexpire },
4152	{ "request-ixfr", dns_peer_setrequestixfr },
4153	{ "request-nsid", dns_peer_setrequestnsid },
4154	{ "send-cookie", dns_peer_setsendcookie },
4155	{ "tcp-keepalive", dns_peer_settcpkeepalive },
4156	{ "tcp-only", dns_peer_setforcetcp },
4157};
4158
4159static isc_result_t
4160check_servers(const cfg_obj_t *config, const cfg_obj_t *voptions,
4161	      isc_symtab_t *symtab, isc_mem_t *mctx, isc_log_t *logctx) {
4162	dns_fixedname_t fname;
4163	isc_result_t result = ISC_R_SUCCESS;
4164	isc_result_t tresult;
4165	const cfg_listelt_t *e1, *e2;
4166	const cfg_obj_t *v1, *v2, *keys;
4167	const cfg_obj_t *servers;
4168	isc_netaddr_t n1, n2;
4169	unsigned int p1, p2;
4170	const cfg_obj_t *obj;
4171	char buf[ISC_NETADDR_FORMATSIZE];
4172	char namebuf[DNS_NAME_FORMATSIZE];
4173	const char *xfr;
4174	const char *keyval;
4175	isc_buffer_t b;
4176	int source;
4177	dns_name_t *keyname;
4178
4179	servers = NULL;
4180	if (voptions != NULL) {
4181		(void)cfg_map_get(voptions, "server", &servers);
4182	}
4183	if (servers == NULL) {
4184		(void)cfg_map_get(config, "server", &servers);
4185	}
4186	if (servers == NULL) {
4187		return (ISC_R_SUCCESS);
4188	}
4189
4190	for (e1 = cfg_list_first(servers); e1 != NULL; e1 = cfg_list_next(e1)) {
4191		dns_peer_t *peer = NULL;
4192		size_t i;
4193		v1 = cfg_listelt_value(e1);
4194		cfg_obj_asnetprefix(cfg_map_getname(v1), &n1, &p1);
4195		/*
4196		 * Check that unused bits are zero.
4197		 */
4198		tresult = isc_netaddr_prefixok(&n1, p1);
4199		if (tresult != ISC_R_SUCCESS) {
4200			INSIST(tresult == ISC_R_FAILURE);
4201			isc_netaddr_format(&n1, buf, sizeof(buf));
4202			cfg_obj_log(v1, logctx, ISC_LOG_ERROR,
4203				    "server '%s/%u': invalid prefix "
4204				    "(extra bits specified)",
4205				    buf, p1);
4206			result = tresult;
4207		}
4208		source = 0;
4209		do {
4210			/*
4211			 * For a v6 server we can't specify a v4 source,
4212			 * and vice versa.
4213			 */
4214			obj = NULL;
4215			if (n1.family == AF_INET) {
4216				xfr = sources[source].v6;
4217			} else {
4218				xfr = sources[source].v4;
4219			}
4220			(void)cfg_map_get(v1, xfr, &obj);
4221			if (obj != NULL) {
4222				isc_netaddr_format(&n1, buf, sizeof(buf));
4223				cfg_obj_log(v1, logctx, ISC_LOG_ERROR,
4224					    "server '%s/%u': %s not legal", buf,
4225					    p1, xfr);
4226				result = ISC_R_FAILURE;
4227			}
4228
4229			/*
4230			 * Check that we aren't using the DNS
4231			 * listener port (i.e. 53, or whatever was set
4232			 * as "port" in options) as a source port.
4233			 */
4234			obj = NULL;
4235			if (n1.family == AF_INET) {
4236				xfr = sources[source].v4;
4237			} else {
4238				xfr = sources[source].v6;
4239			}
4240			(void)cfg_map_get(v1, xfr, &obj);
4241			if (obj != NULL) {
4242				const isc_sockaddr_t *sa =
4243					cfg_obj_assockaddr(obj);
4244				in_port_t port = isc_sockaddr_getport(sa);
4245				if (port == dnsport) {
4246					cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
4247						    "'%s' cannot specify the "
4248						    "DNS listener port (%d)",
4249						    xfr, port);
4250					result = ISC_R_FAILURE;
4251				}
4252			}
4253		} while (sources[++source].v4 != NULL);
4254		e2 = e1;
4255		while ((e2 = cfg_list_next(e2)) != NULL) {
4256			v2 = cfg_listelt_value(e2);
4257			cfg_obj_asnetprefix(cfg_map_getname(v2), &n2, &p2);
4258			if (p1 == p2 && isc_netaddr_equal(&n1, &n2)) {
4259				const char *file = cfg_obj_file(v1);
4260				unsigned int line = cfg_obj_line(v1);
4261
4262				if (file == NULL) {
4263					file = "<unknown file>";
4264				}
4265
4266				isc_netaddr_format(&n2, buf, sizeof(buf));
4267				cfg_obj_log(v2, logctx, ISC_LOG_ERROR,
4268					    "server '%s/%u': already exists "
4269					    "previous definition: %s:%u",
4270					    buf, p2, file, line);
4271				result = ISC_R_FAILURE;
4272			}
4273		}
4274		keys = NULL;
4275		cfg_map_get(v1, "keys", &keys);
4276		if (keys != NULL) {
4277			/*
4278			 * Normalize key name.
4279			 */
4280			keyval = cfg_obj_asstring(keys);
4281			isc_buffer_constinit(&b, keyval, strlen(keyval));
4282			isc_buffer_add(&b, strlen(keyval));
4283			keyname = dns_fixedname_initname(&fname);
4284			tresult = dns_name_fromtext(keyname, &b, dns_rootname,
4285						    0, NULL);
4286			if (tresult != ISC_R_SUCCESS) {
4287				cfg_obj_log(keys, logctx, ISC_LOG_ERROR,
4288					    "bad key name '%s'", keyval);
4289				result = ISC_R_FAILURE;
4290				continue;
4291			}
4292			dns_name_format(keyname, namebuf, sizeof(namebuf));
4293			tresult = isc_symtab_lookup(symtab, namebuf, 1, NULL);
4294			if (tresult != ISC_R_SUCCESS) {
4295				cfg_obj_log(keys, logctx, ISC_LOG_ERROR,
4296					    "unknown key '%s'", keyval);
4297				result = ISC_R_FAILURE;
4298			}
4299		}
4300		(void)dns_peer_newprefix(mctx, &n1, p1, &peer);
4301		for (i = 0; i < ARRAY_SIZE(bools); i++) {
4302			const cfg_obj_t *opt = NULL;
4303			cfg_map_get(v1, bools[i].name, &opt);
4304			if (opt != NULL) {
4305				tresult = (bools[i].set)(
4306					peer, cfg_obj_asboolean(opt));
4307				if (tresult != ISC_R_SUCCESS) {
4308					cfg_obj_log(opt, logctx, ISC_LOG_ERROR,
4309						    "setting server option "
4310						    "'%s' failed: %s",
4311						    bools[i].name,
4312						    isc_result_totext(tresult));
4313					result = ISC_R_FAILURE;
4314				}
4315			}
4316		}
4317		dns_peer_detach(&peer);
4318	}
4319	return (result);
4320}
4321
4322#define ROOT_KSK_STATIC	 0x01
4323#define ROOT_KSK_MANAGED 0x02
4324#define ROOT_KSK_ANY	 0x03
4325#define ROOT_KSK_2010	 0x04
4326#define ROOT_KSK_2017	 0x08
4327
4328static isc_result_t
4329check_trust_anchor(const cfg_obj_t *key, bool managed, unsigned int *flagsp,
4330		   isc_log_t *logctx) {
4331	const char *str = NULL, *namestr = NULL;
4332	dns_fixedname_t fkeyname;
4333	dns_name_t *keyname = NULL;
4334	isc_buffer_t b;
4335	isc_region_t r;
4336	isc_result_t result = ISC_R_SUCCESS;
4337	isc_result_t tresult;
4338	uint32_t rdata1, rdata2, rdata3;
4339	unsigned char data[4096];
4340	const char *atstr = NULL;
4341	enum {
4342		INIT_DNSKEY,
4343		STATIC_DNSKEY,
4344		INIT_DS,
4345		STATIC_DS,
4346		TRUSTED
4347	} anchortype;
4348
4349	/*
4350	 * The 2010 and 2017 IANA root keys - these are used below
4351	 * to check the contents of trusted, initial and
4352	 * static trust anchor configurations.
4353	 */
4354	static const unsigned char root_ksk_2010[] = {
4355		0x03, 0x01, 0x00, 0x01, 0xa8, 0x00, 0x20, 0xa9, 0x55, 0x66,
4356		0xba, 0x42, 0xe8, 0x86, 0xbb, 0x80, 0x4c, 0xda, 0x84, 0xe4,
4357		0x7e, 0xf5, 0x6d, 0xbd, 0x7a, 0xec, 0x61, 0x26, 0x15, 0x55,
4358		0x2c, 0xec, 0x90, 0x6d, 0x21, 0x16, 0xd0, 0xef, 0x20, 0x70,
4359		0x28, 0xc5, 0x15, 0x54, 0x14, 0x4d, 0xfe, 0xaf, 0xe7, 0xc7,
4360		0xcb, 0x8f, 0x00, 0x5d, 0xd1, 0x82, 0x34, 0x13, 0x3a, 0xc0,
4361		0x71, 0x0a, 0x81, 0x18, 0x2c, 0xe1, 0xfd, 0x14, 0xad, 0x22,
4362		0x83, 0xbc, 0x83, 0x43, 0x5f, 0x9d, 0xf2, 0xf6, 0x31, 0x32,
4363		0x51, 0x93, 0x1a, 0x17, 0x6d, 0xf0, 0xda, 0x51, 0xe5, 0x4f,
4364		0x42, 0xe6, 0x04, 0x86, 0x0d, 0xfb, 0x35, 0x95, 0x80, 0x25,
4365		0x0f, 0x55, 0x9c, 0xc5, 0x43, 0xc4, 0xff, 0xd5, 0x1c, 0xbe,
4366		0x3d, 0xe8, 0xcf, 0xd0, 0x67, 0x19, 0x23, 0x7f, 0x9f, 0xc4,
4367		0x7e, 0xe7, 0x29, 0xda, 0x06, 0x83, 0x5f, 0xa4, 0x52, 0xe8,
4368		0x25, 0xe9, 0xa1, 0x8e, 0xbc, 0x2e, 0xcb, 0xcf, 0x56, 0x34,
4369		0x74, 0x65, 0x2c, 0x33, 0xcf, 0x56, 0xa9, 0x03, 0x3b, 0xcd,
4370		0xf5, 0xd9, 0x73, 0x12, 0x17, 0x97, 0xec, 0x80, 0x89, 0x04,
4371		0x1b, 0x6e, 0x03, 0xa1, 0xb7, 0x2d, 0x0a, 0x73, 0x5b, 0x98,
4372		0x4e, 0x03, 0x68, 0x73, 0x09, 0x33, 0x23, 0x24, 0xf2, 0x7c,
4373		0x2d, 0xba, 0x85, 0xe9, 0xdb, 0x15, 0xe8, 0x3a, 0x01, 0x43,
4374		0x38, 0x2e, 0x97, 0x4b, 0x06, 0x21, 0xc1, 0x8e, 0x62, 0x5e,
4375		0xce, 0xc9, 0x07, 0x57, 0x7d, 0x9e, 0x7b, 0xad, 0xe9, 0x52,
4376		0x41, 0xa8, 0x1e, 0xbb, 0xe8, 0xa9, 0x01, 0xd4, 0xd3, 0x27,
4377		0x6e, 0x40, 0xb1, 0x14, 0xc0, 0xa2, 0xe6, 0xfc, 0x38, 0xd1,
4378		0x9c, 0x2e, 0x6a, 0xab, 0x02, 0x64, 0x4b, 0x28, 0x13, 0xf5,
4379		0x75, 0xfc, 0x21, 0x60, 0x1e, 0x0d, 0xee, 0x49, 0xcd, 0x9e,
4380		0xe9, 0x6a, 0x43, 0x10, 0x3e, 0x52, 0x4d, 0x62, 0x87, 0x3d
4381	};
4382	static const unsigned char root_ksk_2017[] = {
4383		0x03, 0x01, 0x00, 0x01, 0xac, 0xff, 0xb4, 0x09, 0xbc, 0xc9,
4384		0x39, 0xf8, 0x31, 0xf7, 0xa1, 0xe5, 0xec, 0x88, 0xf7, 0xa5,
4385		0x92, 0x55, 0xec, 0x53, 0x04, 0x0b, 0xe4, 0x32, 0x02, 0x73,
4386		0x90, 0xa4, 0xce, 0x89, 0x6d, 0x6f, 0x90, 0x86, 0xf3, 0xc5,
4387		0xe1, 0x77, 0xfb, 0xfe, 0x11, 0x81, 0x63, 0xaa, 0xec, 0x7a,
4388		0xf1, 0x46, 0x2c, 0x47, 0x94, 0x59, 0x44, 0xc4, 0xe2, 0xc0,
4389		0x26, 0xbe, 0x5e, 0x98, 0xbb, 0xcd, 0xed, 0x25, 0x97, 0x82,
4390		0x72, 0xe1, 0xe3, 0xe0, 0x79, 0xc5, 0x09, 0x4d, 0x57, 0x3f,
4391		0x0e, 0x83, 0xc9, 0x2f, 0x02, 0xb3, 0x2d, 0x35, 0x13, 0xb1,
4392		0x55, 0x0b, 0x82, 0x69, 0x29, 0xc8, 0x0d, 0xd0, 0xf9, 0x2c,
4393		0xac, 0x96, 0x6d, 0x17, 0x76, 0x9f, 0xd5, 0x86, 0x7b, 0x64,
4394		0x7c, 0x3f, 0x38, 0x02, 0x9a, 0xbd, 0xc4, 0x81, 0x52, 0xeb,
4395		0x8f, 0x20, 0x71, 0x59, 0xec, 0xc5, 0xd2, 0x32, 0xc7, 0xc1,
4396		0x53, 0x7c, 0x79, 0xf4, 0xb7, 0xac, 0x28, 0xff, 0x11, 0x68,
4397		0x2f, 0x21, 0x68, 0x1b, 0xf6, 0xd6, 0xab, 0xa5, 0x55, 0x03,
4398		0x2b, 0xf6, 0xf9, 0xf0, 0x36, 0xbe, 0xb2, 0xaa, 0xa5, 0xb3,
4399		0x77, 0x8d, 0x6e, 0xeb, 0xfb, 0xa6, 0xbf, 0x9e, 0xa1, 0x91,
4400		0xbe, 0x4a, 0xb0, 0xca, 0xea, 0x75, 0x9e, 0x2f, 0x77, 0x3a,
4401		0x1f, 0x90, 0x29, 0xc7, 0x3e, 0xcb, 0x8d, 0x57, 0x35, 0xb9,
4402		0x32, 0x1d, 0xb0, 0x85, 0xf1, 0xb8, 0xe2, 0xd8, 0x03, 0x8f,
4403		0xe2, 0x94, 0x19, 0x92, 0x54, 0x8c, 0xee, 0x0d, 0x67, 0xdd,
4404		0x45, 0x47, 0xe1, 0x1d, 0xd6, 0x3a, 0xf9, 0xc9, 0xfc, 0x1c,
4405		0x54, 0x66, 0xfb, 0x68, 0x4c, 0xf0, 0x09, 0xd7, 0x19, 0x7c,
4406		0x2c, 0xf7, 0x9e, 0x79, 0x2a, 0xb5, 0x01, 0xe6, 0xa8, 0xa1,
4407		0xca, 0x51, 0x9a, 0xf2, 0xcb, 0x9b, 0x5f, 0x63, 0x67, 0xe9,
4408		0x4c, 0x0d, 0x47, 0x50, 0x24, 0x51, 0x35, 0x7b, 0xe1, 0xb5
4409	};
4410	static const unsigned char root_ds_1_2017[] = {
4411		0xae, 0x1e, 0xa5, 0xb9, 0x74, 0xd4, 0xc8, 0x58, 0xb7, 0x40,
4412		0xbd, 0x03, 0xe3, 0xce, 0xd7, 0xeb, 0xfc, 0xbd, 0x17, 0x24
4413	};
4414	static const unsigned char root_ds_2_2017[] = {
4415		0xe0, 0x6d, 0x44, 0xb8, 0x0b, 0x8f, 0x1d, 0x39,
4416		0xa9, 0x5c, 0x0b, 0x0d, 0x7c, 0x65, 0xd0, 0x84,
4417		0x58, 0xe8, 0x80, 0x40, 0x9b, 0xbc, 0x68, 0x34,
4418		0x57, 0x10, 0x42, 0x37, 0xc7, 0xf8, 0xec, 0x8D
4419	};
4420
4421	/* if DNSKEY, flags; if DS, key tag */
4422	rdata1 = cfg_obj_asuint32(cfg_tuple_get(key, "rdata1"));
4423
4424	/* if DNSKEY, protocol; if DS, algorithm */
4425	rdata2 = cfg_obj_asuint32(cfg_tuple_get(key, "rdata2"));
4426
4427	/* if DNSKEY, algorithm; if DS, digest type */
4428	rdata3 = cfg_obj_asuint32(cfg_tuple_get(key, "rdata3"));
4429
4430	namestr = cfg_obj_asstring(cfg_tuple_get(key, "name"));
4431
4432	keyname = dns_fixedname_initname(&fkeyname);
4433	isc_buffer_constinit(&b, namestr, strlen(namestr));
4434	isc_buffer_add(&b, strlen(namestr));
4435	result = dns_name_fromtext(keyname, &b, dns_rootname, 0, NULL);
4436	if (result != ISC_R_SUCCESS) {
4437		cfg_obj_log(key, logctx, ISC_LOG_WARNING, "bad key name: %s\n",
4438			    isc_result_totext(result));
4439		result = ISC_R_FAILURE;
4440	}
4441
4442	if (managed) {
4443		atstr = cfg_obj_asstring(cfg_tuple_get(key, "anchortype"));
4444
4445		if (strcasecmp(atstr, "static-key") == 0) {
4446			managed = false;
4447			anchortype = STATIC_DNSKEY;
4448		} else if (strcasecmp(atstr, "static-ds") == 0) {
4449			managed = false;
4450			anchortype = STATIC_DS;
4451		} else if (strcasecmp(atstr, "initial-key") == 0) {
4452			anchortype = INIT_DNSKEY;
4453		} else if (strcasecmp(atstr, "initial-ds") == 0) {
4454			anchortype = INIT_DS;
4455		} else {
4456			cfg_obj_log(key, logctx, ISC_LOG_ERROR,
4457				    "key '%s': "
4458				    "invalid initialization method '%s'",
4459				    namestr, atstr);
4460			result = ISC_R_FAILURE;
4461
4462			/*
4463			 * We can't interpret the trust anchor, so
4464			 * we skip all other checks.
4465			 */
4466			goto cleanup;
4467		}
4468	} else {
4469		atstr = "trusted-key";
4470		anchortype = TRUSTED;
4471	}
4472
4473	switch (anchortype) {
4474	case INIT_DNSKEY:
4475	case STATIC_DNSKEY:
4476	case TRUSTED:
4477		if (rdata1 > 0xffff) {
4478			cfg_obj_log(key, logctx, ISC_LOG_ERROR,
4479				    "flags too big: %u", rdata1);
4480			result = ISC_R_RANGE;
4481		}
4482		if (rdata1 & DNS_KEYFLAG_REVOKE) {
4483			cfg_obj_log(key, logctx, ISC_LOG_WARNING,
4484				    "key flags revoke bit set");
4485		}
4486		if (rdata2 > 0xff) {
4487			cfg_obj_log(key, logctx, ISC_LOG_ERROR,
4488				    "protocol too big: %u", rdata2);
4489			result = ISC_R_RANGE;
4490		}
4491		if (rdata3 > 0xff) {
4492			cfg_obj_log(key, logctx, ISC_LOG_ERROR,
4493				    "algorithm too big: %u\n", rdata3);
4494			result = ISC_R_RANGE;
4495		}
4496
4497		isc_buffer_init(&b, data, sizeof(data));
4498
4499		str = cfg_obj_asstring(cfg_tuple_get(key, "data"));
4500		tresult = isc_base64_decodestring(str, &b);
4501
4502		if (tresult != ISC_R_SUCCESS) {
4503			cfg_obj_log(key, logctx, ISC_LOG_ERROR, "%s",
4504				    isc_result_totext(tresult));
4505			result = ISC_R_FAILURE;
4506		} else {
4507			isc_buffer_usedregion(&b, &r);
4508
4509			if ((rdata3 == DST_ALG_RSASHA1) && r.length > 1 &&
4510			    r.base[0] == 1 && r.base[1] == 3)
4511			{
4512				cfg_obj_log(key, logctx, ISC_LOG_WARNING,
4513					    "%s '%s' has a weak exponent",
4514					    atstr, namestr);
4515			}
4516		}
4517
4518		if (result == ISC_R_SUCCESS &&
4519		    dns_name_equal(keyname, dns_rootname))
4520		{
4521			/*
4522			 * Flag any use of a root key, regardless of content.
4523			 */
4524			*flagsp |= (managed ? ROOT_KSK_MANAGED
4525					    : ROOT_KSK_STATIC);
4526
4527			if (rdata1 == 257 && rdata2 == 3 && rdata3 == 8 &&
4528			    (isc_buffer_usedlength(&b) ==
4529			     sizeof(root_ksk_2010)) &&
4530			    memcmp(data, root_ksk_2010,
4531				   sizeof(root_ksk_2010)) == 0)
4532			{
4533				*flagsp |= ROOT_KSK_2010;
4534			}
4535
4536			if (rdata1 == 257 && rdata2 == 3 && rdata3 == 8 &&
4537			    (isc_buffer_usedlength(&b) ==
4538			     sizeof(root_ksk_2017)) &&
4539			    memcmp(data, root_ksk_2017,
4540				   sizeof(root_ksk_2017)) == 0)
4541			{
4542				*flagsp |= ROOT_KSK_2017;
4543			}
4544		}
4545		break;
4546
4547	case INIT_DS:
4548	case STATIC_DS:
4549		if (rdata1 > 0xffff) {
4550			cfg_obj_log(key, logctx, ISC_LOG_ERROR,
4551				    "key tag too big: %u", rdata1);
4552			result = ISC_R_RANGE;
4553		}
4554		if (rdata2 > 0xff) {
4555			cfg_obj_log(key, logctx, ISC_LOG_ERROR,
4556				    "algorithm too big: %u\n", rdata2);
4557			result = ISC_R_RANGE;
4558		}
4559		if (rdata3 > 0xff) {
4560			cfg_obj_log(key, logctx, ISC_LOG_ERROR,
4561				    "digest type too big: %u", rdata3);
4562			result = ISC_R_RANGE;
4563		}
4564
4565		isc_buffer_init(&b, data, sizeof(data));
4566
4567		str = cfg_obj_asstring(cfg_tuple_get(key, "data"));
4568		tresult = isc_hex_decodestring(str, &b);
4569
4570		if (tresult != ISC_R_SUCCESS) {
4571			cfg_obj_log(key, logctx, ISC_LOG_ERROR, "%s",
4572				    isc_result_totext(tresult));
4573			result = ISC_R_FAILURE;
4574		}
4575		if (result == ISC_R_SUCCESS &&
4576		    dns_name_equal(keyname, dns_rootname))
4577		{
4578			/*
4579			 * Flag any use of a root key, regardless of content.
4580			 */
4581			*flagsp |= (managed ? ROOT_KSK_MANAGED
4582					    : ROOT_KSK_STATIC);
4583
4584			if (rdata1 == 20326 && rdata2 == 8 && rdata3 == 1 &&
4585			    (isc_buffer_usedlength(&b) ==
4586			     sizeof(root_ds_1_2017)) &&
4587			    memcmp(data, root_ds_1_2017,
4588				   sizeof(root_ds_1_2017)) == 0)
4589			{
4590				*flagsp |= ROOT_KSK_2017;
4591			}
4592
4593			if (rdata1 == 20326 && rdata2 == 8 && rdata3 == 2 &&
4594			    (isc_buffer_usedlength(&b) ==
4595			     sizeof(root_ds_2_2017)) &&
4596			    memcmp(data, root_ds_2_2017,
4597				   sizeof(root_ds_2_2017)) == 0)
4598			{
4599				*flagsp |= ROOT_KSK_2017;
4600			}
4601		}
4602		break;
4603	}
4604
4605cleanup:
4606	return (result);
4607}
4608
4609static isc_result_t
4610record_static_keys(isc_symtab_t *symtab, isc_mem_t *mctx,
4611		   const cfg_obj_t *keylist, isc_log_t *logctx,
4612		   bool autovalidation) {
4613	isc_result_t result, ret = ISC_R_SUCCESS;
4614	const cfg_listelt_t *elt;
4615	dns_fixedname_t fixed;
4616	dns_name_t *name;
4617	char namebuf[DNS_NAME_FORMATSIZE], *p = NULL;
4618
4619	name = dns_fixedname_initname(&fixed);
4620
4621	for (elt = cfg_list_first(keylist); elt != NULL;
4622	     elt = cfg_list_next(elt))
4623	{
4624		const char *initmethod;
4625		const cfg_obj_t *init = NULL;
4626		const cfg_obj_t *obj = cfg_listelt_value(elt);
4627		const char *str = cfg_obj_asstring(cfg_tuple_get(obj, "name"));
4628		isc_symvalue_t symvalue;
4629
4630		result = dns_name_fromstring(name, str, 0, NULL);
4631		if (result != ISC_R_SUCCESS) {
4632			continue;
4633		}
4634
4635		init = cfg_tuple_get(obj, "anchortype");
4636		if (!cfg_obj_isvoid(init)) {
4637			initmethod = cfg_obj_asstring(init);
4638			if (strcasecmp(initmethod, "initial-key") == 0) {
4639				/* initializing key, skip it */
4640				continue;
4641			}
4642			if (strcasecmp(initmethod, "initial-ds") == 0) {
4643				/* initializing key, skip it */
4644				continue;
4645			}
4646		}
4647
4648		dns_name_format(name, namebuf, sizeof(namebuf));
4649		symvalue.as_cpointer = obj;
4650		p = isc_mem_strdup(mctx, namebuf);
4651		result = isc_symtab_define(symtab, p, 1, symvalue,
4652					   isc_symexists_reject);
4653		if (result == ISC_R_EXISTS) {
4654			isc_mem_free(mctx, p);
4655		} else if (result != ISC_R_SUCCESS) {
4656			isc_mem_free(mctx, p);
4657			ret = result;
4658			continue;
4659		}
4660
4661		if (autovalidation && dns_name_equal(name, dns_rootname)) {
4662			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
4663				    "static trust anchor for root zone "
4664				    "cannot be used with "
4665				    "'dnssec-validation auto'.");
4666			ret = ISC_R_FAILURE;
4667			continue;
4668		}
4669	}
4670
4671	return (ret);
4672}
4673
4674static isc_result_t
4675check_initializing_keys(isc_symtab_t *symtab, const cfg_obj_t *keylist,
4676			isc_log_t *logctx) {
4677	isc_result_t result, ret = ISC_R_SUCCESS;
4678	const cfg_listelt_t *elt;
4679	dns_fixedname_t fixed;
4680	dns_name_t *name;
4681	char namebuf[DNS_NAME_FORMATSIZE];
4682
4683	name = dns_fixedname_initname(&fixed);
4684
4685	for (elt = cfg_list_first(keylist); elt != NULL;
4686	     elt = cfg_list_next(elt))
4687	{
4688		const cfg_obj_t *obj = cfg_listelt_value(elt);
4689		const cfg_obj_t *init = NULL;
4690		const char *str;
4691		isc_symvalue_t symvalue;
4692
4693		init = cfg_tuple_get(obj, "anchortype");
4694		if (cfg_obj_isvoid(init) ||
4695		    strcasecmp(cfg_obj_asstring(init), "static-key") == 0 ||
4696		    strcasecmp(cfg_obj_asstring(init), "static-ds") == 0)
4697		{
4698			/* static key, skip it */
4699			continue;
4700		}
4701
4702		str = cfg_obj_asstring(cfg_tuple_get(obj, "name"));
4703		result = dns_name_fromstring(name, str, 0, NULL);
4704		if (result != ISC_R_SUCCESS) {
4705			continue;
4706		}
4707
4708		dns_name_format(name, namebuf, sizeof(namebuf));
4709		result = isc_symtab_lookup(symtab, namebuf, 1, &symvalue);
4710		if (result == ISC_R_SUCCESS) {
4711			const char *file = cfg_obj_file(symvalue.as_cpointer);
4712			unsigned int line = cfg_obj_line(symvalue.as_cpointer);
4713			if (file == NULL) {
4714				file = "<unknown file>";
4715			}
4716			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
4717				    "static and initializing keys "
4718				    "cannot be used for the "
4719				    "same domain. "
4720				    "static key defined at "
4721				    "%s:%u",
4722				    file, line);
4723
4724			ret = ISC_R_FAILURE;
4725		}
4726	}
4727
4728	return (ret);
4729}
4730
4731static isc_result_t
4732record_ds_keys(isc_symtab_t *symtab, isc_mem_t *mctx,
4733	       const cfg_obj_t *keylist) {
4734	isc_result_t result, ret = ISC_R_SUCCESS;
4735	const cfg_listelt_t *elt;
4736	dns_fixedname_t fixed;
4737	dns_name_t *name;
4738	char namebuf[DNS_NAME_FORMATSIZE], *p = NULL;
4739
4740	name = dns_fixedname_initname(&fixed);
4741
4742	for (elt = cfg_list_first(keylist); elt != NULL;
4743	     elt = cfg_list_next(elt))
4744	{
4745		const char *initmethod;
4746		const cfg_obj_t *init = NULL;
4747		const cfg_obj_t *obj = cfg_listelt_value(elt);
4748		const char *str = cfg_obj_asstring(cfg_tuple_get(obj, "name"));
4749		isc_symvalue_t symvalue;
4750
4751		result = dns_name_fromstring(name, str, 0, NULL);
4752		if (result != ISC_R_SUCCESS) {
4753			continue;
4754		}
4755
4756		init = cfg_tuple_get(obj, "anchortype");
4757		if (!cfg_obj_isvoid(init)) {
4758			initmethod = cfg_obj_asstring(init);
4759			if (strcasecmp(initmethod, "initial-key") == 0 ||
4760			    strcasecmp(initmethod, "static-key") == 0)
4761			{
4762				/* Key-style key, skip it */
4763				continue;
4764			}
4765		}
4766
4767		dns_name_format(name, namebuf, sizeof(namebuf));
4768		symvalue.as_cpointer = obj;
4769		p = isc_mem_strdup(mctx, namebuf);
4770		result = isc_symtab_define(symtab, p, 1, symvalue,
4771					   isc_symexists_reject);
4772		if (result == ISC_R_EXISTS) {
4773			isc_mem_free(mctx, p);
4774		} else if (result != ISC_R_SUCCESS) {
4775			isc_mem_free(mctx, p);
4776			ret = result;
4777			continue;
4778		}
4779	}
4780
4781	return (ret);
4782}
4783
4784/*
4785 * Check for conflicts between static and initialiizing keys.
4786 */
4787static isc_result_t
4788check_ta_conflicts(const cfg_obj_t *global_ta, const cfg_obj_t *view_ta,
4789		   const cfg_obj_t *global_tkeys, const cfg_obj_t *view_tkeys,
4790		   bool autovalidation, isc_mem_t *mctx, isc_log_t *logctx) {
4791	isc_result_t result, tresult;
4792	const cfg_listelt_t *elt = NULL;
4793	const cfg_obj_t *keylist = NULL;
4794	isc_symtab_t *statictab = NULL, *dstab = NULL;
4795
4796	result = isc_symtab_create(mctx, 100, freekey, mctx, false, &statictab);
4797	if (result != ISC_R_SUCCESS) {
4798		goto cleanup;
4799	}
4800
4801	result = isc_symtab_create(mctx, 100, freekey, mctx, false, &dstab);
4802	if (result != ISC_R_SUCCESS) {
4803		goto cleanup;
4804	}
4805
4806	/*
4807	 * First we record all the static keys (i.e., old-style
4808	 * trusted-keys and trust-anchors configured with "static-key"),
4809	 * and all the DS-style trust anchors.
4810	 */
4811	for (elt = cfg_list_first(global_ta); elt != NULL;
4812	     elt = cfg_list_next(elt))
4813	{
4814		keylist = cfg_listelt_value(elt);
4815		tresult = record_static_keys(statictab, mctx, keylist, logctx,
4816					     autovalidation);
4817		if (result == ISC_R_SUCCESS) {
4818			result = tresult;
4819		}
4820
4821		tresult = record_ds_keys(dstab, mctx, keylist);
4822		if (result == ISC_R_SUCCESS) {
4823			result = tresult;
4824		}
4825	}
4826
4827	for (elt = cfg_list_first(view_ta); elt != NULL;
4828	     elt = cfg_list_next(elt))
4829	{
4830		keylist = cfg_listelt_value(elt);
4831		tresult = record_static_keys(statictab, mctx, keylist, logctx,
4832					     autovalidation);
4833		if (result == ISC_R_SUCCESS) {
4834			result = tresult;
4835		}
4836
4837		tresult = record_ds_keys(dstab, mctx, keylist);
4838		if (result == ISC_R_SUCCESS) {
4839			result = tresult;
4840		}
4841	}
4842
4843	for (elt = cfg_list_first(global_tkeys); elt != NULL;
4844	     elt = cfg_list_next(elt))
4845	{
4846		keylist = cfg_listelt_value(elt);
4847		tresult = record_static_keys(statictab, mctx, keylist, logctx,
4848					     autovalidation);
4849		if (result == ISC_R_SUCCESS) {
4850			result = tresult;
4851		}
4852	}
4853
4854	for (elt = cfg_list_first(view_tkeys); elt != NULL;
4855	     elt = cfg_list_next(elt))
4856	{
4857		keylist = cfg_listelt_value(elt);
4858		tresult = record_static_keys(statictab, mctx, keylist, logctx,
4859					     autovalidation);
4860		if (result == ISC_R_SUCCESS) {
4861			result = tresult;
4862		}
4863	}
4864
4865	/*
4866	 * Next, ensure that there's no conflict between the
4867	 * static keys and the trust-anchors configured with "initial-key".
4868	 */
4869	for (elt = cfg_list_first(global_ta); elt != NULL;
4870	     elt = cfg_list_next(elt))
4871	{
4872		keylist = cfg_listelt_value(elt);
4873		tresult = check_initializing_keys(statictab, keylist, logctx);
4874		if (result == ISC_R_SUCCESS) {
4875			result = tresult;
4876		}
4877	}
4878
4879	for (elt = cfg_list_first(view_ta); elt != NULL;
4880	     elt = cfg_list_next(elt))
4881	{
4882		keylist = cfg_listelt_value(elt);
4883		tresult = check_initializing_keys(statictab, keylist, logctx);
4884		if (result == ISC_R_SUCCESS) {
4885			result = tresult;
4886		}
4887	}
4888
4889cleanup:
4890	if (statictab != NULL) {
4891		isc_symtab_destroy(&statictab);
4892	}
4893	if (dstab != NULL) {
4894		isc_symtab_destroy(&dstab);
4895	}
4896	return (result);
4897}
4898
4899typedef enum { special_zonetype_rpz, special_zonetype_catz } special_zonetype_t;
4900
4901static isc_result_t
4902check_rpz_catz(const char *rpz_catz, const cfg_obj_t *rpz_obj,
4903	       const char *viewname, isc_symtab_t *symtab, isc_log_t *logctx,
4904	       special_zonetype_t specialzonetype) {
4905	const cfg_listelt_t *element;
4906	const cfg_obj_t *obj, *nameobj, *zoneobj;
4907	const char *zonename, *zonetype;
4908	const char *forview = " for view ";
4909	isc_symvalue_t value;
4910	isc_result_t result, tresult;
4911	dns_fixedname_t fixed;
4912	dns_name_t *name;
4913	char namebuf[DNS_NAME_FORMATSIZE];
4914	unsigned int num_zones = 0;
4915
4916	if (viewname == NULL) {
4917		viewname = "";
4918		forview = "";
4919	}
4920	result = ISC_R_SUCCESS;
4921
4922	name = dns_fixedname_initname(&fixed);
4923	obj = cfg_tuple_get(rpz_obj, "zone list");
4924
4925	for (element = cfg_list_first(obj); element != NULL;
4926	     element = cfg_list_next(element))
4927	{
4928		obj = cfg_listelt_value(element);
4929		nameobj = cfg_tuple_get(obj, "zone name");
4930		zonename = cfg_obj_asstring(nameobj);
4931		zonetype = "";
4932
4933		if (specialzonetype == special_zonetype_rpz) {
4934			if (++num_zones > 64) {
4935				cfg_obj_log(nameobj, logctx, ISC_LOG_ERROR,
4936					    "more than 64 response policy "
4937					    "zones in view '%s'",
4938					    viewname);
4939				return (ISC_R_FAILURE);
4940			}
4941		}
4942
4943		tresult = dns_name_fromstring(name, zonename, 0, NULL);
4944		if (tresult != ISC_R_SUCCESS) {
4945			cfg_obj_log(nameobj, logctx, ISC_LOG_ERROR,
4946				    "bad domain name '%s'", zonename);
4947			if (result == ISC_R_SUCCESS) {
4948				result = tresult;
4949			}
4950			continue;
4951		}
4952		dns_name_format(name, namebuf, sizeof(namebuf));
4953		tresult = isc_symtab_lookup(symtab, namebuf, 3, &value);
4954		if (tresult == ISC_R_SUCCESS) {
4955			obj = NULL;
4956			zoneobj = value.as_cpointer;
4957			if (zoneobj != NULL && cfg_obj_istuple(zoneobj)) {
4958				zoneobj = cfg_tuple_get(zoneobj, "options");
4959			}
4960			if (zoneobj != NULL && cfg_obj_ismap(zoneobj)) {
4961				(void)cfg_map_get(zoneobj, "type", &obj);
4962			}
4963			if (obj != NULL) {
4964				zonetype = cfg_obj_asstring(obj);
4965			}
4966		}
4967		if (strcasecmp(zonetype, "primary") != 0 &&
4968		    strcasecmp(zonetype, "master") != 0 &&
4969		    strcasecmp(zonetype, "secondary") != 0 &&
4970		    strcasecmp(zonetype, "slave") != 0)
4971		{
4972			cfg_obj_log(nameobj, logctx, ISC_LOG_ERROR,
4973				    "%s '%s'%s%s is not a primary or secondary "
4974				    "zone",
4975				    rpz_catz, zonename, forview, viewname);
4976			if (result == ISC_R_SUCCESS) {
4977				result = ISC_R_FAILURE;
4978			}
4979		}
4980	}
4981	return (result);
4982}
4983
4984static isc_result_t
4985check_catz(const cfg_obj_t *catz_obj, const char *viewname, isc_mem_t *mctx,
4986	   isc_log_t *logctx) {
4987	const cfg_listelt_t *element;
4988	const cfg_obj_t *obj, *nameobj, *primariesobj;
4989	const char *zonename;
4990	const char *forview = " for view ";
4991	isc_result_t result, tresult;
4992	isc_symtab_t *symtab = NULL;
4993	dns_fixedname_t fixed;
4994	dns_name_t *name = dns_fixedname_initname(&fixed);
4995
4996	if (viewname == NULL) {
4997		viewname = "";
4998		forview = "";
4999	}
5000
5001	result = isc_symtab_create(mctx, 100, freekey, mctx, false, &symtab);
5002	if (result != ISC_R_SUCCESS) {
5003		return (result);
5004	}
5005
5006	obj = cfg_tuple_get(catz_obj, "zone list");
5007
5008	for (element = cfg_list_first(obj); element != NULL;
5009	     element = cfg_list_next(element))
5010	{
5011		char namebuf[DNS_NAME_FORMATSIZE];
5012
5013		obj = cfg_listelt_value(element);
5014		nameobj = cfg_tuple_get(obj, "zone name");
5015		zonename = cfg_obj_asstring(nameobj);
5016
5017		tresult = dns_name_fromstring(name, zonename, 0, NULL);
5018		if (tresult != ISC_R_SUCCESS) {
5019			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
5020				    "bad domain name '%s'", zonename);
5021			if (result == ISC_R_SUCCESS) {
5022				result = tresult;
5023				continue;
5024			}
5025		}
5026
5027		dns_name_format(name, namebuf, sizeof(namebuf));
5028		tresult =
5029			nameexist(nameobj, namebuf, 1, symtab,
5030				  "catalog zone '%s': already added here %s:%u",
5031				  logctx, mctx);
5032		if (tresult != ISC_R_SUCCESS) {
5033			result = tresult;
5034			continue;
5035		}
5036
5037		primariesobj = cfg_tuple_get(obj, "default-primaries");
5038		if (primariesobj != NULL && cfg_obj_istuple(primariesobj)) {
5039			primariesobj = cfg_tuple_get(obj, "default-masters");
5040			if (primariesobj != NULL &&
5041			    cfg_obj_istuple(primariesobj))
5042			{
5043				cfg_obj_log(nameobj, logctx, ISC_LOG_ERROR,
5044					    "catalog zone '%s'%s%s: "
5045					    "'default-primaries' and "
5046					    "'default-masters' can not be both "
5047					    "defined",
5048					    zonename, forview, viewname);
5049				result = ISC_R_FAILURE;
5050				break;
5051			}
5052		}
5053	}
5054
5055	if (symtab != NULL) {
5056		isc_symtab_destroy(&symtab);
5057	}
5058
5059	return (result);
5060}
5061
5062/*%
5063 * Data structure used for the 'callback_data' argument to check_one_plugin().
5064 */
5065struct check_one_plugin_data {
5066	isc_mem_t *mctx;
5067	isc_log_t *lctx;
5068	cfg_aclconfctx_t *actx;
5069	isc_result_t *check_result;
5070};
5071
5072/*%
5073 * A callback for the cfg_pluginlist_foreach() call in check_viewconf() below.
5074 * Since the point is to check configuration of all plugins even when
5075 * processing some of them fails, always return ISC_R_SUCCESS and indicate any
5076 * check failures through the 'check_result' variable passed in via the
5077 * 'callback_data' structure.
5078 */
5079static isc_result_t
5080check_one_plugin(const cfg_obj_t *config, const cfg_obj_t *obj,
5081		 const char *plugin_path, const char *parameters,
5082		 void *callback_data) {
5083	struct check_one_plugin_data *data = callback_data;
5084	char full_path[PATH_MAX];
5085	isc_result_t result;
5086
5087	result = ns_plugin_expandpath(plugin_path, full_path,
5088				      sizeof(full_path));
5089	if (result != ISC_R_SUCCESS) {
5090		cfg_obj_log(obj, data->lctx, ISC_LOG_ERROR,
5091			    "%s: plugin check failed: "
5092			    "unable to get full plugin path: %s",
5093			    plugin_path, isc_result_totext(result));
5094		return (result);
5095	}
5096
5097	result = ns_plugin_check(full_path, parameters, config,
5098				 cfg_obj_file(obj), cfg_obj_line(obj),
5099				 data->mctx, data->lctx, data->actx);
5100	if (result != ISC_R_SUCCESS) {
5101		cfg_obj_log(obj, data->lctx, ISC_LOG_ERROR,
5102			    "%s: plugin check failed: %s", full_path,
5103			    isc_result_totext(result));
5104		*data->check_result = result;
5105	}
5106
5107	return (ISC_R_SUCCESS);
5108}
5109
5110static isc_result_t
5111check_dnstap(const cfg_obj_t *voptions, const cfg_obj_t *config,
5112	     isc_log_t *logctx) {
5113#ifdef HAVE_DNSTAP
5114	const cfg_obj_t *options = NULL;
5115	const cfg_obj_t *obj = NULL;
5116
5117	if (config != NULL) {
5118		(void)cfg_map_get(config, "options", &options);
5119	}
5120	if (options != NULL) {
5121		(void)cfg_map_get(options, "dnstap-output", &obj);
5122	}
5123	if (obj == NULL) {
5124		if (voptions != NULL) {
5125			(void)cfg_map_get(voptions, "dnstap", &obj);
5126		}
5127		if (options != NULL && obj == NULL) {
5128			(void)cfg_map_get(options, "dnstap", &obj);
5129		}
5130		if (obj != NULL) {
5131			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
5132				    "'dnstap-output' must be set if 'dnstap' "
5133				    "is set");
5134			return (ISC_R_FAILURE);
5135		}
5136	}
5137	return (ISC_R_SUCCESS);
5138#else  /* ifdef HAVE_DNSTAP */
5139	UNUSED(voptions);
5140	UNUSED(config);
5141	UNUSED(logctx);
5142
5143	return (ISC_R_SUCCESS);
5144#endif /* ifdef HAVE_DNSTAP */
5145}
5146
5147static isc_result_t
5148check_viewconf(const cfg_obj_t *config, const cfg_obj_t *voptions,
5149	       const char *viewname, dns_rdataclass_t vclass,
5150	       isc_symtab_t *files, isc_symtab_t *keydirs, bool check_plugins,
5151	       bool nodeprecate, isc_symtab_t *inview, isc_log_t *logctx,
5152	       isc_mem_t *mctx) {
5153	const cfg_obj_t *zones = NULL;
5154	const cfg_obj_t *view_tkeys = NULL, *global_tkeys = NULL;
5155	const cfg_obj_t *view_mkeys = NULL, *global_mkeys = NULL;
5156	const cfg_obj_t *view_ta = NULL, *global_ta = NULL;
5157	const cfg_obj_t *check_keys[2] = { NULL, NULL };
5158	const cfg_obj_t *keys = NULL;
5159	const cfg_listelt_t *element, *element2;
5160	isc_symtab_t *symtab = NULL;
5161	isc_result_t result = ISC_R_SUCCESS;
5162	isc_result_t tresult = ISC_R_SUCCESS;
5163	cfg_aclconfctx_t *actx = NULL;
5164	const cfg_obj_t *obj;
5165	const cfg_obj_t *options = NULL;
5166	const cfg_obj_t *opts = NULL;
5167	const cfg_obj_t *plugin_list = NULL;
5168	bool autovalidation = false;
5169	unsigned int tflags = 0, dflags = 0;
5170	int i;
5171
5172	/*
5173	 * Get global options block
5174	 */
5175	(void)cfg_map_get(config, "options", &options);
5176
5177	/*
5178	 * The most relevant options for this view
5179	 */
5180	if (voptions != NULL) {
5181		opts = voptions;
5182	} else {
5183		opts = options;
5184	}
5185
5186	/*
5187	 * Check that all zone statements are syntactically correct and
5188	 * there are no duplicate zones.
5189	 */
5190	tresult = isc_symtab_create(mctx, 1000, freekey, mctx, false, &symtab);
5191	if (tresult != ISC_R_SUCCESS) {
5192		return (ISC_R_NOMEMORY);
5193	}
5194
5195	cfg_aclconfctx_create(mctx, &actx);
5196
5197	if (voptions != NULL) {
5198		(void)cfg_map_get(voptions, "zone", &zones);
5199	} else {
5200		(void)cfg_map_get(config, "zone", &zones);
5201	}
5202
5203	for (element = cfg_list_first(zones); element != NULL;
5204	     element = cfg_list_next(element))
5205	{
5206		const cfg_obj_t *zone = cfg_listelt_value(element);
5207
5208		tresult = check_zoneconf(zone, voptions, config, symtab, files,
5209					 keydirs, inview, viewname, vclass,
5210					 nodeprecate, actx, logctx, mctx);
5211		if (tresult != ISC_R_SUCCESS) {
5212			result = ISC_R_FAILURE;
5213		}
5214	}
5215
5216	/*
5217	 * Check that the response-policy and catalog-zones options
5218	 * refer to zones that exist.
5219	 */
5220	if (opts != NULL) {
5221		obj = NULL;
5222		if ((cfg_map_get(opts, "response-policy", &obj) ==
5223		     ISC_R_SUCCESS) &&
5224		    (check_rpz_catz("response-policy zone", obj, viewname,
5225				    symtab, logctx,
5226				    special_zonetype_rpz) != ISC_R_SUCCESS))
5227		{
5228			result = ISC_R_FAILURE;
5229		}
5230
5231		obj = NULL;
5232		if ((cfg_map_get(opts, "catalog-zones", &obj) ==
5233		     ISC_R_SUCCESS) &&
5234		    (check_rpz_catz("catalog zone", obj, viewname, symtab,
5235				    logctx,
5236				    special_zonetype_catz) != ISC_R_SUCCESS))
5237		{
5238			result = ISC_R_FAILURE;
5239		}
5240	}
5241
5242	/*
5243	 * Check catalog-zones configuration.
5244	 */
5245	if (opts != NULL) {
5246		obj = NULL;
5247		if ((cfg_map_get(opts, "catalog-zones", &obj) ==
5248		     ISC_R_SUCCESS) &&
5249		    (check_catz(obj, viewname, mctx, logctx) != ISC_R_SUCCESS))
5250		{
5251			result = ISC_R_FAILURE;
5252		}
5253	}
5254
5255	isc_symtab_destroy(&symtab);
5256
5257	/*
5258	 * Check that forwarding is reasonable.
5259	 */
5260	if (opts != NULL && check_forward(opts, NULL, logctx) != ISC_R_SUCCESS)
5261	{
5262		result = ISC_R_FAILURE;
5263	}
5264
5265	/*
5266	 * Check non-zero options at the global and view levels.
5267	 */
5268	if (options != NULL && check_nonzero(options, logctx) != ISC_R_SUCCESS)
5269	{
5270		result = ISC_R_FAILURE;
5271	}
5272	if (voptions != NULL &&
5273	    check_nonzero(voptions, logctx) != ISC_R_SUCCESS)
5274	{
5275		result = ISC_R_FAILURE;
5276	}
5277
5278	/*
5279	 * Check that dual-stack-servers is reasonable.
5280	 */
5281	if (opts != NULL && check_dual_stack(opts, logctx) != ISC_R_SUCCESS) {
5282		result = ISC_R_FAILURE;
5283	}
5284
5285	/*
5286	 * Check that rrset-order is reasonable.
5287	 */
5288	if (opts != NULL && check_order(opts, logctx) != ISC_R_SUCCESS) {
5289		result = ISC_R_FAILURE;
5290	}
5291
5292	/*
5293	 * Check that all key statements are syntactically correct and
5294	 * there are no duplicate keys.
5295	 */
5296	tresult = isc_symtab_create(mctx, 1000, freekey, mctx, false, &symtab);
5297	if (tresult != ISC_R_SUCCESS) {
5298		goto cleanup;
5299	}
5300
5301	(void)cfg_map_get(config, "key", &keys);
5302	tresult = check_keylist(keys, symtab, mctx, logctx);
5303	if (tresult == ISC_R_EXISTS) {
5304		result = ISC_R_FAILURE;
5305	} else if (tresult != ISC_R_SUCCESS) {
5306		result = tresult;
5307		goto cleanup;
5308	}
5309
5310	if (voptions != NULL) {
5311		keys = NULL;
5312		(void)cfg_map_get(voptions, "key", &keys);
5313		tresult = check_keylist(keys, symtab, mctx, logctx);
5314		if (tresult == ISC_R_EXISTS) {
5315			result = ISC_R_FAILURE;
5316		} else if (tresult != ISC_R_SUCCESS) {
5317			result = tresult;
5318			goto cleanup;
5319		}
5320	}
5321
5322	/*
5323	 * Global servers can refer to keys in views.
5324	 */
5325	if (check_servers(config, voptions, symtab, mctx, logctx) !=
5326	    ISC_R_SUCCESS)
5327	{
5328		result = ISC_R_FAILURE;
5329	}
5330
5331	isc_symtab_destroy(&symtab);
5332
5333	/*
5334	 * Load all DNSSEC keys.
5335	 */
5336	if (voptions != NULL) {
5337		(void)cfg_map_get(voptions, "trusted-keys", &view_tkeys);
5338		(void)cfg_map_get(voptions, "trust-anchors", &view_ta);
5339		(void)cfg_map_get(voptions, "managed-keys", &view_mkeys);
5340	}
5341	(void)cfg_map_get(config, "trusted-keys", &global_tkeys);
5342	(void)cfg_map_get(config, "trust-anchors", &global_ta);
5343	(void)cfg_map_get(config, "managed-keys", &global_mkeys);
5344
5345	/*
5346	 * Check trusted-keys.
5347	 */
5348	check_keys[0] = view_tkeys;
5349	check_keys[1] = global_tkeys;
5350	for (i = 0; i < 2; i++) {
5351		if (check_keys[i] != NULL) {
5352			unsigned int flags = 0;
5353
5354			for (element = cfg_list_first(check_keys[i]);
5355			     element != NULL; element = cfg_list_next(element))
5356			{
5357				const cfg_obj_t *keylist =
5358					cfg_listelt_value(element);
5359				for (element2 = cfg_list_first(keylist);
5360				     element2 != NULL;
5361				     element2 = cfg_list_next(element2))
5362				{
5363					obj = cfg_listelt_value(element2);
5364					tresult = check_trust_anchor(
5365						obj, false, &flags, logctx);
5366					if (tresult != ISC_R_SUCCESS) {
5367						result = tresult;
5368					}
5369				}
5370			}
5371
5372			if ((flags & ROOT_KSK_STATIC) != 0) {
5373				cfg_obj_log(check_keys[i], logctx,
5374					    ISC_LOG_WARNING,
5375					    "trusted-keys entry for the root "
5376					    "zone WILL FAIL after key "
5377					    "rollover - use trust-anchors "
5378					    "with initial-key "
5379					    "or initial-ds instead.");
5380			}
5381
5382			tflags |= flags;
5383		}
5384	}
5385
5386	/*
5387	 * Check dnssec/managed-keys. (Only one or the other can be used.)
5388	 */
5389	if ((view_mkeys != NULL || global_mkeys != NULL) &&
5390	    (view_ta != NULL || global_ta != NULL))
5391	{
5392		keys = (view_mkeys != NULL) ? view_mkeys : global_mkeys;
5393
5394		cfg_obj_log(keys, logctx, ISC_LOG_ERROR,
5395			    "use of managed-keys is not allowed when "
5396			    "trust-anchors is also in use");
5397		result = ISC_R_FAILURE;
5398	}
5399
5400	if (view_ta == NULL && global_ta == NULL) {
5401		view_ta = view_mkeys;
5402		global_ta = global_mkeys;
5403	}
5404
5405	check_keys[0] = view_ta;
5406	check_keys[1] = global_ta;
5407	for (i = 0; i < 2; i++) {
5408		if (check_keys[i] != NULL) {
5409			unsigned int flags = 0;
5410
5411			for (element = cfg_list_first(check_keys[i]);
5412			     element != NULL; element = cfg_list_next(element))
5413			{
5414				const cfg_obj_t *keylist =
5415					cfg_listelt_value(element);
5416				for (element2 = cfg_list_first(keylist);
5417				     element2 != NULL;
5418				     element2 = cfg_list_next(element2))
5419				{
5420					obj = cfg_listelt_value(element2);
5421					tresult = check_trust_anchor(
5422						obj, true, &flags, logctx);
5423					if (tresult != ISC_R_SUCCESS) {
5424						result = tresult;
5425					}
5426				}
5427			}
5428
5429			if ((flags & ROOT_KSK_STATIC) != 0) {
5430				cfg_obj_log(check_keys[i], logctx,
5431					    ISC_LOG_WARNING,
5432					    "static entry for the root "
5433					    "zone WILL FAIL after key "
5434					    "rollover - use trust-anchors "
5435					    "with initial-key "
5436					    "or initial-ds instead.");
5437			}
5438
5439			if ((flags & ROOT_KSK_2010) != 0 &&
5440			    (flags & ROOT_KSK_2017) == 0)
5441			{
5442				cfg_obj_log(check_keys[i], logctx,
5443					    ISC_LOG_WARNING,
5444					    "initial-key entry for the root "
5445					    "zone uses the 2010 key without "
5446					    "the updated 2017 key");
5447			}
5448
5449			dflags |= flags;
5450		}
5451	}
5452
5453	if ((tflags & ROOT_KSK_ANY) != 0 && (dflags & ROOT_KSK_ANY) != 0) {
5454		keys = (view_ta != NULL) ? view_ta : global_ta;
5455		cfg_obj_log(keys, logctx, ISC_LOG_WARNING,
5456			    "both trusted-keys and trust-anchors "
5457			    "for the root zone are present");
5458	}
5459
5460	if ((dflags & ROOT_KSK_ANY) == ROOT_KSK_ANY) {
5461		keys = (view_ta != NULL) ? view_ta : global_ta;
5462		cfg_obj_log(keys, logctx, ISC_LOG_WARNING,
5463			    "both initial and static entries for the "
5464			    "root zone are present");
5465	}
5466
5467	obj = NULL;
5468	if (voptions != NULL) {
5469		(void)cfg_map_get(voptions, "dnssec-validation", &obj);
5470	}
5471	if (obj == NULL && options != NULL) {
5472		(void)cfg_map_get(options, "dnssec-validation", &obj);
5473	}
5474	if (obj != NULL && !cfg_obj_isboolean(obj)) {
5475		autovalidation = true;
5476	}
5477
5478	tresult = check_ta_conflicts(global_ta, view_ta, global_tkeys,
5479				     view_tkeys, autovalidation, mctx, logctx);
5480	if (tresult != ISC_R_SUCCESS) {
5481		result = tresult;
5482	}
5483
5484	/*
5485	 * Check options.
5486	 */
5487	if (voptions != NULL) {
5488		tresult = check_options(voptions, NULL, logctx, mctx,
5489					optlevel_view);
5490	} else {
5491		tresult = check_options(config, config, logctx, mctx,
5492					optlevel_config);
5493	}
5494	if (tresult != ISC_R_SUCCESS) {
5495		result = tresult;
5496	}
5497
5498	tresult = check_dnstap(voptions, config, logctx);
5499	if (tresult != ISC_R_SUCCESS) {
5500		result = tresult;
5501	}
5502
5503	tresult = check_viewacls(actx, voptions, config, logctx, mctx);
5504	if (tresult != ISC_R_SUCCESS) {
5505		result = tresult;
5506	}
5507
5508	tresult = check_recursionacls(actx, voptions, viewname, config, logctx,
5509				      mctx);
5510	if (tresult != ISC_R_SUCCESS) {
5511		result = tresult;
5512	}
5513
5514	tresult = check_dns64(actx, voptions, config, logctx, mctx);
5515	if (tresult != ISC_R_SUCCESS) {
5516		result = tresult;
5517	}
5518
5519	tresult = check_ratelimit(actx, voptions, config, logctx, mctx);
5520	if (tresult != ISC_R_SUCCESS) {
5521		result = tresult;
5522	}
5523
5524	/*
5525	 * Load plugins.
5526	 */
5527	if (check_plugins) {
5528		if (voptions != NULL) {
5529			(void)cfg_map_get(voptions, "plugin", &plugin_list);
5530		} else {
5531			(void)cfg_map_get(config, "plugin", &plugin_list);
5532		}
5533	}
5534
5535	{
5536		struct check_one_plugin_data check_one_plugin_data = {
5537			.mctx = mctx,
5538			.lctx = logctx,
5539			.actx = actx,
5540			.check_result = &tresult,
5541		};
5542
5543		(void)cfg_pluginlist_foreach(config, plugin_list, logctx,
5544					     check_one_plugin,
5545					     &check_one_plugin_data);
5546		if (tresult != ISC_R_SUCCESS) {
5547			result = tresult;
5548		}
5549	}
5550
5551cleanup:
5552	if (symtab != NULL) {
5553		isc_symtab_destroy(&symtab);
5554	}
5555	if (actx != NULL) {
5556		cfg_aclconfctx_detach(&actx);
5557	}
5558
5559	return (result);
5560}
5561
5562static const char *default_channels[] = { "default_syslog", "default_stderr",
5563					  "default_debug", "null", NULL };
5564
5565static isc_result_t
5566bind9_check_logging(const cfg_obj_t *config, isc_log_t *logctx,
5567		    isc_mem_t *mctx) {
5568	const cfg_obj_t *categories = NULL;
5569	const cfg_obj_t *category;
5570	const cfg_obj_t *channels = NULL;
5571	const cfg_obj_t *channel;
5572	const cfg_listelt_t *element;
5573	const cfg_listelt_t *delement;
5574	const char *channelname;
5575	const char *catname;
5576	const cfg_obj_t *fileobj = NULL;
5577	const cfg_obj_t *syslogobj = NULL;
5578	const cfg_obj_t *nullobj = NULL;
5579	const cfg_obj_t *stderrobj = NULL;
5580	const cfg_obj_t *logobj = NULL;
5581	isc_result_t result = ISC_R_SUCCESS;
5582	isc_result_t tresult;
5583	isc_symtab_t *symtab = NULL;
5584	isc_symvalue_t symvalue;
5585	int i;
5586
5587	(void)cfg_map_get(config, "logging", &logobj);
5588	if (logobj == NULL) {
5589		return (ISC_R_SUCCESS);
5590	}
5591
5592	result = isc_symtab_create(mctx, 100, NULL, NULL, false, &symtab);
5593	if (result != ISC_R_SUCCESS) {
5594		return (result);
5595	}
5596
5597	symvalue.as_cpointer = NULL;
5598	for (i = 0; default_channels[i] != NULL; i++) {
5599		tresult = isc_symtab_define(symtab, default_channels[i], 1,
5600					    symvalue, isc_symexists_replace);
5601		if (tresult != ISC_R_SUCCESS) {
5602			result = tresult;
5603		}
5604	}
5605
5606	cfg_map_get(logobj, "channel", &channels);
5607
5608	for (element = cfg_list_first(channels); element != NULL;
5609	     element = cfg_list_next(element))
5610	{
5611		channel = cfg_listelt_value(element);
5612		channelname = cfg_obj_asstring(cfg_map_getname(channel));
5613		fileobj = syslogobj = nullobj = stderrobj = NULL;
5614		(void)cfg_map_get(channel, "file", &fileobj);
5615		(void)cfg_map_get(channel, "syslog", &syslogobj);
5616		(void)cfg_map_get(channel, "null", &nullobj);
5617		(void)cfg_map_get(channel, "stderr", &stderrobj);
5618		i = 0;
5619		if (fileobj != NULL) {
5620			i++;
5621		}
5622		if (syslogobj != NULL) {
5623			i++;
5624		}
5625		if (nullobj != NULL) {
5626			i++;
5627		}
5628		if (stderrobj != NULL) {
5629			i++;
5630		}
5631		if (i != 1) {
5632			cfg_obj_log(channel, logctx, ISC_LOG_ERROR,
5633				    "channel '%s': exactly one of file, "
5634				    "syslog, "
5635				    "null, and stderr must be present",
5636				    channelname);
5637			result = ISC_R_FAILURE;
5638		}
5639		tresult = isc_symtab_define(symtab, channelname, 1, symvalue,
5640					    isc_symexists_replace);
5641		if (tresult != ISC_R_SUCCESS) {
5642			result = tresult;
5643		}
5644	}
5645
5646	cfg_map_get(logobj, "category", &categories);
5647
5648	for (element = cfg_list_first(categories); element != NULL;
5649	     element = cfg_list_next(element))
5650	{
5651		category = cfg_listelt_value(element);
5652		catname = cfg_obj_asstring(cfg_tuple_get(category, "name"));
5653		if (isc_log_categorybyname(logctx, catname) == NULL) {
5654			cfg_obj_log(category, logctx, ISC_LOG_ERROR,
5655				    "undefined category: '%s'", catname);
5656			result = ISC_R_FAILURE;
5657		}
5658		channels = cfg_tuple_get(category, "destinations");
5659		for (delement = cfg_list_first(channels); delement != NULL;
5660		     delement = cfg_list_next(delement))
5661		{
5662			channel = cfg_listelt_value(delement);
5663			channelname = cfg_obj_asstring(channel);
5664			tresult = isc_symtab_lookup(symtab, channelname, 1,
5665						    &symvalue);
5666			if (tresult != ISC_R_SUCCESS) {
5667				cfg_obj_log(channel, logctx, ISC_LOG_ERROR,
5668					    "undefined channel: '%s'",
5669					    channelname);
5670				result = tresult;
5671			}
5672		}
5673	}
5674	isc_symtab_destroy(&symtab);
5675	return (result);
5676}
5677
5678static isc_result_t
5679bind9_check_controlskeys(const cfg_obj_t *control, const cfg_obj_t *keylist,
5680			 isc_log_t *logctx) {
5681	isc_result_t result = ISC_R_SUCCESS;
5682	const cfg_obj_t *control_keylist;
5683	const cfg_listelt_t *element;
5684	const cfg_obj_t *key;
5685	const char *keyval;
5686
5687	control_keylist = cfg_tuple_get(control, "keys");
5688	if (cfg_obj_isvoid(control_keylist)) {
5689		return (ISC_R_SUCCESS);
5690	}
5691
5692	for (element = cfg_list_first(control_keylist); element != NULL;
5693	     element = cfg_list_next(element))
5694	{
5695		key = cfg_listelt_value(element);
5696		keyval = cfg_obj_asstring(key);
5697
5698		if (!rndckey_exists(keylist, keyval)) {
5699			cfg_obj_log(key, logctx, ISC_LOG_ERROR,
5700				    "unknown key '%s'", keyval);
5701			result = ISC_R_NOTFOUND;
5702		}
5703	}
5704	return (result);
5705}
5706
5707static isc_result_t
5708bind9_check_controls(const cfg_obj_t *config, isc_log_t *logctx,
5709		     isc_mem_t *mctx) {
5710	isc_result_t result = ISC_R_SUCCESS, tresult;
5711	cfg_aclconfctx_t *actx = NULL;
5712	const cfg_listelt_t *element, *element2;
5713	const cfg_obj_t *allow;
5714	const cfg_obj_t *control;
5715	const cfg_obj_t *controls;
5716	const cfg_obj_t *controlslist = NULL;
5717	const cfg_obj_t *inetcontrols;
5718	const cfg_obj_t *unixcontrols;
5719	const cfg_obj_t *keylist = NULL;
5720	const char *path;
5721	uint32_t perm, mask;
5722	dns_acl_t *acl = NULL;
5723	isc_sockaddr_t addr;
5724	int i;
5725
5726	(void)cfg_map_get(config, "controls", &controlslist);
5727	if (controlslist == NULL) {
5728		return (ISC_R_SUCCESS);
5729	}
5730
5731	(void)cfg_map_get(config, "key", &keylist);
5732
5733	cfg_aclconfctx_create(mctx, &actx);
5734
5735	/*
5736	 * INET: Check allow clause.
5737	 * UNIX: Check "perm" for sanity, check path length.
5738	 */
5739	for (element = cfg_list_first(controlslist); element != NULL;
5740	     element = cfg_list_next(element))
5741	{
5742		controls = cfg_listelt_value(element);
5743		unixcontrols = NULL;
5744		inetcontrols = NULL;
5745		(void)cfg_map_get(controls, "unix", &unixcontrols);
5746		(void)cfg_map_get(controls, "inet", &inetcontrols);
5747		for (element2 = cfg_list_first(inetcontrols); element2 != NULL;
5748		     element2 = cfg_list_next(element2))
5749		{
5750			control = cfg_listelt_value(element2);
5751			allow = cfg_tuple_get(control, "allow");
5752			tresult = cfg_acl_fromconfig(allow, config, logctx,
5753						     actx, mctx, 0, &acl);
5754			if (acl != NULL) {
5755				dns_acl_detach(&acl);
5756			}
5757			if (tresult != ISC_R_SUCCESS) {
5758				result = tresult;
5759			}
5760			tresult = bind9_check_controlskeys(control, keylist,
5761							   logctx);
5762			if (tresult != ISC_R_SUCCESS) {
5763				result = tresult;
5764			}
5765		}
5766		for (element2 = cfg_list_first(unixcontrols); element2 != NULL;
5767		     element2 = cfg_list_next(element2))
5768		{
5769			control = cfg_listelt_value(element2);
5770			path = cfg_obj_asstring(cfg_tuple_get(control, "path"));
5771			tresult = isc_sockaddr_frompath(&addr, path);
5772			if (tresult == ISC_R_NOSPACE) {
5773				cfg_obj_log(control, logctx, ISC_LOG_ERROR,
5774					    "unix control '%s': path too long",
5775					    path);
5776				result = ISC_R_NOSPACE;
5777			}
5778			perm = cfg_obj_asuint32(cfg_tuple_get(control, "perm"));
5779			for (i = 0; i < 3; i++) {
5780#ifdef NEED_SECURE_DIRECTORY
5781				mask = (0x1 << (i * 3)); /* SEARCH */
5782#else  /* ifdef NEED_SECURE_DIRECTORY */
5783				mask = (0x6 << (i * 3)); /* READ + WRITE */
5784#endif /* ifdef NEED_SECURE_DIRECTORY */
5785				if ((perm & mask) == mask) {
5786					break;
5787				}
5788			}
5789			if (i == 0) {
5790				cfg_obj_log(control, logctx, ISC_LOG_WARNING,
5791					    "unix control '%s' allows access "
5792					    "to everyone",
5793					    path);
5794			} else if (i == 3) {
5795				cfg_obj_log(control, logctx, ISC_LOG_WARNING,
5796					    "unix control '%s' allows access "
5797					    "to nobody",
5798					    path);
5799			}
5800			tresult = bind9_check_controlskeys(control, keylist,
5801							   logctx);
5802			if (tresult != ISC_R_SUCCESS) {
5803				result = tresult;
5804			}
5805		}
5806	}
5807	cfg_aclconfctx_detach(&actx);
5808	return (result);
5809}
5810
5811isc_result_t
5812bind9_check_namedconf(const cfg_obj_t *config, bool check_plugins,
5813		      bool nodeprecate, isc_log_t *logctx, isc_mem_t *mctx) {
5814	const cfg_obj_t *options = NULL;
5815	const cfg_obj_t *views = NULL;
5816	const cfg_obj_t *acls = NULL;
5817	const cfg_listelt_t *velement;
5818	isc_result_t result = ISC_R_SUCCESS;
5819	isc_result_t tresult;
5820	isc_symtab_t *symtab = NULL;
5821	isc_symtab_t *files = NULL;
5822	isc_symtab_t *keydirs = NULL;
5823	isc_symtab_t *inview = NULL;
5824
5825	static const char *builtin[] = { "localhost", "localnets", "any",
5826					 "none" };
5827
5828	(void)cfg_map_get(config, "options", &options);
5829
5830	if (options != NULL && check_options(options, config, logctx, mctx,
5831					     optlevel_options) != ISC_R_SUCCESS)
5832	{
5833		result = ISC_R_FAILURE;
5834	}
5835
5836	if (bind9_check_logging(config, logctx, mctx) != ISC_R_SUCCESS) {
5837		result = ISC_R_FAILURE;
5838	}
5839
5840	if (bind9_check_controls(config, logctx, mctx) != ISC_R_SUCCESS) {
5841		result = ISC_R_FAILURE;
5842	}
5843
5844	if (bind9_check_primarylists(config, logctx, mctx) != ISC_R_SUCCESS) {
5845		result = ISC_R_FAILURE;
5846	}
5847
5848	if (bind9_check_parentalagentlists(config, logctx, mctx) !=
5849	    ISC_R_SUCCESS)
5850	{
5851		result = ISC_R_FAILURE;
5852	}
5853
5854#if HAVE_LIBNGHTTP2
5855	if (bind9_check_httpservers(config, logctx, mctx) != ISC_R_SUCCESS) {
5856		result = ISC_R_FAILURE;
5857	}
5858#endif /* HAVE_LIBNGHTTP2 */
5859
5860	if (bind9_check_tls_definitions(config, logctx, mctx) != ISC_R_SUCCESS)
5861	{
5862		result = ISC_R_FAILURE;
5863	}
5864
5865	(void)cfg_map_get(config, "view", &views);
5866
5867	if (views != NULL && options != NULL) {
5868		if (check_dual_stack(options, logctx) != ISC_R_SUCCESS) {
5869			result = ISC_R_FAILURE;
5870
5871			/*
5872			 * Use case insensitive comparison as not all file
5873			 * systems are case sensitive. This will prevent people
5874			 * using FOO.DB and foo.db on case sensitive file
5875			 * systems but that shouldn't be a major issue.
5876			 */
5877		}
5878	}
5879
5880	/*
5881	 * Use case insensitive comparison as not all file systems are
5882	 * case sensitive. This will prevent people using FOO.DB and foo.db
5883	 * on case sensitive file systems but that shouldn't be a major issue.
5884	 */
5885	tresult = isc_symtab_create(mctx, 100, NULL, NULL, false, &files);
5886	if (tresult != ISC_R_SUCCESS) {
5887		result = tresult;
5888		goto cleanup;
5889	}
5890
5891	tresult = isc_symtab_create(mctx, 100, freekey, mctx, false, &keydirs);
5892	if (tresult != ISC_R_SUCCESS) {
5893		result = tresult;
5894		goto cleanup;
5895	}
5896
5897	tresult = isc_symtab_create(mctx, 100, freekey, mctx, true, &inview);
5898	if (tresult != ISC_R_SUCCESS) {
5899		result = tresult;
5900		goto cleanup;
5901	}
5902
5903	if (views == NULL) {
5904		tresult = check_viewconf(config, NULL, NULL, dns_rdataclass_in,
5905					 files, keydirs, check_plugins,
5906					 nodeprecate, inview, logctx, mctx);
5907		if (result == ISC_R_SUCCESS && tresult != ISC_R_SUCCESS) {
5908			result = ISC_R_FAILURE;
5909		}
5910	} else {
5911		const cfg_obj_t *zones = NULL;
5912		const cfg_obj_t *plugins = NULL;
5913
5914		(void)cfg_map_get(config, "zone", &zones);
5915		if (zones != NULL) {
5916			cfg_obj_log(zones, logctx, ISC_LOG_ERROR,
5917				    "when using 'view' statements, "
5918				    "all zones must be in views");
5919			result = ISC_R_FAILURE;
5920		}
5921
5922		(void)cfg_map_get(config, "plugin", &plugins);
5923		if (plugins != NULL) {
5924			cfg_obj_log(plugins, logctx, ISC_LOG_ERROR,
5925				    "when using 'view' statements, "
5926				    "all plugins must be defined in views");
5927			result = ISC_R_FAILURE;
5928		}
5929	}
5930
5931	tresult = isc_symtab_create(mctx, 100, NULL, NULL, true, &symtab);
5932	if (tresult != ISC_R_SUCCESS) {
5933		result = tresult;
5934		goto cleanup;
5935	}
5936	for (velement = cfg_list_first(views); velement != NULL;
5937	     velement = cfg_list_next(velement))
5938	{
5939		const cfg_obj_t *view = cfg_listelt_value(velement);
5940		const cfg_obj_t *vname = cfg_tuple_get(view, "name");
5941		const cfg_obj_t *voptions = cfg_tuple_get(view, "options");
5942		const cfg_obj_t *vclassobj = cfg_tuple_get(view, "class");
5943		dns_rdataclass_t vclass = dns_rdataclass_in;
5944		const char *key = cfg_obj_asstring(vname);
5945		isc_symvalue_t symvalue;
5946		unsigned int symtype;
5947
5948		tresult = ISC_R_SUCCESS;
5949		if (cfg_obj_isstring(vclassobj)) {
5950			isc_textregion_t r;
5951
5952			DE_CONST(cfg_obj_asstring(vclassobj), r.base);
5953			r.length = strlen(r.base);
5954			tresult = dns_rdataclass_fromtext(&vclass, &r);
5955			if (tresult != ISC_R_SUCCESS) {
5956				cfg_obj_log(vclassobj, logctx, ISC_LOG_ERROR,
5957					    "view '%s': invalid class %s",
5958					    cfg_obj_asstring(vname), r.base);
5959			}
5960		}
5961		symtype = vclass + 1;
5962		if (tresult == ISC_R_SUCCESS && symtab != NULL) {
5963			symvalue.as_cpointer = view;
5964			tresult = isc_symtab_define(symtab, key, symtype,
5965						    symvalue,
5966						    isc_symexists_reject);
5967			if (tresult == ISC_R_EXISTS) {
5968				const char *file;
5969				unsigned int line;
5970				RUNTIME_CHECK(isc_symtab_lookup(symtab, key,
5971								symtype,
5972								&symvalue) ==
5973					      ISC_R_SUCCESS);
5974				file = cfg_obj_file(symvalue.as_cpointer);
5975				line = cfg_obj_line(symvalue.as_cpointer);
5976				cfg_obj_log(view, logctx, ISC_LOG_ERROR,
5977					    "view '%s': already exists "
5978					    "previous definition: %s:%u",
5979					    key, file, line);
5980				result = tresult;
5981			} else if (tresult != ISC_R_SUCCESS) {
5982				result = tresult;
5983			} else if ((strcasecmp(key, "_bind") == 0 &&
5984				    vclass == dns_rdataclass_ch) ||
5985				   (strcasecmp(key, "_default") == 0 &&
5986				    vclass == dns_rdataclass_in))
5987			{
5988				cfg_obj_log(view, logctx, ISC_LOG_ERROR,
5989					    "attempt to redefine builtin view "
5990					    "'%s'",
5991					    key);
5992				result = ISC_R_EXISTS;
5993			}
5994		}
5995		if (tresult == ISC_R_SUCCESS) {
5996			tresult = check_viewconf(config, voptions, key, vclass,
5997						 files, keydirs, check_plugins,
5998						 nodeprecate, inview, logctx,
5999						 mctx);
6000		}
6001		if (tresult != ISC_R_SUCCESS) {
6002			result = ISC_R_FAILURE;
6003		}
6004	}
6005
6006	cfg_map_get(config, "acl", &acls);
6007
6008	if (acls != NULL) {
6009		const cfg_listelt_t *elt;
6010		const cfg_listelt_t *elt2;
6011		const char *aclname;
6012
6013		for (elt = cfg_list_first(acls); elt != NULL;
6014		     elt = cfg_list_next(elt))
6015		{
6016			const cfg_obj_t *acl = cfg_listelt_value(elt);
6017			unsigned int line = cfg_obj_line(acl);
6018			unsigned int i;
6019
6020			aclname = cfg_obj_asstring(cfg_tuple_get(acl, "name"));
6021			for (i = 0; i < sizeof(builtin) / sizeof(builtin[0]);
6022			     i++)
6023			{
6024				if (strcasecmp(aclname, builtin[i]) == 0) {
6025					{
6026						cfg_obj_log(acl, logctx,
6027							    ISC_LOG_ERROR,
6028							    "attempt to "
6029							    "redefine "
6030							    "builtin acl '%s'",
6031							    aclname);
6032						result = ISC_R_FAILURE;
6033						break;
6034					}
6035				}
6036			}
6037
6038			for (elt2 = cfg_list_next(elt); elt2 != NULL;
6039			     elt2 = cfg_list_next(elt2))
6040			{
6041				const cfg_obj_t *acl2 = cfg_listelt_value(elt2);
6042				const char *name;
6043				name = cfg_obj_asstring(
6044					cfg_tuple_get(acl2, "name"));
6045				if (strcasecmp(aclname, name) == 0) {
6046					const char *file = cfg_obj_file(acl);
6047
6048					if (file == NULL) {
6049						file = "<unknown file>";
6050					}
6051
6052					cfg_obj_log(acl2, logctx, ISC_LOG_ERROR,
6053						    "attempt to redefine "
6054						    "acl '%s' previous "
6055						    "definition: %s:%u",
6056						    name, file, line);
6057					result = ISC_R_FAILURE;
6058				}
6059			}
6060		}
6061	}
6062
6063cleanup:
6064	if (symtab != NULL) {
6065		isc_symtab_destroy(&symtab);
6066	}
6067	if (inview != NULL) {
6068		isc_symtab_destroy(&inview);
6069	}
6070	if (files != NULL) {
6071		isc_symtab_destroy(&files);
6072	}
6073	if (keydirs != NULL) {
6074		isc_symtab_destroy(&keydirs);
6075	}
6076
6077	return (result);
6078}
6079