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