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