dnssec-signzone.c 38.3 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.96 2000/09/08 08:38:57 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 105 106 107 108
	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));
	}
109

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

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

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

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

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

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

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

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

185
	dns_fixedname_init(&fname);
186
	result = dns_db_find(db, name, version, dns_rdatatype_key, options,
187
			     0, NULL, dns_fixedname_name(&fname), NULL, NULL);
188
	switch (result) {
189 190 191 192 193 194 195 196
	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);
197
	}
198 199 200 201
	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 */
202 203
}

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

213 214 215 216 217
/*
 * 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.
 */
218
static void
219 220
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)
221
{
222 223
	dns_rdataset_t sigset;
	dns_rdata_t sigrdata;
224
	dns_rdata_sig_t sig;
225
	signer_key_t *key;
226
	isc_result_t result;
227
	isc_boolean_t notsigned = ISC_TRUE, nosigs = ISC_FALSE;
228
	isc_boolean_t wassignedby[256], nowsignedby[256];
229 230
	dns_difftuple_t *tuple;
	dns_ttl_t ttl;
231
	int i;
232 233 234 235 236 237
	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);
238

239
	ttl = ISC_MIN(set->ttl, endtime - starttime);
240 241 242 243

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

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

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

Brian Wellington's avatar
Brian Wellington committed
257 258 259
	if (nosigs)
		result = ISC_R_NOMORE;
	else
260
		result = dns_rdataset_first(&sigset);
261

Brian Wellington's avatar
Brian Wellington committed
262 263 264
	while (result == ISC_R_SUCCESS) {
		isc_boolean_t expired, future;
		isc_boolean_t keep = ISC_FALSE, resign = ISC_FALSE;
265

Brian Wellington's avatar
Brian Wellington committed
266
		dns_rdataset_current(&sigset, &sigrdata);
267

Brian Wellington's avatar
Brian Wellington committed
268 269
		result = dns_rdata_tostruct(&sigrdata, &sig, NULL);
		check_result(result, "dns_rdata_tostruct");
270

Brian Wellington's avatar
Brian Wellington committed
271 272
		expired = ISC_TF(now + cycle > sig.timeexpire);
		future = ISC_TF(now < sig.timesigned);
273

Brian Wellington's avatar
Brian Wellington committed
274
		key = keythatsigned(&sig);
275
		sig_format(&sig, sigstr, sizeof sigstr);
Brian Wellington's avatar
Brian Wellington committed
276 277 278 279 280

		if (sig.timesigned > sig.timeexpire) {
			/* sig is dropped and not replaced */
			vbprintf(2, "\tsig by %s dropped - "
				 "invalid validity period\n",
281
				 sigstr);
Brian Wellington's avatar
Brian Wellington committed
282 283 284 285 286 287
		} 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",
288
				 sigstr);
Brian Wellington's avatar
Brian Wellington committed
289 290
		} else if (key == NULL || future) {
			vbprintf(2, "\tsig by %s %s - key not found\n",
291
				 expired ? "retained" : "dropped", sigstr);
Brian Wellington's avatar
Brian Wellington committed
292 293 294 295
			if (!expired)
				keep = ISC_TRUE;
		} else if (issigningkey(key)) {
			if (!expired && setverifies(name, set, key, &sigrdata))
296
			{
297
				vbprintf(2, "\tsig by %s retained\n", sigstr);
298
				keep = ISC_TRUE;
Brian Wellington's avatar
Brian Wellington committed
299
				wassignedby[sig.algorithm] = ISC_TRUE;
300
			} else {
Brian Wellington's avatar
Brian Wellington committed
301
				vbprintf(2, "\tsig by %s dropped - %s\n",
302
					 sigstr,
Brian Wellington's avatar
Brian Wellington committed
303 304 305 306
					 expired ? "expired" :
						   "failed to verify");
				wassignedby[sig.algorithm] = ISC_TRUE;
				resign = ISC_TRUE;
307
			}
Brian Wellington's avatar
Brian Wellington committed
308 309 310
		} else if (iszonekey(key, db)) {
			if (!expired && setverifies(name, set, key, &sigrdata))
			{
311
				vbprintf(2, "\tsig by %s retained\n", sigstr);
Brian Wellington's avatar
Brian Wellington committed
312 313
				keep = ISC_TRUE;
				wassignedby[sig.algorithm] = ISC_TRUE;
314
				nowsignedby[sig.algorithm] = ISC_TRUE;
Brian Wellington's avatar
Brian Wellington committed
315 316
			} else {
				vbprintf(2, "\tsig by %s dropped - %s\n",
317
					 sigstr,
Brian Wellington's avatar
Brian Wellington committed
318 319 320 321 322
					 expired ? "expired" :
						   "failed to verify");
				wassignedby[sig.algorithm] = ISC_TRUE;
				if (dst_key_isprivate(key->key))
					resign = ISC_TRUE;
323
			}
Brian Wellington's avatar
Brian Wellington committed
324
		} else if (!expired) {
325
			vbprintf(2, "\tsig by %s retained\n", sigstr);
Brian Wellington's avatar
Brian Wellington committed
326 327
			keep = ISC_TRUE;
		} else {
328
			vbprintf(2, "\tsig by %s expired\n", sigstr);
Brian Wellington's avatar
Brian Wellington committed
329
		}
330

Brian Wellington's avatar
Brian Wellington committed
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
		if (keep)
			nowsignedby[sig.algorithm] = ISC_TRUE;
		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];
346
			char keystr[KEY_FORMATSIZE];
347

348 349
			key_format(key->key, keystr, sizeof keystr);
			vbprintf(1, "\tresigning with key %s\n", keystr);
Brian Wellington's avatar
Brian Wellington committed
350 351 352 353 354 355 356 357 358
			isc_buffer_init(&b, array, sizeof(array));
			signwithkey(name, set, &trdata, key->key, &b);
			nowsignedby[sig.algorithm] = ISC_TRUE;
			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);
359
		}
Brian Wellington's avatar
Brian Wellington committed
360 361 362

		dns_rdata_freestruct(&sig);
		result = dns_rdataset_next(&sigset);
Brian Wellington's avatar
Brian Wellington committed
363
	}
Brian Wellington's avatar
Brian Wellington committed
364 365 366 367 368 369
	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);
370 371

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

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

388 389
			key_format(key->key, keystr, sizeof keystr);
			vbprintf(1, "\tsigning with key %s\n", keystr);
390 391 392 393 394 395 396 397
			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);
398 399 400 401 402
		}
		key = ISC_LIST_NEXT(key, link);
	}
}

403
/* Determine if a KEY set contains a null key */
404
static isc_boolean_t
405
hasnullkey(dns_rdataset_t *rdataset) {
406 407
	isc_result_t result;
	dns_rdata_t rdata;
408
	isc_boolean_t found = ISC_FALSE;
409

410
	result = dns_rdataset_first(rdataset);
411 412 413
	while (result == ISC_R_SUCCESS) {
		dst_key_t *key = NULL;

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

431 432 433 434 435 436 437 438 439 440 441 442
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()");
443 444 445 446 447
	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);
	}
448 449 450 451 452 453 454 455 456 457 458
	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);
}

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

473 474 475
	dns_rdataset_init(&newset);
	dns_rdataset_init(&sigset);

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

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

488 489 490 491
	if (!dns_rdataset_isassociated(&newset) ||
	    !dns_rdataset_isassociated(&sigset))
		goto failure;

492 493
	if (dns_rdataset_count(set) != dns_rdataset_count(&newset)) {
		result = DNS_R_BADDB;
494
		goto failure;
495
	}
496 497 498 499 500

	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
	if (result == ISC_R_NOMORE)
		result = ISC_R_SUCCESS;
535 536

 failure:
537 538 539 540
	if (dns_rdataset_isassociated(&newset))
		dns_rdataset_disassociate(&newset);
	if (dns_rdataset_isassociated(&sigset))
		dns_rdataset_disassociate(&sigset);
541 542 543 544
	if (newnode != NULL)
		dns_db_detachnode(newdb, &newnode);
	if (newdb != NULL)
		dns_db_detach(&newdb);
545 546
	if (result != ISC_R_SUCCESS)
		fatal("zone signedkey file is invalid or does not match zone");
547 548 549
}

/*
550
 * Looks for our signatures of child keys.  If present, inform the caller.
551 552 553 554 555 556 557 558 559 560 561 562
 */
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;

563 564 565
	dns_rdataset_init(&set);
	dns_rdataset_init(&sigset);

566 567 568 569
	opendb("signedkey-", name, dns_db_class(db), &newdb);
	if (newdb == NULL)
		return (ISC_FALSE);

570 571 572
	result = dns_db_findnode(newdb, name, ISC_FALSE, &newnode);
	if (result != ISC_R_SUCCESS)
		goto failure;
573 574
	result = dns_db_findrdataset(newdb, newnode, NULL, dns_rdatatype_key,
				     0, 0, &set, &sigset);
575 576 577 578 579
	if (result != ISC_R_SUCCESS)
		goto failure;

	if (!dns_rdataset_isassociated(&set) ||
	    !dns_rdataset_isassociated(&sigset))
580
		goto failure;
581 582 583 584 585 586

	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);
587
		result = dns_rdata_tostruct(&sigrdata, &sig, NULL);
588
		if (result != ISC_R_SUCCESS)
589
			goto failure;
590 591 592
		key = keythatsigned(&sig);
		dns_rdata_freestruct(&sig);
		if (key == NULL)
593
			goto failure;
594 595 596 597 598 599 600 601
		result = dns_dnssec_verify(name, &set, key->key,
					   ISC_FALSE, mctx, &sigrdata);
		if (result == ISC_R_SUCCESS) {
			found = ISC_TRUE;
			break;
		}
	}

602
 failure:
603 604 605 606 607 608 609 610 611 612
	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);
613 614
}

615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638
/*
 * 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
639 640 641 642 643 644 645 646 647
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;
648
	char namestr[DNS_NAME_FORMATSIZE];
Brian Wellington's avatar
Brian Wellington committed
649

650 651
	dns_name_format(name, namestr, sizeof namestr);
	vbprintf(2, "adding null key at %s\n", namestr);
Brian Wellington's avatar
Brian Wellington committed
652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681

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

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

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

717
	atorigin = dns_name_equal(name, dns_db_origin(db));
718 719 720 721

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

725 726 727 728 729 730 731 732 733 734
		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);
		}
	}
735 736 737 738 739 740 741 742 743

	/*
	 * 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
744
		neednullkey = ISC_TRUE;
745 746 747 748 749

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

Brian Wellington's avatar
Brian Wellington committed
767 768
		if (neednullkey)
			createnullkey(db, version, name);
769 770 771 772 773 774
	}

	/*
	 * Now iterate through the rdatasets.
	 */
	dns_diff_init(mctx, &diff);
Brian Wellington's avatar
Brian Wellington committed
775 776 777 778 779 780 781
	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
782

783 784 785
		/* If this is a SIG set, skip it. */
		if (rdataset.type == dns_rdatatype_sig)
			goto skip;
Brian Wellington's avatar
Brian Wellington committed
786

787 788 789
		/*
		 * If this is a KEY set at the apex, look for a signedkey file.
		 */
Brian Wellington's avatar
Brian Wellington committed
790
		if (atorigin && rdataset.type == dns_rdatatype_key) {
791
			importparentsig(db, &diff, name, &rdataset);
792
			goto skip;
793
		}
794 795 796

		/*
		 * If this name is a delegation point, skip all records
797
		 * except an NXT set a KEY set containing a null key.
798 799
		 */
		if (isdelegation) {
Brian Wellington's avatar
Brian Wellington committed
800 801 802
			if (!(rdataset.type == dns_rdatatype_nxt ||
			      (rdataset.type == dns_rdatatype_key &&
			       hasnullkey(&rdataset))))
803
				goto skip;
804 805
		}

806
		if (rdataset.type == dns_rdatatype_nxt) {
807 808 809
			nxt_setbit(&rdataset, dns_rdatatype_sig);
			if (neednullkey)
				nxt_setbit(&rdataset, dns_rdatatype_key);
Brian Wellington's avatar
Brian Wellington committed
810
		}
Brian Wellington's avatar
Brian Wellington committed
811

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

814
 skip:
Brian Wellington's avatar
Brian Wellington committed
815 816 817
		dns_rdataset_disassociate(&rdataset);
		result = dns_rdatasetiter_next(rdsiter);
	}
818 819 820
	if (result != ISC_R_NOMORE) {
		char namestr[DNS_NAME_FORMATSIZE];
		dns_name_format(name, namestr, sizeof namestr);
821
		fatal("rdataset iteration for name '%s' failed: %s",
822 823
		      namestr, isc_result_totext(result));
	}
Brian Wellington's avatar
Brian Wellington committed
824
	dns_rdatasetiter_destroy(&rdsiter);
825 826

	result = dns_diff_apply(&diff, db, version);
827 828 829 830 831
	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);
	}
832
	dns_diff_clear(&diff);
Brian Wellington's avatar
Brian Wellington committed
833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854
}

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
855
			result = ISC_R_NOMORE;
Brian Wellington's avatar
Brian Wellington committed
856
	}
857
	if (result != ISC_R_NOMORE)
858 859
		fatal("rdataset iteration failed: %s",
		      isc_result_totext(result));
Brian Wellington's avatar
Brian Wellington committed
860 861 862 863 864 865 866
	dns_rdatasetiter_destroy(&rdsiter);

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

898 899
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
900 901
	    dns_name_t *name, dns_dbnode_t **nodep, dns_name_t *origin,
	    dns_name_t *lastcut)
902
{
903
	isc_result_t result, dresult;
904 905 906 907

	do {
		result = next_active(db, version, dbiter, name, nodep);
		if (result == ISC_R_SUCCESS) {
Brian Wellington's avatar
Brian Wellington committed
908 909 910
			if (dns_name_issubdomain(name, origin) &&
			    (lastcut == NULL ||
			     !dns_name_issubdomain(name, lastcut)))