]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - contrib/bind9/lib/bind9/check.c
MFC r363988:
[FreeBSD/stable/9.git] / contrib / bind9 / lib / bind9 / check.c
1 /*
2  * Copyright (C) 2004-2016  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 2001-2003  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15  * PERFORMANCE OF THIS SOFTWARE.
16  */
17
18 /*! \file */
19
20 #include <config.h>
21
22 #include <stdlib.h>
23
24 #include <isc/base64.h>
25 #include <isc/buffer.h>
26 #include <isc/file.h>
27 #include <isc/log.h>
28 #include <isc/mem.h>
29 #include <isc/netaddr.h>
30 #include <isc/parseint.h>
31 #include <isc/region.h>
32 #include <isc/result.h>
33 #include <isc/sockaddr.h>
34 #include <isc/string.h>
35 #include <isc/symtab.h>
36 #include <isc/util.h>
37
38 #include <dns/acl.h>
39 #include <dns/fixedname.h>
40 #include <dns/rdataclass.h>
41 #include <dns/rdatatype.h>
42 #include <dns/secalg.h>
43
44 #include <dst/dst.h>
45
46 #include <isccfg/aclconf.h>
47 #include <isccfg/cfg.h>
48
49 #include <bind9/check.h>
50
51 static isc_result_t
52 fileexist(const cfg_obj_t *obj, isc_symtab_t *symtab, isc_boolean_t writeable,
53           isc_log_t *logctxlogc);
54
55 static void
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         isc_boolean_t rfc1918 = ISC_FALSE;
1296         isc_boolean_t ula = ISC_FALSE;
1297         const cfg_listelt_t *element;
1298         isc_boolean_t ddns = ISC_FALSE;
1299
1300         static optionstable options[] = {
1301         { "allow-notify", SLAVEZONE | CHECKACL },
1302         { "allow-query", MASTERZONE | SLAVEZONE | STUBZONE | REDIRECTZONE |
1303           CHECKACL | STATICSTUBZONE },
1304         { "allow-transfer", MASTERZONE | SLAVEZONE | CHECKACL },
1305         { "allow-update", MASTERZONE | CHECKACL },
1306         { "allow-update-forwarding", SLAVEZONE | CHECKACL },
1307         { "also-notify", MASTERZONE | SLAVEZONE },
1308         { "auto-dnssec", MASTERZONE | SLAVEZONE },
1309         { "check-dup-records", MASTERZONE },
1310         { "check-mx", MASTERZONE },
1311         { "check-mx-cname", MASTERZONE },
1312         { "check-srv-cname", MASTERZONE },
1313         { "check-wildcard", MASTERZONE },
1314         { "database", MASTERZONE | SLAVEZONE | STUBZONE | REDIRECTZONE },
1315         { "delegation-only", HINTZONE | STUBZONE | FORWARDZONE |
1316           DELEGATIONZONE },
1317         { "dialup", MASTERZONE | SLAVEZONE | STUBZONE | STREDIRECTZONE },
1318         { "dnssec-dnskey-kskonly", MASTERZONE | SLAVEZONE },
1319         { "dnssec-loadkeys-interval", MASTERZONE | SLAVEZONE },
1320         { "dnssec-secure-to-insecure", MASTERZONE },
1321         { "file", MASTERZONE | SLAVEZONE | STUBZONE | HINTZONE | REDIRECTZONE },
1322         { "forward", MASTERZONE | SLAVEZONE | STUBZONE | STATICSTUBZONE |
1323           FORWARDZONE },
1324         { "forwarders", MASTERZONE | SLAVEZONE | STUBZONE | STATICSTUBZONE |
1325           FORWARDZONE },
1326         { "integrity-check", MASTERZONE },
1327         { "ixfr-base", MASTERZONE | SLAVEZONE },
1328         { "ixfr-tmp-file", MASTERZONE | SLAVEZONE },
1329         { "journal", MASTERZONE | SLAVEZONE | STREDIRECTZONE },
1330         { "key-directory", MASTERZONE | SLAVEZONE },
1331         { "maintain-ixfr-base", MASTERZONE | SLAVEZONE | STREDIRECTZONE },
1332         { "masterfile-format", MASTERZONE | SLAVEZONE | STUBZONE |
1333           REDIRECTZONE },
1334         { "masters", SLAVEZONE | STUBZONE | REDIRECTZONE },
1335         { "max-ixfr-log-size", MASTERZONE | SLAVEZONE | STREDIRECTZONE },
1336         { "max-refresh-time", SLAVEZONE | STUBZONE | STREDIRECTZONE },
1337         { "max-retry-time", SLAVEZONE | STUBZONE | STREDIRECTZONE },
1338         { "max-transfer-idle-in", SLAVEZONE | STUBZONE | STREDIRECTZONE },
1339         { "max-transfer-idle-out", MASTERZONE | SLAVEZONE },
1340         { "max-transfer-time-in", SLAVEZONE | STUBZONE | STREDIRECTZONE },
1341         { "max-transfer-time-out", MASTERZONE | SLAVEZONE },
1342         { "min-refresh-time", SLAVEZONE | STUBZONE | STREDIRECTZONE },
1343         { "min-retry-time", SLAVEZONE | STUBZONE | STREDIRECTZONE },
1344         { "notify", MASTERZONE | SLAVEZONE },
1345         { "notify-source", MASTERZONE | SLAVEZONE },
1346         { "notify-source-v6", MASTERZONE | SLAVEZONE },
1347         { "pubkey", MASTERZONE | SLAVEZONE | STUBZONE },
1348         { "request-ixfr", SLAVEZONE | REDIRECTZONE },
1349         { "server-addresses", STATICSTUBZONE },
1350         { "server-names", STATICSTUBZONE },
1351         { "sig-re-signing-interval", MASTERZONE | SLAVEZONE },
1352         { "sig-signing-nodes", MASTERZONE | SLAVEZONE },
1353         { "sig-signing-signatures", MASTERZONE | SLAVEZONE },
1354         { "sig-signing-type", MASTERZONE | SLAVEZONE },
1355         { "sig-validity-interval", MASTERZONE | SLAVEZONE },
1356         { "signing", MASTERZONE | SLAVEZONE },
1357         { "transfer-source", SLAVEZONE | STUBZONE | STREDIRECTZONE },
1358         { "transfer-source-v6", SLAVEZONE | STUBZONE | STREDIRECTZONE },
1359         { "try-tcp-refresh", SLAVEZONE | STREDIRECTZONE },
1360         { "update-check-ksk", MASTERZONE | SLAVEZONE },
1361         { "update-policy", MASTERZONE },
1362         { "zone-statistics", MASTERZONE | SLAVEZONE | STUBZONE |
1363           STATICSTUBZONE | REDIRECTZONE },
1364         };
1365
1366         static optionstable dialups[] = {
1367         { "notify", MASTERZONE | SLAVEZONE | STREDIRECTZONE },
1368         { "notify-passive", SLAVEZONE | STREDIRECTZONE },
1369         { "refresh", SLAVEZONE | STUBZONE | STREDIRECTZONE },
1370         { "passive", SLAVEZONE | STUBZONE | STREDIRECTZONE },
1371         };
1372
1373         znamestr = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
1374
1375         zoptions = cfg_tuple_get(zconfig, "options");
1376
1377         if (config != NULL)
1378                 cfg_map_get(config, "options", &goptions);
1379
1380         obj = NULL;
1381         (void)cfg_map_get(zoptions, "type", &obj);
1382         if (obj == NULL) {
1383                 cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
1384                             "zone '%s': type not present", znamestr);
1385                 return (ISC_R_FAILURE);
1386         }
1387
1388         typestr = cfg_obj_asstring(obj);
1389         if (strcasecmp(typestr, "master") == 0)
1390                 ztype = MASTERZONE;
1391         else if (strcasecmp(typestr, "slave") == 0)
1392                 ztype = SLAVEZONE;
1393         else if (strcasecmp(typestr, "stub") == 0)
1394                 ztype = STUBZONE;
1395         else if (strcasecmp(typestr, "static-stub") == 0)
1396                 ztype = STATICSTUBZONE;
1397         else if (strcasecmp(typestr, "forward") == 0)
1398                 ztype = FORWARDZONE;
1399         else if (strcasecmp(typestr, "hint") == 0)
1400                 ztype = HINTZONE;
1401         else if (strcasecmp(typestr, "delegation-only") == 0)
1402                 ztype = DELEGATIONZONE;
1403         else if (strcasecmp(typestr, "redirect") == 0)
1404                 ztype = REDIRECTZONE;
1405         else {
1406                 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1407                             "zone '%s': invalid type %s",
1408                             znamestr, typestr);
1409                 return (ISC_R_FAILURE);
1410         }
1411
1412         if (ztype == REDIRECTZONE && strcmp(znamestr, ".") != 0) {
1413                 cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
1414                             "redirect zones must be called \".\"");
1415                 return (ISC_R_FAILURE);
1416         }
1417         obj = cfg_tuple_get(zconfig, "class");
1418         if (cfg_obj_isstring(obj)) {
1419                 isc_textregion_t r;
1420
1421                 DE_CONST(cfg_obj_asstring(obj), r.base);
1422                 r.length = strlen(r.base);
1423                 result = dns_rdataclass_fromtext(&zclass, &r);
1424                 if (result != ISC_R_SUCCESS) {
1425                         cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1426                                     "zone '%s': invalid class %s",
1427                                     znamestr, r.base);
1428                         return (ISC_R_FAILURE);
1429                 }
1430                 if (zclass != defclass) {
1431                         cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1432                                     "zone '%s': class '%s' does not "
1433                                     "match view/default class",
1434                                     znamestr, r.base);
1435                         return (ISC_R_FAILURE);
1436                 }
1437         }
1438
1439         /*
1440          * Look for an already existing zone.
1441          * We need to make this canonical as isc_symtab_define()
1442          * deals with strings.
1443          */
1444         dns_fixedname_init(&fixedname);
1445         isc_buffer_constinit(&b, znamestr, strlen(znamestr));
1446         isc_buffer_add(&b, strlen(znamestr));
1447         tresult = dns_name_fromtext(dns_fixedname_name(&fixedname), &b,
1448                                     dns_rootname, DNS_NAME_DOWNCASE, NULL);
1449         if (tresult != ISC_R_SUCCESS) {
1450                 cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
1451                             "zone '%s': is not a valid name", znamestr);
1452                 result = ISC_R_FAILURE;
1453         } else {
1454                 char namebuf[DNS_NAME_FORMATSIZE];
1455
1456                 zname = dns_fixedname_name(&fixedname);
1457                 dns_name_format(zname, namebuf, sizeof(namebuf));
1458                 tresult = nameexist(zconfig, namebuf, ztype == HINTZONE ? 1 :
1459                                     ztype == REDIRECTZONE ? 2 : 3,
1460                                     symtab, "zone '%s': already exists "
1461                                     "previous definition: %s:%u", logctx, mctx);
1462                 if (tresult != ISC_R_SUCCESS)
1463                         result = tresult;
1464                 if (dns_name_equal(zname, dns_rootname))
1465                         root = ISC_TRUE;
1466                 else if (dns_name_isrfc1918(zname))
1467                         rfc1918 = ISC_TRUE;
1468                 else if (dns_name_isula(zname))
1469                         ula = ISC_TRUE;
1470         }
1471
1472         /*
1473          * Check if value is zero.
1474          */
1475         if (check_nonzero(zoptions, logctx) != ISC_R_SUCCESS)
1476                 result = ISC_R_FAILURE;
1477
1478         /*
1479          * Look for inappropriate options for the given zone type.
1480          * Check that ACLs expand correctly.
1481          */
1482         for (i = 0; i < sizeof(options) / sizeof(options[0]); i++) {
1483                 obj = NULL;
1484                 if ((options[i].allowed & ztype) == 0 &&
1485                     cfg_map_get(zoptions, options[i].name, &obj) ==
1486                     ISC_R_SUCCESS)
1487                 {
1488                         if (strcmp(options[i].name, "allow-update") != 0 ||
1489                             ztype != SLAVEZONE) {
1490                                 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1491                                             "option '%s' is not allowed "
1492                                             "in '%s' zone '%s'",
1493                                             options[i].name, typestr,
1494                                             znamestr);
1495                                         result = ISC_R_FAILURE;
1496                         } else
1497                                 cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
1498                                             "option '%s' is not allowed "
1499                                             "in '%s' zone '%s'",
1500                                             options[i].name, typestr,
1501                                             znamestr);
1502                 }
1503                 obj = NULL;
1504                 if ((options[i].allowed & ztype) != 0 &&
1505                     (options[i].allowed & CHECKACL) != 0) {
1506
1507                         tresult = checkacl(options[i].name, actx, zconfig,
1508                                            voptions, config, logctx, mctx);
1509                         if (tresult != ISC_R_SUCCESS)
1510                                 result = tresult;
1511                 }
1512
1513         }
1514
1515         /*
1516          * Master & slave zones may have an "also-notify" field, but
1517          * shouldn't if notify is disabled.
1518          */
1519         if (ztype == MASTERZONE || ztype == SLAVEZONE ) {
1520                 isc_boolean_t donotify = ISC_TRUE;
1521
1522                 obj = NULL;
1523                 tresult = cfg_map_get(zoptions, "notify", &obj);
1524                 if (tresult != ISC_R_SUCCESS && voptions != NULL)
1525                         tresult = cfg_map_get(voptions, "notify", &obj);
1526                 if (tresult != ISC_R_SUCCESS && goptions != NULL)
1527                         tresult = cfg_map_get(goptions, "notify", &obj);
1528                 if (tresult == ISC_R_SUCCESS) {
1529                         if (cfg_obj_isboolean(obj))
1530                                 donotify = cfg_obj_asboolean(obj);
1531                         else {
1532                                 const char *notifystr = cfg_obj_asstring(obj);
1533                                 if (ztype != MASTERZONE &&
1534                                     strcasecmp(notifystr, "master-only") == 0)
1535                                         donotify = ISC_FALSE;
1536                         }
1537                 }
1538
1539                 obj = NULL;
1540                 tresult = cfg_map_get(zoptions, "also-notify", &obj);
1541                 if (tresult == ISC_R_SUCCESS && !donotify) {
1542                         cfg_obj_log(zoptions, logctx, ISC_LOG_WARNING,
1543                                     "zone '%s': 'also-notify' set but "
1544                                     "'notify' is disabled", znamestr);
1545                 } else if (tresult == ISC_R_SUCCESS) {
1546                         isc_uint32_t count;
1547                         tresult = validate_masters(obj, config, &count,
1548                                                    logctx, mctx);
1549                         if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS)
1550                                 result = tresult;
1551                 }
1552         }
1553
1554         /*
1555          * Slave & stub zones must have a "masters" field.
1556          */
1557         if (ztype == SLAVEZONE || ztype == STUBZONE) {
1558                 obj = NULL;
1559                 if (cfg_map_get(zoptions, "masters", &obj) != ISC_R_SUCCESS) {
1560                         cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR,
1561                                     "zone '%s': missing 'masters' entry",
1562                                     znamestr);
1563                         result = ISC_R_FAILURE;
1564                 } else {
1565                         isc_uint32_t count;
1566                         tresult = validate_masters(obj, config, &count,
1567                                                    logctx, mctx);
1568                         if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS)
1569                                 result = tresult;
1570                         if (tresult == ISC_R_SUCCESS && count == 0) {
1571                                 cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR,
1572                                             "zone '%s': empty 'masters' entry",
1573                                             znamestr);
1574                                 result = ISC_R_FAILURE;
1575                         }
1576                 }
1577         }
1578
1579         /*
1580          * Master zones can't have both "allow-update" and "update-policy".
1581          */
1582         if (ztype == MASTERZONE || ztype == SLAVEZONE) {
1583                 isc_boolean_t signing = ISC_FALSE;
1584                 isc_result_t res1, res2, res3;
1585                 const cfg_obj_t *au = NULL;
1586                 const char *arg;
1587
1588                 obj = NULL;
1589                 res1 = cfg_map_get(zoptions, "allow-update", &au);
1590                 obj = NULL;
1591                 res2 = cfg_map_get(zoptions, "update-policy", &obj);
1592                 if (res1 == ISC_R_SUCCESS && res2 == ISC_R_SUCCESS) {
1593                         cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1594                                     "zone '%s': 'allow-update' is ignored "
1595                                     "when 'update-policy' is present",
1596                                     znamestr);
1597                         result = ISC_R_FAILURE;
1598                 } else if (res2 == ISC_R_SUCCESS) {
1599                         res3 = check_update_policy(obj, logctx);
1600                         if (res3 != ISC_R_SUCCESS)
1601                                 result = ISC_R_FAILURE;
1602                 }
1603
1604                 /*
1605                  * To determine whether auto-dnssec is allowed,
1606                  * we should also check for allow-update at the
1607                  * view and options levels.
1608                  */
1609                 if (res1 != ISC_R_SUCCESS && voptions != NULL)
1610                         res1 = cfg_map_get(voptions, "allow-update", &au);
1611                 if (res1 != ISC_R_SUCCESS && goptions != NULL)
1612                         res1 = cfg_map_get(goptions, "allow-update", &au);
1613
1614                 if (res2 == ISC_R_SUCCESS)
1615                         ddns = ISC_TRUE;
1616                 else if (res1 == ISC_R_SUCCESS) {
1617                         dns_acl_t *acl = NULL;
1618                         res1 = cfg_acl_fromconfig(au, config, logctx,
1619                                                   actx, mctx, 0, &acl);
1620                         if (res1 != ISC_R_SUCCESS) {
1621                                 cfg_obj_log(au, logctx, ISC_LOG_ERROR,
1622                                             "acl expansion failed: %s",
1623                                             isc_result_totext(result));
1624                                 result = ISC_R_FAILURE;
1625                         } else if (acl != NULL) {
1626                                 if (!dns_acl_isnone(acl))
1627                                         ddns = ISC_TRUE;
1628                                 dns_acl_detach(&acl);
1629                         }
1630                 }
1631
1632                 obj = NULL;
1633                 res1 = cfg_map_get(zoptions, "inline-signing", &obj);
1634                 if (res1 == ISC_R_SUCCESS)
1635                         signing = cfg_obj_asboolean(obj);
1636
1637                 obj = NULL;
1638                 arg = "off";
1639                 res3 = cfg_map_get(zoptions, "auto-dnssec", &obj);
1640                 if (res3 == ISC_R_SUCCESS)
1641                         arg = cfg_obj_asstring(obj);
1642                 if (strcasecmp(arg, "off") != 0 && !ddns && !signing) {
1643                         cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1644                                     "'auto-dnssec %s;' requires%s "
1645                                     "inline-signing to be configured for "
1646                                     "the zone", arg,
1647                                     (ztype == MASTERZONE) ?
1648                                          " dynamic DNS or" : "");
1649                         result = ISC_R_FAILURE;
1650                 }
1651
1652                 obj = NULL;
1653                 res1 = cfg_map_get(zoptions, "sig-signing-type", &obj);
1654                 if (res1 == ISC_R_SUCCESS) {
1655                         isc_uint32_t type = cfg_obj_asuint32(obj);
1656                         if (type < 0xff00U || type > 0xffffU)
1657                                 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1658                                             "sig-signing-type: %u out of "
1659                                             "range [%u..%u]", type,
1660                                             0xff00U, 0xffffU);
1661                         result = ISC_R_FAILURE;
1662                 }
1663
1664                 obj = NULL;
1665                 res1 = cfg_map_get(zoptions, "dnssec-dnskey-kskonly", &obj);
1666                 if (res1 == ISC_R_SUCCESS && ztype == SLAVEZONE && !signing) {
1667                         cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1668                                     "dnssec-dnskey-kskonly: requires "
1669                                     "inline-signing when used in slave zone");
1670                         result = ISC_R_FAILURE;
1671                 }
1672
1673                 obj = NULL;
1674                 res1 = cfg_map_get(zoptions, "dnssec-loadkeys-interval", &obj);
1675                 if (res1 == ISC_R_SUCCESS && ztype == SLAVEZONE && !signing) {
1676                         cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1677                                     "dnssec-loadkeys-interval: requires "
1678                                     "inline-signing when used in slave zone");
1679                         result = ISC_R_FAILURE;
1680                 }
1681
1682                 obj = NULL;
1683                 res1 = cfg_map_get(zoptions, "update-check-ksk", &obj);
1684                 if (res1 == ISC_R_SUCCESS && ztype == SLAVEZONE && !signing) {
1685                         cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1686                                     "update-check-ksk: requires "
1687                                     "inline-signing when used in slave zone");
1688                         result = ISC_R_FAILURE;
1689                 }
1690         }
1691
1692         /*
1693          * Check the excessively complicated "dialup" option.
1694          */
1695         if (ztype == MASTERZONE || ztype == SLAVEZONE || ztype == STUBZONE) {
1696                 const cfg_obj_t *dialup = NULL;
1697                 (void)cfg_map_get(zoptions, "dialup", &dialup);
1698                 if (dialup != NULL && cfg_obj_isstring(dialup)) {
1699                         const char *str = cfg_obj_asstring(dialup);
1700                         for (i = 0;
1701                              i < sizeof(dialups) / sizeof(dialups[0]);
1702                              i++)
1703                         {
1704                                 if (strcasecmp(dialups[i].name, str) != 0)
1705                                         continue;
1706                                 if ((dialups[i].allowed & ztype) == 0) {
1707                                         cfg_obj_log(obj, logctx,
1708                                                     ISC_LOG_ERROR,
1709                                                     "dialup type '%s' is not "
1710                                                     "allowed in '%s' "
1711                                                     "zone '%s'",
1712                                                     str, typestr, znamestr);
1713                                         result = ISC_R_FAILURE;
1714                                 }
1715                                 break;
1716                         }
1717                         if (i == sizeof(dialups) / sizeof(dialups[0])) {
1718                                 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1719                                             "invalid dialup type '%s' in zone "
1720                                             "'%s'", str, znamestr);
1721                                 result = ISC_R_FAILURE;
1722                         }
1723                 }
1724         }
1725
1726         /*
1727          * Check that forwarding is reasonable.
1728          */
1729         obj = NULL;
1730         if (root) {
1731                 if (voptions != NULL)
1732                         (void)cfg_map_get(voptions, "forwarders", &obj);
1733                 if (obj == NULL && goptions != NULL)
1734                         (void)cfg_map_get(goptions, "forwarders", &obj);
1735         }
1736         if (check_forward(zoptions, obj, logctx) != ISC_R_SUCCESS)
1737                 result = ISC_R_FAILURE;
1738
1739         /*
1740          * Check that a RFC 1918 / ULA reverse zone is not forward first
1741          * unless explictly configured to be so.
1742          */
1743         if (ztype == FORWARDZONE && (rfc1918 || ula)) {
1744                 obj = NULL;
1745                 (void)cfg_map_get(zoptions, "forward", &obj);
1746                 if (obj == NULL) {
1747                         /*
1748                          * Forward mode not explicity configured.
1749                          */
1750                         if (voptions != NULL)
1751                                 cfg_map_get(voptions, "forward", &obj);
1752                         if (obj == NULL && goptions != NULL)
1753                                 cfg_map_get(goptions, "forward", &obj);
1754                         if (obj == NULL ||
1755                             strcasecmp(cfg_obj_asstring(obj), "first") == 0)
1756                                 cfg_obj_log(zconfig, logctx, ISC_LOG_WARNING,
1757                                             "inherited 'forward first;' for "
1758                                             "%s zone '%s' - did you want "
1759                                             "'forward only;'?",
1760                                             rfc1918 ? "rfc1918" : "ula",
1761                                             znamestr);
1762                 }
1763         }
1764
1765         /*
1766          * Check validity of static stub server addresses.
1767          */
1768         obj = NULL;
1769         (void)cfg_map_get(zoptions, "server-addresses", &obj);
1770         if (ztype == STATICSTUBZONE && obj != NULL) {
1771                 for (element = cfg_list_first(obj);
1772                      element != NULL;
1773                      element = cfg_list_next(element))
1774                 {
1775                         isc_sockaddr_t sa;
1776                         isc_netaddr_t na;
1777                         obj = cfg_listelt_value(element);
1778                         sa = *cfg_obj_assockaddr(obj);
1779
1780                         if (isc_sockaddr_getport(&sa) != 0) {
1781                                 result = ISC_R_FAILURE;
1782                                 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1783                                             "port is not configurable for "
1784                                             "static stub server-addresses");
1785                         }
1786
1787                         isc_netaddr_fromsockaddr(&na, &sa);
1788                         if (isc_netaddr_getzone(&na) != 0) {
1789                                 result = ISC_R_FAILURE;
1790                                 cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
1791                                             "scoped address is not allowed "
1792                                             "for static stub "
1793                                             "server-addresses");
1794                         }
1795                 }
1796         }
1797
1798         /*
1799          * Check validity of static stub server names.
1800          */
1801         obj = NULL;
1802         (void)cfg_map_get(zoptions, "server-names", &obj);
1803         if (zname != NULL && ztype == STATICSTUBZONE && obj != NULL) {
1804                 for (element = cfg_list_first(obj);
1805                      element != NULL;
1806                      element = cfg_list_next(element))
1807                 {
1808                         const char *snamestr;
1809                         dns_fixedname_t fixed_sname;
1810                         isc_buffer_t b2;
1811                         dns_name_t *sname;
1812
1813                         obj = cfg_listelt_value(element);
1814                         snamestr = cfg_obj_asstring(obj);
1815
1816                         dns_fixedname_init(&fixed_sname);
1817                         isc_buffer_constinit(&b2, snamestr, strlen(snamestr));
1818                         isc_buffer_add(&b2, strlen(snamestr));
1819                         sname = dns_fixedname_name(&fixed_sname);
1820                         tresult = dns_name_fromtext(sname, &b2, dns_rootname,
1821                                                     0, NULL);
1822                         if (tresult != ISC_R_SUCCESS) {
1823                                 cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
1824                                             "server-name '%s' is not a valid "
1825                                             "name", snamestr);
1826                                 result = ISC_R_FAILURE;
1827                         } else if (dns_name_issubdomain(sname, zname)) {
1828                                 cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
1829                                             "server-name '%s' must not be a "
1830                                             "subdomain of zone name '%s'",
1831                                             snamestr, znamestr);
1832                                 result = ISC_R_FAILURE;
1833                         }
1834                 }
1835         }
1836
1837         /*
1838          * Warn if key-directory doesn't exist
1839          */
1840         obj = NULL;
1841         tresult = cfg_map_get(zoptions, "key-directory", &obj);
1842         if (tresult == ISC_R_SUCCESS) {
1843                 const char *dir = cfg_obj_asstring(obj);
1844                 tresult = isc_file_isdirectory(dir);
1845                 switch (tresult) {
1846                 case ISC_R_SUCCESS:
1847                         break;
1848                 case ISC_R_FILENOTFOUND:
1849                         cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
1850                                     "key-directory: '%s' does not exist",
1851                                     dir);
1852                         break;
1853                 case ISC_R_INVALIDFILE:
1854                         cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
1855                                     "key-directory: '%s' is not a directory",
1856                                     dir);
1857                         break;
1858                 default:
1859                         cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
1860                                     "key-directory: '%s' %s",
1861                                     dir, isc_result_totext(tresult));
1862                         result = tresult;
1863                 }
1864         }
1865
1866         /*
1867          * Check various options.
1868          */
1869         tresult = check_options(zoptions, logctx, mctx, optlevel_zone);
1870         if (tresult != ISC_R_SUCCESS)
1871                 result = tresult;
1872
1873         /*
1874          * If the zone type is rbt/rbt64 then master/hint zones
1875          * require file clauses.
1876          * If inline signing is used, then slave zones require a
1877          * file clause as well
1878          */
1879         obj = NULL;
1880         tresult = cfg_map_get(zoptions, "database", &obj);
1881         if (tresult == ISC_R_NOTFOUND ||
1882             (tresult == ISC_R_SUCCESS &&
1883              (strcmp("rbt", cfg_obj_asstring(obj)) == 0 ||
1884               strcmp("rbt64", cfg_obj_asstring(obj)) == 0)))
1885         {
1886                 isc_result_t res1;
1887                 const cfg_obj_t *fileobj = NULL;
1888                 tresult = cfg_map_get(zoptions, "file", &fileobj);
1889                 obj = NULL;
1890                 res1 = cfg_map_get(zoptions, "inline-signing", &obj);
1891                 if ((tresult != ISC_R_SUCCESS &&
1892                     (ztype == MASTERZONE || ztype == HINTZONE ||
1893                      (ztype == SLAVEZONE && res1 == ISC_R_SUCCESS &&
1894                       cfg_obj_asboolean(obj))))) {
1895                         cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
1896                             "zone '%s': missing 'file' entry",
1897                             znamestr);
1898                         result = tresult;
1899                 } else if (tresult == ISC_R_SUCCESS &&
1900                            (ztype == SLAVEZONE || ddns)) {
1901                         tresult = fileexist(fileobj, files, ISC_TRUE, logctx);
1902                         if (tresult != ISC_R_SUCCESS)
1903                                 result = tresult;
1904                 } else if (tresult == ISC_R_SUCCESS &&
1905                            (ztype == MASTERZONE || ztype == HINTZONE)) {
1906                         tresult = fileexist(fileobj, files, ISC_FALSE, logctx);
1907                         if (tresult != ISC_R_SUCCESS)
1908                                 result = tresult;
1909                 }
1910         }
1911
1912         return (result);
1913 }
1914
1915
1916 typedef struct keyalgorithms {
1917         const char *name;
1918         isc_uint16_t size;
1919 } algorithmtable;
1920
1921 isc_result_t
1922 bind9_check_key(const cfg_obj_t *key, isc_log_t *logctx) {
1923         const cfg_obj_t *algobj = NULL;
1924         const cfg_obj_t *secretobj = NULL;
1925         const char *keyname = cfg_obj_asstring(cfg_map_getname(key));
1926         const char *algorithm;
1927         int i;
1928         size_t len = 0;
1929         isc_result_t result;
1930         isc_buffer_t buf;
1931         unsigned char secretbuf[1024];
1932         static const algorithmtable algorithms[] = {
1933                 { "hmac-md5", 128 },
1934                 { "hmac-md5.sig-alg.reg.int", 0 },
1935                 { "hmac-md5.sig-alg.reg.int.", 0 },
1936                 { "hmac-sha1", 160 },
1937                 { "hmac-sha224", 224 },
1938                 { "hmac-sha256", 256 },
1939                 { "hmac-sha384", 384 },
1940                 { "hmac-sha512", 512 },
1941                 {  NULL, 0 }
1942         };
1943
1944         (void)cfg_map_get(key, "algorithm", &algobj);
1945         (void)cfg_map_get(key, "secret", &secretobj);
1946         if (secretobj == NULL || algobj == NULL) {
1947                 cfg_obj_log(key, logctx, ISC_LOG_ERROR,
1948                             "key '%s' must have both 'secret' and "
1949                             "'algorithm' defined",
1950                             keyname);
1951                 return (ISC_R_FAILURE);
1952         }
1953
1954         isc_buffer_init(&buf, secretbuf, sizeof(secretbuf));
1955         result = isc_base64_decodestring(cfg_obj_asstring(secretobj), &buf);
1956         if (result != ISC_R_SUCCESS) {
1957                 cfg_obj_log(secretobj, logctx, ISC_LOG_ERROR,
1958                             "bad secret '%s'", isc_result_totext(result));
1959                 return (result);
1960         }
1961
1962         algorithm = cfg_obj_asstring(algobj);
1963         for (i = 0; algorithms[i].name != NULL; i++) {
1964                 len = strlen(algorithms[i].name);
1965                 if (strncasecmp(algorithms[i].name, algorithm, len) == 0 &&
1966                     (algorithm[len] == '\0' ||
1967                      (algorithms[i].size != 0 && algorithm[len] == '-')))
1968                         break;
1969         }
1970         if (algorithms[i].name == NULL) {
1971                 cfg_obj_log(algobj, logctx, ISC_LOG_ERROR,
1972                             "unknown algorithm '%s'", algorithm);
1973                 return (ISC_R_NOTFOUND);
1974         }
1975         if (algorithm[len] == '-') {
1976                 isc_uint16_t digestbits;
1977                 result = isc_parse_uint16(&digestbits, algorithm + len + 1, 10);
1978                 if (result == ISC_R_SUCCESS || result == ISC_R_RANGE) {
1979                         if (result == ISC_R_RANGE ||
1980                             digestbits > algorithms[i].size) {
1981                                 cfg_obj_log(algobj, logctx, ISC_LOG_ERROR,
1982                                             "key '%s' digest-bits too large "
1983                                             "[%u..%u]", keyname,
1984                                             algorithms[i].size / 2,
1985                                             algorithms[i].size);
1986                                 return (ISC_R_RANGE);
1987                         }
1988                         if ((digestbits % 8) != 0) {
1989                                 cfg_obj_log(algobj, logctx, ISC_LOG_ERROR,
1990                                             "key '%s' digest-bits not multiple"
1991                                             " of 8", keyname);
1992                                 return (ISC_R_RANGE);
1993                         }
1994                         /*
1995                          * Recommended minima for hmac algorithms.
1996                          */
1997                         if ((digestbits < (algorithms[i].size / 2U) ||
1998                              (digestbits < 80U)))
1999                                 cfg_obj_log(algobj, logctx, ISC_LOG_WARNING,
2000                                             "key '%s' digest-bits too small "
2001                                             "[<%u]", keyname,
2002                                             algorithms[i].size/2);
2003                 } else {
2004                         cfg_obj_log(algobj, logctx, ISC_LOG_ERROR,
2005                                     "key '%s': unable to parse digest-bits",
2006                                     keyname);
2007                         return (result);
2008                 }
2009         }
2010         return (ISC_R_SUCCESS);
2011 }
2012
2013 static isc_result_t
2014 fileexist(const cfg_obj_t *obj, isc_symtab_t *symtab, isc_boolean_t writeable,
2015           isc_log_t *logctx)
2016 {
2017         isc_result_t result;
2018         isc_symvalue_t symvalue;
2019         unsigned int line;
2020         const char *file;
2021
2022         result = isc_symtab_lookup(symtab, cfg_obj_asstring(obj), 0, &symvalue);
2023         if (result == ISC_R_SUCCESS) {
2024                 if (writeable) {
2025                         file = cfg_obj_file(symvalue.as_cpointer);
2026                         line = cfg_obj_line(symvalue.as_cpointer);
2027                         cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
2028                                     "writeable file '%s': already in use: "
2029                                     "%s:%u", cfg_obj_asstring(obj),
2030                                     file, line);
2031                         return (ISC_R_EXISTS);
2032                 }
2033                 result = isc_symtab_lookup(symtab, cfg_obj_asstring(obj), 2,
2034                                            &symvalue);
2035                 if (result == ISC_R_SUCCESS) {
2036                         file = cfg_obj_file(symvalue.as_cpointer);
2037                         line = cfg_obj_line(symvalue.as_cpointer);
2038                         cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
2039                                     "writeable file '%s': already in use: "
2040                                     "%s:%u", cfg_obj_asstring(obj),
2041                                     file, line);
2042                         return (ISC_R_EXISTS);
2043                 }
2044                 return (ISC_R_SUCCESS);
2045         }
2046
2047         symvalue.as_cpointer = obj;
2048         result = isc_symtab_define(symtab, cfg_obj_asstring(obj),
2049                                    writeable ? 2 : 1, symvalue,
2050                                    isc_symexists_reject);
2051         return (result);
2052 }
2053
2054 /*
2055  * Check key list for duplicates key names and that the key names
2056  * are valid domain names as these keys are used for TSIG.
2057  *
2058  * Check the key contents for validity.
2059  */
2060 static isc_result_t
2061 check_keylist(const cfg_obj_t *keys, isc_symtab_t *symtab,
2062               isc_mem_t *mctx, isc_log_t *logctx)
2063 {
2064         char namebuf[DNS_NAME_FORMATSIZE];
2065         dns_fixedname_t fname;
2066         dns_name_t *name;
2067         isc_result_t result = ISC_R_SUCCESS;
2068         isc_result_t tresult;
2069         const cfg_listelt_t *element;
2070
2071         dns_fixedname_init(&fname);
2072         name = dns_fixedname_name(&fname);
2073         for (element = cfg_list_first(keys);
2074              element != NULL;
2075              element = cfg_list_next(element))
2076         {
2077                 const cfg_obj_t *key = cfg_listelt_value(element);
2078                 const char *keyid = cfg_obj_asstring(cfg_map_getname(key));
2079                 isc_symvalue_t symvalue;
2080                 isc_buffer_t b;
2081                 char *keyname;
2082
2083                 isc_buffer_constinit(&b, keyid, strlen(keyid));
2084                 isc_buffer_add(&b, strlen(keyid));
2085                 tresult = dns_name_fromtext(name, &b, dns_rootname,
2086                                             0, NULL);
2087                 if (tresult != ISC_R_SUCCESS) {
2088                         cfg_obj_log(key, logctx, ISC_LOG_ERROR,
2089                                     "key '%s': bad key name", keyid);
2090                         result = tresult;
2091                         continue;
2092                 }
2093                 tresult = bind9_check_key(key, logctx);
2094                 if (tresult != ISC_R_SUCCESS)
2095                         return (tresult);
2096
2097                 dns_name_format(name, namebuf, sizeof(namebuf));
2098                 keyname = isc_mem_strdup(mctx, namebuf);
2099                 if (keyname == NULL)
2100                         return (ISC_R_NOMEMORY);
2101                 symvalue.as_cpointer = key;
2102                 tresult = isc_symtab_define(symtab, keyname, 1, symvalue,
2103                                             isc_symexists_reject);
2104                 if (tresult == ISC_R_EXISTS) {
2105                         const char *file;
2106                         unsigned int line;
2107
2108                         RUNTIME_CHECK(isc_symtab_lookup(symtab, keyname,
2109                                             1, &symvalue) == ISC_R_SUCCESS);
2110                         file = cfg_obj_file(symvalue.as_cpointer);
2111                         line = cfg_obj_line(symvalue.as_cpointer);
2112
2113                         if (file == NULL)
2114                                 file = "<unknown file>";
2115                         cfg_obj_log(key, logctx, ISC_LOG_ERROR,
2116                                     "key '%s': already exists "
2117                                     "previous definition: %s:%u",
2118                                     keyid, file, line);
2119                         isc_mem_free(mctx, keyname);
2120                         result = tresult;
2121                 } else if (tresult != ISC_R_SUCCESS) {
2122                         isc_mem_free(mctx, keyname);
2123                         return (tresult);
2124                 }
2125         }
2126         return (result);
2127 }
2128
2129 static struct {
2130         const char *v4;
2131         const char *v6;
2132 } sources[] = {
2133         { "transfer-source", "transfer-source-v6" },
2134         { "notify-source", "notify-source-v6" },
2135         { "query-source", "query-source-v6" },
2136         { NULL, NULL }
2137 };
2138
2139 /*
2140  * RNDC keys are not normalised unlike TSIG keys.
2141  *
2142  *      "foo." is different to "foo".
2143  */
2144 static isc_boolean_t
2145 rndckey_exists(const cfg_obj_t *keylist, const char *keyname) {
2146         const cfg_listelt_t *element;
2147         const cfg_obj_t *obj;
2148         const char *str;
2149
2150         if (keylist == NULL)
2151                 return (ISC_FALSE);
2152
2153         for (element = cfg_list_first(keylist);
2154              element != NULL;
2155              element = cfg_list_next(element))
2156         {
2157                 obj = cfg_listelt_value(element);
2158                 str = cfg_obj_asstring(cfg_map_getname(obj));
2159                 if (!strcasecmp(str, keyname))
2160                         return (ISC_TRUE);
2161         }
2162         return (ISC_FALSE);
2163 }
2164
2165 static isc_result_t
2166 check_servers(const cfg_obj_t *config, const cfg_obj_t *voptions,
2167               isc_symtab_t *symtab, isc_log_t *logctx)
2168 {
2169         dns_fixedname_t fname;
2170         isc_result_t result = ISC_R_SUCCESS;
2171         isc_result_t tresult;
2172         const cfg_listelt_t *e1, *e2;
2173         const cfg_obj_t *v1, *v2, *keys;
2174         const cfg_obj_t *servers;
2175         isc_netaddr_t n1, n2;
2176         unsigned int p1, p2;
2177         const cfg_obj_t *obj;
2178         char buf[ISC_NETADDR_FORMATSIZE];
2179         char namebuf[DNS_NAME_FORMATSIZE];
2180         const char *xfr;
2181         const char *keyval;
2182         isc_buffer_t b;
2183         int source;
2184         dns_name_t *keyname;
2185
2186         servers = NULL;
2187         if (voptions != NULL)
2188                 (void)cfg_map_get(voptions, "server", &servers);
2189         if (servers == NULL)
2190                 (void)cfg_map_get(config, "server", &servers);
2191         if (servers == NULL)
2192                 return (ISC_R_SUCCESS);
2193
2194         for (e1 = cfg_list_first(servers); e1 != NULL; e1 = cfg_list_next(e1)) {
2195                 v1 = cfg_listelt_value(e1);
2196                 cfg_obj_asnetprefix(cfg_map_getname(v1), &n1, &p1);
2197                 /*
2198                  * Check that unused bits are zero.
2199                  */
2200                 tresult = isc_netaddr_prefixok(&n1, p1);
2201                 if (tresult != ISC_R_SUCCESS) {
2202                         INSIST(tresult == ISC_R_FAILURE);
2203                         isc_netaddr_format(&n1, buf, sizeof(buf));
2204                         cfg_obj_log(v1, logctx, ISC_LOG_ERROR,
2205                                     "server '%s/%u': invalid prefix "
2206                                     "(extra bits specified)", buf, p1);
2207                         result = tresult;
2208                 }
2209                 source = 0;
2210                 do {
2211                         obj = NULL;
2212                         if (n1.family == AF_INET)
2213                                 xfr = sources[source].v6;
2214                         else
2215                                 xfr = sources[source].v4;
2216                         (void)cfg_map_get(v1, xfr, &obj);
2217                         if (obj != NULL) {
2218                                 isc_netaddr_format(&n1, buf, sizeof(buf));
2219                                 cfg_obj_log(v1, logctx, ISC_LOG_ERROR,
2220                                             "server '%s/%u': %s not legal",
2221                                             buf, p1, xfr);
2222                                 result = ISC_R_FAILURE;
2223                         }
2224                 } while (sources[++source].v4 != NULL);
2225                 e2 = e1;
2226                 while ((e2 = cfg_list_next(e2)) != NULL) {
2227                         v2 = cfg_listelt_value(e2);
2228                         cfg_obj_asnetprefix(cfg_map_getname(v2), &n2, &p2);
2229                         if (p1 == p2 && isc_netaddr_equal(&n1, &n2)) {
2230                                 const char *file = cfg_obj_file(v1);
2231                                 unsigned int line = cfg_obj_line(v1);
2232
2233                                 if (file == NULL)
2234                                         file = "<unknown file>";
2235
2236                                 isc_netaddr_format(&n2, buf, sizeof(buf));
2237                                 cfg_obj_log(v2, logctx, ISC_LOG_ERROR,
2238                                             "server '%s/%u': already exists "
2239                                             "previous definition: %s:%u",
2240                                             buf, p2, file, line);
2241                                 result = ISC_R_FAILURE;
2242                         }
2243                 }
2244                 keys = NULL;
2245                 cfg_map_get(v1, "keys", &keys);
2246                 if (keys != NULL) {
2247                         /*
2248                          * Normalize key name.
2249                          */
2250                         keyval = cfg_obj_asstring(keys);
2251                         dns_fixedname_init(&fname);
2252                         isc_buffer_constinit(&b, keyval, strlen(keyval));
2253                         isc_buffer_add(&b, strlen(keyval));
2254                         keyname = dns_fixedname_name(&fname);
2255                         tresult = dns_name_fromtext(keyname, &b, dns_rootname,
2256                                                     0, NULL);
2257                         if (tresult != ISC_R_SUCCESS) {
2258                                 cfg_obj_log(keys, logctx, ISC_LOG_ERROR,
2259                                             "bad key name '%s'", keyval);
2260                                 result = ISC_R_FAILURE;
2261                                 continue;
2262                         }
2263                         dns_name_format(keyname, namebuf, sizeof(namebuf));
2264                         tresult = isc_symtab_lookup(symtab, namebuf, 1, NULL);
2265                         if (tresult != ISC_R_SUCCESS) {
2266                                 cfg_obj_log(keys, logctx, ISC_LOG_ERROR,
2267                                             "unknown key '%s'", keyval);
2268                                 result = ISC_R_FAILURE;
2269                         }
2270                 }
2271         }
2272         return (result);
2273 }
2274
2275 static isc_result_t
2276 check_trusted_key(const cfg_obj_t *key, isc_boolean_t managed,
2277                   isc_log_t *logctx)
2278 {
2279         const char *keystr, *keynamestr;
2280         dns_fixedname_t fkeyname;
2281         dns_name_t *keyname;
2282         isc_buffer_t b;
2283         isc_region_t r;
2284         isc_result_t result = ISC_R_SUCCESS;
2285         isc_result_t tresult;
2286         isc_uint32_t flags, proto, alg;
2287         unsigned char keydata[4096];
2288
2289         flags = cfg_obj_asuint32(cfg_tuple_get(key, "flags"));
2290         proto = cfg_obj_asuint32(cfg_tuple_get(key, "protocol"));
2291         alg = cfg_obj_asuint32(cfg_tuple_get(key, "algorithm"));
2292
2293         dns_fixedname_init(&fkeyname);
2294         keyname = dns_fixedname_name(&fkeyname);
2295         keynamestr = cfg_obj_asstring(cfg_tuple_get(key, "name"));
2296
2297         isc_buffer_constinit(&b, keynamestr, strlen(keynamestr));
2298         isc_buffer_add(&b, strlen(keynamestr));
2299         result = dns_name_fromtext(keyname, &b, dns_rootname, 0, NULL);
2300         if (result != ISC_R_SUCCESS) {
2301                 cfg_obj_log(key, logctx, ISC_LOG_WARNING, "bad key name: %s\n",
2302                             isc_result_totext(result));
2303                 result = ISC_R_FAILURE;
2304         }
2305
2306         if (flags > 0xffff) {
2307                 cfg_obj_log(key, logctx, ISC_LOG_WARNING,
2308                             "flags too big: %u\n", flags);
2309                 result = ISC_R_FAILURE;
2310         }
2311         if (proto > 0xff) {
2312                 cfg_obj_log(key, logctx, ISC_LOG_WARNING,
2313                             "protocol too big: %u\n", proto);
2314                 result = ISC_R_FAILURE;
2315         }
2316         if (alg > 0xff) {
2317                 cfg_obj_log(key, logctx, ISC_LOG_WARNING,
2318                             "algorithm too big: %u\n", alg);
2319                 result = ISC_R_FAILURE;
2320         }
2321
2322         if (managed) {
2323                 const char *initmethod;
2324                 initmethod = cfg_obj_asstring(cfg_tuple_get(key, "init"));
2325
2326                 if (strcasecmp(initmethod, "initial-key") != 0) {
2327                         cfg_obj_log(key, logctx, ISC_LOG_ERROR,
2328                                     "managed key '%s': "
2329                                     "invalid initialization method '%s'",
2330                                     keynamestr, initmethod);
2331                         result = ISC_R_FAILURE;
2332                 }
2333         }
2334
2335         isc_buffer_init(&b, keydata, sizeof(keydata));
2336
2337         keystr = cfg_obj_asstring(cfg_tuple_get(key, "key"));
2338         tresult = isc_base64_decodestring(keystr, &b);
2339
2340         if (tresult != ISC_R_SUCCESS) {
2341                 cfg_obj_log(key, logctx, ISC_LOG_ERROR,
2342                             "%s", isc_result_totext(tresult));
2343                 result = ISC_R_FAILURE;
2344         } else {
2345                 isc_buffer_usedregion(&b, &r);
2346
2347                 if ((alg == DST_ALG_RSASHA1 || alg == DST_ALG_RSAMD5) &&
2348                     r.length > 1 && r.base[0] == 1 && r.base[1] == 3)
2349                         cfg_obj_log(key, logctx, ISC_LOG_WARNING,
2350                                     "%s key '%s' has a weak exponent",
2351                                     managed ? "managed" : "trusted",
2352                                     keynamestr);
2353         }
2354
2355         return (result);
2356 }
2357
2358 static isc_result_t
2359 check_viewconf(const cfg_obj_t *config, const cfg_obj_t *voptions,
2360                const char *viewname, dns_rdataclass_t vclass,
2361                isc_symtab_t *files, isc_log_t *logctx, isc_mem_t *mctx)
2362 {
2363         const cfg_obj_t *zones = NULL;
2364         const cfg_obj_t *keys = NULL;
2365         const cfg_listelt_t *element, *element2;
2366         isc_symtab_t *symtab = NULL;
2367         isc_result_t result = ISC_R_SUCCESS;
2368         isc_result_t tresult = ISC_R_SUCCESS;
2369         cfg_aclconfctx_t *actx = NULL;
2370         const cfg_obj_t *obj;
2371         const cfg_obj_t *options = NULL;
2372         isc_boolean_t enablednssec, enablevalidation;
2373         const char *valstr = "no";
2374
2375         /*
2376          * Get global options block
2377          */
2378         (void)cfg_map_get(config, "options", &options);
2379
2380         /*
2381          * Check that all zone statements are syntactically correct and
2382          * there are no duplicate zones.
2383          */
2384         tresult = isc_symtab_create(mctx, 1000, freekey, mctx,
2385                                     ISC_FALSE, &symtab);
2386         if (tresult != ISC_R_SUCCESS)
2387                 return (ISC_R_NOMEMORY);
2388
2389         cfg_aclconfctx_create(mctx, &actx);
2390
2391         if (voptions != NULL)
2392                 (void)cfg_map_get(voptions, "zone", &zones);
2393         else
2394                 (void)cfg_map_get(config, "zone", &zones);
2395
2396         for (element = cfg_list_first(zones);
2397              element != NULL;
2398              element = cfg_list_next(element))
2399         {
2400                 const cfg_obj_t *zone = cfg_listelt_value(element);
2401
2402                 tresult = check_zoneconf(zone, voptions, config, symtab,
2403                                          files, vclass, actx, logctx,
2404                                          mctx);
2405                 if (tresult != ISC_R_SUCCESS)
2406                         result = ISC_R_FAILURE;
2407         }
2408
2409         isc_symtab_destroy(&symtab);
2410
2411         /*
2412          * Check that forwarding is reasonable.
2413          */
2414         if (voptions == NULL) {
2415                 if (options != NULL)
2416                         if (check_forward(options, NULL,
2417                                           logctx) != ISC_R_SUCCESS)
2418                                 result = ISC_R_FAILURE;
2419         } else {
2420                 if (check_forward(voptions, NULL, logctx) != ISC_R_SUCCESS)
2421                         result = ISC_R_FAILURE;
2422         }
2423
2424         /*
2425          * Check non-zero options at the global and view levels.
2426          */
2427         if (options != NULL && check_nonzero(options, logctx) != ISC_R_SUCCESS)
2428                 result = ISC_R_FAILURE;
2429         if (voptions != NULL &&check_nonzero(voptions, logctx) != ISC_R_SUCCESS)
2430                 result = ISC_R_FAILURE;
2431
2432         /*
2433          * Check that dual-stack-servers is reasonable.
2434          */
2435         if (voptions == NULL) {
2436                 if (options != NULL)
2437                         if (check_dual_stack(options, logctx) != ISC_R_SUCCESS)
2438                                 result = ISC_R_FAILURE;
2439         } else {
2440                 if (check_dual_stack(voptions, logctx) != ISC_R_SUCCESS)
2441                         result = ISC_R_FAILURE;
2442         }
2443
2444         /*
2445          * Check that rrset-order is reasonable.
2446          */
2447         if (voptions != NULL) {
2448                 if (check_order(voptions, logctx) != ISC_R_SUCCESS)
2449                         result = ISC_R_FAILURE;
2450         }
2451
2452         /*
2453          * Check that all key statements are syntactically correct and
2454          * there are no duplicate keys.
2455          */
2456         tresult = isc_symtab_create(mctx, 1000, freekey, mctx,
2457                                     ISC_FALSE, &symtab);
2458         if (tresult != ISC_R_SUCCESS)
2459                 goto cleanup;
2460
2461         (void)cfg_map_get(config, "key", &keys);
2462         tresult = check_keylist(keys, symtab, mctx, logctx);
2463         if (tresult == ISC_R_EXISTS)
2464                 result = ISC_R_FAILURE;
2465         else if (tresult != ISC_R_SUCCESS) {
2466                 result = tresult;
2467                 goto cleanup;
2468         }
2469
2470         if (voptions != NULL) {
2471                 keys = NULL;
2472                 (void)cfg_map_get(voptions, "key", &keys);
2473                 tresult = check_keylist(keys, symtab, mctx, logctx);
2474                 if (tresult == ISC_R_EXISTS)
2475                         result = ISC_R_FAILURE;
2476                 else if (tresult != ISC_R_SUCCESS) {
2477                         result = tresult;
2478                         goto cleanup;
2479                 }
2480         }
2481
2482         /*
2483          * Global servers can refer to keys in views.
2484          */
2485         if (check_servers(config, voptions, symtab, logctx) != ISC_R_SUCCESS)
2486                 result = ISC_R_FAILURE;
2487
2488         isc_symtab_destroy(&symtab);
2489
2490         /*
2491          * Check that dnssec-enable/dnssec-validation are sensible.
2492          */
2493         obj = NULL;
2494         if (voptions != NULL)
2495                 (void)cfg_map_get(voptions, "dnssec-enable", &obj);
2496         if (obj == NULL && options != NULL)
2497                 (void)cfg_map_get(options, "dnssec-enable", &obj);
2498         if (obj == NULL)
2499                 enablednssec = ISC_TRUE;
2500         else
2501                 enablednssec = cfg_obj_asboolean(obj);
2502
2503         obj = NULL;
2504         if (voptions != NULL)
2505                 (void)cfg_map_get(voptions, "dnssec-validation", &obj);
2506         if (obj == NULL && options != NULL)
2507                 (void)cfg_map_get(options, "dnssec-validation", &obj);
2508         if (obj == NULL) {
2509                 enablevalidation = enablednssec;
2510                 valstr = "yes";
2511         } else if (cfg_obj_isboolean(obj)) {
2512                 enablevalidation = cfg_obj_asboolean(obj);
2513                 valstr = enablevalidation ? "yes" : "no";
2514         } else {
2515                 enablevalidation = ISC_TRUE;
2516                 valstr = "auto";
2517         }
2518
2519         if (enablevalidation && !enablednssec)
2520                 cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
2521                             "'dnssec-validation %s;' and 'dnssec-enable no;'",
2522                             valstr);
2523
2524         /*
2525          * Check trusted-keys and managed-keys.
2526          */
2527         keys = NULL;
2528         if (voptions != NULL)
2529                 (void)cfg_map_get(voptions, "trusted-keys", &keys);
2530         if (keys == NULL)
2531                 (void)cfg_map_get(config, "trusted-keys", &keys);
2532
2533         for (element = cfg_list_first(keys);
2534              element != NULL;
2535              element = cfg_list_next(element))
2536         {
2537                 const cfg_obj_t *keylist = cfg_listelt_value(element);
2538                 for (element2 = cfg_list_first(keylist);
2539                      element2 != NULL;
2540                      element2 = cfg_list_next(element2)) {
2541                         obj = cfg_listelt_value(element2);
2542                         tresult = check_trusted_key(obj, ISC_FALSE, logctx);
2543                         if (tresult != ISC_R_SUCCESS)
2544                                 result = tresult;
2545                 }
2546         }
2547
2548         keys = NULL;
2549         if (voptions != NULL)
2550                 (void)cfg_map_get(voptions, "managed-keys", &keys);
2551         if (keys == NULL)
2552                 (void)cfg_map_get(config, "managed-keys", &keys);
2553
2554         for (element = cfg_list_first(keys);
2555              element != NULL;
2556              element = cfg_list_next(element))
2557         {
2558                 const cfg_obj_t *keylist = cfg_listelt_value(element);
2559                 for (element2 = cfg_list_first(keylist);
2560                      element2 != NULL;
2561                      element2 = cfg_list_next(element2)) {
2562                         obj = cfg_listelt_value(element2);
2563                         tresult = check_trusted_key(obj, ISC_TRUE, logctx);
2564                         if (tresult != ISC_R_SUCCESS)
2565                                 result = tresult;
2566                 }
2567         }
2568
2569         /*
2570          * Check options.
2571          */
2572         if (voptions != NULL)
2573                 tresult = check_options(voptions, logctx, mctx,
2574                                         optlevel_view);
2575         else
2576                 tresult = check_options(config, logctx, mctx,
2577                                         optlevel_config);
2578         if (tresult != ISC_R_SUCCESS)
2579                 result = tresult;
2580
2581         tresult = check_viewacls(actx, voptions, config, logctx, mctx);
2582         if (tresult != ISC_R_SUCCESS)
2583                 result = tresult;
2584
2585         tresult = check_recursionacls(actx, voptions, viewname,
2586                                       config, logctx, mctx);
2587         if (tresult != ISC_R_SUCCESS)
2588                 result = tresult;
2589
2590         tresult = check_filteraaaa(actx, voptions, viewname, config,
2591                                    logctx, mctx);
2592         if (tresult != ISC_R_SUCCESS)
2593                 result = tresult;
2594
2595         tresult = check_dns64(actx, voptions, config, logctx, mctx);
2596         if (tresult != ISC_R_SUCCESS)
2597                 result = tresult;
2598
2599  cleanup:
2600         if (symtab != NULL)
2601                 isc_symtab_destroy(&symtab);
2602         if (actx != NULL)
2603                 cfg_aclconfctx_detach(&actx);
2604
2605         return (result);
2606 }
2607
2608 static const char *
2609 default_channels[] = {
2610         "default_syslog",
2611         "default_stderr",
2612         "default_debug",
2613         "null",
2614         NULL
2615 };
2616
2617 static isc_result_t
2618 bind9_check_logging(const cfg_obj_t *config, isc_log_t *logctx,
2619                     isc_mem_t *mctx)
2620 {
2621         const cfg_obj_t *categories = NULL;
2622         const cfg_obj_t *category;
2623         const cfg_obj_t *channels = NULL;
2624         const cfg_obj_t *channel;
2625         const cfg_listelt_t *element;
2626         const cfg_listelt_t *delement;
2627         const char *channelname;
2628         const char *catname;
2629         const cfg_obj_t *fileobj = NULL;
2630         const cfg_obj_t *syslogobj = NULL;
2631         const cfg_obj_t *nullobj = NULL;
2632         const cfg_obj_t *stderrobj = NULL;
2633         const cfg_obj_t *logobj = NULL;
2634         isc_result_t result = ISC_R_SUCCESS;
2635         isc_result_t tresult;
2636         isc_symtab_t *symtab = NULL;
2637         isc_symvalue_t symvalue;
2638         int i;
2639
2640         (void)cfg_map_get(config, "logging", &logobj);
2641         if (logobj == NULL)
2642                 return (ISC_R_SUCCESS);
2643
2644         result = isc_symtab_create(mctx, 100, NULL, NULL, ISC_FALSE, &symtab);
2645         if (result != ISC_R_SUCCESS)
2646                 return (result);
2647
2648         symvalue.as_cpointer = NULL;
2649         for (i = 0; default_channels[i] != NULL; i++) {
2650                 tresult = isc_symtab_define(symtab, default_channels[i], 1,
2651                                             symvalue, isc_symexists_replace);
2652                 if (tresult != ISC_R_SUCCESS)
2653                         result = tresult;
2654         }
2655
2656         cfg_map_get(logobj, "channel", &channels);
2657
2658         for (element = cfg_list_first(channels);
2659              element != NULL;
2660              element = cfg_list_next(element))
2661         {
2662                 channel = cfg_listelt_value(element);
2663                 channelname = cfg_obj_asstring(cfg_map_getname(channel));
2664                 fileobj = syslogobj = nullobj = stderrobj = NULL;
2665                 (void)cfg_map_get(channel, "file", &fileobj);
2666                 (void)cfg_map_get(channel, "syslog", &syslogobj);
2667                 (void)cfg_map_get(channel, "null", &nullobj);
2668                 (void)cfg_map_get(channel, "stderr", &stderrobj);
2669                 i = 0;
2670                 if (fileobj != NULL)
2671                         i++;
2672                 if (syslogobj != NULL)
2673                         i++;
2674                 if (nullobj != NULL)
2675                         i++;
2676                 if (stderrobj != NULL)
2677                         i++;
2678                 if (i != 1) {
2679                         cfg_obj_log(channel, logctx, ISC_LOG_ERROR,
2680                                     "channel '%s': exactly one of file, syslog, "
2681                                     "null, and stderr must be present",
2682                                      channelname);
2683                         result = ISC_R_FAILURE;
2684                 }
2685                 tresult = isc_symtab_define(symtab, channelname, 1,
2686                                             symvalue, isc_symexists_replace);
2687                 if (tresult != ISC_R_SUCCESS)
2688                         result = tresult;
2689         }
2690
2691         cfg_map_get(logobj, "category", &categories);
2692
2693         for (element = cfg_list_first(categories);
2694              element != NULL;
2695              element = cfg_list_next(element))
2696         {
2697                 category = cfg_listelt_value(element);
2698                 catname = cfg_obj_asstring(cfg_tuple_get(category, "name"));
2699                 if (isc_log_categorybyname(logctx, catname) == NULL) {
2700                         cfg_obj_log(category, logctx, ISC_LOG_ERROR,
2701                                     "undefined category: '%s'", catname);
2702                         result = ISC_R_FAILURE;
2703                 }
2704                 channels = cfg_tuple_get(category, "destinations");
2705                 for (delement = cfg_list_first(channels);
2706                      delement != NULL;
2707                      delement = cfg_list_next(delement))
2708                 {
2709                         channel = cfg_listelt_value(delement);
2710                         channelname = cfg_obj_asstring(channel);
2711                         tresult = isc_symtab_lookup(symtab, channelname, 1,
2712                                                     &symvalue);
2713                         if (tresult != ISC_R_SUCCESS) {
2714                                 cfg_obj_log(channel, logctx, ISC_LOG_ERROR,
2715                                             "undefined channel: '%s'",
2716                                             channelname);
2717                                 result = tresult;
2718                         }
2719                 }
2720         }
2721         isc_symtab_destroy(&symtab);
2722         return (result);
2723 }
2724
2725 static isc_result_t
2726 bind9_check_controlskeys(const cfg_obj_t *control, const cfg_obj_t *keylist,
2727                          isc_log_t *logctx)
2728 {
2729         isc_result_t result = ISC_R_SUCCESS;
2730         const cfg_obj_t *control_keylist;
2731         const cfg_listelt_t *element;
2732         const cfg_obj_t *key;
2733         const char *keyval;
2734
2735         control_keylist = cfg_tuple_get(control, "keys");
2736         if (cfg_obj_isvoid(control_keylist))
2737                 return (ISC_R_SUCCESS);
2738
2739         for (element = cfg_list_first(control_keylist);
2740              element != NULL;
2741              element = cfg_list_next(element))
2742         {
2743                 key = cfg_listelt_value(element);
2744                 keyval = cfg_obj_asstring(key);
2745
2746                 if (!rndckey_exists(keylist, keyval)) {
2747                         cfg_obj_log(key, logctx, ISC_LOG_ERROR,
2748                                     "unknown key '%s'", keyval);
2749                         result = ISC_R_NOTFOUND;
2750                 }
2751         }
2752         return (result);
2753 }
2754
2755 static isc_result_t
2756 bind9_check_controls(const cfg_obj_t *config, isc_log_t *logctx,
2757                      isc_mem_t *mctx)
2758 {
2759         isc_result_t result = ISC_R_SUCCESS, tresult;
2760         cfg_aclconfctx_t *actx = NULL;
2761         const cfg_listelt_t *element, *element2;
2762         const cfg_obj_t *allow;
2763         const cfg_obj_t *control;
2764         const cfg_obj_t *controls;
2765         const cfg_obj_t *controlslist = NULL;
2766         const cfg_obj_t *inetcontrols;
2767         const cfg_obj_t *unixcontrols;
2768         const cfg_obj_t *keylist = NULL;
2769         const char *path;
2770         isc_uint32_t perm, mask;
2771         dns_acl_t *acl = NULL;
2772         isc_sockaddr_t addr;
2773         int i;
2774
2775         (void)cfg_map_get(config, "controls", &controlslist);
2776         if (controlslist == NULL)
2777                 return (ISC_R_SUCCESS);
2778
2779         (void)cfg_map_get(config, "key", &keylist);
2780
2781         cfg_aclconfctx_create(mctx, &actx);
2782
2783         /*
2784          * INET: Check allow clause.
2785          * UNIX: Check "perm" for sanity, check path length.
2786          */
2787         for (element = cfg_list_first(controlslist);
2788              element != NULL;
2789              element = cfg_list_next(element)) {
2790                 controls = cfg_listelt_value(element);
2791                 unixcontrols = NULL;
2792                 inetcontrols = NULL;
2793                 (void)cfg_map_get(controls, "unix", &unixcontrols);
2794                 (void)cfg_map_get(controls, "inet", &inetcontrols);
2795                 for (element2 = cfg_list_first(inetcontrols);
2796                      element2 != NULL;
2797                      element2 = cfg_list_next(element2)) {
2798                         control = cfg_listelt_value(element2);
2799                         allow = cfg_tuple_get(control, "allow");
2800                         tresult = cfg_acl_fromconfig(allow, config, logctx,
2801                                                      actx, mctx, 0, &acl);
2802                         if (acl != NULL)
2803                                 dns_acl_detach(&acl);
2804                         if (tresult != ISC_R_SUCCESS)
2805                                 result = tresult;
2806                         tresult = bind9_check_controlskeys(control, keylist,
2807                                                            logctx);
2808                         if (tresult != ISC_R_SUCCESS)
2809                                 result = tresult;
2810                 }
2811                 for (element2 = cfg_list_first(unixcontrols);
2812                      element2 != NULL;
2813                      element2 = cfg_list_next(element2)) {
2814                         control = cfg_listelt_value(element2);
2815                         path = cfg_obj_asstring(cfg_tuple_get(control, "path"));
2816                         tresult = isc_sockaddr_frompath(&addr, path);
2817                         if (tresult == ISC_R_NOSPACE) {
2818                                 cfg_obj_log(control, logctx, ISC_LOG_ERROR,
2819                                             "unix control '%s': path too long",
2820                                             path);
2821                                 result = ISC_R_NOSPACE;
2822                         }
2823                         perm = cfg_obj_asuint32(cfg_tuple_get(control, "perm"));
2824                         for (i = 0; i < 3; i++) {
2825 #ifdef NEED_SECURE_DIRECTORY
2826                                 mask = (0x1 << (i*3));  /* SEARCH */
2827 #else
2828                                 mask = (0x6 << (i*3));  /* READ + WRITE */
2829 #endif
2830                                 if ((perm & mask) == mask)
2831                                         break;
2832                         }
2833                         if (i == 0) {
2834                                 cfg_obj_log(control, logctx, ISC_LOG_WARNING,
2835                                             "unix control '%s' allows access "
2836                                             "to everyone", path);
2837                         } else if (i == 3) {
2838                                 cfg_obj_log(control, logctx, ISC_LOG_WARNING,
2839                                             "unix control '%s' allows access "
2840                                             "to nobody", path);
2841                         }
2842                         tresult = bind9_check_controlskeys(control, keylist,
2843                                                            logctx);
2844                         if (tresult != ISC_R_SUCCESS)
2845                                 result = tresult;
2846                 }
2847         }
2848         cfg_aclconfctx_detach(&actx);
2849         return (result);
2850 }
2851
2852 isc_result_t
2853 bind9_check_namedconf(const cfg_obj_t *config, isc_log_t *logctx,
2854                       isc_mem_t *mctx)
2855 {
2856         const cfg_obj_t *options = NULL;
2857         const cfg_obj_t *views = NULL;
2858         const cfg_obj_t *acls = NULL;
2859         const cfg_obj_t *kals = NULL;
2860         const cfg_obj_t *obj;
2861         const cfg_listelt_t *velement;
2862         isc_result_t result = ISC_R_SUCCESS;
2863         isc_result_t tresult;
2864         isc_symtab_t *symtab = NULL;
2865         isc_symtab_t *files = NULL;
2866
2867         static const char *builtin[] = { "localhost", "localnets",
2868                                          "any", "none"};
2869
2870         (void)cfg_map_get(config, "options", &options);
2871
2872         if (options != NULL &&
2873             check_options(options, logctx, mctx,
2874                           optlevel_options) != ISC_R_SUCCESS)
2875                 result = ISC_R_FAILURE;
2876
2877         if (bind9_check_logging(config, logctx, mctx) != ISC_R_SUCCESS)
2878                 result = ISC_R_FAILURE;
2879
2880         if (bind9_check_controls(config, logctx, mctx) != ISC_R_SUCCESS)
2881                 result = ISC_R_FAILURE;
2882
2883         if (options != NULL &&
2884             check_order(options, logctx) != ISC_R_SUCCESS)
2885                 result = ISC_R_FAILURE;
2886
2887         (void)cfg_map_get(config, "view", &views);
2888
2889         if (views != NULL && options != NULL)
2890                 if (check_dual_stack(options, logctx) != ISC_R_SUCCESS)
2891                         result = ISC_R_FAILURE;
2892
2893         /*
2894          * Use case insensitve comparision as not all file systems are
2895          * case sensitive. This will prevent people using FOO.DB and foo.db
2896          * on case sensitive file systems but that shouldn't be a major issue.
2897          */
2898         tresult = isc_symtab_create(mctx, 100, NULL, NULL, ISC_FALSE,
2899                                     &files);
2900         if (tresult != ISC_R_SUCCESS)
2901                 result = tresult;
2902
2903         if (views == NULL) {
2904                 if (check_viewconf(config, NULL, NULL, dns_rdataclass_in,
2905                                    files, logctx, mctx) != ISC_R_SUCCESS)
2906                         result = ISC_R_FAILURE;
2907         } else {
2908                 const cfg_obj_t *zones = NULL;
2909
2910                 (void)cfg_map_get(config, "zone", &zones);
2911                 if (zones != NULL) {
2912                         cfg_obj_log(zones, logctx, ISC_LOG_ERROR,
2913                                     "when using 'view' statements, "
2914                                     "all zones must be in views");
2915                         result = ISC_R_FAILURE;
2916                 }
2917         }
2918
2919         tresult = isc_symtab_create(mctx, 100, NULL, NULL, ISC_TRUE, &symtab);
2920         if (tresult != ISC_R_SUCCESS)
2921                 result = tresult;
2922         for (velement = cfg_list_first(views);
2923              velement != NULL;
2924              velement = cfg_list_next(velement))
2925         {
2926                 const cfg_obj_t *view = cfg_listelt_value(velement);
2927                 const cfg_obj_t *vname = cfg_tuple_get(view, "name");
2928                 const cfg_obj_t *voptions = cfg_tuple_get(view, "options");
2929                 const cfg_obj_t *vclassobj = cfg_tuple_get(view, "class");
2930                 dns_rdataclass_t vclass = dns_rdataclass_in;
2931                 const char *key = cfg_obj_asstring(vname);
2932                 isc_symvalue_t symvalue;
2933                 unsigned int symtype;
2934
2935                 tresult = ISC_R_SUCCESS;
2936                 if (cfg_obj_isstring(vclassobj)) {
2937                         isc_textregion_t r;
2938
2939                         DE_CONST(cfg_obj_asstring(vclassobj), r.base);
2940                         r.length = strlen(r.base);
2941                         tresult = dns_rdataclass_fromtext(&vclass, &r);
2942                         if (tresult != ISC_R_SUCCESS)
2943                                 cfg_obj_log(vclassobj, logctx, ISC_LOG_ERROR,
2944                                             "view '%s': invalid class %s",
2945                                             cfg_obj_asstring(vname), r.base);
2946                 }
2947                 symtype = vclass + 1;
2948                 if (tresult == ISC_R_SUCCESS && symtab != NULL) {
2949                         symvalue.as_cpointer = view;
2950                         tresult = isc_symtab_define(symtab, key, symtype,
2951                                                     symvalue,
2952                                                     isc_symexists_reject);
2953                         if (tresult == ISC_R_EXISTS) {
2954                                 const char *file;
2955                                 unsigned int line;
2956                                 RUNTIME_CHECK(isc_symtab_lookup(symtab, key,
2957                                          symtype, &symvalue) == ISC_R_SUCCESS);
2958                                 file = cfg_obj_file(symvalue.as_cpointer);
2959                                 line = cfg_obj_line(symvalue.as_cpointer);
2960                                 cfg_obj_log(view, logctx, ISC_LOG_ERROR,
2961                                             "view '%s': already exists "
2962                                             "previous definition: %s:%u",
2963                                             key, file, line);
2964                                 result = tresult;
2965                         } else if (tresult != ISC_R_SUCCESS) {
2966                                 result = tresult;
2967                         } else if ((strcasecmp(key, "_bind") == 0 &&
2968                                     vclass == dns_rdataclass_ch) ||
2969                                    (strcasecmp(key, "_default") == 0 &&
2970                                     vclass == dns_rdataclass_in)) {
2971                                 cfg_obj_log(view, logctx, ISC_LOG_ERROR,
2972                                             "attempt to redefine builtin view "
2973                                             "'%s'", key);
2974                                 result = ISC_R_EXISTS;
2975                         }
2976                 }
2977                 if (tresult == ISC_R_SUCCESS)
2978                         tresult = check_viewconf(config, voptions, key, vclass,
2979                                                  files, logctx, mctx);
2980                 if (tresult != ISC_R_SUCCESS)
2981                         result = ISC_R_FAILURE;
2982         }
2983         if (symtab != NULL)
2984                 isc_symtab_destroy(&symtab);
2985         if (files != NULL)
2986                 isc_symtab_destroy(&files);
2987
2988         if (views != NULL && options != NULL) {
2989                 obj = NULL;
2990                 tresult = cfg_map_get(options, "cache-file", &obj);
2991                 if (tresult == ISC_R_SUCCESS) {
2992                         cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
2993                                     "'cache-file' cannot be a global "
2994                                     "option if views are present");
2995                         result = ISC_R_FAILURE;
2996                 }
2997         }
2998
2999         cfg_map_get(config, "acl", &acls);
3000
3001         if (acls != NULL) {
3002                 const cfg_listelt_t *elt;
3003                 const cfg_listelt_t *elt2;
3004                 const char *aclname;
3005
3006                 for (elt = cfg_list_first(acls);
3007                      elt != NULL;
3008                      elt = cfg_list_next(elt)) {
3009                         const cfg_obj_t *acl = cfg_listelt_value(elt);
3010                         unsigned int line = cfg_obj_line(acl);
3011                         unsigned int i;
3012
3013                         aclname = cfg_obj_asstring(cfg_tuple_get(acl, "name"));
3014                         for (i = 0;
3015                              i < sizeof(builtin) / sizeof(builtin[0]);
3016                              i++)
3017                                 if (strcasecmp(aclname, builtin[i]) == 0) {
3018                                         cfg_obj_log(acl, logctx, ISC_LOG_ERROR,
3019                                                     "attempt to redefine "
3020                                                     "builtin acl '%s'",
3021                                                     aclname);
3022                                         result = ISC_R_FAILURE;
3023                                         break;
3024                                 }
3025
3026                         for (elt2 = cfg_list_next(elt);
3027                              elt2 != NULL;
3028                              elt2 = cfg_list_next(elt2)) {
3029                                 const cfg_obj_t *acl2 = cfg_listelt_value(elt2);
3030                                 const char *name;
3031                                 name = cfg_obj_asstring(cfg_tuple_get(acl2,
3032                                                                       "name"));
3033                                 if (strcasecmp(aclname, name) == 0) {
3034                                         const char *file = cfg_obj_file(acl);
3035
3036                                         if (file == NULL)
3037                                                 file = "<unknown file>";
3038
3039                                         cfg_obj_log(acl2, logctx, ISC_LOG_ERROR,
3040                                                     "attempt to redefine "
3041                                                     "acl '%s' previous "
3042                                                     "definition: %s:%u",
3043                                                      name, file, line);
3044                                         result = ISC_R_FAILURE;
3045                                 }
3046                         }
3047                 }
3048         }
3049
3050         tresult = cfg_map_get(config, "kal", &kals);
3051         if (tresult == ISC_R_SUCCESS) {
3052                 const cfg_listelt_t *elt;
3053                 const cfg_listelt_t *elt2;
3054                 const char *aclname;
3055
3056                 for (elt = cfg_list_first(kals);
3057                      elt != NULL;
3058                      elt = cfg_list_next(elt)) {
3059                         const cfg_obj_t *acl = cfg_listelt_value(elt);
3060
3061                         aclname = cfg_obj_asstring(cfg_tuple_get(acl, "name"));
3062
3063                         for (elt2 = cfg_list_next(elt);
3064                              elt2 != NULL;
3065                              elt2 = cfg_list_next(elt2)) {
3066                                 const cfg_obj_t *acl2 = cfg_listelt_value(elt2);
3067                                 const char *name;
3068                                 name = cfg_obj_asstring(cfg_tuple_get(acl2,
3069                                                                       "name"));
3070                                 if (strcasecmp(aclname, name) == 0) {
3071                                         const char *file = cfg_obj_file(acl);
3072                                         unsigned int line = cfg_obj_line(acl);
3073
3074                                         if (file == NULL)
3075                                                 file = "<unknown file>";
3076
3077                                         cfg_obj_log(acl2, logctx, ISC_LOG_ERROR,
3078                                                     "attempt to redefine "
3079                                                     "kal '%s' previous "
3080                                                     "definition: %s:%u",
3081                                                      name, file, line);
3082                                         result = ISC_R_FAILURE;
3083                                 }
3084                         }
3085                 }
3086         }
3087
3088         return (result);
3089 }