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