dnssec-signzone.c 39.1 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.101 2000/10/17 07:22:22 marka 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
#include <dns/rdata.h>
#include <dns/rdataset.h>
45
#include <dns/rdataclass.h>
Brian Wellington's avatar
Brian Wellington committed
46
#include <dns/rdatasetiter.h>
47
#include <dns/rdatastruct.h>
48
#include <dns/rdatatype.h>
Brian Wellington's avatar
Brian Wellington committed
49
#include <dns/result.h>
50
#include <dns/secalg.h>
51
#include <dns/time.h>
Brian Wellington's avatar
Brian Wellington committed
52

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

56 57
#include "dnssectool.h"

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

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

63 64 65 66 67
typedef struct signer_key_struct signer_key_t;

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

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

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

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

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

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

102
	dns_rdata_init(rdata);
103
	result = dns_dnssec_sign(name, rdataset, key, &starttime, &endtime,
104
				 mctx, b, rdata);
105
	isc_entropy_stopcallbacksources(ectx);
106 107 108 109 110 111
	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));
	}
112

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

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

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

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

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

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

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

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

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

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

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

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

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

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

259 260 261 262 263 264 265 266 267 268 269 270 271
	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
272 273 274
	if (nosigs)
		result = ISC_R_NOMORE;
	else
275
		result = dns_rdataset_first(&sigset);
276

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

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

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

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

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

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

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

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

		dns_rdata_freestruct(&sig);
		result = dns_rdataset_next(&sigset);
Brian Wellington's avatar
Brian Wellington committed
377
	}
Brian Wellington's avatar
Brian Wellington committed
378 379 380 381 382 383
	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);
384 385 386

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

393 394
			key_format(key->key, keystr, sizeof keystr);
			vbprintf(1, "\tsigning with key %s\n", keystr);
395 396 397 398 399 400 401 402
			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);
403 404 405
		}
		key = ISC_LIST_NEXT(key, link);
	}
406 407 408

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

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

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

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

439 440 441 442 443 444 445 446 447 448 449 450
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()");
451 452 453 454 455
	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);
	}
456 457 458 459 460 461 462
	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);
463
	if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE)
464 465 466
		dns_db_detach(db);
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

610
 failure:
611 612 613 614 615 616 617 618 619 620
	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);
621 622
}

623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646
/*
 * 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
647 648 649 650 651 652 653 654 655
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;
656
	char namestr[DNS_NAME_FORMATSIZE];
Brian Wellington's avatar
Brian Wellington committed
657

658 659
	dns_name_format(name, namestr, sizeof namestr);
	vbprintf(2, "adding null key at %s\n", namestr);
Brian Wellington's avatar
Brian Wellington committed
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 689

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

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

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

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

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

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

	/*
	 * 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
752
		neednullkey = ISC_TRUE;
753 754 755 756 757

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

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

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

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

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

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

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

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

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

	result = dns_diff_apply(&diff, db, version);
835 836 837 838 839
	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);
	}
840
	dns_diff_clear(&diff);
Brian Wellington's avatar
Brian Wellington committed
841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862
}

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

	if (!active) {
		/*
		 * Make sure there is no NXT record for this node.
		 */
		result = dns_db_deleterdataset(db, node, version,
875
					       dns_rdatatype_nxt, 0);
Brian Wellington's avatar
Brian Wellington committed
876 877 878 879 880 881 882 883 884 885 886 887 888 889 890