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