dnssec-signzone.c 38.7 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.97 2000/09/08 14:16:43 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
typedef struct signer_key_struct signer_key_t;

struct signer_key_struct {
	dst_key_t *key;
	isc_boolean_t isdefault;
67
	unsigned int position;
68 69 70
	ISC_LINK(signer_key_t) link;
};

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

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

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

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

95 96 97
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
98 99 100
{
	isc_result_t result;

101
	dns_rdata_init(rdata);
102
	result = dns_dnssec_sign(name, rdataset, key, &starttime, &endtime,
103
				 mctx, b, rdata);
104
	isc_entropy_stopcallbacksources(ectx);
105 106 107 108 109 110
	if (result != ISC_R_SUCCESS) {
		char keystr[KEY_FORMATSIZE];
		key_format(key, keystr, sizeof keystr);
		fatal("key '%s' failed to sign data: %s",
		      keystr, isc_result_totext(result));
	}
111

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

122 123 124
static inline isc_boolean_t
issigningkey(signer_key_t *key) {
	return (key->isdefault);
Brian Wellington's avatar
Brian Wellington committed
125 126
}

127 128
static inline isc_boolean_t
iszonekey(signer_key_t *key, dns_db_t *db) {
129 130
	return (ISC_TF(dns_name_equal(dst_key_name(key->key),
				      dns_db_origin(db)) &&
131
		       dst_key_iszonekey(key->key)));
132 133
}

134 135 136 137
/*
 * 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.
 */
138
static signer_key_t *
139
keythatsigned(dns_rdata_sig_t *sig) {
140
	isc_result_t result;
141 142 143 144 145 146 147
	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) &&
148
		    dns_name_equal(&sig->signer, dst_key_name(key->key)))
149
			return key;
150
		key = ISC_LIST_NEXT(key, link);
151
	}
152

153
	result = dst_key_fromfile(&sig->signer, sig->keyid, sig->algorithm,
Brian Wellington's avatar
Brian Wellington committed
154
				  DST_TYPE_PUBLIC, NULL, mctx, &pubkey);
155 156
	if (result != ISC_R_SUCCESS)
		return (NULL);
157

158 159
	key = isc_mem_get(mctx, sizeof(signer_key_t));
	if (key == NULL)
160
		fatal("out of memory");
161

162
	result = dst_key_fromfile(&sig->signer, sig->keyid, sig->algorithm,
Brian Wellington's avatar
Brian Wellington committed
163
				  DST_TYPE_PRIVATE, NULL, mctx, &privkey);
164 165
	if (result == ISC_R_SUCCESS) {
		key->key = privkey;
166
		dst_key_free(&pubkey);
167 168 169 170
	}
	else
		key->key = pubkey;
	key->isdefault = ISC_FALSE;
171
	key->position = keycount++;
172 173
	ISC_LIST_APPEND(keylist, key, link);
	return key;
174 175
}

176 177 178 179 180
/*
 * 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.
 */
181 182 183
static isc_boolean_t
expecttofindkey(dns_name_t *name, dns_db_t *db, dns_dbversion_t *version) {
	unsigned int options = DNS_DBFIND_NOWILD;
184
	dns_fixedname_t fname;
185
	isc_result_t result;
186
	char namestr[DNS_NAME_FORMATSIZE];
187

188
	dns_fixedname_init(&fname);
189
	result = dns_db_find(db, name, version, dns_rdatatype_key, options,
190
			     0, NULL, dns_fixedname_name(&fname), NULL, NULL);
191
	switch (result) {
192 193 194 195 196 197 198 199
	case ISC_R_SUCCESS:
	case DNS_R_NXDOMAIN:
	case DNS_R_NXRRSET:
		return (ISC_TRUE);
	case DNS_R_DELEGATION:
	case DNS_R_CNAME:
	case DNS_R_DNAME:
		return (ISC_FALSE);
200
	}
201 202 203 204
	dns_name_format(name, namestr, sizeof namestr);
	fatal("failure looking for '%s KEY' in database: %s",
	      namestr, isc_result_totext(result));
	return (ISC_FALSE); /* removes a warning */
205 206
}

207
static inline isc_boolean_t
208 209
setverifies(dns_name_t *name, dns_rdataset_t *set, signer_key_t *key,
	    dns_rdata_t *sig)
210
{
211 212
	isc_result_t result;
	result = dns_dnssec_verify(name, set, key->key, ISC_FALSE, mctx, sig);
213
	return (ISC_TF(result == ISC_R_SUCCESS));
214 215
}

216 217 218 219 220
/*
 * 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.
 */
221
static void
222 223
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)
224
{
225 226
	dns_rdataset_t sigset;
	dns_rdata_t sigrdata;
227
	dns_rdata_sig_t sig;
228
	signer_key_t *key;
229
	isc_result_t result;
230 231 232
	isc_boolean_t nosigs = ISC_FALSE;
	isc_boolean_t *wassignedby, *nowsignedby;
	int arraysize;
233 234
	dns_difftuple_t *tuple;
	dns_ttl_t ttl;
235
	int i;
236 237 238 239 240 241
	char namestr[DNS_NAME_FORMATSIZE];
	char typestr[TYPE_FORMATSIZE];
	char sigstr[SIG_FORMATSIZE];

	dns_name_format(name, namestr, sizeof namestr);
	type_format(set->type, typestr, sizeof typestr);
242

243
	ttl = ISC_MIN(set->ttl, endtime - starttime);
244

245
	dns_rdataset_init(&sigset);
246
	result = dns_db_findrdataset(db, node, version, dns_rdatatype_sig,
247
				     set->type, 0, &sigset, NULL);
248 249 250 251
	if (result == ISC_R_NOTFOUND) {
		result = ISC_R_SUCCESS;
		nosigs = ISC_TRUE;
	}
252 253
	if (result != ISC_R_SUCCESS)
		fatal("failed while looking for '%s SIG %s': %s",
254
		      namestr, typestr, isc_result_totext(result));
255

256
	vbprintf(1, "%s/%s:\n", namestr, typestr);
257

258 259 260 261 262 263 264 265 266 267 268 269 270
	arraysize = keycount;
	if (!nosigs)
		arraysize += dns_rdataset_count(&sigset);
	wassignedby = isc_mem_get(mctx,
				  arraysize * sizeof(isc_boolean_t));
	nowsignedby = isc_mem_get(mctx,
				  arraysize * sizeof(isc_boolean_t));
	if (wassignedby == NULL || nowsignedby == NULL)
		fatal("out of memory");

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

Brian Wellington's avatar
Brian Wellington committed
271 272 273
	if (nosigs)
		result = ISC_R_NOMORE;
	else
274
		result = dns_rdataset_first(&sigset);
275

Brian Wellington's avatar
Brian Wellington committed
276 277 278
	while (result == ISC_R_SUCCESS) {
		isc_boolean_t expired, future;
		isc_boolean_t keep = ISC_FALSE, resign = ISC_FALSE;
279

Brian Wellington's avatar
Brian Wellington committed
280
		dns_rdataset_current(&sigset, &sigrdata);
281

Brian Wellington's avatar
Brian Wellington committed
282 283
		result = dns_rdata_tostruct(&sigrdata, &sig, NULL);
		check_result(result, "dns_rdata_tostruct");
284

Brian Wellington's avatar
Brian Wellington committed
285 286
		expired = ISC_TF(now + cycle > sig.timeexpire);
		future = ISC_TF(now < sig.timesigned);
287

Brian Wellington's avatar
Brian Wellington committed
288
		key = keythatsigned(&sig);
289
		sig_format(&sig, sigstr, sizeof sigstr);
Brian Wellington's avatar
Brian Wellington committed
290 291 292 293 294

		if (sig.timesigned > sig.timeexpire) {
			/* sig is dropped and not replaced */
			vbprintf(2, "\tsig by %s dropped - "
				 "invalid validity period\n",
295
				 sigstr);
Brian Wellington's avatar
Brian Wellington committed
296 297 298 299 300 301
		} else if (key == NULL && !future &&
			 expecttofindkey(&sig.signer, db, version))
		{
			/* sig is dropped and not replaced */
			vbprintf(2, "\tsig by %s dropped - "
				 "private key not found\n",
302
				 sigstr);
Brian Wellington's avatar
Brian Wellington committed
303 304
		} else if (key == NULL || future) {
			vbprintf(2, "\tsig by %s %s - key not found\n",
305
				 expired ? "retained" : "dropped", sigstr);
Brian Wellington's avatar
Brian Wellington committed
306 307 308 309
			if (!expired)
				keep = ISC_TRUE;
		} else if (issigningkey(key)) {
			if (!expired && setverifies(name, set, key, &sigrdata))
310
			{
311
				vbprintf(2, "\tsig by %s retained\n", sigstr);
312
				keep = ISC_TRUE;
313 314
				wassignedby[key->position] = ISC_TRUE;
				nowsignedby[key->position] = ISC_TRUE;
315
			} else {
Brian Wellington's avatar
Brian Wellington committed
316
				vbprintf(2, "\tsig by %s dropped - %s\n",
317
					 sigstr,
Brian Wellington's avatar
Brian Wellington committed
318 319
					 expired ? "expired" :
						   "failed to verify");
320
				wassignedby[key->position] = ISC_TRUE;
Brian Wellington's avatar
Brian Wellington committed
321
				resign = ISC_TRUE;
322
			}
Brian Wellington's avatar
Brian Wellington committed
323 324 325
		} else if (iszonekey(key, db)) {
			if (!expired && setverifies(name, set, key, &sigrdata))
			{
326
				vbprintf(2, "\tsig by %s retained\n", sigstr);
Brian Wellington's avatar
Brian Wellington committed
327
				keep = ISC_TRUE;
328 329
				wassignedby[key->position] = ISC_TRUE;
				nowsignedby[key->position] = ISC_TRUE;
Brian Wellington's avatar
Brian Wellington committed
330 331
			} else {
				vbprintf(2, "\tsig by %s dropped - %s\n",
332
					 sigstr,
Brian Wellington's avatar
Brian Wellington committed
333 334
					 expired ? "expired" :
						   "failed to verify");
335
				wassignedby[key->position] = ISC_TRUE;
336
			}
Brian Wellington's avatar
Brian Wellington committed
337
		} else if (!expired) {
338
			vbprintf(2, "\tsig by %s retained\n", sigstr);
Brian Wellington's avatar
Brian Wellington committed
339 340
			keep = ISC_TRUE;
		} else {
341
			vbprintf(2, "\tsig by %s expired\n", sigstr);
Brian Wellington's avatar
Brian Wellington committed
342
		}
343

Brian Wellington's avatar
Brian Wellington committed
344
		if (keep)
345
			nowsignedby[key->position] = ISC_TRUE;
Brian Wellington's avatar
Brian Wellington committed
346 347 348 349 350 351 352 353 354 355 356 357 358
		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);
		}

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

361 362
			key_format(key->key, keystr, sizeof keystr);
			vbprintf(1, "\tresigning with key %s\n", keystr);
Brian Wellington's avatar
Brian Wellington committed
363 364
			isc_buffer_init(&b, array, sizeof(array));
			signwithkey(name, set, &trdata, key->key, &b);
365
			nowsignedby[key->position] = ISC_TRUE;
Brian Wellington's avatar
Brian Wellington committed
366 367 368 369 370 371
			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);
372
		}
Brian Wellington's avatar
Brian Wellington committed
373 374 375

		dns_rdata_freestruct(&sig);
		result = dns_rdataset_next(&sigset);
Brian Wellington's avatar
Brian Wellington committed
376
	}
Brian Wellington's avatar
Brian Wellington committed
377 378 379 380 381 382
	if (result == ISC_R_NOMORE)
		result = ISC_R_SUCCESS;

	check_result(result, "dns_rdataset_first/next");
	if (dns_rdataset_isassociated(&sigset))
		dns_rdataset_disassociate(&sigset);
383 384 385

	key = ISC_LIST_HEAD(keylist);
	while (key != NULL) {
386
		if (key->isdefault && !nowsignedby[key->position]) {
387 388 389
			isc_buffer_t b;
			dns_rdata_t trdata;
			unsigned char array[BUFSIZE];
390
			char keystr[KEY_FORMATSIZE];
391

392 393
			key_format(key->key, keystr, sizeof keystr);
			vbprintf(1, "\tsigning with key %s\n", keystr);
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
		}
		key = ISC_LIST_NEXT(key, link);
	}
405 406 407

	isc_mem_put(mctx, wassignedby, arraysize * sizeof(isc_boolean_t));
	isc_mem_put(mctx, nowsignedby, arraysize * sizeof(isc_boolean_t));
408 409
}

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

417
	result = dns_rdataset_first(rdataset);
418 419 420
	while (result == ISC_R_SUCCESS) {
		dst_key_t *key = NULL;

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

438 439 440 441 442 443 444 445 446 447 448 449
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()");
450 451 452 453 454
	if (isc_buffer_availablelength(&b) == 0) {
		char namestr[DNS_NAME_FORMATSIZE];
		dns_name_format(name, namestr, sizeof namestr);
		fatal("name '%s' is too long", namestr);
	}
455 456 457 458 459 460 461 462 463 464 465
	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);
}

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

480 481 482
	dns_rdataset_init(&newset);
	dns_rdataset_init(&sigset);

483 484 485 486
	opendb("signedkey-", name, dns_db_class(db), &newdb);
	if (newdb == NULL)
		return;

487 488 489
	result = dns_db_findnode(newdb, name, ISC_FALSE, &newnode);
	if (result != ISC_R_SUCCESS)
		goto failure;
490 491
	result = dns_db_findrdataset(newdb, newnode, NULL, dns_rdatatype_key,
				     0, 0, &newset, &sigset);
492 493 494
	if (result != ISC_R_SUCCESS)
		goto failure;

495 496 497 498
	if (!dns_rdataset_isassociated(&newset) ||
	    !dns_rdataset_isassociated(&sigset))
		goto failure;

499 500
	if (dns_rdataset_count(set) != dns_rdataset_count(&newset)) {
		result = DNS_R_BADDB;
501
		goto failure;
502
	}
503 504 505 506 507

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

	result = dns_rdataset_first(set);
508
	check_result(result, "dns_rdataset_first()");
509 510 511
	for (; result == ISC_R_SUCCESS; result = dns_rdataset_next(set)) {
		dns_rdataset_current(set, &rdata);
		result = dns_rdataset_first(&newset);
512
		check_result(result, "dns_rdataset_first()");
513 514 515 516 517 518 519 520 521 522
		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;
523
	}
524
	if (result != ISC_R_NOMORE)
525 526 527 528
		goto failure;

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

529 530 531 532 533 534 535 536 537 538 539
	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);
	}
540 541
	if (result == ISC_R_NOMORE)
		result = ISC_R_SUCCESS;
542 543

 failure:
544 545 546 547
	if (dns_rdataset_isassociated(&newset))
		dns_rdataset_disassociate(&newset);
	if (dns_rdataset_isassociated(&sigset))
		dns_rdataset_disassociate(&sigset);
548 549 550 551
	if (newnode != NULL)
		dns_db_detachnode(newdb, &newnode);
	if (newdb != NULL)
		dns_db_detach(&newdb);
552 553
	if (result != ISC_R_SUCCESS)
		fatal("zone signedkey file is invalid or does not match zone");
554 555 556
}

/*
557
 * Looks for our signatures of child keys.  If present, inform the caller.
558 559 560 561 562 563 564 565 566 567 568 569
 */
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;

570 571 572
	dns_rdataset_init(&set);
	dns_rdataset_init(&sigset);

573 574 575 576
	opendb("signedkey-", name, dns_db_class(db), &newdb);
	if (newdb == NULL)
		return (ISC_FALSE);

577 578 579
	result = dns_db_findnode(newdb, name, ISC_FALSE, &newnode);
	if (result != ISC_R_SUCCESS)
		goto failure;
580 581
	result = dns_db_findrdataset(newdb, newnode, NULL, dns_rdatatype_key,
				     0, 0, &set, &sigset);
582 583 584 585 586
	if (result != ISC_R_SUCCESS)
		goto failure;

	if (!dns_rdataset_isassociated(&set) ||
	    !dns_rdataset_isassociated(&sigset))
587
		goto failure;
588 589 590 591 592 593

	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);
594
		result = dns_rdata_tostruct(&sigrdata, &sig, NULL);
595
		if (result != ISC_R_SUCCESS)
596
			goto failure;
597 598 599
		key = keythatsigned(&sig);
		dns_rdata_freestruct(&sig);
		if (key == NULL)
600
			goto failure;
601 602 603 604 605 606 607 608
		result = dns_dnssec_verify(name, &set, key->key,
					   ISC_FALSE, mctx, &sigrdata);
		if (result == ISC_R_SUCCESS) {
			found = ISC_TRUE;
			break;
		}
	}

609
 failure:
610 611 612 613 614 615 616 617 618 619
	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);
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
/*
 * 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);
}

Brian Wellington's avatar
Brian Wellington committed
646 647 648 649 650 651 652 653 654
static void
createnullkey(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name) {
	unsigned char keydata[4];
	dns_rdata_t keyrdata;
	dns_rdata_key_t key;
	dns_diff_t diff;
	dns_difftuple_t *tuple = NULL;
	isc_buffer_t b;
	isc_result_t result;
655
	char namestr[DNS_NAME_FORMATSIZE];
Brian Wellington's avatar
Brian Wellington committed
656

657 658
	dns_name_format(name, namestr, sizeof namestr);
	vbprintf(2, "adding null key at %s\n", namestr);
Brian Wellington's avatar
Brian Wellington committed
659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688

	key.common.rdclass = dns_db_class(db);
	key.common.rdtype = dns_rdatatype_key;
	ISC_LINK_INIT(&key.common, link);
	key.mctx = NULL;
	key.flags = DNS_KEYTYPE_NOKEY | DNS_KEYOWNER_ZONE;
	key.protocol = DNS_KEYPROTO_DNSSEC;
	key.algorithm = DNS_KEYALG_DSA;
	key.datalen = 0;
	key.data = NULL;
	isc_buffer_init(&b, keydata, sizeof keydata);
	result = dns_rdata_fromstruct(&keyrdata, dns_db_class(db),
				      dns_rdatatype_key, &key, &b);
	if (result != ISC_R_SUCCESS)
		fatal("failed to build null key");

	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);
}

689 690
/*
 * Signs all records at a name.  This mostly just signs each set individually,
691 692
 * but also adds the SIG bit to any NXTs generated earlier, deals with
 * parent/child KEY signatures, and handles other exceptional cases.
693
 */
Brian Wellington's avatar
Brian Wellington committed
694
static void
695
signname(dns_db_t *db, dns_dbversion_t *version, dns_dbnode_t *node,
696
	 dns_name_t *name)
Brian Wellington's avatar
Brian Wellington committed
697 698
{
	isc_result_t result;
699
	dns_rdataset_t rdataset;
Brian Wellington's avatar
Brian Wellington committed
700
	dns_rdatasetiter_t *rdsiter;
Brian Wellington's avatar
Brian Wellington committed
701
	isc_boolean_t isdelegation = ISC_FALSE;
702
	isc_boolean_t childkey = ISC_FALSE;
703
	static int warnwild = 0;
704
	isc_boolean_t atorigin;
Brian Wellington's avatar
Brian Wellington committed
705
	isc_boolean_t neednullkey = ISC_FALSE;
706
	dns_diff_t diff;
707 708

	if (dns_name_iswildcard(name)) {
709 710
		char namestr[DNS_NAME_FORMATSIZE];
		dns_name_format(name, namestr, sizeof namestr);
Brian Wellington's avatar
Brian Wellington committed
711 712
		if (warnwild++ == 0) {
			fprintf(stderr, "%s: warning: BIND 9 doesn't properly "
David Lawrence's avatar
David Lawrence committed
713
				"handle wildcards in secure zones:\n",
714
				program);
Brian Wellington's avatar
Brian Wellington committed
715 716 717 718 719 720
			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",
721
			program, namestr);
722
	}
723

724
	atorigin = dns_name_equal(name, dns_db_origin(db));
725 726 727 728

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

732 733 734 735 736 737 738 739 740 741
		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);
		}
	}
742 743 744 745 746 747 748 749 750

	/*
	 * 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);
Brian Wellington's avatar
Brian Wellington committed
751
		neednullkey = ISC_TRUE;
752 753 754 755 756

		dns_rdataset_init(&keyset);
		result = dns_db_findrdataset(db, node, version,
					     dns_rdatatype_key, 0, 0, &keyset,
					     NULL);
757 758 759
		if (result == ISC_R_SUCCESS && childkey) {
			char namestr[DNS_NAME_FORMATSIZE];
			dns_name_format(name, namestr, sizeof namestr);
760
			fatal("%s has both a signedkey file and KEY "
761 762
			      "records in the zone.  Aborting.", namestr);
		}
763 764 765 766 767
		else if (result == ISC_R_SUCCESS) {
			if (hasnullkey(&keyset))
				neednullkey = ISC_FALSE;
			dns_rdataset_disassociate(&keyset);
		} else if (childkey) {
768 769 770
			char namestr[DNS_NAME_FORMATSIZE];
			dns_name_format(name, namestr, sizeof namestr);
			vbprintf(2, "child key for %s found\n", namestr);
771 772 773
			neednullkey = ISC_FALSE;
		}

Brian Wellington's avatar
Brian Wellington committed
774 775
		if (neednullkey)
			createnullkey(db, version, name);
776 777 778 779 780 781
	}

	/*
	 * Now iterate through the rdatasets.
	 */
	dns_diff_init(mctx, &diff);
Brian Wellington's avatar
Brian Wellington committed
782 783 784 785 786 787 788
	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
789

790 791 792
		/* If this is a SIG set, skip it. */
		if (rdataset.type == dns_rdatatype_sig)
			goto skip;
Brian Wellington's avatar
Brian Wellington committed
793

794 795 796
		/*
		 * If this is a KEY set at the apex, look for a signedkey file.
		 */
Brian Wellington's avatar
Brian Wellington committed
797
		if (atorigin && rdataset.type == dns_rdatatype_key) {
798
			importparentsig(db, &diff, name, &rdataset);
799
			goto skip;
800
		}
801 802 803

		/*
		 * If this name is a delegation point, skip all records
804
		 * except an NXT set a KEY set containing a null key.
805 806
		 */
		if (isdelegation) {
Brian Wellington's avatar
Brian Wellington committed
807 808 809
			if (!(rdataset.type == dns_rdatatype_nxt ||
			      (rdataset.type == dns_rdatatype_key &&
			       hasnullkey(&rdataset))))
810
				goto skip;
811 812
		}

813
		if (rdataset.type == dns_rdatatype_nxt) {
814 815 816
			nxt_setbit(&rdataset, dns_rdatatype_sig);
			if (neednullkey)
				nxt_setbit(&rdataset, dns_rdatatype_key);
Brian Wellington's avatar
Brian Wellington committed
817
		}
Brian Wellington's avatar
Brian Wellington committed
818

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

821
 skip:
Brian Wellington's avatar
Brian Wellington committed
822 823 824
		dns_rdataset_disassociate(&rdataset);
		result = dns_rdatasetiter_next(rdsiter);
	}
825 826 827
	if (result != ISC_R_NOMORE) {
		char namestr[DNS_NAME_FORMATSIZE];
		dns_name_format(name, namestr, sizeof namestr);
828
		fatal("rdataset iteration for name '%s' failed: %s",
829 830
		      namestr, isc_result_totext(result));
	}
Brian Wellington's avatar
Brian Wellington committed
831
	dns_rdatasetiter_destroy(&rdsiter);
832 833

	result = dns_diff_apply(&diff, db, version);
834 835 836 837 838
	if (result != ISC_R_SUCCESS) {
		char namestr[DNS_NAME_FORMATSIZE];
		dns_name_format(name, namestr, sizeof namestr);
		fatal("failed to add SIGs at node %s", namestr);
	}
839
	dns_diff_clear(&diff);
Brian Wellington's avatar
Brian Wellington committed
840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861
}

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
862
			result = ISC_R_NOMORE;
Brian Wellington's avatar
Brian Wellington committed
863
	}
864
	if (result != ISC_R_NOMORE)
865 866
		fatal("rdataset iteration failed: %s",
		      isc_result_totext(result));
Brian Wellington's avatar
Brian Wellington committed
867 868 869 870 871 872 873
	dns_rdatasetiter_destroy(&rdsiter);

	if (!active) {
		/*
		 * Make sure there is no NXT record for this node.
		 */
		result = dns_db_deleterdataset(db, node, version,
874
					       dns_rdatatype_nxt, 0);
Brian Wellington's avatar
Brian Wellington committed
875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904
		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