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