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