dnssec-signzone.c 38 KB
Newer Older
Michael Graff's avatar
Michael Graff committed
1
/*
2 3
 * Portions Copyright (C) 1999, 2000  Internet Software Consortium.
 * Portions Copyright (C) 1995-2000 by Network Associates, Inc.
4
 *
Michael Graff's avatar
Michael Graff committed
5 6 7
 * 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.
8
 *
9 10 11 12 13 14 15 16 17
 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM AND
 * NETWORK ASSOCIATES DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
 * SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE CONSORTIUM OR NETWORK
 * ASSOCIATES 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.
Michael Graff's avatar
Michael Graff committed
18
 */
Brian Wellington's avatar
Brian Wellington committed
19

20
/* $Id: dnssec-signzone.c,v 1.92 2000/08/11 23:23:19 bwelling Exp $ */
David Lawrence's avatar
David Lawrence committed
21

Brian Wellington's avatar
Brian Wellington committed
22 23 24 25
#include <config.h>

#include <stdlib.h>

26
#include <isc/commandline.h>
Brian Wellington's avatar
Brian Wellington committed
27
#include <isc/entropy.h>
Brian Wellington's avatar
Brian Wellington committed
28
#include <isc/mem.h>
29
#include <isc/stdio.h>
30
#include <isc/string.h>
Bob Halley's avatar
Bob Halley committed
31
#include <isc/util.h>
Brian Wellington's avatar
Brian Wellington committed
32 33 34

#include <dns/db.h>
#include <dns/dbiterator.h>
35
#include <dns/dnssec.h>
36
#include <dns/fixedname.h>
37
#include <dns/journal.h>
38 39
#include <dns/keyvalues.h>
#include <dns/log.h>
40 41
#include <dns/master.h>
#include <dns/masterdump.h>
42
#include <dns/nxt.h>
Brian Wellington's avatar
Brian Wellington committed
43 44 45
#include <dns/rdata.h>
#include <dns/rdataset.h>
#include <dns/rdatasetiter.h>
46
#include <dns/rdatastruct.h>
47
#include <dns/rdatatype.h>
Brian Wellington's avatar
Brian Wellington committed
48
#include <dns/result.h>
49
#include <dns/secalg.h>
50
#include <dns/time.h>
Brian Wellington's avatar
Brian Wellington committed
51

52
#include <dst/dst.h>
53
#include <dst/result.h>
Brian Wellington's avatar
Brian Wellington committed
54

55 56
#include "dnssectool.h"

David Lawrence's avatar
David Lawrence committed
57
const char *program = "dnssec-signzone";
58
int verbose;
59

60
#define BUFSIZE 2048
Brian Wellington's avatar
Brian Wellington committed
61

62 63 64 65 66 67 68 69
typedef struct signer_key_struct signer_key_t;

struct signer_key_struct {
	dst_key_t *key;
	isc_boolean_t isdefault;
	ISC_LINK(signer_key_t) link;
};

70 71 72
static ISC_LIST(signer_key_t) keylist;
static isc_stdtime_t starttime = 0, endtime = 0, now;
static int cycle = -1;
73
static isc_boolean_t tryverify = ISC_FALSE;
Brian Wellington's avatar
Brian Wellington committed
74
static isc_mem_t *mctx = NULL;
Brian Wellington's avatar
Brian Wellington committed
75
static isc_entropy_t *ectx = NULL;
76
static dns_ttl_t zonettl;
77 78
static FILE *fp;
static const dns_master_style_t *masterstyle = &dns_master_style_explicitttl;
Brian Wellington's avatar
Brian Wellington committed
79

80
static inline void
Brian Wellington's avatar
Brian Wellington committed
81
set_bit(unsigned char *array, unsigned int index, unsigned int bit) {
82
	unsigned int shift, mask;
83

Brian Wellington's avatar
Brian Wellington committed
84 85 86
	shift = 7 - (index % 8);
	mask = 1 << shift;

87
	if (bit != 0)
Brian Wellington's avatar
Brian Wellington committed
88 89 90 91 92
		array[index / 8] |= mask;
	else
		array[index / 8] &= (~mask & 0xFF);
}

93 94 95
static void
signwithkey(dns_name_t *name, dns_rdataset_t *rdataset, dns_rdata_t *rdata,
	    dst_key_t *key, isc_buffer_t *b)
Brian Wellington's avatar
Brian Wellington committed
96 97 98
{
	isc_result_t result;

99
	dns_rdata_init(rdata);
100
	result = dns_dnssec_sign(name, rdataset, key, &starttime, &endtime,
101
				 mctx, b, rdata);
102
	isc_entropy_stopcallbacksources(ectx);
103 104
	if (result != ISC_R_SUCCESS)
		fatal("key '%s/%s/%d' failed to sign data: %s",
105
		      nametostr(dst_key_name(key)), algtostr(dst_key_alg(key)),
106
		      dst_key_id(key), isc_result_totext(result));
107

108
	if (tryverify) {
109 110
		result = dns_dnssec_verify(name, rdataset, key,
					   ISC_TRUE, mctx, rdata);
111 112 113 114
		if (result == ISC_R_SUCCESS)
			vbprintf(3, "\tsignature verified\n");
		else
			vbprintf(3, "\tsignature failed to verify\n");
115
	}
116
}
Brian Wellington's avatar
Brian Wellington committed
117

118 119 120
static inline isc_boolean_t
issigningkey(signer_key_t *key) {
	return (key->isdefault);
Brian Wellington's avatar
Brian Wellington committed
121 122
}

123 124
static inline isc_boolean_t
iszonekey(signer_key_t *key, dns_db_t *db) {
125 126
	return (ISC_TF(dns_name_equal(dst_key_name(key->key),
				      dns_db_origin(db)) &&
127
		       dst_key_iszonekey(key->key)));
128 129
}

130 131 132 133
/*
 * Finds the key that generated a SIG, if possible.  First look at the keys
 * that we've loaded already, and then see if there's a key on disk.
 */
134
static signer_key_t *
135
keythatsigned(dns_rdata_sig_t *sig) {
136
	isc_result_t result;
137 138 139 140 141 142 143
	dst_key_t *pubkey = NULL, *privkey = NULL;
	signer_key_t *key;

	key = ISC_LIST_HEAD(keylist);
	while (key != NULL) {
		if (sig->keyid == dst_key_id(key->key) &&
		    sig->algorithm == dst_key_alg(key->key) &&
144
		    dns_name_equal(&sig->signer, dst_key_name(key->key)))
145
			return key;
146
		key = ISC_LIST_NEXT(key, link);
147
	}
148

149
	result = dst_key_fromfile(&sig->signer, sig->keyid, sig->algorithm,
Brian Wellington's avatar
Brian Wellington committed
150
				  DST_TYPE_PUBLIC, NULL, mctx, &pubkey);
151 152
	if (result != ISC_R_SUCCESS)
		return (NULL);
153

154 155
	key = isc_mem_get(mctx, sizeof(signer_key_t));
	if (key == NULL)
156
		fatal("out of memory");
157

158
	result = dst_key_fromfile(&sig->signer, sig->keyid, sig->algorithm,
Brian Wellington's avatar
Brian Wellington committed
159
				  DST_TYPE_PRIVATE, NULL, mctx, &privkey);
160 161
	if (result == ISC_R_SUCCESS) {
		key->key = privkey;
162
		dst_key_free(&pubkey);
163 164 165 166 167 168
	}
	else
		key->key = pubkey;
	key->isdefault = ISC_FALSE;
	ISC_LIST_APPEND(keylist, key, link);
	return key;
169 170
}

171 172 173 174 175
/*
 * Check to see if we expect to find a key at this name.  If we see a SIG
 * and can't find the signing key that we expect to find, we drop the sig.
 * I'm not sure if this is completely correct, but it seems to work.
 */
176 177 178
static isc_boolean_t
expecttofindkey(dns_name_t *name, dns_db_t *db, dns_dbversion_t *version) {
	unsigned int options = DNS_DBFIND_NOWILD;
179
	dns_fixedname_t fname;
180 181
	isc_result_t result;

182
	dns_fixedname_init(&fname);
183
	result = dns_db_find(db, name, version, dns_rdatatype_key, options,
184
			     0, NULL, dns_fixedname_name(&fname), NULL, NULL);
185
	switch (result) {
186
		case ISC_R_SUCCESS:
187
		case DNS_R_NXDOMAIN:
188
		case DNS_R_NXRRSET:
189 190 191 192 193 194
			return ISC_TRUE;
		case DNS_R_DELEGATION:
		case DNS_R_CNAME:
		case DNS_R_DNAME:
			return ISC_FALSE;
		default:
195 196
			fatal("failure looking for '%s KEY' in database: %s",
			      nametostr(name), isc_result_totext(result));
197
			return ISC_FALSE; /* removes a warning */
198 199 200
	}
}

201
static inline isc_boolean_t
202 203
setverifies(dns_name_t *name, dns_rdataset_t *set, signer_key_t *key,
	    dns_rdata_t *sig)
204
{
205 206
	isc_result_t result;
	result = dns_dnssec_verify(name, set, key->key, ISC_FALSE, mctx, sig);
207
	return (ISC_TF(result == ISC_R_SUCCESS));
208 209
}

210 211 212 213 214
/*
 * Signs a set.  Goes through contortions to decide if each SIG should
 * be dropped or retained, and then determines if any new SIGs need to
 * be generated.
 */
215
static void
216 217
signset(dns_db_t *db, dns_dbversion_t *version, dns_diff_t *diff,
	dns_dbnode_t *node, dns_name_t *name, dns_rdataset_t *set)
218
{
219 220
	dns_rdataset_t sigset;
	dns_rdata_t sigrdata;
221
	dns_rdata_sig_t sig;
222
	signer_key_t *key;
223
	isc_result_t result;
224
	isc_boolean_t notsigned = ISC_TRUE, nosigs = ISC_FALSE;
225
	isc_boolean_t wassignedby[256], nowsignedby[256];
226 227
	dns_difftuple_t *tuple;
	dns_ttl_t ttl;
228
	int i;
229

230
	ttl = ISC_MIN(set->ttl, endtime - starttime);
231 232 233 234

	for (i = 0; i < 256; i++)
		wassignedby[i] = nowsignedby[i] = ISC_FALSE;

235
	dns_rdataset_init(&sigset);
236
	result = dns_db_findrdataset(db, node, version, dns_rdatatype_sig,
237
				     set->type, 0, &sigset, NULL);
238 239 240 241
	if (result == ISC_R_NOTFOUND) {
		result = ISC_R_SUCCESS;
		nosigs = ISC_TRUE;
	}
242 243 244 245
	if (result != ISC_R_SUCCESS)
		fatal("failed while looking for '%s SIG %s': %s",
		      nametostr(name), typetostr(set->type),
		      isc_result_totext(result));
246

247 248
	vbprintf(1, "%s/%s:\n", nametostr(name), typetostr(set->type));

249
	if (!nosigs) {
250
		result = dns_rdataset_first(&sigset);
251
		while (result == ISC_R_SUCCESS) {
252
			isc_boolean_t expired, future;
253 254
			isc_boolean_t keep = ISC_FALSE, resign = ISC_FALSE;

255
			dns_rdataset_current(&sigset, &sigrdata);
256

257
			result = dns_rdata_tostruct(&sigrdata, &sig, NULL);
258 259
			check_result(result, "dns_rdata_tostruct");

260 261
			expired = ISC_TF(now + cycle > sig.timeexpire);
			future = ISC_TF(now < sig.timesigned);
262 263 264

			key = keythatsigned(&sig);

265 266
			if (sig.timesigned > sig.timeexpire) {
				/* sig is dropped and not replaced */
267
				vbprintf(2, "\tsig by %s dropped - "
268
					 "invalid validity period\n",
269 270
					 sigtostr(&sig));
			} else if (key == NULL && !future &&
271
				 expecttofindkey(&sig.signer, db, version))
272 273
			{
				/* sig is dropped and not replaced */
274
				vbprintf(2, "\tsig by %s dropped - "
275
					 "private key not found\n",
276 277 278
					 sigtostr(&sig));
			} else if (key == NULL || future) {
				vbprintf(2, "\tsig by %s %s - key not found\n",
279
					 expired ? "retained" : "dropped",
280
					 sigtostr(&sig));
281 282
				if (!expired)
					keep = ISC_TRUE;
283
			} else if (issigningkey(key)) {
284
				if (!expired &&
285
				    setverifies(name, set, key, &sigrdata))
286
				{
287
					vbprintf(2,
288 289
						"\tsig by %s retained\n",
						 sigtostr(&sig));
290 291
					keep = ISC_TRUE;
					wassignedby[sig.algorithm] = ISC_TRUE;
292
				} else {
293
					vbprintf(2,
294 295
						 "\tsig by %s dropped - %s\n",
						 sigtostr(&sig),
296 297
						 expired ? "expired" :
							   "failed to verify");
298 299 300
					wassignedby[sig.algorithm] = ISC_TRUE;
					resign = ISC_TRUE;
				}
301
			} else if (iszonekey(key, db)) {
302
				if (!expired &&
303
				    setverifies(name, set, key, &sigrdata))
304
				{
305
					vbprintf(2,
306 307
						"\tsig by %s retained\n",
						 sigtostr(&sig));
308 309 310
					keep = ISC_TRUE;
					wassignedby[sig.algorithm] = ISC_TRUE;
					nowsignedby[sig.algorithm] = ISC_TRUE;
311
				} else {
312
					vbprintf(2,
313 314
						 "\tsig by %s dropped - %s\n",
						 sigtostr(&sig),
315 316
						 expired ? "expired" :
							   "failed to verify");
317 318 319 320
					wassignedby[sig.algorithm] = ISC_TRUE;
					if (dst_key_isprivate(key->key))
						resign = ISC_TRUE;
				}
321 322 323
			} else if (!expired) {
				vbprintf(2, "\tsig by %s retained\n",
					 sigtostr(&sig));
324
				keep = ISC_TRUE;
325 326 327
			} else {
				vbprintf(2, "\tsig by %s expired\n",
					 sigtostr(&sig));
328
			}
329

330
			if (keep)
331
				nowsignedby[sig.algorithm] = ISC_TRUE;
332 333 334 335 336 337 338 339 340
			else {
				tuple = NULL;
				result = dns_difftuple_create(mctx,
							      DNS_DIFFOP_DEL,
							      name, 0,
							      &sigrdata,
							      &tuple);
				check_result(result, "dns_difftuple_create");
				dns_diff_append(diff, &tuple);
341
			}
342 343 344 345 346 347

			if (resign) {
				isc_buffer_t b;
				dns_rdata_t trdata;
				unsigned char array[BUFSIZE];

348
				vbprintf(1, "\tresigning with key %s/%s/%d\n",
349
				       nametostr(dst_key_name(key->key)),
350 351
				       algtostr(dst_key_alg(key->key)),
				       dst_key_id(key->key));
352 353
				isc_buffer_init(&b, array, sizeof(array));
				signwithkey(name, set, &trdata, key->key, &b);
354
				nowsignedby[sig.algorithm] = ISC_TRUE;
355 356 357 358 359 360 361 362
				tuple = NULL;
				result = dns_difftuple_create(mctx,
							      DNS_DIFFOP_ADD,
							      name, ttl,
							      &trdata,
							      &tuple);
				check_result(result, "dns_difftuple_create");
				dns_diff_append(diff, &tuple);
363 364 365
			}

			dns_rdata_freestruct(&sig);
366
			result = dns_rdataset_next(&sigset);
367
		}
368
		if (result == ISC_R_NOMORE)
369 370
			result = ISC_R_SUCCESS;
		check_result(result, "dns_db_dns_rdataset_first()/next()");
371
		dns_rdataset_disassociate(&sigset);
Brian Wellington's avatar
Brian Wellington committed
372
	}
373 374

	for (i = 0; i < 256; i++)
375
		if (wassignedby[i]) {
376 377
			notsigned = ISC_FALSE;
			break;
Brian Wellington's avatar
Brian Wellington committed
378
		}
379 380 381

	key = ISC_LIST_HEAD(keylist);
	while (key != NULL) {
382
		unsigned int alg = dst_key_alg(key->key);
383 384 385
		if (key->isdefault &&
		    (notsigned || (wassignedby[alg] && !nowsignedby[alg])))
		{
386 387 388 389
			isc_buffer_t b;
			dns_rdata_t trdata;
			unsigned char array[BUFSIZE];

390
			vbprintf(1, "\tsigning with key %s/%s/%d\n",
391
			       nametostr(dst_key_name(key->key)),
392 393
			       algtostr(dst_key_alg(key->key)),
			       dst_key_id(key->key));
394 395 396 397 398 399 400 401
			isc_buffer_init(&b, array, sizeof(array));
			signwithkey(name, set, &trdata, key->key, &b);
			tuple = NULL;
			result = dns_difftuple_create(mctx, DNS_DIFFOP_ADD,
						      name, ttl, &trdata,
						      &tuple);
			check_result(result, "dns_difftuple_create");
			dns_diff_append(diff, &tuple);
402 403 404 405 406
		}
		key = ISC_LIST_NEXT(key, link);
	}
}

407
/* Determine if a KEY set contains a null key */
408
static isc_boolean_t
409
hasnullkey(dns_rdataset_t *rdataset) {
410 411
	isc_result_t result;
	dns_rdata_t rdata;
412
	isc_boolean_t found = ISC_FALSE;
413

414
	result = dns_rdataset_first(rdataset);
415 416 417
	while (result == ISC_R_SUCCESS) {
		dst_key_t *key = NULL;

418
		dns_rdataset_current(rdataset, &rdata);
419 420
		result = dns_dnssec_keyfromrdata(dns_rootname,
						 &rdata, mctx, &key);
421 422
		if (result != ISC_R_SUCCESS)
			fatal("could not convert KEY into internal format");
423 424
		if (dst_key_isnullkey(key))
			found = ISC_TRUE;
425
                dst_key_free(&key);
426
		if (found == ISC_TRUE)
427
			return (ISC_TRUE);
428 429 430
                result = dns_rdataset_next(rdataset);
        }
        if (result != ISC_R_NOMORE)
431
                fatal("failure looking for null keys");
432 433 434
        return (ISC_FALSE);
}

435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
static void
opendb(const char *prefix, dns_name_t *name, dns_rdataclass_t rdclass,
       dns_db_t **db)
{
	char filename[256];
	isc_buffer_t b;
	isc_result_t result;

	isc_buffer_init(&b, filename, sizeof(filename));
	isc_buffer_putstr(&b, prefix);
	result = dns_name_totext(name, ISC_FALSE, &b);
	check_result(result, "dns_name_totext()");
	if (isc_buffer_availablelength(&b) == 0)
		fatal("name '%s' is too long", nametostr(name));
	isc_buffer_putuint8(&b, 0);

	result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone,
			       rdclass, 0, NULL, db);
	check_result(result, "dns_db_create()");

	result = dns_db_load(*db, filename);
	if (result != ISC_R_SUCCESS)
		dns_db_detach(db);
}


461 462 463 464 465
/*
 * Looks for signatures of the zone keys by the parent, and imports them
 * if found.
 */
static void
466 467
importparentsig(dns_db_t *db, dns_diff_t *diff, dns_name_t *name,
		dns_rdataset_t *set)
468 469 470 471 472 473 474
{
	dns_db_t *newdb = NULL;
	dns_dbnode_t *newnode = NULL;
	dns_rdataset_t newset, sigset;
	dns_rdata_t rdata, newrdata;
	isc_result_t result;

475 476 477
	dns_rdataset_init(&newset);
	dns_rdataset_init(&sigset);

478 479 480 481
	opendb("signedkey-", name, dns_db_class(db), &newdb);
	if (newdb == NULL)
		return;

482 483 484
	result = dns_db_findnode(newdb, name, ISC_FALSE, &newnode);
	if (result != ISC_R_SUCCESS)
		goto failure;
485 486
	result = dns_db_findrdataset(newdb, newnode, NULL, dns_rdatatype_key,
				     0, 0, &newset, &sigset);
487 488 489
	if (result != ISC_R_SUCCESS)
		goto failure;

490 491 492 493
	if (!dns_rdataset_isassociated(&newset) ||
	    !dns_rdataset_isassociated(&sigset))
		goto failure;

494 495 496 497 498 499 500
	if (dns_rdataset_count(set) != dns_rdataset_count(&newset))
		goto failure;

	dns_rdata_init(&rdata);
	dns_rdata_init(&newrdata);

	result = dns_rdataset_first(set);
501
	check_result(result, "dns_rdataset_first()");
502 503 504
	for (; result == ISC_R_SUCCESS; result = dns_rdataset_next(set)) {
		dns_rdataset_current(set, &rdata);
		result = dns_rdataset_first(&newset);
505
		check_result(result, "dns_rdataset_first()");
506 507 508 509 510 511 512 513 514 515
		for (;
		     result == ISC_R_SUCCESS;
		     result = dns_rdataset_next(&newset))
		{
			dns_rdataset_current(&newset, &newrdata);
			if (dns_rdata_compare(&rdata, &newrdata) == 0)
				break;
		}
		if (result != ISC_R_SUCCESS)
			break;
516
	}
517
	if (result != ISC_R_NOMORE)
518 519 520 521
		goto failure;

	vbprintf(2, "found the parent's signature of our zone key\n");

522 523 524 525 526 527 528 529 530 531 532
	result = dns_rdataset_first(&sigset);
	while (result == ISC_R_SUCCESS) {
		dns_difftuple_t *tuple = NULL;

		dns_rdataset_current(&sigset, &rdata);
		result = dns_difftuple_create(mctx, DNS_DIFFOP_ADD, name, 
					      sigset.ttl, &rdata, &tuple);
		check_result(result, "dns_difftuple_create");
		dns_diff_append(diff, &tuple);
		result = dns_rdataset_next(&sigset);
	}
533 534

 failure:
535 536 537 538
	if (dns_rdataset_isassociated(&newset))
		dns_rdataset_disassociate(&newset);
	if (dns_rdataset_isassociated(&sigset))
		dns_rdataset_disassociate(&sigset);
539 540 541 542 543 544 545
	if (newnode != NULL)
		dns_db_detachnode(newdb, &newnode);
	if (newdb != NULL)
		dns_db_detach(&newdb);
}

/*
546
 * Looks for our signatures of child keys.  If present, inform the caller.
547 548 549 550 551 552 553 554 555 556 557 558
 */
static isc_boolean_t
haschildkey(dns_db_t *db, dns_name_t *name) {
	dns_db_t *newdb = NULL;
	dns_dbnode_t *newnode = NULL;
	dns_rdataset_t set, sigset;
	dns_rdata_t sigrdata;
	isc_result_t result;
	isc_boolean_t found = ISC_FALSE;
	dns_rdata_sig_t sig;
	signer_key_t *key;

559 560 561
	dns_rdataset_init(&set);
	dns_rdataset_init(&sigset);

562 563 564 565
	opendb("signedkey-", name, dns_db_class(db), &newdb);
	if (newdb == NULL)
		return (ISC_FALSE);

566 567 568
	result = dns_db_findnode(newdb, name, ISC_FALSE, &newnode);
	if (result != ISC_R_SUCCESS)
		goto failure;
569 570
	result = dns_db_findrdataset(newdb, newnode, NULL, dns_rdatatype_key,
				     0, 0, &set, &sigset);
571 572 573 574 575
	if (result != ISC_R_SUCCESS)
		goto failure;

	if (!dns_rdataset_isassociated(&set) ||
	    !dns_rdataset_isassociated(&sigset))
576
		goto failure;
577 578 579 580 581 582

	result = dns_rdataset_first(&sigset);
	check_result(result, "dns_rdataset_first()");
	dns_rdata_init(&sigrdata);
	for (; result == ISC_R_SUCCESS; result = dns_rdataset_next(&sigset)) {
		dns_rdataset_current(&sigset, &sigrdata);
583
		result = dns_rdata_tostruct(&sigrdata, &sig, NULL);
584
		if (result != ISC_R_SUCCESS)
585
			goto failure;
586 587 588
		key = keythatsigned(&sig);
		dns_rdata_freestruct(&sig);
		if (key == NULL)
589
			goto failure;
590 591 592 593 594 595 596 597
		result = dns_dnssec_verify(name, &set, key->key,
					   ISC_FALSE, mctx, &sigrdata);
		if (result == ISC_R_SUCCESS) {
			found = ISC_TRUE;
			break;
		}
	}

598
 failure:
599 600 601 602 603 604 605 606 607 608
	if (dns_rdataset_isassociated(&set))
		dns_rdataset_disassociate(&set);
	if (dns_rdataset_isassociated(&sigset))
		dns_rdataset_disassociate(&sigset);
	if (newnode != NULL)
		dns_db_detachnode(newdb, &newnode);
	if (newdb != NULL)
		dns_db_detach(&newdb);

	return (found);
609 610
}

611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
/*
 * There probably should be a dns_nxt_setbit, but it can get complicated if
 * the length of the bit set needs to be increased.  In this case, since the
 * NXT bit is set and both SIG and KEY are less than NXT, the easy way works.
 */
static void
nxt_setbit(dns_rdataset_t *rdataset, dns_rdatatype_t type) {
	unsigned char *nxt_bits;
	dns_name_t nxtname;
	isc_region_t r, r2;
	isc_result_t result;
	dns_rdata_t rdata;

	result = dns_rdataset_first(rdataset);
	check_result(result, "dns_rdataset_first()");
	dns_rdataset_current(rdataset, &rdata);
	dns_rdata_toregion(&rdata, &r);
	dns_name_init(&nxtname, NULL);
	dns_name_fromregion(&nxtname, &r);
	dns_name_toregion(&nxtname, &r2);
	nxt_bits = r.base + r2.length;
	set_bit(nxt_bits, type, 1);
}

635 636
/*
 * Signs all records at a name.  This mostly just signs each set individually,
637 638
 * but also adds the SIG bit to any NXTs generated earlier, deals with
 * parent/child KEY signatures, and handles other exceptional cases.
639
 */
Brian Wellington's avatar
Brian Wellington committed
640
static void
641
signname(dns_db_t *db, dns_dbversion_t *version, dns_dbnode_t *node,
642
	 dns_name_t *name)
Brian Wellington's avatar
Brian Wellington committed
643 644
{
	isc_result_t result;
645
	dns_rdataset_t rdataset;
Brian Wellington's avatar
Brian Wellington committed
646
	dns_rdatasetiter_t *rdsiter;
Brian Wellington's avatar
Brian Wellington committed
647
	isc_boolean_t isdelegation = ISC_FALSE;
648
	isc_boolean_t childkey = ISC_FALSE;
649
	static int warnwild = 0;
650
	isc_boolean_t atorigin;
651 652
	isc_boolean_t neednullkey = ISC_TRUE;
	dns_diff_t diff;
653 654

	if (dns_name_iswildcard(name)) {
Brian Wellington's avatar
Brian Wellington committed
655 656
		if (warnwild++ == 0) {
			fprintf(stderr, "%s: warning: BIND 9 doesn't properly "
David Lawrence's avatar
David Lawrence committed
657
				"handle wildcards in secure zones:\n",
658
				program);
Brian Wellington's avatar
Brian Wellington committed
659 660 661 662 663 664
			fprintf(stderr, "\t- wildcard nonexistence proof is "
				"not generated by the server\n");
			fprintf(stderr, "\t- wildcard nonexistence proof is "
				"not required by the resolver\n");
		}
		fprintf(stderr, "%s: warning: wildcard name seen: %s\n",
665
			program, nametostr(name));
666
	}
667

668
	atorigin = dns_name_equal(name, dns_db_origin(db));
669 670 671 672

	/*
	 * If this is not the origin, determine if it's a delegation point.
	 */
673
	if (!atorigin) {
674 675
		dns_rdataset_t nsset;

676 677 678 679 680 681 682 683 684 685
		dns_rdataset_init(&nsset);
		result = dns_db_findrdataset(db, node, version,
					     dns_rdatatype_ns, 0, 0, &nsset,
					     NULL);
		/* Is this a delegation point? */
		if (result == ISC_R_SUCCESS) {
			isdelegation = ISC_TRUE;
			dns_rdataset_disassociate(&nsset);
		}
	}
686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762

	/*
	 * If this is a delegation point, determine if we need to generate
	 * a null key.
	 */
	if (isdelegation) {
		dns_rdataset_t keyset;

		childkey = haschildkey(db, name);

		dns_rdataset_init(&keyset);
		result = dns_db_findrdataset(db, node, version,
					     dns_rdatatype_key, 0, 0, &keyset,
					     NULL);
		if (result == ISC_R_SUCCESS && childkey)
			fatal("%s has both a signedkey file and KEY "
			      "records in the zone.  Aborting.",
			      nametostr(name));
		else if (result == ISC_R_SUCCESS) {
			if (hasnullkey(&keyset))
				neednullkey = ISC_FALSE;
			dns_rdataset_disassociate(&keyset);
		} else if (childkey) {
			vbprintf(2, "child key for %s found\n",
				 nametostr(name));
			neednullkey = ISC_FALSE;
		}

		if (neednullkey) {
			unsigned char keydata[4];
			dns_rdata_t keyrdata;
			dst_key_t *dstkey;
			dns_difftuple_t *tuple = NULL;
			isc_buffer_t b;
			isc_region_t r;

			vbprintf(2, "adding null key at %s\n",
				 nametostr(name));
			dstkey = NULL;
			result = dst_key_generate(name, DNS_KEYALG_DSA, 0, 0,
						  DNS_KEYTYPE_NOKEY |
						  DNS_KEYOWNER_ZONE,
						  DNS_KEYPROTO_DNSSEC,
						  mctx, &dstkey);
			if (result != ISC_R_SUCCESS)
				fatal("failed to generate null key");

			isc_buffer_init(&b, keydata, sizeof keydata);
			result = dst_key_todns(dstkey, &b);
			if (result != ISC_R_SUCCESS)
				fatal("failed to convert null key");

			dst_key_free(&dstkey);
			isc_buffer_usedregion(&b, &r);
			dns_rdata_fromregion(&keyrdata, dns_db_class(db),
					     dns_rdatatype_key, &r);

			dns_diff_init(mctx, &diff);

			result = dns_difftuple_create(mctx, DNS_DIFFOP_ADD,
						      name, zonettl, &keyrdata,
						      &tuple);
			check_result(result, "dns_difftuple_create");

			dns_diff_append(&diff, &tuple);

			result = dns_diff_apply(&diff, db, version);
			check_result(result, "dns_diff_apply");

			dns_diff_clear(&diff);
		}
	}

	/*
	 * Now iterate through the rdatasets.
	 */
	dns_diff_init(mctx, &diff);
Brian Wellington's avatar
Brian Wellington committed
763 764 765 766 767 768 769
	dns_rdataset_init(&rdataset);
	rdsiter = NULL;
	result = dns_db_allrdatasets(db, node, version, 0, &rdsiter);
	check_result(result, "dns_db_allrdatasets()");
	result = dns_rdatasetiter_first(rdsiter);
	while (result == ISC_R_SUCCESS) {
		dns_rdatasetiter_current(rdsiter, &rdataset);
Brian Wellington's avatar
Brian Wellington committed
770

771 772 773
		/* If this is a SIG set, skip it. */
		if (rdataset.type == dns_rdatatype_sig)
			goto skip;
Brian Wellington's avatar
Brian Wellington committed
774

775 776 777 778
		/*
		 * If this is a KEY set at the apex, look for a signedkey file.
		 */
		if (rdataset.type == dns_rdatatype_key && atorigin) {
779
			importparentsig(db, &diff, name, &rdataset);
780
			goto skip;
781
		}
782 783 784

		/*
		 * If this name is a delegation point, skip all records
785
		 * except an NXT set a KEY set containing a null key.
786 787 788
		 */
		if (isdelegation) {
			switch (rdataset.type) {
789 790 791 792
			case dns_rdatatype_nxt:
				break;
			case dns_rdatatype_key:
				if (hasnullkey(&rdataset))
793
					break;
794 795 796
				goto skip;
			default:
				goto skip;
797
			}
798

799 800
		}

801
		if (rdataset.type == dns_rdatatype_nxt) {
802 803 804
			nxt_setbit(&rdataset, dns_rdatatype_sig);
			if (neednullkey)
				nxt_setbit(&rdataset, dns_rdatatype_key);
Brian Wellington's avatar
Brian Wellington committed
805
		}
Brian Wellington's avatar
Brian Wellington committed
806

807
		signset(db, version, &diff, node, name, &rdataset);
Brian Wellington's avatar
Brian Wellington committed
808

809
 skip:
Brian Wellington's avatar
Brian Wellington committed
810 811 812
		dns_rdataset_disassociate(&rdataset);
		result = dns_rdatasetiter_next(rdsiter);
	}
813
	if (result != ISC_R_NOMORE)
814 815
		fatal("rdataset iteration for name '%s' failed: %s",
		      nametostr(name), isc_result_totext(result));
Brian Wellington's avatar
Brian Wellington committed
816
	dns_rdatasetiter_destroy(&rdsiter);
817 818 819 820 821

	result = dns_diff_apply(&diff, db, version);
	if (result != ISC_R_SUCCESS)
		fatal("failed to add SIGs at node %s", nametostr(name));
	dns_diff_clear(&diff);
Brian Wellington's avatar
Brian Wellington committed
822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843
}

static inline isc_boolean_t
active_node(dns_db_t *db, dns_dbversion_t *version, dns_dbnode_t *node) {
	dns_rdatasetiter_t *rdsiter;
	isc_boolean_t active = ISC_FALSE;
	isc_result_t result;
	dns_rdataset_t rdataset;

	dns_rdataset_init(&rdataset);
	rdsiter = NULL;
	result = dns_db_allrdatasets(db, node, version, 0, &rdsiter);
	check_result(result, "dns_db_allrdatasets()");
	result = dns_rdatasetiter_first(rdsiter);
	while (result == ISC_R_SUCCESS) {
		dns_rdatasetiter_current(rdsiter, &rdataset);
		if (rdataset.type != dns_rdatatype_nxt)
			active = ISC_TRUE;
		dns_rdataset_disassociate(&rdataset);
		if (!active)
			result = dns_rdatasetiter_next(rdsiter);
		else
844
			result = ISC_R_NOMORE;
Brian Wellington's avatar
Brian Wellington committed
845
	}
846
	if (result != ISC_R_NOMORE)
847 848
		fatal("rdataset iteration failed: %s",
		      isc_result_totext(result));
Brian Wellington's avatar
Brian Wellington committed
849 850 851 852 853 854 855
	dns_rdatasetiter_destroy(&rdsiter);

	if (!active) {
		/*
		 * Make sure there is no NXT record for this node.
		 */
		result = dns_db_deleterdataset(db, node, version,
856
					       dns_rdatatype_nxt, 0);
Brian Wellington's avatar
Brian Wellington committed
857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886
		if (result == DNS_R_UNCHANGED)
			result = ISC_R_SUCCESS;
		check_result(result, "dns_db_deleterdataset");
	}

	return (active);
}

static inline isc_result_t
next_active(dns_db_t *db, dns_dbversion_t *version, dns_dbiterator_t *dbiter,
	    dns_name_t *name, dns_dbnode_t **nodep)
{
	isc_result_t result;
	isc_boolean_t active;

	do {
		active = ISC_FALSE;
		result = dns_dbiterator_current(dbiter, nodep, name);
		if (result == ISC_R_SUCCESS) {
			active = active_node(db, version, *nodep);
			if (!active) {
				dns_db_detachnode(db, nodep);
				result = dns_dbiterator_next(dbiter);
			}
		}
	} while (result == ISC_R_SUCCESS && !active);

	return (result);
}

887 888
static inline isc_result_t
next_nonglue(dns_db_t *db, dns_dbversion_t *version, dns_dbiterator_t *dbiter,
Brian Wellington's avatar
Brian Wellington committed
889 890
	    dns_name_t *name, dns_dbnode_t **nodep, dns_name_t *origin,
	    dns_name_t *lastcut)
891
{
892
	isc_result_t result, dresult;
893 894 895 896

	do {
		result = next_active(db, version, dbiter, name, nodep);
		if (result == ISC_R_SUCCESS) {
Brian Wellington's avatar
Brian Wellington committed
897 898 899
			if (dns_name_issubdomain(name, origin) &&
			    (lastcut == NULL ||
			     !dns_name_issubdomain(name, lastcut)))
900
				return (ISC_R_SUCCESS);
901 902 903 904 905
			dresult = dns_master_dumpnodetostream(mctx, db,
							      version,
							      *nodep, name,
							      masterstyle, fp);
			check_result(dresult, "dns_master_dumpnodetostream");
906 907 908 909 910 911 912
			dns_db_detachnode(db, nodep);
			result = dns_dbiterator_next(dbiter);
		}
	} while (result == ISC_R_SUCCESS);
	return (result);
}

913
/*
914
 * Extracts the zone minimum TTL from the SOA.
915
 */
916 917
static dns_ttl_t
minimumttl(dns_db_t *db, dns_dbversion_t *version) {