dnssec-signzone.c 39.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.103 2000/10/25 04:26:18 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
	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
	ISC_LIST_APPENDUNSAFE(keylist, key, link);
173
	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
	dns_rdataset_t sigset;
226
	dns_rdata_t sigrdata = DNS_RDATA_INIT;
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
		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;
357
			dns_rdata_t trdata = DNS_RDATA_INIT;
Brian Wellington's avatar
Brian Wellington committed
358
			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
		dns_rdata_invalidate(&sigrdata);
Brian Wellington's avatar
Brian Wellington committed
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
			isc_buffer_t b;
389
			dns_rdata_t trdata = DNS_RDATA_INIT;
390
			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_rdata_init(&rdata);
423
		dns_rdataset_current(rdataset, &rdata);
424 425
		result = dns_dnssec_keyfromrdata(dns_rootname,
						 &rdata, mctx, &key);
426 427
		if (result != ISC_R_SUCCESS)
			fatal("could not convert KEY into internal format");
428 429
		if (dst_key_isnullkey(key))
			found = ISC_TRUE;
430
                dst_key_free(&key);
431
		if (found == ISC_TRUE)
432
			return (ISC_TRUE);
433 434 435
                result = dns_rdataset_next(rdataset);
        }
        if (result != ISC_R_NOMORE)
436
                fatal("failure looking for null keys");
437 438 439
        return (ISC_FALSE);
}

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

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

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

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

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

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

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

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

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

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

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

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

/*
559
 * Looks for our signatures of child keys.  If present, inform the caller.
560 561 562 563 564 565
 */
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;
566
	dns_rdata_t sigrdata = DNS_RDATA_INIT;
567 568 569 570 571
	isc_result_t result;
	isc_boolean_t found = ISC_FALSE;
	dns_rdata_sig_t sig;
	signer_key_t *key;

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

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

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

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

	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);
596
		result = dns_rdata_tostruct(&sigrdata, &sig, NULL);
597
		if (result != ISC_R_SUCCESS)
598
			goto failure;
599 600 601
		key = keythatsigned(&sig);
		dns_rdata_freestruct(&sig);
		if (key == NULL)
602
			goto failure;
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
		dns_rdata_invalidate(&sigrdata);
610 611
	}

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

625 626 627 628 629 630 631 632 633 634 635
/*
 * 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;
636
	dns_rdata_t rdata = DNS_RDATA_INIT;
637 638 639 640 641 642 643 644 645 646 647 648

	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
649 650 651
static void
createnullkey(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name) {
	unsigned char keydata[4];
652
	dns_rdata_t keyrdata = DNS_RDATA_INIT;
Brian Wellington's avatar
Brian Wellington committed
653 654 655 656 657
	dns_rdata_key_t key;
	dns_diff_t diff;
	dns_difftuple_t *tuple = NULL;
	isc_buffer_t b;
	isc_result_t result;
658
	char namestr[DNS_NAME_FORMATSIZE];
Brian Wellington's avatar
Brian Wellington committed
659

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

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

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

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

727
	atorigin = dns_name_equal(name, dns_db_origin(db));
728 729 730 731

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

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

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

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

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

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

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

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

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

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

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

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

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