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