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