dnssec-signzone.c 41.6 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.112 2000/10/31 20:09:15 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;
84
static const char *directory;
Brian Wellington's avatar
Brian Wellington committed
85

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

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

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

99 100 101 102 103 104 105 106 107 108 109 110 111 112
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);
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

387 388
			key_format(key->key, keystr, sizeof keystr);
			vbprintf(1, "\tresigning with key %s\n", keystr);
Brian Wellington's avatar
Brian Wellington committed
389 390
			isc_buffer_init(&b, array, sizeof(array));
			signwithkey(name, set, &trdata, key->key, &b);
391
			nowsignedby[key->position] = ISC_TRUE;
Brian Wellington's avatar
Brian Wellington committed
392 393 394 395 396 397
			tuple = NULL;
			result = dns_difftuple_create(mctx, DNS_DIFFOP_ADD,
						      name, ttl, &trdata,
						      &tuple);
			check_result(result, "dns_difftuple_create");
			dns_diff_append(diff, &tuple);
398
		}
Brian Wellington's avatar
Brian Wellington committed
399

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

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

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

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

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

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

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

466 467 468 469 470 471 472 473 474
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));
475 476 477 478 479
	if (directory != NULL) {
		isc_buffer_putstr(&b, directory);
		if (directory[strlen(directory) - 1] != '/')
			isc_buffer_putstr(&b, "/");
	}
480 481 482
	isc_buffer_putstr(&b, prefix);
	result = dns_name_totext(name, ISC_FALSE, &b);
	check_result(result, "dns_name_totext()");
483 484 485 486 487
	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);
	}
488 489 490 491 492 493 494
	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);
495
	if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE)
496 497 498
		dns_db_detach(db);
}

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

513 514
	dns_rdataset_init(&newset);
	dns_rdataset_init(&sigset);
515 516
	dns_rdata_init(&rdata);
	dns_rdata_init(&newrdata);
517

518 519 520 521
	opendb("signedkey-", name, dns_db_class(db), &newdb);
	if (newdb == NULL)
		return;

522 523 524
	result = dns_db_findnode(newdb, name, ISC_FALSE, &newnode);
	if (result != ISC_R_SUCCESS)
		goto failure;
525 526
	result = dns_db_findrdataset(newdb, newnode, NULL, dns_rdatatype_key,
				     0, 0, &newset, &sigset);
527 528 529
	if (result != ISC_R_SUCCESS)
		goto failure;

530 531 532 533
	if (!dns_rdataset_isassociated(&newset) ||
	    !dns_rdataset_isassociated(&sigset))
		goto failure;

534 535
	if (dns_rdataset_count(set) != dns_rdataset_count(&newset)) {
		result = DNS_R_BADDB;
536
		goto failure;
537
	}
538 539

	result = dns_rdataset_first(set);
540
	check_result(result, "dns_rdataset_first()");
541 542 543
	for (; result == ISC_R_SUCCESS; result = dns_rdataset_next(set)) {
		dns_rdataset_current(set, &rdata);
		result = dns_rdataset_first(&newset);
544
		check_result(result, "dns_rdataset_first()");
545 546 547 548 549 550 551
		for (;
		     result == ISC_R_SUCCESS;
		     result = dns_rdataset_next(&newset))
		{
			dns_rdataset_current(&newset, &newrdata);
			if (dns_rdata_compare(&rdata, &newrdata) == 0)
				break;
552
			dns_rdata_reset(&newrdata);
553 554 555
		}
		if (result != ISC_R_SUCCESS)
			break;
556 557
		dns_rdata_reset(&newrdata);
		dns_rdata_reset(&rdata);
558
	}
559
	if (result != ISC_R_NOMORE)
560 561 562 563
		goto failure;

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

564 565 566 567 568 569 570 571 572 573 574
	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);
	}
575 576
	if (result == ISC_R_NOMORE)
		result = ISC_R_SUCCESS;
577 578

 failure:
579 580 581 582
	if (dns_rdataset_isassociated(&newset))
		dns_rdataset_disassociate(&newset);
	if (dns_rdataset_isassociated(&sigset))
		dns_rdataset_disassociate(&sigset);
583 584 585 586
	if (newnode != NULL)
		dns_db_detachnode(newdb, &newnode);
	if (newdb != NULL)
		dns_db_detach(&newdb);
587 588
	if (result != ISC_R_SUCCESS)
		fatal("zone signedkey file is invalid or does not match zone");
589 590 591
}

/*
592
 * Looks for our signatures of child keys.  If present, inform the caller.
593 594 595 596 597 598
 */
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;
599
	dns_rdata_t sigrdata = DNS_RDATA_INIT;
600 601 602 603 604
	isc_result_t result;
	isc_boolean_t found = ISC_FALSE;
	dns_rdata_sig_t sig;
	signer_key_t *key;

605 606 607
	dns_rdataset_init(&set);
	dns_rdataset_init(&sigset);

608 609 610 611
	opendb("signedkey-", name, dns_db_class(db), &newdb);
	if (newdb == NULL)
		return (ISC_FALSE);

612 613 614
	result = dns_db_findnode(newdb, name, ISC_FALSE, &newnode);
	if (result != ISC_R_SUCCESS)
		goto failure;
615 616
	result = dns_db_findrdataset(newdb, newnode, NULL, dns_rdatatype_key,
				     0, 0, &set, &sigset);
617 618 619 620 621
	if (result != ISC_R_SUCCESS)
		goto failure;

	if (!dns_rdataset_isassociated(&set) ||
	    !dns_rdataset_isassociated(&sigset))
622
		goto failure;
623 624 625 626 627 628

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

645
 failure:
646 647 648 649 650 651 652 653 654 655
	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);
656 657
}

658 659 660 661 662 663 664 665
/*
 * 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;
666
	dns_rdata_t rdata = DNS_RDATA_INIT;
667
	dns_rdata_nxt_t nxt;
668 669 670 671

	result = dns_rdataset_first(rdataset);
	check_result(result, "dns_rdataset_first()");
	dns_rdataset_current(rdataset, &rdata);
672 673 674 675
	result = dns_rdata_tostruct(&rdata, &nxt, NULL);
	check_result(result, "dns_rdata_tostruct");
	set_bit(nxt.typebits, type, 1);
	dns_rdata_freestruct(&nxt);
676 677
}

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

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

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

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

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

756
	atorigin = dns_name_equal(name, dns_db_origin(db));
757 758 759 760

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

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

	/*
	 * 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
783
		neednullkey = ISC_TRUE;
784 785 786 787 788

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

Brian Wellington's avatar
Brian Wellington committed
806 807
		if (neednullkey)
			createnullkey(db, version, name);
808 809 810 811 812 813
	}

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

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

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

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