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