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