dnssec-signzone.c 37.9 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

Brian Wellington's avatar
Brian Wellington committed
20
/* $Id: dnssec-signzone.c,v 1.93 2000/08/11 23:59:46 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 45
#include <dns/rdata.h>
#include <dns/rdataset.h>
#include <dns/rdatasetiter.h>
46
#include <dns/rdatastruct.h>
47
#include <dns/rdatatype.h>
Brian Wellington's avatar
Brian Wellington committed
48
#include <dns/result.h>
49
#include <dns/secalg.h>
50
#include <dns/time.h>
Brian Wellington's avatar
Brian Wellington committed
51

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

55 56
#include "dnssectool.h"

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

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

62 63 64 65 66 67 68 69
typedef struct signer_key_struct signer_key_t;

struct signer_key_struct {
	dst_key_t *key;
	isc_boolean_t isdefault;
	ISC_LINK(signer_key_t) link;
};

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

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

Brian Wellington's avatar
Brian Wellington committed
84 85 86
	shift = 7 - (index % 8);
	mask = 1 << shift;

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

93 94 95
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
96 97 98
{
	isc_result_t result;

99
	dns_rdata_init(rdata);
100
	result = dns_dnssec_sign(name, rdataset, key, &starttime, &endtime,
101
				 mctx, b, rdata);
102
	isc_entropy_stopcallbacksources(ectx);
103 104
	if (result != ISC_R_SUCCESS)
		fatal("key '%s/%s/%d' failed to sign data: %s",
105
		      nametostr(dst_key_name(key)), algtostr(dst_key_alg(key)),
106
		      dst_key_id(key), isc_result_totext(result));
107

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

118 119 120
static inline isc_boolean_t
issigningkey(signer_key_t *key) {
	return (key->isdefault);
Brian Wellington's avatar
Brian Wellington committed
121 122
}

123 124
static inline isc_boolean_t
iszonekey(signer_key_t *key, dns_db_t *db) {
125 126
	return (ISC_TF(dns_name_equal(dst_key_name(key->key),
				      dns_db_origin(db)) &&
127
		       dst_key_iszonekey(key->key)));
128 129
}

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

149
	result = dst_key_fromfile(&sig->signer, sig->keyid, sig->algorithm,
Brian Wellington's avatar
Brian Wellington committed
150
				  DST_TYPE_PUBLIC, NULL, mctx, &pubkey);
151 152
	if (result != ISC_R_SUCCESS)
		return (NULL);
153

154 155
	key = isc_mem_get(mctx, sizeof(signer_key_t));
	if (key == NULL)
156
		fatal("out of memory");
157

158
	result = dst_key_fromfile(&sig->signer, sig->keyid, sig->algorithm,
Brian Wellington's avatar
Brian Wellington committed
159
				  DST_TYPE_PRIVATE, NULL, mctx, &privkey);
160 161
	if (result == ISC_R_SUCCESS) {
		key->key = privkey;
162
		dst_key_free(&pubkey);
163 164 165 166 167 168
	}
	else
		key->key = pubkey;
	key->isdefault = ISC_FALSE;
	ISC_LIST_APPEND(keylist, key, link);
	return key;
169 170
}

171 172 173 174 175
/*
 * 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.
 */
176 177 178
static isc_boolean_t
expecttofindkey(dns_name_t *name, dns_db_t *db, dns_dbversion_t *version) {
	unsigned int options = DNS_DBFIND_NOWILD;
179
	dns_fixedname_t fname;
180 181
	isc_result_t result;

182
	dns_fixedname_init(&fname);
183
	result = dns_db_find(db, name, version, dns_rdatatype_key, options,
184
			     0, NULL, dns_fixedname_name(&fname), NULL, NULL);
185
	switch (result) {
186
		case ISC_R_SUCCESS:
187
		case DNS_R_NXDOMAIN:
188
		case DNS_R_NXRRSET:
189 190 191 192 193 194
			return ISC_TRUE;
		case DNS_R_DELEGATION:
		case DNS_R_CNAME:
		case DNS_R_DNAME:
			return ISC_FALSE;
		default:
195 196
			fatal("failure looking for '%s KEY' in database: %s",
			      nametostr(name), isc_result_totext(result));
197
			return ISC_FALSE; /* removes a warning */
198 199 200
	}
}

201
static inline isc_boolean_t
202 203
setverifies(dns_name_t *name, dns_rdataset_t *set, signer_key_t *key,
	    dns_rdata_t *sig)
204
{
205 206
	isc_result_t result;
	result = dns_dnssec_verify(name, set, key->key, ISC_FALSE, mctx, sig);
207
	return (ISC_TF(result == ISC_R_SUCCESS));
208 209
}

210 211 212 213 214
/*
 * 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.
 */
215
static void
216 217
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)
218
{
219 220
	dns_rdataset_t sigset;
	dns_rdata_t sigrdata;
221
	dns_rdata_sig_t sig;
222
	signer_key_t *key;
223
	isc_result_t result;
224
	isc_boolean_t notsigned = ISC_TRUE, nosigs = ISC_FALSE;
225
	isc_boolean_t wassignedby[256], nowsignedby[256];
226 227
	dns_difftuple_t *tuple;
	dns_ttl_t ttl;
228
	int i;
229

230
	ttl = ISC_MIN(set->ttl, endtime - starttime);
231 232 233 234

	for (i = 0; i < 256; i++)
		wassignedby[i] = nowsignedby[i] = ISC_FALSE;

235
	dns_rdataset_init(&sigset);
236
	result = dns_db_findrdataset(db, node, version, dns_rdatatype_sig,
237
				     set->type, 0, &sigset, NULL);
238 239 240 241
	if (result == ISC_R_NOTFOUND) {
		result = ISC_R_SUCCESS;
		nosigs = ISC_TRUE;
	}
242 243 244 245
	if (result != ISC_R_SUCCESS)
		fatal("failed while looking for '%s SIG %s': %s",
		      nametostr(name), typetostr(set->type),
		      isc_result_totext(result));
246

247 248
	vbprintf(1, "%s/%s:\n", nametostr(name), typetostr(set->type));

Brian Wellington's avatar
Brian Wellington committed
249 250 251
	if (nosigs)
		result = ISC_R_NOMORE;
	else
252
		result = dns_rdataset_first(&sigset);
253

Brian Wellington's avatar
Brian Wellington committed
254 255 256
	while (result == ISC_R_SUCCESS) {
		isc_boolean_t expired, future;
		isc_boolean_t keep = ISC_FALSE, resign = ISC_FALSE;
257

Brian Wellington's avatar
Brian Wellington committed
258
		dns_rdataset_current(&sigset, &sigrdata);
259

Brian Wellington's avatar
Brian Wellington committed
260 261
		result = dns_rdata_tostruct(&sigrdata, &sig, NULL);
		check_result(result, "dns_rdata_tostruct");
262

Brian Wellington's avatar
Brian Wellington committed
263 264
		expired = ISC_TF(now + cycle > sig.timeexpire);
		future = ISC_TF(now < sig.timesigned);
265

Brian Wellington's avatar
Brian Wellington committed
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
		key = keythatsigned(&sig);

		if (sig.timesigned > sig.timeexpire) {
			/* sig is dropped and not replaced */
			vbprintf(2, "\tsig by %s dropped - "
				 "invalid validity period\n",
				 sigtostr(&sig));
		} 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",
				 sigtostr(&sig));
		} else if (key == NULL || future) {
			vbprintf(2, "\tsig by %s %s - key not found\n",
				 expired ? "retained" : "dropped",
				 sigtostr(&sig));
			if (!expired)
				keep = ISC_TRUE;
		} else if (issigningkey(key)) {
			if (!expired && setverifies(name, set, key, &sigrdata))
288
			{
289 290
				vbprintf(2, "\tsig by %s retained\n",
					 sigtostr(&sig));
291
				keep = ISC_TRUE;
Brian Wellington's avatar
Brian Wellington committed
292
				wassignedby[sig.algorithm] = ISC_TRUE;
293
			} else {
Brian Wellington's avatar
Brian Wellington committed
294 295 296 297 298 299
				vbprintf(2, "\tsig by %s dropped - %s\n",
					 sigtostr(&sig),
					 expired ? "expired" :
						   "failed to verify");
				wassignedby[sig.algorithm] = ISC_TRUE;
				resign = ISC_TRUE;
300
			}
Brian Wellington's avatar
Brian Wellington committed
301 302 303 304 305 306 307
		} else if (iszonekey(key, db)) {
			if (!expired && setverifies(name, set, key, &sigrdata))
			{
				vbprintf(2, "\tsig by %s retained\n",
					 sigtostr(&sig));
				keep = ISC_TRUE;
				wassignedby[sig.algorithm] = ISC_TRUE;
308
				nowsignedby[sig.algorithm] = ISC_TRUE;
Brian Wellington's avatar
Brian Wellington committed
309 310 311 312 313 314 315 316
			} else {
				vbprintf(2, "\tsig by %s dropped - %s\n",
					 sigtostr(&sig),
					 expired ? "expired" :
						   "failed to verify");
				wassignedby[sig.algorithm] = ISC_TRUE;
				if (dst_key_isprivate(key->key))
					resign = ISC_TRUE;
317
			}
Brian Wellington's avatar
Brian Wellington committed
318 319 320 321 322 323 324 325
		} else if (!expired) {
			vbprintf(2, "\tsig by %s retained\n",
				 sigtostr(&sig));
			keep = ISC_TRUE;
		} else {
			vbprintf(2, "\tsig by %s expired\n",
				 sigtostr(&sig));
		}
326

Brian Wellington's avatar
Brian Wellington committed
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
		if (keep)
			nowsignedby[sig.algorithm] = ISC_TRUE;
		else {
			tuple = NULL;
			result = dns_difftuple_create(mctx, DNS_DIFFOP_DEL,
						      name, 0, &sigrdata,
						      &tuple);
			check_result(result, "dns_difftuple_create");
			dns_diff_append(diff, &tuple);
		}

		if (resign) {
			isc_buffer_t b;
			dns_rdata_t trdata;
			unsigned char array[BUFSIZE];
342

Brian Wellington's avatar
Brian Wellington committed
343 344 345 346 347 348 349 350 351 352 353 354 355
			vbprintf(1, "\tresigning with key %s/%s/%d\n",
			       nametostr(dst_key_name(key->key)),
			       algtostr(dst_key_alg(key->key)),
			       dst_key_id(key->key));
			isc_buffer_init(&b, array, sizeof(array));
			signwithkey(name, set, &trdata, key->key, &b);
			nowsignedby[sig.algorithm] = ISC_TRUE;
			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);
356
		}
Brian Wellington's avatar
Brian Wellington committed
357 358 359

		dns_rdata_freestruct(&sig);
		result = dns_rdataset_next(&sigset);
Brian Wellington's avatar
Brian Wellington committed
360
	}
Brian Wellington's avatar
Brian Wellington committed
361 362 363 364 365 366
	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);
367 368

	for (i = 0; i < 256; i++)
369
		if (wassignedby[i]) {
370 371
			notsigned = ISC_FALSE;
			break;
Brian Wellington's avatar
Brian Wellington committed
372
		}
373 374 375

	key = ISC_LIST_HEAD(keylist);
	while (key != NULL) {
376
		unsigned int alg = dst_key_alg(key->key);
377 378 379
		if (key->isdefault &&
		    (notsigned || (wassignedby[alg] && !nowsignedby[alg])))
		{
380 381 382 383
			isc_buffer_t b;
			dns_rdata_t trdata;
			unsigned char array[BUFSIZE];

384
			vbprintf(1, "\tsigning with key %s/%s/%d\n",
385
			       nametostr(dst_key_name(key->key)),
386 387
			       algtostr(dst_key_alg(key->key)),
			       dst_key_id(key->key));
388 389 390 391 392 393 394 395
			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);
396 397 398 399 400
		}
		key = ISC_LIST_NEXT(key, link);
	}
}

401
/* Determine if a KEY set contains a null key */
402
static isc_boolean_t
403
hasnullkey(dns_rdataset_t *rdataset) {
404 405
	isc_result_t result;
	dns_rdata_t rdata;
406
	isc_boolean_t found = ISC_FALSE;
407

408
	result = dns_rdataset_first(rdataset);
409 410 411
	while (result == ISC_R_SUCCESS) {
		dst_key_t *key = NULL;

412
		dns_rdataset_current(rdataset, &rdata);
413 414
		result = dns_dnssec_keyfromrdata(dns_rootname,
						 &rdata, mctx, &key);
415 416
		if (result != ISC_R_SUCCESS)
			fatal("could not convert KEY into internal format");
417 418
		if (dst_key_isnullkey(key))
			found = ISC_TRUE;
419
                dst_key_free(&key);
420
		if (found == ISC_TRUE)
421
			return (ISC_TRUE);
422 423 424
                result = dns_rdataset_next(rdataset);
        }
        if (result != ISC_R_NOMORE)
425
                fatal("failure looking for null keys");
426 427 428
        return (ISC_FALSE);
}

429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
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()");
	if (isc_buffer_availablelength(&b) == 0)
		fatal("name '%s' is too long", nametostr(name));
	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);
	if (result != ISC_R_SUCCESS)
		dns_db_detach(db);
}

454 455 456 457 458
/*
 * Looks for signatures of the zone keys by the parent, and imports them
 * if found.
 */
static void
459 460
importparentsig(dns_db_t *db, dns_diff_t *diff, dns_name_t *name,
		dns_rdataset_t *set)
461 462 463 464 465 466 467
{
	dns_db_t *newdb = NULL;
	dns_dbnode_t *newnode = NULL;
	dns_rdataset_t newset, sigset;
	dns_rdata_t rdata, newrdata;
	isc_result_t result;

468 469 470
	dns_rdataset_init(&newset);
	dns_rdataset_init(&sigset);

471 472 473 474
	opendb("signedkey-", name, dns_db_class(db), &newdb);
	if (newdb == NULL)
		return;

475 476 477
	result = dns_db_findnode(newdb, name, ISC_FALSE, &newnode);
	if (result != ISC_R_SUCCESS)
		goto failure;
478 479
	result = dns_db_findrdataset(newdb, newnode, NULL, dns_rdatatype_key,
				     0, 0, &newset, &sigset);
480 481 482
	if (result != ISC_R_SUCCESS)
		goto failure;

483 484 485 486
	if (!dns_rdataset_isassociated(&newset) ||
	    !dns_rdataset_isassociated(&sigset))
		goto failure;

487 488 489 490 491 492 493
	if (dns_rdataset_count(set) != dns_rdataset_count(&newset))
		goto failure;

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

	result = dns_rdataset_first(set);
494
	check_result(result, "dns_rdataset_first()");
495 496 497
	for (; result == ISC_R_SUCCESS; result = dns_rdataset_next(set)) {
		dns_rdataset_current(set, &rdata);
		result = dns_rdataset_first(&newset);
498
		check_result(result, "dns_rdataset_first()");
499 500 501 502 503 504 505 506 507 508
		for (;
		     result == ISC_R_SUCCESS;
		     result = dns_rdataset_next(&newset))
		{
			dns_rdataset_current(&newset, &newrdata);
			if (dns_rdata_compare(&rdata, &newrdata) == 0)
				break;
		}
		if (result != ISC_R_SUCCESS)
			break;
509
	}
510
	if (result != ISC_R_NOMORE)
511 512 513 514
		goto failure;

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

515 516 517 518 519 520 521 522 523 524 525
	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);
	}
526 527

 failure:
528 529 530 531
	if (dns_rdataset_isassociated(&newset))
		dns_rdataset_disassociate(&newset);
	if (dns_rdataset_isassociated(&sigset))
		dns_rdataset_disassociate(&sigset);
532 533 534 535 536 537 538
	if (newnode != NULL)
		dns_db_detachnode(newdb, &newnode);
	if (newdb != NULL)
		dns_db_detach(&newdb);
}

/*
539
 * Looks for our signatures of child keys.  If present, inform the caller.
540 541 542 543 544 545 546 547 548 549 550 551
 */
static isc_boolean_t
haschildkey(dns_db_t *db, dns_name_t *name) {
	dns_db_t *newdb = NULL;
	dns_dbnode_t *newnode = NULL;
	dns_rdataset_t set, sigset;
	dns_rdata_t sigrdata;
	isc_result_t result;
	isc_boolean_t found = ISC_FALSE;
	dns_rdata_sig_t sig;
	signer_key_t *key;

552 553 554
	dns_rdataset_init(&set);
	dns_rdataset_init(&sigset);

555 556 557 558
	opendb("signedkey-", name, dns_db_class(db), &newdb);
	if (newdb == NULL)
		return (ISC_FALSE);

559 560 561
	result = dns_db_findnode(newdb, name, ISC_FALSE, &newnode);
	if (result != ISC_R_SUCCESS)
		goto failure;
562 563
	result = dns_db_findrdataset(newdb, newnode, NULL, dns_rdatatype_key,
				     0, 0, &set, &sigset);
564 565 566 567 568
	if (result != ISC_R_SUCCESS)
		goto failure;

	if (!dns_rdataset_isassociated(&set) ||
	    !dns_rdataset_isassociated(&sigset))
569
		goto failure;
570 571 572 573 574 575

	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);
576
		result = dns_rdata_tostruct(&sigrdata, &sig, NULL);
577
		if (result != ISC_R_SUCCESS)
578
			goto failure;
579 580 581
		key = keythatsigned(&sig);
		dns_rdata_freestruct(&sig);
		if (key == NULL)
582
			goto failure;
583 584 585 586 587 588 589 590
		result = dns_dnssec_verify(name, &set, key->key,
					   ISC_FALSE, mctx, &sigrdata);
		if (result == ISC_R_SUCCESS) {
			found = ISC_TRUE;
			break;
		}
	}

591
 failure:
592 593 594 595 596 597 598 599 600 601
	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);
602 603
}

604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
/*
 * There probably should be a dns_nxt_setbit, but it can get complicated if
 * the length of the bit set needs to be increased.  In this case, since the
 * NXT bit is set and both SIG and KEY are less than NXT, the easy way works.
 */
static void
nxt_setbit(dns_rdataset_t *rdataset, dns_rdatatype_t type) {
	unsigned char *nxt_bits;
	dns_name_t nxtname;
	isc_region_t r, r2;
	isc_result_t result;
	dns_rdata_t rdata;

	result = dns_rdataset_first(rdataset);
	check_result(result, "dns_rdataset_first()");
	dns_rdataset_current(rdataset, &rdata);
	dns_rdata_toregion(&rdata, &r);
	dns_name_init(&nxtname, NULL);
	dns_name_fromregion(&nxtname, &r);
	dns_name_toregion(&nxtname, &r2);
	nxt_bits = r.base + r2.length;
	set_bit(nxt_bits, type, 1);
}

Brian Wellington's avatar
Brian Wellington committed
628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668
static void
createnullkey(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name) {
	unsigned char keydata[4];
	dns_rdata_t keyrdata;
	dns_rdata_key_t key;
	dns_diff_t diff;
	dns_difftuple_t *tuple = NULL;
	isc_buffer_t b;
	isc_result_t result;

	vbprintf(2, "adding null key at %s\n", nametostr(name));

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

669 670
/*
 * Signs all records at a name.  This mostly just signs each set individually,
671 672
 * but also adds the SIG bit to any NXTs generated earlier, deals with
 * parent/child KEY signatures, and handles other exceptional cases.
673
 */
Brian Wellington's avatar
Brian Wellington committed
674
static void
675
signname(dns_db_t *db, dns_dbversion_t *version, dns_dbnode_t *node,
676
	 dns_name_t *name)
Brian Wellington's avatar
Brian Wellington committed
677 678
{
	isc_result_t result;
679
	dns_rdataset_t rdataset;
Brian Wellington's avatar
Brian Wellington committed
680
	dns_rdatasetiter_t *rdsiter;
Brian Wellington's avatar
Brian Wellington committed
681
	isc_boolean_t isdelegation = ISC_FALSE;
682
	isc_boolean_t childkey = ISC_FALSE;
683
	static int warnwild = 0;
684
	isc_boolean_t atorigin;
Brian Wellington's avatar
Brian Wellington committed
685
	isc_boolean_t neednullkey = ISC_FALSE;
686
	dns_diff_t diff;
687 688

	if (dns_name_iswildcard(name)) {
Brian Wellington's avatar
Brian Wellington committed
689 690
		if (warnwild++ == 0) {
			fprintf(stderr, "%s: warning: BIND 9 doesn't properly "
David Lawrence's avatar
David Lawrence committed
691
				"handle wildcards in secure zones:\n",
692
				program);
Brian Wellington's avatar
Brian Wellington committed
693 694 695 696 697 698
			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",
699
			program, nametostr(name));
700
	}
701

702
	atorigin = dns_name_equal(name, dns_db_origin(db));
703 704 705 706

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

710 711 712 713 714 715 716 717 718 719
		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);
		}
	}
720 721 722 723 724 725 726 727 728

	/*
	 * 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
729
		neednullkey = ISC_TRUE;
730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748

		dns_rdataset_init(&keyset);
		result = dns_db_findrdataset(db, node, version,
					     dns_rdatatype_key, 0, 0, &keyset,
					     NULL);
		if (result == ISC_R_SUCCESS && childkey)
			fatal("%s has both a signedkey file and KEY "
			      "records in the zone.  Aborting.",
			      nametostr(name));
		else if (result == ISC_R_SUCCESS) {
			if (hasnullkey(&keyset))
				neednullkey = ISC_FALSE;
			dns_rdataset_disassociate(&keyset);
		} else if (childkey) {
			vbprintf(2, "child key for %s found\n",
				 nametostr(name));
			neednullkey = ISC_FALSE;
		}

Brian Wellington's avatar
Brian Wellington committed
749 750
		if (neednullkey)
			createnullkey(db, version, name);
751 752 753 754 755 756
	}

	/*
	 * Now iterate through the rdatasets.
	 */
	dns_diff_init(mctx, &diff);
Brian Wellington's avatar
Brian Wellington committed
757 758 759 760 761 762 763
	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
764

765 766 767
		/* If this is a SIG set, skip it. */
		if (rdataset.type == dns_rdatatype_sig)
			goto skip;
Brian Wellington's avatar
Brian Wellington committed
768

769 770 771
		/*
		 * If this is a KEY set at the apex, look for a signedkey file.
		 */
Brian Wellington's avatar
Brian Wellington committed
772
		if (atorigin && rdataset.type == dns_rdatatype_key) {
773
			importparentsig(db, &diff, name, &rdataset);
774
			goto skip;
775
		}
776 777 778

		/*
		 * If this name is a delegation point, skip all records
779
		 * except an NXT set a KEY set containing a null key.
780 781
		 */
		if (isdelegation) {
Brian Wellington's avatar
Brian Wellington committed
782 783 784
			if (!(rdataset.type == dns_rdatatype_nxt ||
			      (rdataset.type == dns_rdatatype_key &&
			       hasnullkey(&rdataset))))
785
				goto skip;
786 787
		}

788
		if (rdataset.type == dns_rdatatype_nxt) {
789 790 791
			nxt_setbit(&rdataset, dns_rdatatype_sig);
			if (neednullkey)
				nxt_setbit(&rdataset, dns_rdatatype_key);
Brian Wellington's avatar
Brian Wellington committed
792
		}
Brian Wellington's avatar
Brian Wellington committed
793

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

796
 skip:
Brian Wellington's avatar
Brian Wellington committed
797 798 799
		dns_rdataset_disassociate(&rdataset);
		result = dns_rdatasetiter_next(rdsiter);
	}
800
	if (result != ISC_R_NOMORE)
801 802
		fatal("rdataset iteration for name '%s' failed: %s",
		      nametostr(name), isc_result_totext(result));
Brian Wellington's avatar
Brian Wellington committed
803
	dns_rdatasetiter_destroy(&rdsiter);
804 805 806 807 808

	result = dns_diff_apply(&diff, db, version);
	if (result != ISC_R_SUCCESS)
		fatal("failed to add SIGs at node %s", nametostr(name));
	dns_diff_clear(&diff);
Brian Wellington's avatar
Brian Wellington committed
809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830
}

static inline isc_boolean_t
active_node(dns_db_t *db, dns_dbversion_t *version, dns_dbnode_t *node) {
	dns_rdatasetiter_t *rdsiter;
	isc_boolean_t active = ISC_FALSE;
	isc_result_t result;
	dns_rdataset_t rdataset;

	dns_rdataset_init(&rdataset);
	rdsiter = NULL;
	result = dns_db_allrdatasets(db, node, version, 0, &rdsiter);
	check_result(result, "dns_db_allrdatasets()");
	result = dns_rdatasetiter_first(rdsiter);
	while (result == ISC_R_SUCCESS) {
		dns_rdatasetiter_current(rdsiter, &rdataset);
		if (rdataset.type != dns_rdatatype_nxt)
			active = ISC_TRUE;
		dns_rdataset_disassociate(&rdataset);
		if (!active)
			result = dns_rdatasetiter_next(rdsiter);
		else
831
			result = ISC_R_NOMORE;
Brian Wellington's avatar
Brian Wellington committed
832
	}
833
	if (result != ISC_R_NOMORE)
834 835
		fatal("rdataset iteration failed: %s",
		      isc_result_totext(result));
Brian Wellington's avatar
Brian Wellington committed
836 837 838 839 840 841 842
	dns_rdatasetiter_destroy(&rdsiter);

	if (!active) {
		/*
		 * Make sure there is no NXT record for this node.
		 */
		result = dns_db_deleterdataset(db, node, version,
843
					       dns_rdatatype_nxt, 0);
Brian Wellington's avatar
Brian Wellington committed
844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873
		if (result == DNS_R_UNCHANGED)
			result = ISC_R_SUCCESS;
		check_result(result, "dns_db_deleterdataset");
	}

	return (active);
}

static inline isc_result_t
next_active(dns_db_t *db, dns_dbversion_t *version, dns_dbiterator_t *dbiter,
	    dns_name_t *name, dns_dbnode_t **nodep)
{
	isc_result_t result;
	isc_boolean_t active;

	do {
		active = ISC_FALSE;
		result = dns_dbiterator_current(dbiter, nodep, name);
		if (result == ISC_R_SUCCESS) {
			active = active_node(db, version, *nodep);
			if (!active) {
				dns_db_detachnode(db, nodep);
				result = dns_dbiterator_next(dbiter);
			}
		}
	} while (result == ISC_R_SUCCESS && !active);

	return (result);
}

874 875
static inline isc_result_t
next_nonglue(dns_db_t *db, dns_dbversion_t *version, dns_dbiterator_t *dbiter,
Brian Wellington's avatar
Brian Wellington committed
876 877
	    dns_name_t *name, dns_dbnode_t **nodep, dns_name_t *origin,
	    dns_name_t *lastcut)
878
{
879
	isc_result_t result, dresult;
880 881 882 883

	do {
		result = next_active(db, version, dbiter, name, nodep);
		if (result == ISC_R_SUCCESS) {
Brian Wellington's avatar
Brian Wellington committed
884 885 886
			if (dns_name_issubdomain(name, origin) &&
			    (lastcut == NULL ||
			     !dns_name_issubdomain(name, lastcut)))
887
				return (ISC_R_SUCCESS);
888 889 890 891 892
			dresult = dns_master_dumpnodetostream(mctx, db,
							      version,
							      *nodep, name,
							      masterstyle, fp);
			check_result(dresult, "dns_master_dumpnodetostream");
893 894 895 896 897 898 899
			dns_db_detachnode(db, nodep);
			result = dns_dbiterator_next(dbiter);
		}
	} while (result == ISC_R_SUCCESS);
	return (result);
}

900
/*
901
 * Extracts the zone minimum TTL from the SOA.
902
 */
903 904
static dns_ttl_t
minimumttl(dns_db_t *db, dns_dbversion_t *version) {
905
	dns_rdataset_t soaset;
906 907 908
	dns_name_t *origin;
	dns_fixedname_t fname;
	dns_name_t *name;
909 910
	dns_rdata_t soarr;
	dns_rdata_soa_t soa;
911 912
	isc_result_t result;
	dns_ttl_t ttl;
Brian Wellington's avatar
Brian Wellington committed
913

914 915
	origin = dns_db_origin(db);

916 917
	dns_fixedname_init(&fname);
	name = dns_fixedname_name(&fname);
918 919 920
	dns_rdataset_init(&soaset);
	result = dns_db_find(db, origin, version, dns_rdatatype_soa,
			     0, 0, NULL, name, &soaset, NULL);
921 922 923
	if (result != ISC_R_SUCCESS)
		fatal("failed to find '%s SOA' in the zone: %s",
		      nametostr(name), isc_result_totext(result));
924
	result = dns_rdataset_first(&soaset);
925
	check_result(result, "dns_rdataset_first()");
926
	dns_rdataset_current(&soaset, &soarr);
927
	result = dns_rdata_tostruct(&soarr, &soa, NULL);
928
	check_result(result, "dns_rdataset_tostruct()");
929
	ttl = soa.minimum;
930 931 932
	dns_rdata_freestruct(&soa);
	dns_rdataset_disassociate(&soaset);

933 934 935
	return (ttl);
}

936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968
static void
cleannode(dns_db_t *db, dns_dbversion_t *version, dns_dbnode_t *node) {
	dns_rdatasetiter_t *rdsiter = NULL;
	dns_rdataset_t set;
	isc_result_t result, dresult;

	dns_rdataset_init(&set);
	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) {
		isc_boolean_t destroy = ISC_FALSE;
		dns_rdatatype_t covers = 0;
		dns_rdatasetiter_current(rdsiter, &set);
		if (set.type == dns_rdatatype_sig) {
			covers = set.covers;
			destroy = ISC_TRUE;
		}
		dns_rdataset_disassociate(&set);
		result = dns_rdatasetiter_next(rdsiter);
		if (destroy) {
			dresult = dns_db_deleterdataset(db, node, version,
							dns_rdatatype_sig,
							covers);
			check_result(dresult, "dns_db_allrdatasets");
		}
	}
	if (result != ISC_R_NOMORE)
		fatal("rdataset iteration failed: %s",
		      isc_result_totext(result));
	dns_rdatasetiter_destroy(&rdsiter);
}