dnssec-signzone.c 41.2 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.111 2000/10/31 03:21:41 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;
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_reset(&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
	dns_rdataset_init(&newset);
	dns_rdataset_init(&sigset);
509 510
	dns_rdata_init(&rdata);
	dns_rdata_init(&newrdata);
511

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

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

524 525 526 527
	if (!dns_rdataset_isassociated(&newset) ||
	    !dns_rdataset_isassociated(&sigset))
		goto failure;

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

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

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

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

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

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

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

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

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

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

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

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

652 653 654 655 656 657 658 659
/*
 * 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) {
	isc_result_t result;
660
	dns_rdata_t rdata = DNS_RDATA_INIT;
661
	dns_rdata_nxt_t nxt;
662 663 664 665

	result = dns_rdataset_first(rdataset);
	check_result(result, "dns_rdataset_first()");
	dns_rdataset_current(rdataset, &rdata);
666 667 668 669
	result = dns_rdata_tostruct(&rdata, &nxt, NULL);
	check_result(result, "dns_rdata_tostruct");
	set_bit(nxt.typebits, type, 1);
	dns_rdata_freestruct(&nxt);
670 671
}

Brian Wellington's avatar
Brian Wellington committed
672 673 674
static void
createnullkey(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name) {
	unsigned char keydata[4];
675
	dns_rdata_t keyrdata = DNS_RDATA_INIT;
Brian Wellington's avatar
Brian Wellington committed
676 677 678 679 680
	dns_rdata_key_t key;
	dns_diff_t diff;
	dns_difftuple_t *tuple = NULL;
	isc_buffer_t b;
	isc_result_t result;
681
	char namestr[DNS_NAME_FORMATSIZE];
Brian Wellington's avatar
Brian Wellington committed
682

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

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

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

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

750
	atorigin = dns_name_equal(name, dns_db_origin(db));
751 752 753 754

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

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

	/*
	 * 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
777
		neednullkey = ISC_TRUE;
778 779 780 781 782

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

Brian Wellington's avatar
Brian Wellington committed
800 801
		if (neednullkey)
			createnullkey(db, version, name);
802 803 804 805 806 807
	}

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

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

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

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

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