dnssec-signzone.c 41.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.108 2000/10/28 00:53:39 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
#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;
82 83
static unsigned int nsigned = 0, nretained = 0, ndropped = 0;
static unsigned int nverified = 0, nverifyfailed = 0;
Brian Wellington's avatar
Brian Wellington committed
84

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

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

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

98 99 100 101 102 103 104 105 106 107 108 109 110 111
static signer_key_t *
newkeystruct(dst_key_t *dstkey, isc_boolean_t isdefault) {
	signer_key_t *key;

	key = isc_mem_get(mctx, sizeof(signer_key_t));
	if (key == NULL)
		fatal("out of memory");
	key->key = dstkey;
	key->isdefault = isdefault;
	key->position = keycount++;
	ISC_LINK_INIT(key, link);
	return (key);
}

112 113 114
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
115 116 117
{
	isc_result_t result;

118
	result = dns_dnssec_sign(name, rdataset, key, &starttime, &endtime,
119
				 mctx, b, rdata);
120
	isc_entropy_stopcallbacksources(ectx);
121 122 123 124 125 126
	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));
	}
127
	nsigned++;
128

129
	if (tryverify) {
130 131
		result = dns_dnssec_verify(name, rdataset, key,
					   ISC_TRUE, mctx, rdata);
132
		if (result == ISC_R_SUCCESS) {
133
			vbprintf(3, "\tsignature verified\n");
134 135
			nverified++;
		} else {
136
			vbprintf(3, "\tsignature failed to verify\n");
137 138
			nverifyfailed++;
		}
139
	}
140
}
Brian Wellington's avatar
Brian Wellington committed
141

142 143 144
static inline isc_boolean_t
issigningkey(signer_key_t *key) {
	return (key->isdefault);
Brian Wellington's avatar
Brian Wellington committed
145 146
}

147 148
static inline isc_boolean_t
iszonekey(signer_key_t *key, dns_db_t *db) {
149 150
	return (ISC_TF(dns_name_equal(dst_key_name(key->key),
				      dns_db_origin(db)) &&
151
		       dst_key_iszonekey(key->key)));
152 153
}

154 155 156 157
/*
 * 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.
 */
158
static signer_key_t *
159
keythatsigned(dns_rdata_sig_t *sig) {
160
	isc_result_t result;
161 162 163 164 165 166 167
	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) &&
168
		    dns_name_equal(&sig->signer, dst_key_name(key->key)))
169
			return key;
170
		key = ISC_LIST_NEXT(key, link);
171
	}
172

173
	result = dst_key_fromfile(&sig->signer, sig->keyid, sig->algorithm,
Brian Wellington's avatar
Brian Wellington committed
174
				  DST_TYPE_PUBLIC, NULL, mctx, &pubkey);
175 176
	if (result != ISC_R_SUCCESS)
		return (NULL);
177

178 179
	key = isc_mem_get(mctx, sizeof(signer_key_t));
	if (key == NULL)
180
		fatal("out of memory");
181

182
	result = dst_key_fromfile(&sig->signer, sig->keyid, sig->algorithm,
Brian Wellington's avatar
Brian Wellington committed
183
				  DST_TYPE_PRIVATE, NULL, mctx, &privkey);
184
	if (result == ISC_R_SUCCESS) {
185
		dst_key_free(&pubkey);
186 187 188 189 190
		key = newkeystruct(privkey, ISC_FALSE);
	} else
		key = newkeystruct(pubkey, ISC_FALSE);
	ISC_LIST_APPEND(keylist, key, link);
	return (key);
191 192
}

193 194 195 196 197
/*
 * 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.
 */
198 199 200
static isc_boolean_t
expecttofindkey(dns_name_t *name, dns_db_t *db, dns_dbversion_t *version) {
	unsigned int options = DNS_DBFIND_NOWILD;
201
	dns_fixedname_t fname;
202
	isc_result_t result;
203
	char namestr[DNS_NAME_FORMATSIZE];
204

205
	dns_fixedname_init(&fname);
206
	result = dns_db_find(db, name, version, dns_rdatatype_key, options,
207
			     0, NULL, dns_fixedname_name(&fname), NULL, NULL);
208
	switch (result) {
209 210 211 212 213 214 215 216
	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);
217
	}
218 219 220 221
	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 */
222 223
}

224
static inline isc_boolean_t
225 226
setverifies(dns_name_t *name, dns_rdataset_t *set, signer_key_t *key,
	    dns_rdata_t *sig)
227
{
228 229
	isc_result_t result;
	result = dns_dnssec_verify(name, set, key->key, ISC_FALSE, mctx, sig);
230 231 232 233 234 235 236
	if (result == ISC_R_SUCCESS) {
		nverified++;
		return (ISC_TRUE);
	} else {
		nverifyfailed++;
		return (ISC_FALSE);
	}
237 238
}

239 240 241 242 243
/*
 * 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.
 */
244
static void
245 246
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)
247
{
248
	dns_rdataset_t sigset;
249
	dns_rdata_t sigrdata = DNS_RDATA_INIT;
250
	dns_rdata_sig_t sig;
251
	signer_key_t *key;
252
	isc_result_t result;
253 254 255
	isc_boolean_t nosigs = ISC_FALSE;
	isc_boolean_t *wassignedby, *nowsignedby;
	int arraysize;
256 257
	dns_difftuple_t *tuple;
	dns_ttl_t ttl;
258
	int i;
259 260 261 262 263 264
	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);
265

266
	ttl = ISC_MIN(set->ttl, endtime - starttime);
267

268
	dns_rdataset_init(&sigset);
269
	result = dns_db_findrdataset(db, node, version, dns_rdatatype_sig,
270
				     set->type, 0, &sigset, NULL);
271 272 273 274
	if (result == ISC_R_NOTFOUND) {
		result = ISC_R_SUCCESS;
		nosigs = ISC_TRUE;
	}
275 276
	if (result != ISC_R_SUCCESS)
		fatal("failed while looking for '%s SIG %s': %s",
277
		      namestr, typestr, isc_result_totext(result));
278

279
	vbprintf(1, "%s/%s:\n", namestr, typestr);
280

281 282 283 284 285 286 287 288 289 290 291 292 293
	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
294 295 296
	if (nosigs)
		result = ISC_R_NOMORE;
	else
297
		result = dns_rdataset_first(&sigset);
298

Brian Wellington's avatar
Brian Wellington committed
299 300 301
	while (result == ISC_R_SUCCESS) {
		isc_boolean_t expired, future;
		isc_boolean_t keep = ISC_FALSE, resign = ISC_FALSE;
302

Brian Wellington's avatar
Brian Wellington committed
303
		dns_rdataset_current(&sigset, &sigrdata);
304

Brian Wellington's avatar
Brian Wellington committed
305 306
		result = dns_rdata_tostruct(&sigrdata, &sig, NULL);
		check_result(result, "dns_rdata_tostruct");
307

Brian Wellington's avatar
Brian Wellington committed
308 309
		expired = ISC_TF(now + cycle > sig.timeexpire);
		future = ISC_TF(now < sig.timesigned);
310

Brian Wellington's avatar
Brian Wellington committed
311
		key = keythatsigned(&sig);
312
		sig_format(&sig, sigstr, sizeof sigstr);
Brian Wellington's avatar
Brian Wellington committed
313 314 315 316 317

		if (sig.timesigned > sig.timeexpire) {
			/* sig is dropped and not replaced */
			vbprintf(2, "\tsig by %s dropped - "
				 "invalid validity period\n",
318
				 sigstr);
Brian Wellington's avatar
Brian Wellington committed
319 320 321 322 323 324
		} 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",
325
				 sigstr);
Brian Wellington's avatar
Brian Wellington committed
326 327
		} else if (key == NULL || future) {
			vbprintf(2, "\tsig by %s %s - key not found\n",
328
				 expired ? "retained" : "dropped", sigstr);
Brian Wellington's avatar
Brian Wellington committed
329 330 331 332
			if (!expired)
				keep = ISC_TRUE;
		} else if (issigningkey(key)) {
			if (!expired && setverifies(name, set, key, &sigrdata))
333
			{
334
				vbprintf(2, "\tsig by %s retained\n", sigstr);
335
				keep = ISC_TRUE;
336 337
				wassignedby[key->position] = ISC_TRUE;
				nowsignedby[key->position] = ISC_TRUE;
338
			} else {
Brian Wellington's avatar
Brian Wellington committed
339
				vbprintf(2, "\tsig by %s dropped - %s\n",
340
					 sigstr,
Brian Wellington's avatar
Brian Wellington committed
341 342
					 expired ? "expired" :
						   "failed to verify");
343
				wassignedby[key->position] = ISC_TRUE;
Brian Wellington's avatar
Brian Wellington committed
344
				resign = ISC_TRUE;
345
			}
Brian Wellington's avatar
Brian Wellington committed
346 347 348
		} else if (iszonekey(key, db)) {
			if (!expired && setverifies(name, set, key, &sigrdata))
			{
349
				vbprintf(2, "\tsig by %s retained\n", sigstr);
Brian Wellington's avatar
Brian Wellington committed
350
				keep = ISC_TRUE;
351 352
				wassignedby[key->position] = ISC_TRUE;
				nowsignedby[key->position] = ISC_TRUE;
Brian Wellington's avatar
Brian Wellington committed
353 354
			} else {
				vbprintf(2, "\tsig by %s dropped - %s\n",
355
					 sigstr,
Brian Wellington's avatar
Brian Wellington committed
356 357
					 expired ? "expired" :
						   "failed to verify");
358
				wassignedby[key->position] = ISC_TRUE;
359
			}
Brian Wellington's avatar
Brian Wellington committed
360
		} else if (!expired) {
361
			vbprintf(2, "\tsig by %s retained\n", sigstr);
Brian Wellington's avatar
Brian Wellington committed
362 363
			keep = ISC_TRUE;
		} else {
364
			vbprintf(2, "\tsig by %s expired\n", sigstr);
Brian Wellington's avatar
Brian Wellington committed
365
		}
366

367
		if (keep) {
368
			nowsignedby[key->position] = ISC_TRUE;
369 370
			nretained++;
		} else {
Brian Wellington's avatar
Brian Wellington committed
371 372 373 374 375 376
			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);
377
			ndropped++;
Brian Wellington's avatar
Brian Wellington committed
378 379 380 381
		}

		if (resign) {
			isc_buffer_t b;
382
			dns_rdata_t trdata = DNS_RDATA_INIT;
Brian Wellington's avatar
Brian Wellington committed
383
			unsigned char array[BUFSIZE];
384
			char keystr[KEY_FORMATSIZE];
385

386 387
			key_format(key->key, keystr, sizeof keystr);
			vbprintf(1, "\tresigning with key %s\n", keystr);
Brian Wellington's avatar
Brian Wellington committed
388 389
			isc_buffer_init(&b, array, sizeof(array));
			signwithkey(name, set, &trdata, key->key, &b);
390
			nowsignedby[key->position] = ISC_TRUE;
Brian Wellington's avatar
Brian Wellington committed
391 392 393 394 395 396
			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);
397
		}
Brian Wellington's avatar
Brian Wellington committed
398

399
		dns_rdata_invalidate(&sigrdata);
Brian Wellington's avatar
Brian Wellington committed
400 401
		dns_rdata_freestruct(&sig);
		result = dns_rdataset_next(&sigset);
Brian Wellington's avatar
Brian Wellington committed
402
	}
Brian Wellington's avatar
Brian Wellington committed
403 404 405 406 407 408
	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);
409 410 411

	key = ISC_LIST_HEAD(keylist);
	while (key != NULL) {
412
		if (key->isdefault && !nowsignedby[key->position]) {
413
			isc_buffer_t b;
414
			dns_rdata_t trdata = DNS_RDATA_INIT;
415
			unsigned char array[BUFSIZE];
416
			char keystr[KEY_FORMATSIZE];
417

418 419
			key_format(key->key, keystr, sizeof keystr);
			vbprintf(1, "\tsigning with key %s\n", keystr);
420 421 422 423 424 425 426 427
			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);
428 429 430
		}
		key = ISC_LIST_NEXT(key, link);
	}
431 432 433

	isc_mem_put(mctx, wassignedby, arraysize * sizeof(isc_boolean_t));
	isc_mem_put(mctx, nowsignedby, arraysize * sizeof(isc_boolean_t));
434 435
}

436
/* Determine if a KEY set contains a null key */
437
static isc_boolean_t
438
hasnullkey(dns_rdataset_t *rdataset) {
439 440
	isc_result_t result;
	dns_rdata_t rdata;
441
	isc_boolean_t found = ISC_FALSE;
442

443
	result = dns_rdataset_first(rdataset);
444 445 446
	while (result == ISC_R_SUCCESS) {
		dst_key_t *key = NULL;

447
		dns_rdata_init(&rdata);
448
		dns_rdataset_current(rdataset, &rdata);
449 450
		result = dns_dnssec_keyfromrdata(dns_rootname,
						 &rdata, mctx, &key);
451 452
		if (result != ISC_R_SUCCESS)
			fatal("could not convert KEY into internal format");
453 454
		if (dst_key_isnullkey(key))
			found = ISC_TRUE;
455
                dst_key_free(&key);
456
		if (found == ISC_TRUE)
457
			return (ISC_TRUE);
458 459 460
                result = dns_rdataset_next(rdataset);
        }
        if (result != ISC_R_NOMORE)
461
                fatal("failure looking for null keys");
462 463 464
        return (ISC_FALSE);
}

465 466 467 468 469 470 471 472 473 474 475 476
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()");
477 478 479 480 481
	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);
	}
482 483 484 485 486 487 488
	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);
489
	if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE)
490 491 492
		dns_db_detach(db);
}

493 494 495 496 497
/*
 * Looks for signatures of the zone keys by the parent, and imports them
 * if found.
 */
static void
498 499
importparentsig(dns_db_t *db, dns_diff_t *diff, dns_name_t *name,
		dns_rdataset_t *set)
500 501 502 503 504 505 506
{
	dns_db_t *newdb = NULL;
	dns_dbnode_t *newnode = NULL;
	dns_rdataset_t newset, sigset;
	dns_rdata_t rdata, newrdata;
	isc_result_t result;

507 508 509
	dns_rdataset_init(&newset);
	dns_rdataset_init(&sigset);

510 511 512 513
	opendb("signedkey-", name, dns_db_class(db), &newdb);
	if (newdb == NULL)
		return;

514 515 516
	result = dns_db_findnode(newdb, name, ISC_FALSE, &newnode);
	if (result != ISC_R_SUCCESS)
		goto failure;
517 518
	result = dns_db_findrdataset(newdb, newnode, NULL, dns_rdatatype_key,
				     0, 0, &newset, &sigset);
519 520 521
	if (result != ISC_R_SUCCESS)
		goto failure;

522 523 524 525
	if (!dns_rdataset_isassociated(&newset) ||
	    !dns_rdataset_isassociated(&sigset))
		goto failure;

526 527
	if (dns_rdataset_count(set) != dns_rdataset_count(&newset)) {
		result = DNS_R_BADDB;
528
		goto failure;
529
	}
530 531

	result = dns_rdataset_first(set);
532
	check_result(result, "dns_rdataset_first()");
533
	for (; result == ISC_R_SUCCESS; result = dns_rdataset_next(set)) {
534
		dns_rdata_init(&rdata);
535 536
		dns_rdataset_current(set, &rdata);
		result = dns_rdataset_first(&newset);
537
		check_result(result, "dns_rdataset_first()");
538 539 540 541
		for (;
		     result == ISC_R_SUCCESS;
		     result = dns_rdataset_next(&newset))
		{
542
			dns_rdata_init(&newrdata);
543 544 545
			dns_rdataset_current(&newset, &newrdata);
			if (dns_rdata_compare(&rdata, &newrdata) == 0)
				break;
546
			dns_rdata_invalidate(&newrdata);
547 548 549
		}
		if (result != ISC_R_SUCCESS)
			break;
550
		dns_rdata_invalidate(&rdata);
551
	}
552
	if (result != ISC_R_NOMORE)
553 554 555 556
		goto failure;

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

557 558 559 560 561 562 563 564 565 566 567
	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);
	}
568 569
	if (result == ISC_R_NOMORE)
		result = ISC_R_SUCCESS;
570 571

 failure:
572 573 574 575
	if (dns_rdataset_isassociated(&newset))
		dns_rdataset_disassociate(&newset);
	if (dns_rdataset_isassociated(&sigset))
		dns_rdataset_disassociate(&sigset);
576 577 578 579
	if (newnode != NULL)
		dns_db_detachnode(newdb, &newnode);
	if (newdb != NULL)
		dns_db_detach(&newdb);
580 581
	if (result != ISC_R_SUCCESS)
		fatal("zone signedkey file is invalid or does not match zone");
582 583 584
}

/*
585
 * Looks for our signatures of child keys.  If present, inform the caller.
586 587 588 589 590 591
 */
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;
592
	dns_rdata_t sigrdata = DNS_RDATA_INIT;
593 594 595 596 597
	isc_result_t result;
	isc_boolean_t found = ISC_FALSE;
	dns_rdata_sig_t sig;
	signer_key_t *key;

598 599 600
	dns_rdataset_init(&set);
	dns_rdataset_init(&sigset);

601 602 603 604
	opendb("signedkey-", name, dns_db_class(db), &newdb);
	if (newdb == NULL)
		return (ISC_FALSE);

605 606 607
	result = dns_db_findnode(newdb, name, ISC_FALSE, &newnode);
	if (result != ISC_R_SUCCESS)
		goto failure;
608 609
	result = dns_db_findrdataset(newdb, newnode, NULL, dns_rdatatype_key,
				     0, 0, &set, &sigset);
610 611 612 613 614
	if (result != ISC_R_SUCCESS)
		goto failure;

	if (!dns_rdataset_isassociated(&set) ||
	    !dns_rdataset_isassociated(&sigset))
615
		goto failure;
616 617 618 619 620 621

	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);
622
		result = dns_rdata_tostruct(&sigrdata, &sig, NULL);
623
		if (result != ISC_R_SUCCESS)
624
			goto failure;
625 626 627
		key = keythatsigned(&sig);
		dns_rdata_freestruct(&sig);
		if (key == NULL)
628
			goto failure;
629 630 631 632 633 634
		result = dns_dnssec_verify(name, &set, key->key,
					   ISC_FALSE, mctx, &sigrdata);
		if (result == ISC_R_SUCCESS) {
			found = ISC_TRUE;
			break;
		}
635
		dns_rdata_invalidate(&sigrdata);
636 637
	}

638
 failure:
639 640 641 642 643 644 645 646 647 648
	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);
649 650
}

651 652 653 654 655 656 657 658 659 660 661
/*
 * 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;
662
	dns_rdata_t rdata = DNS_RDATA_INIT;
663 664 665 666 667 668 669 670 671 672 673 674

	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
675 676 677
static void
createnullkey(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name) {
	unsigned char keydata[4];
678
	dns_rdata_t keyrdata = DNS_RDATA_INIT;
Brian Wellington's avatar
Brian Wellington committed
679 680 681 682 683
	dns_rdata_key_t key;
	dns_diff_t diff;
	dns_difftuple_t *tuple = NULL;
	isc_buffer_t b;
	isc_result_t result;
684
	char namestr[DNS_NAME_FORMATSIZE];
Brian Wellington's avatar
Brian Wellington committed
685

686 687
	dns_name_format(name, namestr, sizeof namestr);
	vbprintf(2, "adding null key at %s\n", namestr);
Brian Wellington's avatar
Brian Wellington committed
688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717

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

718 719
/*
 * Signs all records at a name.  This mostly just signs each set individually,
720 721
 * but also adds the SIG bit to any NXTs generated earlier, deals with
 * parent/child KEY signatures, and handles other exceptional cases.
722
 */
Brian Wellington's avatar
Brian Wellington committed
723
static void
724
signname(dns_db_t *db, dns_dbversion_t *version, dns_dbnode_t *node,
725
	 dns_name_t *name)
Brian Wellington's avatar
Brian Wellington committed
726 727
{
	isc_result_t result;
728
	dns_rdataset_t rdataset;
Brian Wellington's avatar
Brian Wellington committed
729
	dns_rdatasetiter_t *rdsiter;
Brian Wellington's avatar
Brian Wellington committed
730
	isc_boolean_t isdelegation = ISC_FALSE;
731
	isc_boolean_t childkey = ISC_FALSE;
732
	static int warnwild = 0;
733
	isc_boolean_t atorigin;
Brian Wellington's avatar
Brian Wellington committed
734
	isc_boolean_t neednullkey = ISC_FALSE;
735
	dns_diff_t diff;
736 737

	if (dns_name_iswildcard(name)) {
738 739
		char namestr[DNS_NAME_FORMATSIZE];
		dns_name_format(name, namestr, sizeof namestr);
Brian Wellington's avatar
Brian Wellington committed
740 741
		if (warnwild++ == 0) {
			fprintf(stderr, "%s: warning: BIND 9 doesn't properly "
David Lawrence's avatar
David Lawrence committed
742
				"handle wildcards in secure zones:\n",
743
				program);
Brian Wellington's avatar
Brian Wellington committed
744 745 746 747 748 749
			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",
750
			program, namestr);
751
	}
752

753
	atorigin = dns_name_equal(name, dns_db_origin(db));
754 755 756 757

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

761 762 763 764 765 766 767 768 769 770
		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);
		}
	}
771 772 773 774 775 776 777 778 779

	/*
	 * 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
780
		neednullkey = ISC_TRUE;
781 782 783 784 785

		dns_rdataset_init(&keyset);
		result = dns_db_findrdataset(db, node, version,
					     dns_rdatatype_key, 0, 0, &keyset,
					     NULL);
786 787 788
		if (result == ISC_R_SUCCESS && childkey) {
			char namestr[DNS_NAME_FORMATSIZE];
			dns_name_format(name, namestr, sizeof namestr);
789
			fatal("%s has both a signedkey file and KEY "
790 791
			      "records in the zone.  Aborting.", namestr);
		}
792 793 794 795 796
		else if (result == ISC_R_SUCCESS) {
			if (hasnullkey(&keyset))
				neednullkey = ISC_FALSE;
			dns_rdataset_disassociate(&keyset);
		} else if (childkey) {
797 798 799
			char namestr[DNS_NAME_FORMATSIZE];
			dns_name_format(name, namestr, sizeof namestr);
			vbprintf(2, "child key for %s found\n", namestr);
800 801 802
			neednullkey = ISC_FALSE;
		}

Brian Wellington's avatar
Brian Wellington committed
803 804
		if (neednullkey)
			createnullkey(db, version, name);
805 806 807 808 809 810
	}

	/*
	 * Now iterate through the rdatasets.
	 */
	dns_diff_init(mctx, &diff);
Brian Wellington's avatar
Brian Wellington committed
811 812 813 814 815 816 817
	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
818

819 820 821
		/* If this is a SIG set, skip it. */
		if (rdataset.type == dns_rdatatype_sig)
			goto skip;
Brian Wellington's avatar
Brian Wellington committed
822

823 824 825
		/*
		 * If this is a KEY set at the apex, look for a signedkey file.
		 */
Brian Wellington's avatar
Brian Wellington committed
826
		if (atorigin && rdataset.type == dns_rdatatype_key) {
827
			importparentsig(db, &diff, name, &rdataset);
828
			goto skip;
829
		}
830 831 832

		/*
		 * If this name is a delegation point, skip all records
833
		 * except an NXT set a KEY set containing a null key.
834 835
		 */
		if (isdelegation) {
Brian Wellington's avatar
Brian Wellington committed
836 837 838
			if (!(rdataset.type == dns_rdatatype_nxt ||
			      (rdataset.type == dns_rdatatype_key &&
			       hasnullkey(&rdataset))))
839
				goto skip;
840 841
		}

842
		if (rdataset.type == dns_rdatatype_nxt) {
843 844 845
			nxt_setbit(&rdataset, dns_rdatatype_sig);
			if (neednullkey)
				nxt_setbit(&rdataset, dns_rdatatype_key);
Brian Wellington's avatar
Brian Wellington committed
846
		}
Brian Wellington's avatar
Brian Wellington committed
847