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