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