dnssec-signzone.c 38.7 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.
Bob Halley's avatar
Bob Halley committed
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.
Bob Halley's avatar
Bob Halley committed
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 21 22 23

#include <config.h>

#include <stdlib.h>

24
#include <isc/commandline.h>
Brian Wellington's avatar
Brian Wellington committed
25
#include <isc/entropy.h>
Brian Wellington's avatar
Brian Wellington committed
26
#include <isc/mem.h>
27
#include <isc/string.h>
Bob Halley's avatar
Bob Halley committed
28
#include <isc/util.h>
Brian Wellington's avatar
Brian Wellington committed
29 30 31

#include <dns/db.h>
#include <dns/dbiterator.h>
32 33 34 35
#include <dns/dnssec.h>
#include <dns/keyvalues.h>
#include <dns/log.h>
#include <dns/nxt.h>
Brian Wellington's avatar
Brian Wellington committed
36 37 38 39
#include <dns/rdata.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
#include <dns/rdatasetiter.h>
40
#include <dns/rdatastruct.h>
41
#include <dns/rdatatype.h>
Brian Wellington's avatar
Brian Wellington committed
42
#include <dns/result.h>
43
#include <dns/secalg.h>
44
#include <dns/time.h>
Brian Wellington's avatar
Brian Wellington committed
45

46
#include <dst/dst.h>
47
#include <dst/result.h>
Brian Wellington's avatar
Brian Wellington committed
48

49 50
#include "dnssectool.h"

David Lawrence's avatar
David Lawrence committed
51
const char *program = "dnssec-signzone";
52
int verbose;
53

54 55
/*#define USE_ZONESTATUS*/

56
#define BUFSIZE 2048
Brian Wellington's avatar
Brian Wellington committed
57

58 59 60 61 62 63 64 65 66 67 68 69 70 71
typedef struct signer_key_struct signer_key_t;
typedef struct signer_array_struct signer_array_t;

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

struct signer_array_struct {
	unsigned char array[BUFSIZE];
	ISC_LINK(signer_array_t) link;
};

72 73 74
static ISC_LIST(signer_key_t) keylist;
static isc_stdtime_t starttime = 0, endtime = 0, now;
static int cycle = -1;
75
static isc_boolean_t tryverify = ISC_FALSE;
Brian Wellington's avatar
Brian Wellington committed
76
static isc_mem_t *mctx = NULL;
Brian Wellington's avatar
Brian Wellington committed
77
static isc_entropy_t *ectx = NULL;
Brian Wellington's avatar
Brian Wellington committed
78

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

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

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

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

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

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

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

121 122
static inline isc_boolean_t
iszonekey(signer_key_t *key, dns_db_t *db) {
123 124 125 126
	return (ISC_TF(dns_name_equal(dst_key_name(key->key),
				      dns_db_origin(db)) &&
		       (dst_key_flags(key->key) & DNS_KEYFLAG_OWNERMASK) ==
		       DNS_KEYOWNER_ZONE));
127 128
}

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

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

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

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

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

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

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

#define allocbufferandrdata \
	isc_buffer_t b; \
	trdata = isc_mem_get(mctx, sizeof(dns_rdata_t)); \
	tdata = isc_mem_get(mctx, sizeof(signer_array_t)); \
	ISC_LIST_APPEND(arraylist, tdata, link); \
	if (trdata == NULL || tdata == NULL) \
215
		fatal("out of memory"); \
216
	isc_buffer_init(&b, tdata->array, sizeof(tdata->array));
217

218 219 220 221 222
/*
 * 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.
 */
223 224 225 226 227 228 229 230
static void
signset(dns_db_t *db, dns_dbversion_t *version, dns_dbnode_t *node,
	 dns_name_t *name, dns_rdataset_t *set)
{
	dns_rdatalist_t siglist;
	dns_rdataset_t sigset, oldsigset;
	dns_rdata_t oldsigrdata;
	dns_rdata_t *trdata;
231
	dns_rdata_sig_t sig;
232
	signer_key_t *key;
233
	isc_result_t result;
234
	isc_boolean_t notsigned = ISC_TRUE, nosigs = ISC_FALSE;
235 236 237
	isc_boolean_t wassignedby[256], nowsignedby[256];
	signer_array_t *tdata;
	ISC_LIST(signer_array_t) arraylist;
238
	int i;
239 240 241 242 243 244 245 246 247 248

	ISC_LIST_INIT(siglist.rdata);
	ISC_LIST_INIT(arraylist);

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

	dns_rdataset_init(&oldsigset);
	result = dns_db_findrdataset(db, node, version, dns_rdatatype_sig,
				     set->type, 0, &oldsigset, NULL);
249 250 251 252
	if (result == ISC_R_NOTFOUND) {
		result = ISC_R_SUCCESS;
		nosigs = ISC_TRUE;
	}
253 254 255 256
	if (result != ISC_R_SUCCESS)
		fatal("failed while looking for '%s SIG %s': %s",
		      nametostr(name), typetostr(set->type),
		      isc_result_totext(result));
257

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

260
	if (!nosigs) {
261 262
		result = dns_rdataset_first(&oldsigset);
		while (result == ISC_R_SUCCESS) {
263
			isc_boolean_t expired, future;
264 265 266 267 268 269 270
			isc_boolean_t keep = ISC_FALSE, resign = ISC_FALSE;

			dns_rdataset_current(&oldsigset, &oldsigrdata);

			result = dns_rdata_tostruct(&oldsigrdata, &sig, mctx);
			check_result(result, "dns_rdata_tostruct");

271 272
			expired = ISC_TF(now + cycle > sig.timeexpire);
			future = ISC_TF(now < sig.timesigned);
273 274 275

			key = keythatsigned(&sig);

276 277 278 279 280 281 282 283
			if (sig.timesigned > sig.timeexpire) {
				/* sig is dropped and not replaced */
				vbprintf(2, "\tsig by %s/%s/%d dropped - "
					 "invalid validity period\n",
					 nametostr(&sig.signer),
					 algtostr(sig.algorithm),
					 sig.keyid);
			}
284
			else if (key == NULL && !future &&
285
				 expecttofindkey(&sig.signer, db, version))
286 287 288 289 290 291 292 293
			{
				/* sig is dropped and not replaced */
				vbprintf(2, "\tsig by %s/%s/%d dropped - "
					 "private key not found\n",
					 nametostr(&sig.signer),
					 algtostr(sig.algorithm),
					 sig.keyid);
			}
294
			else if (key == NULL || future) {
295 296 297 298 299 300
				vbprintf(2, "\tsig by %s/%s/%d %s - "
					 "key not found\n",
					 expired ? "retained" : "dropped",
					 nametostr(&sig.signer),
					 algtostr(sig.algorithm),
					 sig.keyid);
301 302 303
				if (!expired)
					keep = ISC_TRUE;
			}
304 305 306 307
			else if (issigningkey(key)) {
				if (!expired &&
				    setverifies(name, set, key, &oldsigrdata))
				{
308
					vbprintf(2,
309
						"\tsig by %s/%s/%d retained\n",
310 311 312
						 nametostr(&sig.signer),
						 algtostr(sig.algorithm),
						 sig.keyid);
313 314 315 316
					keep = ISC_TRUE;
					wassignedby[sig.algorithm] = ISC_TRUE;
				}
				else {
317
					vbprintf(2,
318
						 "\tsig by %s/%s/%d dropped - "
319 320 321
						 "%s\n",
						 nametostr(&sig.signer),
						 algtostr(sig.algorithm),
322 323 324
						 sig.keyid,
						 expired ? "expired" :
							   "failed to verify");
325 326 327
					wassignedby[sig.algorithm] = ISC_TRUE;
					resign = ISC_TRUE;
				}
Brian Wellington's avatar
Brian Wellington committed
328
			}
329 330 331 332
			else if (iszonekey(key, db)) {
				if (!expired &&
				    setverifies(name, set, key, &oldsigrdata))
				{
333
					vbprintf(2,
334
						"\tsig by %s/%s/%d retained\n",
335 336 337
						 nametostr(&sig.signer),
						 algtostr(sig.algorithm),
						 sig.keyid);
338 339 340 341 342
					keep = ISC_TRUE;
					wassignedby[sig.algorithm] = ISC_TRUE;
					nowsignedby[sig.algorithm] = ISC_TRUE;
				}
				else {
343
					vbprintf(2,
344 345
						 "\tsig by %s/%s/%d "
						 "dropped - %s\n",
346 347
						 nametostr(&sig.signer),
						 algtostr(sig.algorithm),
348 349 350
						 sig.keyid,
						 expired ? "expired" :
							   "failed to verify");
351 352 353 354 355
					wassignedby[sig.algorithm] = ISC_TRUE;
					if (dst_key_isprivate(key->key))
						resign = ISC_TRUE;
				}
			}
356
			else if (!expired) {
Brian Wellington's avatar
Brian Wellington committed
357
				vbprintf(2, "\tsig by %s/%s/%d retained\n",
358 359 360
					 nametostr(&sig.signer),
					 algtostr(sig.algorithm),
					 sig.keyid);
361
				keep = ISC_TRUE;
362 363
			}
			else {
Brian Wellington's avatar
Brian Wellington committed
364
				vbprintf(2, "\tsig by %s/%s/%d expired\n",
365 366 367 368
					 nametostr(&sig.signer),
					 algtostr(sig.algorithm),
					 sig.keyid);
			}
369 370 371 372 373

			if (keep) {
				allocbufferandrdata;
				result = dns_rdata_fromstruct(trdata,
							      set->rdclass,
374
							     dns_rdatatype_sig,
375 376 377 378 379 380
							      &sig, &b);
				nowsignedby[sig.algorithm] = ISC_TRUE;
				ISC_LIST_APPEND(siglist.rdata, trdata, link);
			}
			else if (resign) {
				allocbufferandrdata;
381
				vbprintf(1, "\tresigning with key %s/%s/%d\n",
382
				       nametostr(dst_key_name(key->key)),
383 384
				       algtostr(dst_key_alg(key->key)),
				       dst_key_id(key->key));
385 386 387 388 389 390 391
				signwithkey(name, set, trdata, key->key, &b);
				nowsignedby[sig.algorithm] = ISC_TRUE;
				ISC_LIST_APPEND(siglist.rdata, trdata, link);
			}

			dns_rdata_freestruct(&sig);
			result = dns_rdataset_next(&oldsigset);
392
		}
393
		if (result == ISC_R_NOMORE)
394 395 396
			result = ISC_R_SUCCESS;
		check_result(result, "dns_db_dns_rdataset_first()/next()");
		dns_rdataset_disassociate(&oldsigset);
Brian Wellington's avatar
Brian Wellington committed
397
	}
398 399 400 401 402

	for (i = 0; i < 256; i++)
		if (wassignedby[i] != 0) {
			notsigned = ISC_FALSE;
			break;
Brian Wellington's avatar
Brian Wellington committed
403
		}
404 405 406

	key = ISC_LIST_HEAD(keylist);
	while (key != NULL) {
407
		unsigned int alg = dst_key_alg(key->key);
408 409 410 411
		if (key->isdefault &&
		    (notsigned || (wassignedby[alg] && !nowsignedby[alg])))
		{
			allocbufferandrdata;
412
			vbprintf(1, "\tsigning with key %s/%s/%d\n",
413
			       nametostr(dst_key_name(key->key)),
414 415
			       algtostr(dst_key_alg(key->key)),
			       dst_key_id(key->key));
416
			signwithkey(name, set, trdata, key->key, &b);
417 418 419 420 421
			ISC_LIST_APPEND(siglist.rdata, trdata, link);
		}
		key = ISC_LIST_NEXT(key, link);
	}

422 423 424 425
	if (!ISC_LIST_EMPTY(siglist.rdata)) {
		siglist.rdclass = set->rdclass;
		siglist.type = dns_rdatatype_sig;
		siglist.covers = set->type;
426 427
		if (endtime - starttime < set->ttl)
			siglist.ttl = endtime - starttime;
428 429 430 431 432 433
		else
			siglist.ttl = set->ttl;
		dns_rdataset_init(&sigset);
		result = dns_rdatalist_tordataset(&siglist, &sigset);
		check_result(result, "dns_rdatalist_tordataset");
		result = dns_db_addrdataset(db, node, version, 0, &sigset,
434
					    0, NULL);
435 436 437 438 439 440
		if (result == DNS_R_UNCHANGED)
			result = ISC_R_SUCCESS;
		check_result(result, "dns_db_addrdataset");
		dns_rdataset_disassociate(&sigset);
	}
	else if (!nosigs) {
441 442 443 444 445 446
#if 0
		/*
		 * If this is compiled in, running a signed set through the
		 * signer with no private keys causes DNS_R_BADDB to occur
		 * later.  This is bad.
		 */
Brian Wellington's avatar
Brian Wellington committed
447 448 449 450 451
		result = dns_db_deleterdataset(db, node, version,
					       dns_rdatatype_sig, set->type);
		if (result == ISC_R_NOTFOUND)
			result = ISC_R_SUCCESS;
		check_result(result, "dns_db_deleterdataset");
452
#endif
453 454
		fatal("File is currently signed but no private keys were "
		      "found.  This won't work.");
455
	}
456 457 458 459 460 461

	trdata = ISC_LIST_HEAD(siglist.rdata);
	while (trdata != NULL) {
		dns_rdata_t *next = ISC_LIST_NEXT(trdata, link);
		isc_mem_put(mctx, trdata, sizeof(dns_rdata_t));
		trdata = next;
462
	}
Brian Wellington's avatar
Brian Wellington committed
463

464 465 466 467 468 469 470 471
	tdata = ISC_LIST_HEAD(arraylist);
	while (tdata != NULL) {
		signer_array_t *next = ISC_LIST_NEXT(tdata, link);
		isc_mem_put(mctx, tdata, sizeof(signer_array_t));
		tdata = next;
	}
}

472
#ifndef USE_ZONESTATUS
473
/* Determine if a KEY set contains a null key */
474
static isc_boolean_t
475
hasnullkey(dns_rdataset_t *rdataset) {
476 477
	isc_result_t result;
	dns_rdata_t rdata;
478
	isc_boolean_t found = ISC_FALSE;
479

480
	result = dns_rdataset_first(rdataset);
481 482 483
	while (result == ISC_R_SUCCESS) {
		dst_key_t *key = NULL;

484
		dns_rdataset_current(rdataset, &rdata);
485 486
		result = dns_dnssec_keyfromrdata(dns_rootname,
						 &rdata, mctx, &key);
487 488
		if (result != ISC_R_SUCCESS)
			fatal("could not convert KEY into internal format");
489 490
		if (dst_key_isnullkey(key))
			found = ISC_TRUE;
491
                dst_key_free(&key);
492
		if (found == ISC_TRUE)
493
			return (ISC_TRUE);
494 495 496
                result = dns_rdataset_next(rdataset);
        }
        if (result != ISC_R_NOMORE)
497
                fatal("failure looking for null keys");
498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
        return (ISC_FALSE);
}
#endif

/*
 * Looks for signatures of the zone keys by the parent, and imports them
 * if found.
 */
static void
importparentsig(dns_db_t *db, dns_dbversion_t *version, dns_dbnode_t *node,
		dns_name_t *name, dns_rdataset_t *set)
{
	unsigned char filename[256];
	isc_buffer_t b;
	isc_region_t r;
	dns_db_t *newdb = NULL;
	dns_dbnode_t *newnode = NULL;
	dns_rdataset_t newset, sigset;
	dns_rdata_t rdata, newrdata;
	isc_result_t result;

	isc_buffer_init(&b, filename, sizeof(filename) - 10);
	result = dns_name_totext(name, ISC_FALSE, &b);
	check_result(result, "dns_name_totext()");
	isc_buffer_usedregion(&b, &r);
	strcpy((char *)r.base + r.length, "signedkey");
524 525
	result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone,
			       dns_db_class(db), 0, NULL, &newdb);
526
	check_result(result, "dns_db_create()");
527
	result = dns_db_load(newdb, (char *)filename);
528 529 530 531 532 533 534
	if (result != ISC_R_SUCCESS)
		goto failure;
	result = dns_db_findnode(newdb, name, ISC_FALSE, &newnode);
	if (result != ISC_R_SUCCESS)
		goto failure;
	dns_rdataset_init(&newset);
	dns_rdataset_init(&sigset);
535 536
	result = dns_db_findrdataset(newdb, newnode, NULL, dns_rdatatype_key,
				     0, 0, &newset, &sigset);
537 538 539 540 541 542 543 544 545 546
	if (result != ISC_R_SUCCESS)
		goto failure;

	if (dns_rdataset_count(set) != dns_rdataset_count(&newset))
		goto failure;

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

	result = dns_rdataset_first(set);
547
	check_result(result, "dns_rdataset_first()");
548 549 550
	for (; result == ISC_R_SUCCESS; result = dns_rdataset_next(set)) {
		dns_rdataset_current(set, &rdata);
		result = dns_rdataset_first(&newset);
551
		check_result(result, "dns_rdataset_first()");
552 553 554 555 556 557 558 559 560 561
		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;
562
	}
563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602
	if (result != ISC_R_NOMORE) 
		goto failure;

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

	result = dns_db_addrdataset(db, node, version, 0, &sigset, 0, NULL);
	check_result(result, "dns_db_addrdataset");
	dns_rdataset_disassociate(&newset);
	dns_rdataset_disassociate(&sigset);

 failure:
	if (newnode != NULL)
		dns_db_detachnode(newdb, &newnode);
	if (newdb != NULL)
		dns_db_detach(&newdb);
}

/*
 * Looks for our signatures of child keys.  If present, inform the caller,
 * who will set the zone status (KEY) bit in the NXT record.
 */
static isc_boolean_t
haschildkey(dns_db_t *db, dns_name_t *name) {
	unsigned char filename[256];
	isc_buffer_t b;
	isc_region_t r;
	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;

	isc_buffer_init(&b, filename, sizeof(filename) - 10);
	result = dns_name_totext(name, ISC_FALSE, &b);
	check_result(result, "dns_name_totext()");
	isc_buffer_usedregion(&b, &r);
	strcpy((char *)r.base + r.length, "signedkey");
603 604
	result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone,
			      dns_db_class(db), 0, NULL, &newdb);
605
	check_result(result, "dns_db_create()");
606
	result = dns_db_load(newdb, (char *)filename);
607 608 609 610 611 612 613
	if (result != ISC_R_SUCCESS)
		goto failure;
	result = dns_db_findnode(newdb, name, ISC_FALSE, &newnode);
	if (result != ISC_R_SUCCESS)
		goto failure;
	dns_rdataset_init(&set);
	dns_rdataset_init(&sigset);
614 615
	result = dns_db_findrdataset(newdb, newnode, NULL, dns_rdatatype_key,
				     0, 0, &set, &sigset);
616 617 618 619 620 621 622 623 624 625 626 627 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
	if (result != ISC_R_SUCCESS)
		goto failure;

	if (!dns_rdataset_isassociated(&set) ||
	    !dns_rdataset_isassociated(&sigset))
		goto disfail;

	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);
		result = dns_rdata_tostruct(&sigrdata, &sig, mctx);
		if (result != ISC_R_SUCCESS)
			goto disfail;
		key = keythatsigned(&sig);
		dns_rdata_freestruct(&sig);
		if (key == NULL)
			goto disfail;
		result = dns_dnssec_verify(name, &set, key->key,
					   ISC_FALSE, mctx, &sigrdata);
		if (result == ISC_R_SUCCESS) {
			found = ISC_TRUE;
			break;
		}
	}

 disfail:
	if (dns_rdataset_isassociated(&set))
		dns_rdataset_disassociate(&set);
	if (dns_rdataset_isassociated(&sigset))
		dns_rdataset_disassociate(&sigset);

 failure:
	if (newnode != NULL)
		dns_db_detachnode(newdb, &newnode);
	if (newdb != NULL)
		dns_db_detach(&newdb);

	return (found);
656 657
}

658 659
/*
 * Signs all records at a name.  This mostly just signs each set individually,
660 661
 * but also adds the SIG bit to any NXTs generated earlier, deals with
 * parent/child KEY signatures, and handles other exceptional cases.
662
 */
Brian Wellington's avatar
Brian Wellington committed
663
static void
664
signname(dns_db_t *db, dns_dbversion_t *version, dns_dbnode_t *node,
Brian Wellington's avatar
Brian Wellington committed
665
	 dns_name_t *name, isc_boolean_t atorigin)
Brian Wellington's avatar
Brian Wellington committed
666 667
{
	isc_result_t result;
668
	dns_rdata_t rdata;
669
	dns_rdataset_t rdataset;
Brian Wellington's avatar
Brian Wellington committed
670
	dns_rdatasetiter_t *rdsiter;
Brian Wellington's avatar
Brian Wellington committed
671
	isc_boolean_t isdelegation = ISC_FALSE;
672
	isc_boolean_t childkey = ISC_FALSE;
673 674 675
	static int warnwild = 0;

	if (dns_name_iswildcard(name)) {
Brian Wellington's avatar
Brian Wellington committed
676 677
		if (warnwild++ == 0) {
			fprintf(stderr, "%s: warning: BIND 9 doesn't properly "
David Lawrence's avatar
David Lawrence committed
678
				"handle wildcards in secure zones:\n",
679
				program);
Brian Wellington's avatar
Brian Wellington committed
680 681 682 683 684 685
			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",
686
			program, nametostr(name));
687
	}
688
	if (!atorigin) {
689 690
		dns_rdataset_t nsset;

691 692 693 694 695 696 697 698 699 700
		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);
		}
	}
Brian Wellington's avatar
Brian Wellington committed
701 702 703 704 705 706 707
	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
708

709 710 711
		/* If this is a SIG set, skip it. */
		if (rdataset.type == dns_rdatatype_sig)
			goto skip;
Brian Wellington's avatar
Brian Wellington committed
712

713 714 715 716 717
		/*
		 * If this is a KEY set at the apex, look for a signedkey file.
		 */
		if (rdataset.type == dns_rdatatype_key && atorigin) {
			importparentsig(db, version, node, name, &rdataset);
718
			goto skip;
719
		}
720 721 722

		/*
		 * If this name is a delegation point, skip all records
723 724 725
		 * except an NXT set, unless we're using null keys, in
		 * which case we need to check for a null key and add one
		 * if it's not present.
726 727 728
		 */
		if (isdelegation) {
			switch (rdataset.type) {
729 730 731 732 733 734
			case dns_rdatatype_nxt:
				childkey = haschildkey(db, name);
				break;
#ifndef USE_ZONESTATUS
			case dns_rdatatype_key:
				if (hasnullkey(&rdataset))
735
					break;
736 737 738 739
				goto skip;
#endif
			default:
				goto skip;
740
			}
741

742 743
		}

744
		/*
745 746 747
		 * There probably should be a dns_nxtsetbit, but it can get
		 * complicated if we need to extend the length of the
		 * bit set.  In this case, since the NXT bit is set and
748
		 * SIG < NXT and KEY < NXT, the easy way works.
749
		 */
750
		if (rdataset.type == dns_rdatatype_nxt) {
Brian Wellington's avatar
Brian Wellington committed
751 752 753
			unsigned char *nxt_bits;
			dns_name_t nxtname;
			isc_region_t r, r2;
754 755 756
			unsigned char keydata[4];
			dst_key_t *dstkey;
			isc_buffer_t b;
Brian Wellington's avatar
Brian Wellington committed
757 758 759 760 761 762 763 764 765 766

			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, dns_rdatatype_sig, 1);
767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791
#ifdef USE_ZONESTATUS
			if (isdelegation && childkey) {
				set_bit(nxt_bits, dns_rdatatype_key, 1);
				vbprintf(2, "found a child key for %s, "
					 "setting KEY bit in NXT\n",
					 nametostr(name));
			}

#else
			if (isdelegation && !childkey) {
				dns_rdataset_t keyset;
				dns_rdatalist_t keyrdatalist;
				dns_rdata_t keyrdata;

				dns_rdataset_init(&keyset);
				result = dns_db_findrdataset(db, node, version,
							     dns_rdatatype_key,
							     0, 0, &keyset,
							     NULL);
				if (result == ISC_R_SUCCESS &&
				    hasnullkey(&keyset))
					goto alreadyhavenullkey;

				if (result == ISC_R_NOTFOUND)
					result = ISC_R_SUCCESS;
792 793 794 795
				if (result != ISC_R_SUCCESS)
					fatal("failure looking for null key "
					      "at '%s': %s", nametostr(name),
					      isc_result_totext(result));
796 797 798 799 800 801 802 803 804 805

				if (dns_rdataset_isassociated(&keyset))
					dns_rdataset_disassociate(&keyset);

				vbprintf(2, "no child key for %s, "
					 "adding null key\n",
					 nametostr(name));
				dns_rdatalist_init(&keyrdatalist);
				dstkey = NULL;
				
806
				result = dst_key_generate(name, DNS_KEYALG_DSA,
807
							  0, 0,
808 809
							  DNS_KEYTYPE_NOKEY |
							  DNS_KEYOWNER_ZONE,
810 811
							  DNS_KEYPROTO_DNSSEC,
							  mctx, &dstkey);
812 813
				if (result != ISC_R_SUCCESS)
					fatal("failed to generate null key");
814 815
				isc_buffer_init(&b, keydata, sizeof keydata);
				result = dst_key_todns(dstkey, &b);
816
				dst_key_free(&dstkey);
817 818 819 820 821 822 823 824 825 826 827
				isc_buffer_usedregion(&b, &r);
				dns_rdata_fromregion(&keyrdata,
						     rdataset.rdclass,
						     dns_rdatatype_key, &r);
				
				ISC_LIST_APPEND(keyrdatalist.rdata, &keyrdata,
						link);
				keyrdatalist.rdclass = rdataset.rdclass;
				keyrdatalist.type = dns_rdatatype_key;
				keyrdatalist.covers = 0;
				keyrdatalist.ttl = rdataset.ttl;
828 829 830
				result =
					dns_rdatalist_tordataset(&keyrdatalist,
								 &keyset);
831 832 833 834 835 836 837 838 839 840 841
				check_result(result,
					     "dns_rdatalist_tordataset");
				dns_db_addrdataset(db, node, version, 0,
						   &keyset, DNS_DBADD_MERGE,
						   NULL);
				set_bit(nxt_bits, dns_rdatatype_key, 1);
				signset(db, version, node, name, &keyset);

				dns_rdataset_disassociate(&keyset);

 alreadyhavenullkey:
842
				;
843 844
			}
#endif
Brian Wellington's avatar
Brian Wellington committed
845
		}
Brian Wellington's avatar
Brian Wellington committed
846

847
		signset(db, version, node, name, &rdataset);
Brian Wellington's avatar
Brian Wellington committed
848

849
 skip:
Brian Wellington's avatar
Brian Wellington committed
850 851 852
		dns_rdataset_disassociate(&rdataset);
		result = dns_rdatasetiter_next(rdsiter);
	}
853
	if (result != ISC_R_NOMORE)
854 855
		fatal("rdataset iteration for name '%s' failed: %s",
		      nametostr(name), isc_result_totext(result));
Brian Wellington's avatar
Brian Wellington committed
856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878
	dns_rdatasetiter_destroy(&rdsiter);
}

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
879
			result = ISC_R_NOMORE;
Brian Wellington's avatar
Brian Wellington committed
880
	}
881
	if (result != ISC_R_NOMORE)
882 883
		fatal("rdataset iteration failed: %s",
		      isc_result_totext(result));
Brian Wellington's avatar
Brian Wellington committed
884 885 886 887 888 889 890
	dns_rdatasetiter_destroy(&rdsiter);

	if (!active) {
		/*
		 * Make sure there is no NXT record for this node.
		 */
		result = dns_db_deleterdataset(db, node, version,
891
					       dns_rdatatype_nxt, 0);
Brian Wellington's avatar
Brian Wellington committed
892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921
		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);
}

922 923
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
924 925
	    dns_name_t *name, dns_dbnode_t **nodep, dns_name_t *origin,
	    dns_name_t *lastcut)
926 927 928 929 930 931
{
	isc_result_t result;

	do {
		result = next_active(db, version, dbiter, name, nodep);
		if (result == ISC_R_SUCCESS) {
Brian Wellington's avatar
Brian Wellington committed
932 933 934
			if (dns_name_issubdomain(name, origin) &&
			    (lastcut == NULL ||
			     !dns_name_issubdomain(name, lastcut)))
935 936 937 938 939 940 941 942
				return (ISC_R_SUCCESS);
			dns_db_detachnode(db, nodep);
			result = dns_dbiterator_next(dbiter);
		}
	} while (result == ISC_R_SUCCESS);
	return (result);
}

943 944 945
/*
 * Generates NXTs and SIGs for each non-glue name in the zone.
 */
Brian Wellington's avatar
Brian Wellington committed
946
static void
947
signzone(dns_db_t *db, dns_dbversion_t *version) {
948
	isc_result_t result, nxtresult;
Brian Wellington's avatar
Brian Wellington committed
949
	dns_dbnode_t *node, *nextnode, *curnode;
Brian Wellington's avatar
Brian Wellington committed
950
	dns_fixedname_t fname, fnextname, fcurname;
951
	dns_name_t *name, *nextname, *target, *curname, *lastcut;
Brian Wellington's avatar
Brian Wellington committed
952
	dns_dbiterator_t *dbiter;
Brian Wellington's avatar
Brian Wellington committed
953
	isc_boolean_t atorigin = ISC_TRUE;
Brian Wellington's avatar
Brian Wellington committed
954
	dns_name_t *origin;
955 956 957 958
	dns_rdataset_t soaset;
	dns_rdata_t soarr;
	dns_rdata_soa_t soa;
	dns_ttl_t zonettl;
Brian Wellington's avatar
Brian Wellington committed
959 960 961 962 963

	dns_fixedname_init(&fname);
	name = dns_fixedname_name(&fname);
	dns_fixedname_init(&fnextname);
	nextname = dns_fixedname_name(&fnextname);
Brian Wellington's avatar
Brian Wellington committed
964 965
	dns_fixedname_init(&fcurname);
	curname = dns_fixedname_name(&fcurname);
Brian Wellington's avatar
Brian Wellington committed
966

967 968 969 970 971
	origin = dns_db_origin(db);

	dns_rdataset_init(&soaset);
	result = dns_db_find(db, origin, version, dns_rdatatype_soa,
			     0, 0, NULL, name, &soaset, NULL);
972 973 974
	if (result != ISC_R_SUCCESS)
		fatal("failed to find '%s SOA' in the zone: %s",
		      nametostr(name), isc_result_totext(result));
975
	result = dns_rdataset_first(&soaset);
976
	check_result(result, "dns_rdataset_first()");
977 978
	dns_rdataset_current(&soaset, &soarr);
	result = dns_rdata_tostruct(&soarr, &soa, mctx);
979
	check_result(result, "dns_rdataset_tostruct()");
980 981 982 983
	zonettl = soa.minimum;
	dns_rdata_freestruct(&soa);
	dns_rdataset_disassociate(&soaset);

984
	lastcut = NULL;
Brian Wellington's avatar
Brian Wellington committed