check.c 56.8 KB
Newer Older
1
/*
Automatic Updater's avatar
Automatic Updater committed
2
 * Copyright (C) 2004-2007  Internet Systems Consortium, Inc. ("ISC")
Mark Andrews's avatar
Mark Andrews committed
3
 * Copyright (C) 2001-2003  Internet Software Consortium.
4
5
6
7
8
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
Mark Andrews's avatar
Mark Andrews committed
9
10
11
12
13
14
15
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
16
17
 */

Automatic Updater's avatar
Automatic Updater committed
18
/* $Id: check.c,v 1.78 2007/03/14 23:46:54 tbox Exp $ */
19
20

/*! \file */
21
22
23
24
25
26

#include <config.h>

#include <stdlib.h>
#include <string.h>

27
#include <isc/buffer.h>
28
#include <isc/log.h>
29
#include <isc/mem.h>
30
#include <isc/netaddr.h>
31
#include <isc/parseint.h>
32
#include <isc/region.h>
33
#include <isc/result.h>
34
#include <isc/sockaddr.h>
35
#include <isc/symtab.h>
Brian Wellington's avatar
Brian Wellington committed
36
#include <isc/util.h>
37

38
#include <dns/acl.h>
39
#include <dns/fixedname.h>
Mark Andrews's avatar
Mark Andrews committed
40
41
#include <dns/rdataclass.h>
#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
50
51
52
#ifndef DNS_RDATASET_FIXED
#define DNS_RDATASET_FIXED 1
#endif

53
54
55
56
57
58
59
static void
freekey(char *key, unsigned int type, isc_symvalue_t value, void *userarg) {
	UNUSED(type);
	UNUSED(value);
	isc_mem_free(userarg, key);
}

Mark Andrews's avatar
Mark Andrews committed
60
static isc_result_t
61
check_orderent(const cfg_obj_t *ent, isc_log_t *logctx) {
Mark Andrews's avatar
Mark Andrews committed
62
63
64
65
	isc_result_t result = ISC_R_SUCCESS;
	isc_result_t tresult;
	isc_textregion_t r;
	dns_fixedname_t fixed;
66
	const cfg_obj_t *obj;
Mark Andrews's avatar
Mark Andrews committed
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
	dns_rdataclass_t rdclass;
	dns_rdatatype_t rdtype;
	isc_buffer_t b;
	const char *str;

	dns_fixedname_init(&fixed);
	obj = cfg_tuple_get(ent, "class");
	if (cfg_obj_isstring(obj)) {

		DE_CONST(cfg_obj_asstring(obj), r.base);
		r.length = strlen(r.base);
		tresult = dns_rdataclass_fromtext(&rdclass, &r);
		if (tresult != ISC_R_SUCCESS) {
			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
				    "rrset-order: invalid class '%s'",
				    r.base);
			result = ISC_R_FAILURE;
		}
	}

	obj = cfg_tuple_get(ent, "type");
	if (cfg_obj_isstring(obj)) {

		DE_CONST(cfg_obj_asstring(obj), r.base);
		r.length = strlen(r.base);
		tresult = dns_rdatatype_fromtext(&rdtype, &r);
		if (tresult != ISC_R_SUCCESS) {
			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
				    "rrset-order: invalid type '%s'",
				    r.base);
			result = ISC_R_FAILURE;
		}
	}

	obj = cfg_tuple_get(ent, "name");
	if (cfg_obj_isstring(obj)) {
		str = cfg_obj_asstring(obj);
		isc_buffer_init(&b, str, strlen(str));
		isc_buffer_add(&b, strlen(str));
		tresult = dns_name_fromtext(dns_fixedname_name(&fixed), &b,
					    dns_rootname, ISC_FALSE, NULL);
		if (tresult != ISC_R_SUCCESS) {
			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
				    "rrset-order: invalid name '%s'", str);
			result = ISC_R_FAILURE;
		}
	}

	obj = cfg_tuple_get(ent, "order");
	if (!cfg_obj_isstring(obj) ||
	    strcasecmp("order", cfg_obj_asstring(obj)) != 0) {
		cfg_obj_log(ent, logctx, ISC_LOG_ERROR,
			    "rrset-order: keyword 'order' missing");
		result = ISC_R_FAILURE;
	}

	obj = cfg_tuple_get(ent, "ordering");
	if (!cfg_obj_isstring(obj)) {
	    cfg_obj_log(ent, logctx, ISC_LOG_ERROR,
			"rrset-order: missing ordering");
		result = ISC_R_FAILURE;
128
129
130
131
132
133
	} else if (strcasecmp(cfg_obj_asstring(obj), "fixed") == 0) {
#if !DNS_RDATASET_FIXED
		cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
			    "rrset-order: order 'fixed' not fully implemented");
#endif
	} else if (strcasecmp(cfg_obj_asstring(obj), "random") != 0 &&
Mark Andrews's avatar
Mark Andrews committed
134
135
136
137
138
139
140
141
142
143
		   strcasecmp(cfg_obj_asstring(obj), "cyclic") != 0) {
		cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
			    "rrset-order: invalid order '%s'",
			    cfg_obj_asstring(obj));
		result = ISC_R_FAILURE;
	}
	return (result);
}

static isc_result_t
144
check_order(const cfg_obj_t *options, isc_log_t *logctx) {
Mark Andrews's avatar
Mark Andrews committed
145
146
	isc_result_t result = ISC_R_SUCCESS;
	isc_result_t tresult;
147
148
	const cfg_listelt_t *element;
	const cfg_obj_t *obj = NULL;
Mark Andrews's avatar
Mark Andrews committed
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163

	if (cfg_map_get(options, "rrset-order", &obj) != ISC_R_SUCCESS)
		return (result);

	for (element = cfg_list_first(obj);
	     element != NULL;
	     element = cfg_list_next(element))
	{
		tresult = check_orderent(cfg_listelt_value(element), logctx);
		if (tresult != ISC_R_SUCCESS)
			result = tresult;
	}
	return (result);
}

164
static isc_result_t
165
166
167
168
169
check_dual_stack(const cfg_obj_t *options, isc_log_t *logctx) {
	const cfg_listelt_t *element;
	const cfg_obj_t *alternates = NULL;
	const cfg_obj_t *value;
	const cfg_obj_t *obj;
170
	const char *str;
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
	dns_fixedname_t fixed;
	dns_name_t *name;
	isc_buffer_t buffer;
	isc_result_t result = ISC_R_SUCCESS;
	isc_result_t tresult;

	(void)cfg_map_get(options, "dual-stack-servers", &alternates);

	if (alternates == NULL)
		return (ISC_R_SUCCESS);

	obj = cfg_tuple_get(alternates, "port");
	if (cfg_obj_isuint32(obj)) {
		isc_uint32_t val = cfg_obj_asuint32(obj);
		if (val > ISC_UINT16_MAX) {
			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
				    "port '%u' out of range", val);
			result = ISC_R_FAILURE;
		}
	}
	obj = cfg_tuple_get(alternates, "addresses");
	for (element = cfg_list_first(obj);
	     element != NULL;
	     element = cfg_list_next(element)) {
		value = cfg_listelt_value(element);
		if (cfg_obj_issockaddr(value))
			continue;
		obj = cfg_tuple_get(value, "name");
		str = cfg_obj_asstring(obj);
		isc_buffer_init(&buffer, str, strlen(str));
		isc_buffer_add(&buffer, strlen(str));
		dns_fixedname_init(&fixed);
		name = dns_fixedname_name(&fixed);
		tresult = dns_name_fromtext(name, &buffer, dns_rootname,
					   ISC_FALSE, NULL);
		if (tresult != ISC_R_SUCCESS) {
			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
				    "bad name '%s'", str);
			result = ISC_R_FAILURE;
		}
		obj = cfg_tuple_get(value, "port");
		if (cfg_obj_isuint32(obj)) {
			isc_uint32_t val = cfg_obj_asuint32(obj);
			if (val > ISC_UINT16_MAX) {
				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
					    "port '%u' out of range", val);
				result = ISC_R_FAILURE;
			}
		}
	}
	return (result);
}

224
static isc_result_t
225
226
227
check_forward(const cfg_obj_t *options, isc_log_t *logctx) {
	const cfg_obj_t *forward = NULL;
	const cfg_obj_t *forwarders = NULL;
228
229
230
231
232
233
234
235
236
237
238
239

	(void)cfg_map_get(options, "forward", &forward);
	(void)cfg_map_get(options, "forwarders", &forwarders);

	if (forward != NULL && forwarders == NULL) {
		cfg_obj_log(forward, logctx, ISC_LOG_ERROR,
			    "no matching 'forwarders' statement");
		return (ISC_R_FAILURE);
	}
	return (ISC_R_SUCCESS);
}

240
static isc_result_t
241
disabled_algorithms(const cfg_obj_t *disabled, isc_log_t *logctx) {
242
243
	isc_result_t result = ISC_R_SUCCESS;
	isc_result_t tresult;
244
	const cfg_listelt_t *element;
245
246
247
248
	const char *str;
	isc_buffer_t b;
	dns_fixedname_t fixed;
	dns_name_t *name;
249
	const cfg_obj_t *obj;
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273

	dns_fixedname_init(&fixed);
	name = dns_fixedname_name(&fixed);
	obj = cfg_tuple_get(disabled, "name");
	str = cfg_obj_asstring(obj);
	isc_buffer_init(&b, str, strlen(str));
	isc_buffer_add(&b, strlen(str));
	tresult = dns_name_fromtext(name, &b, dns_rootname, ISC_FALSE, NULL);
	if (tresult != ISC_R_SUCCESS) {
		cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
			    "bad domain name '%s'", str);
		result = tresult;
	}

	obj = cfg_tuple_get(disabled, "algorithms");

	for (element = cfg_list_first(obj);
	     element != NULL;
	     element = cfg_list_next(element))
	{
		isc_textregion_t r;
		dns_secalg_t alg;
		isc_result_t tresult;

274
		DE_CONST(cfg_obj_asstring(cfg_listelt_value(element)), r.base);
275
276
277
278
279
280
281
282
283
		r.length = strlen(r.base);

		tresult = dns_secalg_fromtext(&alg, &r);
		if (tresult != ISC_R_SUCCESS) {
			isc_uint8_t ui;
			result = isc_parse_uint8(&ui, r.base, 10);
		}
		if (tresult != ISC_R_SUCCESS) {
			cfg_obj_log(cfg_listelt_value(element), logctx,
284
285
				    ISC_LOG_ERROR, "invalid algorithm '%s'",
				    r.base);
286
287
288
289
290
291
			result = tresult;
		}
	}
	return (result);
}

292
static isc_result_t
293
294
295
nameexist(const cfg_obj_t *obj, const char *name, int value,
	  isc_symtab_t *symtab, const char *fmt, isc_log_t *logctx,
	  isc_mem_t *mctx)
296
297
298
299
300
301
302
303
304
305
{
	char *key;
	const char *file;
	unsigned int line;
	isc_result_t result;
	isc_symvalue_t symvalue;

	key = isc_mem_strdup(mctx, name);
	if (key == NULL)
		return (ISC_R_NOMEMORY);
306
	symvalue.as_cpointer = obj;
307
308
309
310
311
	result = isc_symtab_define(symtab, key, value, symvalue,
				   isc_symexists_reject);
	if (result == ISC_R_EXISTS) {
		RUNTIME_CHECK(isc_symtab_lookup(symtab, key, value,
						&symvalue) == ISC_R_SUCCESS);
312
313
		file = cfg_obj_file(symvalue.as_cpointer);
		line = cfg_obj_line(symvalue.as_cpointer);
314
315
316
317
318
319
320
321
322
323
324
325

		if (file == NULL)
			file = "<unknown file>";
		cfg_obj_log(obj, logctx, ISC_LOG_ERROR, fmt, key, file, line);
		isc_mem_free(mctx, key);
		result = ISC_R_EXISTS;
	} else if (result != ISC_R_SUCCESS) {
		isc_mem_free(mctx, key);
	}
	return (result);
}

326
static isc_result_t
327
mustbesecure(const cfg_obj_t *secure, isc_symtab_t *symtab, isc_log_t *logctx,
328
329
	     isc_mem_t *mctx)
{
330
	const cfg_obj_t *obj;
331
332
333
334
335
336
337
338
339
340
341
342
343
	char namebuf[DNS_NAME_FORMATSIZE];
	const char *str;
	dns_fixedname_t fixed;
	dns_name_t *name;
	isc_buffer_t b;
	isc_result_t result = ISC_R_SUCCESS;

	dns_fixedname_init(&fixed);
	name = dns_fixedname_name(&fixed);
	obj = cfg_tuple_get(secure, "name");
	str = cfg_obj_asstring(obj);
	isc_buffer_init(&b, str, strlen(str));
	isc_buffer_add(&b, strlen(str));
344
345
	result = dns_name_fromtext(name, &b, dns_rootname, ISC_FALSE, NULL);
	if (result != ISC_R_SUCCESS) {
346
347
348
349
		cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
			    "bad domain name '%s'", str);
	} else {
		dns_name_format(name, namebuf, sizeof(namebuf));
350
351
352
353
		result = nameexist(secure, namebuf, 1, symtab,
				   "dnssec-must-be-secure '%s': already "
				   "exists previous definition: %s:%u",
				   logctx, mctx);
354
355
356
357
	}
	return (result);
}

358
static isc_result_t
359
360
361
checkacl(const char *aclname, cfg_aclconfctx_t *actx, const cfg_obj_t *zconfig,
	 const cfg_obj_t *voptions, const cfg_obj_t *config,
	 isc_log_t *logctx, isc_mem_t *mctx)
362
363
{
	isc_result_t result;
364
365
	const cfg_obj_t *aclobj = NULL;
	const cfg_obj_t *options;
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
	dns_acl_t *acl = NULL;

	if (zconfig != NULL) {
		options = cfg_tuple_get(zconfig, "options");
		cfg_map_get(options, aclname, &aclobj);
	}
	if (voptions != NULL && aclobj == NULL)
		cfg_map_get(voptions, aclname, &aclobj);
	if (config != NULL && aclobj == NULL) {
		options = NULL;
		cfg_map_get(config, "options", &options);
		if (options != NULL)
			cfg_map_get(options, aclname, &aclobj);
	}
	if (aclobj == NULL)
		return (ISC_R_SUCCESS);
382
	result = cfg_acl_fromconfig(aclobj, config, logctx, actx, mctx, &acl);
383
384
385
386
387
388
	if (acl != NULL)
		dns_acl_detach(&acl);
	return (result);
}

static isc_result_t
389
390
check_viewacls(cfg_aclconfctx_t *actx, const cfg_obj_t *voptions,
	       const cfg_obj_t *config, isc_log_t *logctx, isc_mem_t *mctx)
391
392
393
394
395
396
397
398
399
{
	isc_result_t result = ISC_R_SUCCESS, tresult;
	int i = 0;
	
	static const char *acls[] = { "allow-query", "allow-query-cache",
		"allow-recursion", "blackhole", "match-clients",
		"match-destinations", "sortlist", NULL };

	while (acls[i] != NULL) {
400
		tresult = checkacl(acls[i++], actx, NULL, voptions, config,
401
402
403
404
405
406
407
				   logctx, mctx);
		if (tresult != ISC_R_SUCCESS)
			result = tresult;  
	}
	return (result);
}

408
409
410
typedef struct {
	const char *name;
	unsigned int scale;
411
	unsigned int max;
412
413
414
} intervaltable;

static isc_result_t
415
check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx) {
416
	isc_result_t result = ISC_R_SUCCESS;
417
	isc_result_t tresult;
418
	unsigned int i;
419
420
	const cfg_obj_t *obj = NULL;
	const cfg_listelt_t *element;
421
	isc_symtab_t *symtab = NULL;
422
423
424
425
	dns_fixedname_t fixed;
	const char *str;
	dns_name_t *name;
	isc_buffer_t b;
426
427

	static intervaltable intervals[] = {
428
429
430
431
432
433
434
435
436
	{ "cleaning-interval", 60, 28 * 24 * 60 },	/* 28 days */
	{ "heartbeat-interval", 60, 28 * 24 * 60 },	/* 28 days */
	{ "interface-interval", 60, 28 * 24 * 60 },	/* 28 days */
	{ "max-transfer-idle-in", 60, 28 * 24 * 60 },	/* 28 days */
	{ "max-transfer-idle-out", 60, 28 * 24 * 60 },	/* 28 days */
	{ "max-transfer-time-in", 60, 28 * 24 * 60 },	/* 28 days */
	{ "max-transfer-time-out", 60, 28 * 24 * 60 },	/* 28 days */
	{ "sig-validity-interval", 86400, 10 * 366 },	/* 10 years */
	{ "statistics-interval", 60, 28 * 24 * 60 },	/* 28 days */
437
438
439
440
441
442
443
444
	};

	/*
	 * Check that fields specified in units of time other than seconds
	 * have reasonable values.
	 */
	for (i = 0; i < sizeof(intervals) / sizeof(intervals[0]); i++) {
		isc_uint32_t val;
445
		obj = NULL;
446
447
448
449
		(void)cfg_map_get(options, intervals[i].name, &obj);
		if (obj == NULL)
			continue;
		val = cfg_obj_asuint32(obj);
450
451
452
453
454
455
456
		if (val > intervals[i].max) {
			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
				    "%s '%u' is out of range (0..%u)",
				    intervals[i].name, val,
				    intervals[i].max);
			result = ISC_R_RANGE;
		} else if (val > (ISC_UINT32_MAX / intervals[i].scale)) {
457
458
459
460
461
462
			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
				    "%s '%d' is out of range",
				    intervals[i].name, val);
			result = ISC_R_RANGE;
		}
	}
463
464
465
466
467
468
469
470
471
472
473
474
	obj = NULL;
	(void)cfg_map_get(options, "preferred-glue", &obj);
	if (obj != NULL) {
		const char *str;
                str = cfg_obj_asstring(obj);
                if (strcasecmp(str, "a") != 0 &&
		    strcasecmp(str, "aaaa") != 0 &&
		    strcasecmp(str, "none") != 0)
			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
				    "preferred-glue unexpected value '%s'",
				    str);
	}
475
476
477
478
	obj = NULL;
	(void)cfg_map_get(options, "root-delegation-only", &obj);
	if (obj != NULL) {
		if (!cfg_obj_isvoid(obj)) {
479
480
			const cfg_listelt_t *element;
			const cfg_obj_t *exclude;
481
			const char *str;
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
			dns_fixedname_t fixed;
			dns_name_t *name;
			isc_buffer_t b;

			dns_fixedname_init(&fixed);
			name = dns_fixedname_name(&fixed);
			for (element = cfg_list_first(obj);
			     element != NULL;
			     element = cfg_list_next(element)) {
				exclude = cfg_listelt_value(element);
				str = cfg_obj_asstring(exclude);
				isc_buffer_init(&b, str, strlen(str));
				isc_buffer_add(&b, strlen(str));
				tresult = dns_name_fromtext(name, &b,
							   dns_rootname,
                                                           ISC_FALSE, NULL);
				if (tresult != ISC_R_SUCCESS) {
					cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
						    "bad domain name '%s'",
						    str);
					result = tresult;
				}
			}
		}
	}
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
       
	/*
	 * Set supported DNSSEC algorithms.
	 */
	obj = NULL;
	(void)cfg_map_get(options, "disable-algorithms", &obj);
	if (obj != NULL) {
		for (element = cfg_list_first(obj);
		     element != NULL;
		     element = cfg_list_next(element))
		{
			obj = cfg_listelt_value(element);
			tresult = disabled_algorithms(obj, logctx);
			if (tresult != ISC_R_SUCCESS)
				result = tresult;
		}
	}

525
526
527
	dns_fixedname_init(&fixed);
	name = dns_fixedname_name(&fixed);

528
529
530
531
532
533
	/*
	 * Check the DLV zone name.
	 */
	obj = NULL;
	(void)cfg_map_get(options, "dnssec-lookaside", &obj);
	if (obj != NULL) {
534
535
536
		tresult = isc_symtab_create(mctx, 100, freekey, mctx,
					    ISC_TRUE, &symtab);
		if (tresult != ISC_R_SUCCESS)
537
			result = tresult;
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
		for (element = cfg_list_first(obj);
		     element != NULL;
		     element = cfg_list_next(element))
		{
			const char *dlv;

			obj = cfg_listelt_value(element);

			dlv = cfg_obj_asstring(cfg_tuple_get(obj, "domain"));
			isc_buffer_init(&b, dlv, strlen(dlv));
			isc_buffer_add(&b, strlen(dlv));
			tresult = dns_name_fromtext(name, &b, dns_rootname,
						    ISC_TRUE, NULL);
			if (tresult != ISC_R_SUCCESS) {
				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
					    "bad domain name '%s'", dlv);
				result = tresult;
			}
			if (symtab != NULL) {
				tresult = nameexist(obj, dlv, 1, symtab,
						    "dnssec-lookaside '%s': "
						    "already exists previous "
						    "definition: %s:%u",
						    logctx, mctx);
				if (tresult != ISC_R_SUCCESS &&
				    result == ISC_R_SUCCESS)
					result = tresult;
			}
			/*
			 * XXXMPA to be removed when multiple lookaside
			 * namespaces are supported.
			 */
			if (!dns_name_equal(dns_rootname, name)) {
				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
					    "dnssec-lookaside '%s': "
					    "non-root not yet supported", dlv);
				if (result == ISC_R_SUCCESS)
					result = ISC_R_FAILURE;
			}
			dlv = cfg_obj_asstring(cfg_tuple_get(obj,
					       "trust-anchor"));
			isc_buffer_init(&b, dlv, strlen(dlv));
			isc_buffer_add(&b, strlen(dlv));
			tresult = dns_name_fromtext(name, &b, dns_rootname,
						    ISC_TRUE, NULL);
			if (tresult != ISC_R_SUCCESS) {
				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
					    "bad domain name '%s'", dlv);
				if (result == ISC_R_SUCCESS)
					result = tresult;
			}
589
		}
590
591
		if (symtab != NULL)
			isc_symtab_destroy(&symtab);
592
	}
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617

	/*
	 * Check dnssec-must-be-secure.
	 */
	obj = NULL;
	(void)cfg_map_get(options, "dnssec-must-be-secure", &obj);
	if (obj != NULL) {
		isc_symtab_t *symtab = NULL;
		tresult = isc_symtab_create(mctx, 100, freekey, mctx,
					    ISC_FALSE, &symtab);
		if (tresult != ISC_R_SUCCESS)
			result = tresult;
		for (element = cfg_list_first(obj);
		     element != NULL;
		     element = cfg_list_next(element))
		{
			obj = cfg_listelt_value(element);
			tresult = mustbesecure(obj, symtab, logctx, mctx);
			if (tresult != ISC_R_SUCCESS)
				result = tresult;
		}
		if (symtab != NULL)
			isc_symtab_destroy(&symtab);
	}

618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
	/*
	 * Check empty zone configuration.
	 */
	obj = NULL;
	(void)cfg_map_get(options, "empty-server", &obj);
	if (obj != NULL) {
		str = cfg_obj_asstring(obj);
		isc_buffer_init(&b, str, strlen(str));
		isc_buffer_add(&b, strlen(str));
		tresult = dns_name_fromtext(dns_fixedname_name(&fixed), &b,
					    dns_rootname, ISC_FALSE, NULL);
		if (tresult != ISC_R_SUCCESS) {
			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
				    "empty-server: invalid name '%s'", str);
			result = ISC_R_FAILURE;
		}
	}

	obj = NULL;
	(void)cfg_map_get(options, "empty-contact", &obj);
	if (obj != NULL) {
		str = cfg_obj_asstring(obj);
		isc_buffer_init(&b, str, strlen(str));
		isc_buffer_add(&b, strlen(str));
		tresult = dns_name_fromtext(dns_fixedname_name(&fixed), &b,
					    dns_rootname, ISC_FALSE, NULL);
		if (tresult != ISC_R_SUCCESS) {
			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
				    "empty-contact: invalid name '%s'", str);
			result = ISC_R_FAILURE;
		}
	}

	obj = NULL;
	(void)cfg_map_get(options, "disable-empty-zone", &obj);
	for (element = cfg_list_first(obj);
	     element != NULL;
	     element = cfg_list_next(element))
	{
		obj = cfg_listelt_value(element);
		str = cfg_obj_asstring(obj);
		isc_buffer_init(&b, str, strlen(str));
		isc_buffer_add(&b, strlen(str));
		tresult = dns_name_fromtext(dns_fixedname_name(&fixed), &b,
					    dns_rootname, ISC_FALSE, NULL);
		if (tresult != ISC_R_SUCCESS) {
			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
				    "disable-empty-zone: invalid name '%s'",
				    str);
			result = ISC_R_FAILURE;
		}
	}

671
672
673
	return (result);
}

674
static isc_result_t
675
get_masters_def(const cfg_obj_t *cctx, const char *name, const cfg_obj_t **ret) {
676
	isc_result_t result;
677
678
	const cfg_obj_t *masters = NULL;
	const cfg_listelt_t *elt;
679
680
681
682
683
684
685

	result = cfg_map_get(cctx, "masters", &masters);
	if (result != ISC_R_SUCCESS)
		return (result);
	for (elt = cfg_list_first(masters);
	     elt != NULL;
	     elt = cfg_list_next(elt)) {
686
		const cfg_obj_t *list;
687
688
689
690
691
692
693
694
695
696
697
698
699
700
		const char *listname;

		list = cfg_listelt_value(elt);
		listname = cfg_obj_asstring(cfg_tuple_get(list, "name"));

		if (strcasecmp(listname, name) == 0) {
			*ret = list;
			return (ISC_R_SUCCESS);
		}
	}
	return (ISC_R_NOTFOUND);
}

static isc_result_t
701
702
validate_masters(const cfg_obj_t *obj, const cfg_obj_t *config,
	         isc_uint32_t *countp, isc_log_t *logctx, isc_mem_t *mctx)
703
704
705
706
707
708
{
	isc_result_t result = ISC_R_SUCCESS;
	isc_result_t tresult;
	isc_uint32_t count = 0;
	isc_symtab_t *symtab = NULL;
	isc_symvalue_t symvalue;
709
710
	const cfg_listelt_t *element;
	const cfg_listelt_t **stack = NULL;
711
	isc_uint32_t stackcount = 0, pushed = 0;
712
	const cfg_obj_t *list;
713
714
715

	REQUIRE(countp != NULL);
	result = isc_symtab_create(mctx, 100, NULL, NULL, ISC_FALSE, &symtab);
716
717
	if (result != ISC_R_SUCCESS) {
		*countp = count;
718
		return (result);
719
	}
720
721
722
723
724
725
726
727
728

 newlist:
	list = cfg_tuple_get(obj, "addresses");
	element = cfg_list_first(list);
 resume:	
	for ( ;
	     element != NULL;
	     element = cfg_list_next(element))
	{
729
		const char *listname;
730
731
		const cfg_obj_t *addr;
		const cfg_obj_t *key;
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748

		addr = cfg_tuple_get(cfg_listelt_value(element),
				     "masterselement");
		key = cfg_tuple_get(cfg_listelt_value(element), "key");

		if (cfg_obj_issockaddr(addr)) {
			count++;
			continue;
		}
		if (!cfg_obj_isvoid(key)) {
			cfg_obj_log(key, logctx, ISC_LOG_ERROR,
				    "unexpected token '%s'",
				    cfg_obj_asstring(key));
			if (result == ISC_R_SUCCESS)
				result = ISC_R_FAILURE;
		}
		listname = cfg_obj_asstring(addr);
749
		symvalue.as_cpointer = addr;
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
		tresult = isc_symtab_define(symtab, listname, 1, symvalue,
					    isc_symexists_reject);
		if (tresult == ISC_R_EXISTS)
			continue;
		tresult = get_masters_def(config, listname, &obj);
		if (tresult != ISC_R_SUCCESS) {
			if (result == ISC_R_SUCCESS)
				result = tresult;
			cfg_obj_log(addr, logctx, ISC_LOG_ERROR,
				    "unable to find masters list '%s'",
				    listname);
			continue;
		}
		/* Grow stack? */
		if (stackcount == pushed) {
			void * new;
			isc_uint32_t newlen = stackcount + 16;
			size_t newsize, oldsize;

			newsize = newlen * sizeof(*stack);
			oldsize = stackcount * sizeof(*stack);
			new = isc_mem_get(mctx, newsize);
			if (new == NULL)
				goto cleanup;
			if (stackcount != 0) {
				memcpy(new, stack, oldsize);
				isc_mem_put(mctx, stack, oldsize);
			}
			stack = new;
			stackcount = newlen;
		}
		stack[pushed++] = cfg_list_next(element);
		goto newlist;
	}
	if (pushed != 0) {
		element = stack[--pushed];
		goto resume;
	}
 cleanup:
	if (stack != NULL)
		isc_mem_put(mctx, stack, stackcount * sizeof(*stack));
	isc_symtab_destroy(&symtab);
	*countp = count;
	return (result);
}

796
static isc_result_t
797
check_update_policy(const cfg_obj_t *policy, isc_log_t *logctx) {
798
799
	isc_result_t result = ISC_R_SUCCESS;
	isc_result_t tresult;
800
801
	const cfg_listelt_t *element;
	const cfg_listelt_t *element2;
802
	dns_fixedname_t fixed;
803
	const char *str;
804
805
806
807
808
809
	isc_buffer_t b;

	for (element = cfg_list_first(policy);
	     element != NULL;
	     element = cfg_list_next(element))
	{
810
811
812
813
814
		const cfg_obj_t *stmt = cfg_listelt_value(element);
		const cfg_obj_t *identity = cfg_tuple_get(stmt, "identity");
		const cfg_obj_t *matchtype = cfg_tuple_get(stmt, "matchtype");
		const cfg_obj_t *dname = cfg_tuple_get(stmt, "name");
		const cfg_obj_t *typelist = cfg_tuple_get(stmt, "types");
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838

		dns_fixedname_init(&fixed);
		str = cfg_obj_asstring(identity);
		isc_buffer_init(&b, str, strlen(str));
		isc_buffer_add(&b, strlen(str));
		tresult = dns_name_fromtext(dns_fixedname_name(&fixed), &b,
                                            dns_rootname, ISC_FALSE, NULL);
		if (tresult != ISC_R_SUCCESS) {
			cfg_obj_log(identity, logctx, ISC_LOG_ERROR,
				    "'%s' is not a valid name", str);
			result = tresult;
		}

		dns_fixedname_init(&fixed);
		str = cfg_obj_asstring(dname);
		isc_buffer_init(&b, str, strlen(str));
		isc_buffer_add(&b, strlen(str));
		tresult = dns_name_fromtext(dns_fixedname_name(&fixed), &b,
					    dns_rootname, ISC_FALSE, NULL);
		if (tresult != ISC_R_SUCCESS) {
			cfg_obj_log(dname, logctx, ISC_LOG_ERROR,
				    "'%s' is not a valid name", str);
			result = tresult;
		}
839
840
841
842
843
844
845
		if (tresult == ISC_R_SUCCESS &&
		    strcasecmp(cfg_obj_asstring(matchtype), "wildcard") == 0 &&
		    !dns_name_iswildcard(dns_fixedname_name(&fixed))) {
			cfg_obj_log(identity, logctx, ISC_LOG_ERROR,
				    "'%s' is not a wildcard", str);
			result = ISC_R_FAILURE;
		}
846
847
848
849
850

		for (element2 = cfg_list_first(typelist);
		     element2 != NULL;
		     element2 = cfg_list_next(element2))
		{
851
			const cfg_obj_t *typeobj;
852
853
854
855
			isc_textregion_t r;
			dns_rdatatype_t type;
			
			typeobj = cfg_listelt_value(element2);
856
857
			DE_CONST(cfg_obj_asstring(typeobj), r.base);
			r.length = strlen(r.base);
858
859
860
861

			tresult = dns_rdatatype_fromtext(&type, &r);
			if (tresult != ISC_R_SUCCESS) {
				cfg_obj_log(typeobj, logctx, ISC_LOG_ERROR,
862
                                            "'%s' is not a valid type", r.base);
863
864
865
866
867
868
869
				result = tresult;
			}
		}
	}
	return (result);
}

870
871
872
873
874
#define MASTERZONE	1
#define SLAVEZONE	2
#define STUBZONE	4
#define HINTZONE	8
#define FORWARDZONE	16
875
#define DELEGATIONZONE	32
876
#define CHECKACL	64
877
878
879
880
881
882
883

typedef struct {
	const char *name;
	int allowed;
} optionstable;

static isc_result_t
884
885
886
887
check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions,
	       const cfg_obj_t *config, isc_symtab_t *symtab,
	       dns_rdataclass_t defclass, cfg_aclconfctx_t *actx,
	       isc_log_t *logctx, isc_mem_t *mctx)
888
{
889
890
891
	const char *zname;
	const char *typestr;
	unsigned int ztype;
892
893
	const cfg_obj_t *zoptions;
	const cfg_obj_t *obj = NULL;
Brian Wellington's avatar
Brian Wellington committed
894
	isc_result_t result = ISC_R_SUCCESS;
895
	isc_result_t tresult;
896
	unsigned int i;
897
	dns_rdataclass_t zclass;
898
899
	dns_fixedname_t fixedname;
	isc_buffer_t b;
900
901

	static optionstable options[] = {
902
903
904
	{ "allow-query", MASTERZONE | SLAVEZONE | STUBZONE | CHECKACL },
	{ "allow-notify", SLAVEZONE | CHECKACL },
	{ "allow-transfer", MASTERZONE | SLAVEZONE | CHECKACL },
905
906
907
	{ "notify", MASTERZONE | SLAVEZONE },
	{ "also-notify", MASTERZONE | SLAVEZONE },
	{ "dialup", MASTERZONE | SLAVEZONE | STUBZONE },
908
	{ "delegation-only", HINTZONE | STUBZONE },
909
910
911
912
	{ "forward", MASTERZONE | SLAVEZONE | STUBZONE | FORWARDZONE},
	{ "forwarders", MASTERZONE | SLAVEZONE | STUBZONE | FORWARDZONE},
	{ "maintain-ixfr-base", MASTERZONE | SLAVEZONE },
	{ "max-ixfr-log-size", MASTERZONE | SLAVEZONE },
913
914
	{ "notify-source", MASTERZONE | SLAVEZONE },
	{ "notify-source-v6", MASTERZONE | SLAVEZONE },
915
916
	{ "transfer-source", SLAVEZONE | STUBZONE },
	{ "transfer-source-v6", SLAVEZONE | STUBZONE },
917
918
919
920
921
922
923
924
925
926
	{ "max-transfer-time-in", SLAVEZONE | STUBZONE },
	{ "max-transfer-time-out", MASTERZONE | SLAVEZONE },
	{ "max-transfer-idle-in", SLAVEZONE | STUBZONE },
	{ "max-transfer-idle-out", MASTERZONE | SLAVEZONE },
	{ "max-retry-time", SLAVEZONE | STUBZONE },
	{ "min-retry-time", SLAVEZONE | STUBZONE },
	{ "max-refresh-time", SLAVEZONE | STUBZONE },
	{ "min-refresh-time", SLAVEZONE | STUBZONE },
	{ "sig-validity-interval", MASTERZONE },
	{ "zone-statistics", MASTERZONE | SLAVEZONE | STUBZONE },
927
928
	{ "allow-update", MASTERZONE | CHECKACL },
	{ "allow-update-forwarding", SLAVEZONE | CHECKACL },
929
	{ "file", MASTERZONE | SLAVEZONE | STUBZONE | HINTZONE },
930
	{ "journal", MASTERZONE | SLAVEZONE },
931
932
933
934
935
936
	{ "ixfr-base", MASTERZONE | SLAVEZONE },
	{ "ixfr-tmp-file", MASTERZONE | SLAVEZONE },
	{ "masters", SLAVEZONE | STUBZONE },
	{ "pubkey", MASTERZONE | SLAVEZONE | STUBZONE },
	{ "update-policy", MASTERZONE },
	{ "database", MASTERZONE | SLAVEZONE | STUBZONE },
937
	{ "key-directory", MASTERZONE },
938
	{ "check-wildcard", MASTERZONE },
939
940
	{ "check-mx", MASTERZONE },
	{ "integrity-check", MASTERZONE },
941
942
	{ "check-mx-cname", MASTERZONE },
	{ "check-srv-cname", MASTERZONE },
943
	{ "masterfile-format", MASTERZONE | SLAVEZONE | STUBZONE | HINTZONE },
944
	{ "update-check-ksk", MASTERZONE },
945
	{ "try-tcp-refresh", SLAVEZONE },
946
947
948
949
950
951
952
953
954
	};

	static optionstable dialups[] = {
	{ "notify", MASTERZONE | SLAVEZONE },
	{ "notify-passive", SLAVEZONE },
	{ "refresh", SLAVEZONE | STUBZONE },
	{ "passive", SLAVEZONE | STUBZONE },
	};

Brian Wellington's avatar
bugs    
Brian Wellington committed
955
956
957
	zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));

	zoptions = cfg_tuple_get(zconfig, "options");
958
959

	obj = NULL;
Brian Wellington's avatar
bugs    
Brian Wellington committed
960
	(void)cfg_map_get(zoptions, "type", &obj);
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
	if (obj == NULL) {
		cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
			    "zone '%s': type not present", zname);
		return (ISC_R_FAILURE);
	}

	typestr = cfg_obj_asstring(obj);
	if (strcasecmp(typestr, "master") == 0)
		ztype = MASTERZONE;
	else if (strcasecmp(typestr, "slave") == 0)
		ztype = SLAVEZONE;
	else if (strcasecmp(typestr, "stub") == 0)
		ztype = STUBZONE;
	else if (strcasecmp(typestr, "forward") == 0)
		ztype = FORWARDZONE;
	else if (strcasecmp(typestr, "hint") == 0)
		ztype = HINTZONE;
978
979
	else if (strcasecmp(typestr, "delegation-only") == 0)
		ztype = DELEGATIONZONE;
980
981
982
983
984
985
986
	else {
		cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
			    "zone '%s': invalid type %s",
			    zname, typestr);
		return (ISC_R_FAILURE);
	}

987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
	obj = cfg_tuple_get(zconfig, "class");
	if (cfg_obj_isstring(obj)) {
		isc_textregion_t r;

		DE_CONST(cfg_obj_asstring(obj), r.base);
		r.length = strlen(r.base);
		result = dns_rdataclass_fromtext(&zclass, &r);
		if (result != ISC_R_SUCCESS) {
			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
				    "zone '%s': invalid class %s",
				    zname, r.base);
			return (ISC_R_FAILURE);
		}
		if (zclass != defclass) {
			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
				    "zone '%s': class '%s' does not "
				    "match view/default class",
				    zname, r.base);
			return (ISC_R_FAILURE);
		}
	}

1009
1010
	/*
	 * Look for an already existing zone.
1011
1012
	 * We need to make this cannonical as isc_symtab_define()
	 * deals with strings.
1013
	 */
1014
1015
1016
	dns_fixedname_init(&fixedname);
	isc_buffer_init(&b, zname, strlen(zname));
	isc_buffer_add(&b, strlen(zname));
1017
	tresult = dns_name_fromtext(dns_fixedname_name(&fixedname), &b,
1018
1019
				   dns_rootname, ISC_TRUE, NULL);
	if (result != ISC_R_SUCCESS) {
1020
		cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
1021
			    "zone '%s': is not a valid name", zname);
1022
		tresult = ISC_R_FAILURE;
1023
1024
	} else {
		char namebuf[DNS_NAME_FORMATSIZE];
1025

1026
1027
		dns_name_format(dns_fixedname_name(&fixedname),
				namebuf, sizeof(namebuf));
1028
1029
1030
1031
1032
		tresult = nameexist(zconfig, namebuf, ztype == HINTZONE ? 1 : 2,
				   symtab, "zone '%s': already exists "
				   "previous definition: %s:%u", logctx, mctx);
		if (tresult != ISC_R_SUCCESS)
			result = tresult;
1033
	}
1034
1035
1036

	/*
	 * Look for inappropriate options for the given zone type.
1037
	 * Check that ACLs expand correctly.
1038
	 */
1039
1040
1041
1042
1043
1044
	for (i = 0; i < sizeof(options) / sizeof(options[0]); i++) {
		obj = NULL;
		if ((options[i].allowed & ztype) == 0 &&
		    cfg_map_get(zoptions, options[i].name, &obj) ==
		    ISC_R_SUCCESS)
		{
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
			if (strcmp(options[i].name, "allow-update") != 0 ||
			    ztype != SLAVEZONE) {
				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
					    "option '%s' is not allowed "
					    "in '%s' zone '%s'",
					    options[i].name, typestr, zname);
					result = ISC_R_FAILURE;
			} else
				cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
					    "option '%s' is not allowed "
					    "in '%s' zone '%s'",
					    options[i].name, typestr, zname);
1057
		}
1058
1059
1060
1061
		obj = NULL;
		if ((options[i].allowed & ztype) != 0 &&
		    (options[i].allowed & CHECKACL) != 0) {

1062
			tresult = checkacl(options[i].name, actx, zconfig,
1063
1064
1065
1066
1067
				           voptions, config, logctx, mctx);
			if (tresult != ISC_R_SUCCESS)
				result = tresult;
		}

1068
1069
	}

1070
1071
1072
	/*
	 * Slave & stub zones must have a "masters" field.
	 */
1073
1074
1075
	if (ztype == SLAVEZONE || ztype == STUBZONE) {
		obj = NULL;
		if (cfg_map_get(zoptions, "masters", &obj) != ISC_R_SUCCESS) {
1076
			cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR,
1077
1078
1079
				    "zone '%s': missing 'masters' entry",
				    zname);
			result = ISC_R_FAILURE;
1080
		} else {
1081
1082
1083
1084
1085
1086
			isc_uint32_t count;
			tresult = validate_masters(obj, config, &count,
						   logctx, mctx);
			if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS)
				result = tresult;
			if (tresult == ISC_R_SUCCESS && count == 0) {
1087
1088
1089
1090
1091
				cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR,
					    "zone '%s': empty 'masters' entry",
					    zname);
				result = ISC_R_FAILURE;
			}
1092
		}
1093
1094
	}

1095
1096
1097
	/*
	 * Master zones can't have both "allow-update" and "update-policy".
	 */
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
	if (ztype == MASTERZONE) {
		isc_result_t res1, res2;
		obj = NULL;
		res1 = cfg_map_get(zoptions, "allow-update", &obj);
		obj = NULL;
		res2 = cfg_map_get(zoptions, "update-policy", &obj);
		if (res1 == ISC_R_SUCCESS && res2 == ISC_R_SUCCESS) {
			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
				    "zone '%s': 'allow-update' is ignored "
				    "when 'update-policy' is present",
				    zname);
			result = ISC_R_FAILURE;
1110
1111
1112
		} else if (res2 == ISC_R_SUCCESS &&
			   check_update_policy(obj, logctx) != ISC_R_SUCCESS)
			result = ISC_R_FAILURE;
1113
1114
	}

1115
1116
1117
	/*
	 * Check the excessively complicated "dialup" option.
	 */
1118
	if (ztype == MASTERZONE || ztype == SLAVEZONE || ztype == STUBZONE) {
1119
		const cfg_obj_t *dialup = NULL;
1120
		(void)cfg_map_get(zoptions, "dialup", &dialup);
1121
		if (dialup != NULL && cfg_obj_isstring(dialup)) {
1122
			const char *str = cfg_obj_asstring(dialup);
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
			for (i = 0;
			     i < sizeof(dialups) / sizeof(dialups[0]);
			     i++)
			{
				if (strcasecmp(dialups[i].name, str) != 0)
					continue;
				if ((dialups[i].allowed & ztype) == 0) {
					cfg_obj_log(obj, logctx,
						    ISC_LOG_ERROR,
						    "dialup type '%s' is not "
						    "allowed in '%s' "
						    "zone '%s'",
						    str, typestr, zname);
					result = ISC_R_FAILURE;
				}
				break;
			}
			if (i == sizeof(dialups) / sizeof(dialups[0])) {
				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
					    "invalid dialup type '%s' in zone "
					    "'%s'", str, zname);
				result = ISC_R_FAILURE;
			}
		}
	}

1149
1150
1151
1152
1153
1154
	/*
	 * Check that forwarding is reasonable.
	 */
	if (check_forward(zoptions, logctx) != ISC_R_SUCCESS)
		result = ISC_R_FAILURE;

1155
1156
1157
	/*
	 * Check various options.
	 */
1158
	tresult = check_options(zoptions, logctx, mctx);
1159
1160
1161
	if (tresult != ISC_R_SUCCESS)
		result = tresult;

1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
	/*
	 * If the zone type is rbt/rbt64 then master/hint zones
	 * require file clauses.
	 */
	obj = NULL;
	tresult = cfg_map_get(zoptions, "database", &obj);
	if (tresult == ISC_R_NOTFOUND ||
	    (tresult == ISC_R_SUCCESS &&
	     (strcmp("rbt", cfg_obj_asstring(obj)) == 0 ||
	      strcmp("rbt64", cfg_obj_asstring(obj)) == 0))) {
		obj = NULL;
		tresult = cfg_map_get(zoptions, "file", &obj);
		if (tresult != ISC_R_SUCCESS &&
		    (ztype == MASTERZONE || ztype == HINTZONE)) {
			cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
				    "zone '%s': missing 'file' entry",
				    zname);
			result = tresult;
		}
	}
	
1183
1184
1185
	return (result);
}

1186
1187
1188
1189
1190
1191

typedef struct keyalgorithms {
	const char *name;
	isc_uint16_t size;
} algorithmtable;

1192
isc_result_t
1193
1194
1195
bind9_check_key(const cfg_obj_t *key, isc_log_t *logctx) {
	const cfg_obj_t *algobj = NULL;
	const cfg_obj_t *secretobj = NULL;
1196
	const char *keyname = cfg_obj_asstring(cfg_map_getname(key));
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
	const char *algorithm;
	int i;
	size_t len = 0;
	static const algorithmtable algorithms[] = {
		{ "hmac-md5", 128 },
		{ "hmac-md5.sig-alg.reg.int", 0 },
		{ "hmac-md5.sig-alg.reg.int.", 0 },
		{ "hmac-sha1", 160 },
		{ "hmac-sha224", 224 },
		{ "hmac-sha256", 256 },
		{ "hmac-sha384", 384 },
		{ "hmac-sha512", 512 },
		{  NULL, 0 }
	};
1211
	
1212
1213
	(void)cfg_map_get(key, "algorithm", &algobj);
	(void)cfg_map_get(key, "secret", &secretobj);
1214
1215
1216
1217
1218
	if (secretobj == NULL || algobj == NULL) {
		cfg_obj_log(key, logctx, ISC_LOG_ERROR,
			    "key '%s' must have both 'secret' and "
			    "'algorithm' defined",
			    keyname);
Brian Wellington's avatar
style    
Brian Wellington committed
1219
		return (ISC_R_FAILURE);
1220
	}
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270

	algorithm = cfg_obj_asstring(algobj);
	for (i = 0; algorithms[i].name != NULL; i++) {
		len = strlen(algorithms[i].name);
		if (strncasecmp(algorithms[i].name, algorithm, len) == 0 &&
		    (algorithm[len] == '\0' ||
		     (algorithms[i].size != 0 && algorithm[len] == '-')))
			break;
	}
	if (algorithms[i].name == NULL) {
		cfg_obj_log(algobj, logctx, ISC_LOG_ERROR,
			    "unknown algorithm '%s'", algorithm);
		return (ISC_R_NOTFOUND);
	}
	if (algorithm[len] == '-') {
		isc_uint16_t digestbits;
		isc_result_t result;
		result = isc_parse_uint16(&digestbits, algorithm + len + 1, 10);
		if (result == ISC_R_SUCCESS || result == ISC_R_RANGE) {
			if (result == ISC_R_RANGE ||
			    digestbits > algorithms[i].size) {
				cfg_obj_log(algobj, logctx, ISC_LOG_ERROR,
					    "key '%s' digest-bits too large "
					    "[%u..%u]", keyname,
					    algorithms[i].size / 2,
					    algorithms[i].size);
				return (ISC_R_RANGE);
			}
			if ((digestbits % 8) != 0) {
				cfg_obj_log(algobj, logctx, ISC_LOG_ERROR,
					    "key '%s' digest-bits not multiple"
					    " of 8", keyname);
				return (ISC_R_RANGE);
			}
			/*
			 * Recommended minima for hmac algorithms.
			 */
			if ((digestbits < (algorithms[i].size / 2U) ||
			     (digestbits < 80U)))
				cfg_obj_log(algobj, logctx, ISC_LOG_WARNING,
					    "key '%s' digest-bits too small "
					    "[<%u]", keyname, 
					    algorithms[i].size/2);
		} else {
			cfg_obj_log(algobj, logctx, ISC_LOG_ERROR,
				    "key '%s': unable to parse digest-bits",
				    keyname);
			return (result);
		}
	}
Brian Wellington's avatar
style    
Brian Wellington committed
1271
	return (ISC_R_SUCCESS);
1272
}
1273
1274

static isc_result_t
1275
check_keylist(const cfg_obj_t *keys, isc_symtab_t *symtab, isc_log_t *logctx) {
1276
1277
	isc_result_t result = ISC_R_SUCCESS;
	isc_result_t tresult;
1278
	const cfg_listelt_t *element;
1279
1280
1281
1282
1283

	for (element = cfg_list_first(keys);
	     element != NULL;
	     element = cfg_list_next(element))
	{
1284
		const cfg_obj_t *key = cfg_listelt_value(element);
1285
1286
1287
		const char *keyname = cfg_obj_asstring(cfg_map_getname(key));
		isc_symvalue_t symvalue;

1288
1289
1290
1291
		tresult = bind9_check_key(key, logctx);
		if (tresult != ISC_R_SUCCESS)
			return (tresult);

1292
		symvalue.as_cpointer = key;
1293
1294
1295
		tresult = isc_symtab_define(symtab, keyname, 1,
					    symvalue, isc_symexists_reject);
		if (tresult == ISC_R_EXISTS) {
1296
1297
1298
1299
1300
			const char *file;
			unsigned int line;

			RUNTIME_CHECK(isc_symtab_lookup(symtab, keyname,
					    1, &symvalue) == ISC_R_SUCCESS);
1301
1302
			file = cfg_obj_file(symvalue.as_cpointer);
			line = cfg_obj_line(symvalue.as_cpointer);
1303
1304
1305

			if (file == NULL)
				file = "<unknown file>";
1306
			cfg_obj_log(key, logctx, ISC_LOG_ERROR,
1307
1308
1309
				    "key '%s': already exists "
				    "previous definition: %s:%u",
				    keyname, file, line);
1310
1311
1312
1313
1314
1315
			result = tresult;
		} else if (tresult != ISC_R_SUCCESS)
			return (tresult);
	}
	return (result);
}
1316

1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
static struct {
	const char *v4;
	const char *v6;
} sources[] = {
	{ "transfer-source", "transfer-source-v6" },
	{ "notify-source", "notify-source-v6" },
	{ "query-source", "query-source-v6" },
	{ NULL, NULL }
};

1327
static isc_result_t
1328
check_servers(const cfg_obj_t *servers, isc_log_t *logctx) {
1329
	isc_result_t result = ISC_R_SUCCESS;
1330
	isc_result_t tresult;
1331
1332
	const cfg_listelt_t *e1, *e2;
	const cfg_obj_t *v1, *v2;
1333
1334
	isc_netaddr_t n1, n2;
	unsigned int p1, p2;
1335
	const cfg_obj_t *obj;
1336
	char buf[ISC_NETADDR_FORMATSIZE];
1337
	const char *xfr;
1338
	int source;
1339
1340
1341

	for (e1 = cfg_list_first(servers); e1 != NULL; e1 = cfg_list_next(e1)) {
		v1 = cfg_listelt_value(e1);
1342
1343
1344
1345
		cfg_obj_asnetprefix(cfg_map_getname(v1), &n1, &p1);
		/*
		 * Check that unused bits are zero.
		 */
1346
1347
1348
1349
		tresult = isc_netaddr_prefixok(&n1, p1);
		if (tresult != ISC_R_SUCCESS) {
			INSIST(tresult == ISC_R_FAILURE);
			isc_netaddr_format(&n1, buf, sizeof(buf));
1350
1351
1352
			cfg_obj_log(v1, logctx, ISC_LOG_ERROR,
				    "server '%s/%u': invalid prefix "
				    "(extra bits specified)", buf, p1);
1353
			result = tresult;
1354
		}
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
		source = 0;
		do {
			obj = NULL;
			if (n1.family == AF_INET)
				xfr = sources[source].v6;
			else
				xfr = sources[source].v4;
			(void)cfg_map_get(v1, xfr, &obj);
			if (obj != NULL) {
				isc_netaddr_format(&n1, buf, sizeof(buf));
				cfg_obj_log(v1, logctx, ISC_LOG_ERROR,
					    "server '%s': %s not legal",
					    buf, xfr);
				result = ISC_R_FAILURE;
			}
		} while (sources[++source].v4 != NULL);
1371
1372
1373
		e2 = e1;
		while ((e2 = cfg_list_next(e2)) != NULL) {
			v2 = cfg_listelt_value(e2);
1374
1375
			cfg_obj_asnetprefix(cfg_map_getname(v2), &n2, &p2);
			if (p1 == p2 && isc_netaddr_equal(&n1, &n2)) {
1376
1377
1378
1379
1380
1381
				const char *file = cfg_obj_file(v1);
				unsigned int line = cfg_obj_line(v1);

				if (file == NULL)
					file = "<unknown file>";

1382
				isc_netaddr_format(&n2, buf, sizeof(buf));
1383
				cfg_obj_log(v2, logctx, ISC_LOG_ERROR,
1384
					    "server '%s/%u': already exists "
1385
					    "previous definition: %s:%u",
1386
					    buf, p2, file, line);
1387
1388
1389
1390
1391
1392
				result = ISC_R_FAILURE;
			}
		}
	}
	return (result);
}
1393
		
Brian Wellington's avatar
Brian Wellington committed
1394
static isc_result_t
1395
1396
check_viewconf(const cfg_obj_t *config, const cfg_obj_t *voptions,
	       dns_rdataclass_t vclass, isc_log_t *logctx, isc_mem_t *mctx)
1397
{
1398
1399
1400
1401
	const cfg_obj_t *servers = NULL;
	const cfg_obj_t *zones = NULL;
	const cfg_obj_t *keys = NULL;
	const cfg_listelt_t *element;
1402
	isc_symtab_t *symtab = NULL;
Brian Wellington's avatar
Brian Wellington committed
1403
	isc_result_t result = ISC_R_SUCCESS;
1404
	isc_result_t tresult = ISC_R_SUCCESS;
1405
	cfg_aclconfctx_t actx;
Mark Andrews's avatar
const    
Mark Andrews committed
1406
	const cfg_obj_t *obj;
1407
	isc_boolean_t enablednssec, enablevalidation;
Brian Wellington's avatar
Brian Wellington committed
1408

1409
1410
1411
1412
	/*
	 * Check that all zone statements are syntactically correct and
	 * there are no duplicate zones.
	 */
1413
	tresult = isc_symtab_create(mctx, 100, freekey, mctx,
1414
				    ISC_FALSE, &symtab);
1415
	if (tresult != ISC_R_SUCCESS)
1416
1417
		return (ISC_R_NOMEMORY);

1418
1419
	cfg_aclconfctx_init(&actx);