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

20
/* $Id: dnssec-signzone.c,v 1.91 2000/08/11 19:39:10 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 38
#include <dns/keyvalues.h>
#include <dns/log.h>
39 40
#include <dns/master.h>
#include <dns/masterdump.h>
41
#include <dns/nxt.h>
Brian Wellington's avatar
Brian Wellington committed
42 43 44 45
#include <dns/rdata.h>
#include <dns/rdatalist.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 70 71 72 73 74 75
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;
};

76 77 78
static ISC_LIST(signer_key_t) keylist;
static isc_stdtime_t starttime = 0, endtime = 0, now;
static int cycle = -1;
79
static isc_boolean_t tryverify = ISC_FALSE;
Brian Wellington's avatar
Brian Wellington committed
80
static isc_mem_t *mctx = NULL;
Brian Wellington's avatar
Brian Wellington committed
81
static isc_entropy_t *ectx = NULL;
82
static dns_ttl_t zonettl;
83 84
static FILE *fp;
static const dns_master_style_t *masterstyle = &dns_master_style_explicitttl;
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
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
102 103 104
{
	isc_result_t result;

105
	dns_rdata_init(rdata);
106
	result = dns_dnssec_sign(name, rdataset, key, &starttime, &endtime,
107
				 mctx, b, rdata);
108
	isc_entropy_stopcallbacksources(ectx);
109 110
	if (result != ISC_R_SUCCESS)
		fatal("key '%s/%s/%d' failed to sign data: %s",
111
		      nametostr(dst_key_name(key)), algtostr(dst_key_alg(key)),
112
		      dst_key_id(key), isc_result_totext(result));
113

114
	if (tryverify) {
115 116
		result = dns_dnssec_verify(name, rdataset, key,
					   ISC_TRUE, mctx, rdata);
117 118 119 120
		if (result == ISC_R_SUCCESS)
			vbprintf(3, "\tsignature verified\n");
		else
			vbprintf(3, "\tsignature failed to verify\n");
121
	}
122
}
Brian Wellington's avatar
Brian Wellington committed
123

124 125 126
static inline isc_boolean_t
issigningkey(signer_key_t *key) {
	return (key->isdefault);
Brian Wellington's avatar
Brian Wellington committed
127 128
}

129 130
static inline isc_boolean_t
iszonekey(signer_key_t *key, dns_db_t *db) {
131 132
	return (ISC_TF(dns_name_equal(dst_key_name(key->key),
				      dns_db_origin(db)) &&
133
		       dst_key_iszonekey(key->key)));
134 135
}

136 137 138 139
/*
 * 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.
 */
140
static signer_key_t *
141
keythatsigned(dns_rdata_sig_t *sig) {
142
	isc_result_t result;
143 144 145 146 147 148 149
	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) &&
150
		    dns_name_equal(&sig->signer, dst_key_name(key->key)))
151
			return key;
152
		key = ISC_LIST_NEXT(key, link);
153
	}
154

155
	result = dst_key_fromfile(&sig->signer, sig->keyid, sig->algorithm,
Brian Wellington's avatar
Brian Wellington committed
156
				  DST_TYPE_PUBLIC, NULL, mctx, &pubkey);
157 158
	if (result != ISC_R_SUCCESS)
		return (NULL);
159

160 161
	key = isc_mem_get(mctx, sizeof(signer_key_t));
	if (key == NULL)
162
		fatal("out of memory");
163

164
	result = dst_key_fromfile(&sig->signer, sig->keyid, sig->algorithm,
Brian Wellington's avatar
Brian Wellington committed
165
				  DST_TYPE_PRIVATE, NULL, mctx, &privkey);
166 167
	if (result == ISC_R_SUCCESS) {
		key->key = privkey;
168
		dst_key_free(&pubkey);
169 170 171 172 173 174
	}
	else
		key->key = pubkey;
	key->isdefault = ISC_FALSE;
	ISC_LIST_APPEND(keylist, key, link);
	return key;
175 176
}

177 178 179 180 181
/*
 * 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.
 */
182 183 184
static isc_boolean_t
expecttofindkey(dns_name_t *name, dns_db_t *db, dns_dbversion_t *version) {
	unsigned int options = DNS_DBFIND_NOWILD;
185
	dns_fixedname_t fname;
186 187
	isc_result_t result;

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

207
static inline isc_boolean_t
208 209
setverifies(dns_name_t *name, dns_rdataset_t *set, signer_key_t *key,
	    dns_rdata_t *sig)
210
{
211 212
	isc_result_t result;
	result = dns_dnssec_verify(name, set, key->key, ISC_FALSE, mctx, sig);
213
	return (ISC_TF(result == ISC_R_SUCCESS));
214 215 216 217 218 219 220 221
}

#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) \
222
		fatal("out of memory"); \
223
	isc_buffer_init(&b, tdata->array, sizeof(tdata->array));
224

225 226 227 228 229
/*
 * 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.
 */
230 231 232 233 234 235 236 237
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;
238
	dns_rdata_sig_t sig;
239
	signer_key_t *key;
240
	isc_result_t result;
241
	isc_boolean_t notsigned = ISC_TRUE, nosigs = ISC_FALSE;
242 243 244
	isc_boolean_t wassignedby[256], nowsignedby[256];
	signer_array_t *tdata;
	ISC_LIST(signer_array_t) arraylist;
245
	int i;
246 247 248 249 250 251 252 253 254 255

	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);
256 257 258 259
	if (result == ISC_R_NOTFOUND) {
		result = ISC_R_SUCCESS;
		nosigs = ISC_TRUE;
	}
260 261 262 263
	if (result != ISC_R_SUCCESS)
		fatal("failed while looking for '%s SIG %s': %s",
		      nametostr(name), typetostr(set->type),
		      isc_result_totext(result));
264

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

267
	if (!nosigs) {
268 269
		result = dns_rdataset_first(&oldsigset);
		while (result == ISC_R_SUCCESS) {
270
			isc_boolean_t expired, future;
271 272 273 274 275 276 277
			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");

278 279
			expired = ISC_TF(now + cycle > sig.timeexpire);
			future = ISC_TF(now < sig.timesigned);
280 281 282

			key = keythatsigned(&sig);

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

			if (keep) {
				allocbufferandrdata;
				result = dns_rdata_fromstruct(trdata,
							      set->rdclass,
381
							     dns_rdatatype_sig,
382 383 384 385 386 387
							      &sig, &b);
				nowsignedby[sig.algorithm] = ISC_TRUE;
				ISC_LIST_APPEND(siglist.rdata, trdata, link);
			}
			else if (resign) {
				allocbufferandrdata;
388
				vbprintf(1, "\tresigning with key %s/%s/%d\n",
389
				       nametostr(dst_key_name(key->key)),
390 391
				       algtostr(dst_key_alg(key->key)),
				       dst_key_id(key->key));
392 393 394 395 396 397 398
				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);
399
		}
400
		if (result == ISC_R_NOMORE)
401 402 403
			result = ISC_R_SUCCESS;
		check_result(result, "dns_db_dns_rdataset_first()/next()");
		dns_rdataset_disassociate(&oldsigset);
Brian Wellington's avatar
Brian Wellington committed
404
	}
405 406 407 408 409

	for (i = 0; i < 256; i++)
		if (wassignedby[i] != 0) {
			notsigned = ISC_FALSE;
			break;
Brian Wellington's avatar
Brian Wellington committed
410
		}
411 412 413

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

429 430 431 432
	if (!ISC_LIST_EMPTY(siglist.rdata)) {
		siglist.rdclass = set->rdclass;
		siglist.type = dns_rdatatype_sig;
		siglist.covers = set->type;
433 434
		if (endtime - starttime < set->ttl)
			siglist.ttl = endtime - starttime;
435 436 437 438 439 440
		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,
441
					    0, NULL);
442 443 444 445 446 447
		if (result == DNS_R_UNCHANGED)
			result = ISC_R_SUCCESS;
		check_result(result, "dns_db_addrdataset");
		dns_rdataset_disassociate(&sigset);
	}
	else if (!nosigs) {
448 449 450 451 452 453
#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
454 455 456 457 458
		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");
459
#endif
460 461
		fatal("File is currently signed but no private keys were "
		      "found.  This won't work.");
462
	}
463 464 465 466 467 468

	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;
469
	}
Brian Wellington's avatar
Brian Wellington committed
470

471 472 473 474 475 476 477 478
	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;
	}
}

479
/* Determine if a KEY set contains a null key */
480
static isc_boolean_t
481
hasnullkey(dns_rdataset_t *rdataset) {
482 483
	isc_result_t result;
	dns_rdata_t rdata;
484
	isc_boolean_t found = ISC_FALSE;
485

486
	result = dns_rdataset_first(rdataset);
487 488 489
	while (result == ISC_R_SUCCESS) {
		dst_key_t *key = NULL;

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

/*
 * 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;
	dns_db_t *newdb = NULL;
	dns_dbnode_t *newnode = NULL;
	dns_rdataset_t newset, sigset;
	dns_rdata_t rdata, newrdata;
	isc_result_t result;

523 524 525
	dns_rdataset_init(&newset);
	dns_rdataset_init(&sigset);

526 527
	isc_buffer_init(&b, filename, sizeof(filename));
	isc_buffer_putstr(&b, "signedkey-");
528 529
	result = dns_name_totext(name, ISC_FALSE, &b);
	check_result(result, "dns_name_totext()");
530 531
	if (isc_buffer_availablelength(&b) == 0)
		fatal("name '%s' is too long", nametostr(name));
532
	isc_buffer_putuint8(&b, 0);
533 534
	result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone,
			       dns_db_class(db), 0, NULL, &newdb);
535
	check_result(result, "dns_db_create()");
536
	result = dns_db_load(newdb, (char *)filename);
537 538 539 540 541
	if (result != ISC_R_SUCCESS)
		goto failure;
	result = dns_db_findnode(newdb, name, ISC_FALSE, &newnode);
	if (result != ISC_R_SUCCESS)
		goto failure;
542 543
	result = dns_db_findrdataset(newdb, newnode, NULL, dns_rdatatype_key,
				     0, 0, &newset, &sigset);
544 545 546 547 548 549 550 551 552 553
	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);
554
	check_result(result, "dns_rdataset_first()");
555 556 557
	for (; result == ISC_R_SUCCESS; result = dns_rdataset_next(set)) {
		dns_rdataset_current(set, &rdata);
		result = dns_rdataset_first(&newset);
558
		check_result(result, "dns_rdataset_first()");
559 560 561 562 563 564 565 566 567 568
		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;
569
	}
570
	if (result != ISC_R_NOMORE)
571 572 573 574 575 576 577 578
		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");

 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 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605
	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;
	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;

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

609 610
	isc_buffer_init(&b, filename, sizeof(filename));
	isc_buffer_putstr(&b, "signedkey-");
611 612
	result = dns_name_totext(name, ISC_FALSE, &b);
	check_result(result, "dns_name_totext()");
613 614
	if (isc_buffer_availablelength(&b) == 0)
		fatal("name '%s' is too long", nametostr(name));
615
	isc_buffer_putuint8(&b, 0); /* Add a NUL. */
616 617
	result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone,
			      dns_db_class(db), 0, NULL, &newdb);
618
	check_result(result, "dns_db_create()");
619
	result = dns_db_load(newdb, (char *)filename);
620 621 622 623 624
	if (result != ISC_R_SUCCESS)
		goto failure;
	result = dns_db_findnode(newdb, name, ISC_FALSE, &newnode);
	if (result != ISC_R_SUCCESS)
		goto failure;
625 626
	result = dns_db_findrdataset(newdb, newnode, NULL, dns_rdatatype_key,
				     0, 0, &set, &sigset);
627 628 629 630 631
	if (result != ISC_R_SUCCESS)
		goto failure;

	if (!dns_rdataset_isassociated(&set) ||
	    !dns_rdataset_isassociated(&sigset))
632
		goto failure;
633 634 635 636 637 638 639 640

	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)
641
			goto failure;
642 643 644
		key = keythatsigned(&sig);
		dns_rdata_freestruct(&sig);
		if (key == NULL)
645
			goto failure;
646 647 648 649 650 651 652 653
		result = dns_dnssec_verify(name, &set, key->key,
					   ISC_FALSE, mctx, &sigrdata);
		if (result == ISC_R_SUCCESS) {
			found = ISC_TRUE;
			break;
		}
	}

654
 failure:
655 656 657 658 659 660 661 662 663 664
	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);
665 666
}

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

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

702 703 704 705 706 707 708 709 710 711
		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
712 713 714 715 716 717 718
	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
719

720 721 722
		/* If this is a SIG set, skip it. */
		if (rdataset.type == dns_rdatatype_sig)
			goto skip;
Brian Wellington's avatar
Brian Wellington committed
723

724 725 726 727 728
		/*
		 * 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);
729
			goto skip;
730
		}
731 732 733

		/*
		 * If this name is a delegation point, skip all records
734 735 736
		 * 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.
737 738 739
		 */
		if (isdelegation) {
			switch (rdataset.type) {
740 741 742 743 744
			case dns_rdatatype_nxt:
				childkey = haschildkey(db, name);
				break;
			case dns_rdatatype_key:
				if (hasnullkey(&rdataset))
745
					break;
746 747 748
				goto skip;
			default:
				goto skip;
749
			}
750

751 752
		}

753
		/*
754 755 756
		 * 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
757
		 * SIG < NXT and KEY < NXT, the easy way works.
758
		 */
759
		if (rdataset.type == dns_rdatatype_nxt) {
Brian Wellington's avatar
Brian Wellington committed
760 761 762
			unsigned char *nxt_bits;
			dns_name_t nxtname;
			isc_region_t r, r2;
763 764 765
			unsigned char keydata[4];
			dst_key_t *dstkey;
			isc_buffer_t b;
Brian Wellington's avatar
Brian Wellington committed
766 767 768 769 770 771 772 773 774 775

			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);
776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791
			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

				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;
805

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
				isc_buffer_usedregion(&b, &r);
				dns_rdata_fromregion(&keyrdata,
						     rdataset.rdclass,
						     dns_rdatatype_key, &r);
821

822 823 824 825 826 827
				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
				result = dns_rdatalist_tordataset(&keyrdatalist,
								  &keyset);
830 831 832 833 834 835 836 837
				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);
 alreadyhavenullkey:
838
				dns_rdataset_disassociate(&keyset);
839 840 841 842
			} else if (isdelegation) {
				vbprintf(2, "child key for %s found\n",
					 nametostr(name));

843
			}
Brian Wellington's avatar
Brian Wellington committed
844
		}
Brian Wellington's avatar
Brian Wellington committed
845

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

848
 skip:
Brian Wellington's avatar
Brian Wellington committed
849 850 851
		dns_rdataset_disassociate(&rdataset);
		result = dns_rdatasetiter_next(rdsiter);
	}
852
	if (result != ISC_R_NOMORE)
853 854
		fatal("rdataset iteration for name '%s' failed: %s",
		      nametostr(name), isc_result_totext(result));
Brian Wellington's avatar
Brian Wellington committed
855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877
	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
878
			result = ISC_R_NOMORE;
Brian Wellington's avatar
Brian Wellington committed
879
	}
880
	if (result != ISC_R_NOMORE)
881 882
		fatal("rdataset iteration failed: %s",
		      isc_result_totext(result));
Brian Wellington's avatar
Brian Wellington committed
883 884 885 886 887 888 889
	dns_rdatasetiter_destroy(&rdsiter);

	if (!active) {
		/*
		 * Make sure there is no NXT record for this node.
		 */
		result = dns_db_deleterdataset(db, node, version,
890
					       dns_rdatatype_nxt, 0);
Brian Wellington's avatar
Brian Wellington committed
891 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
		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);
}

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

	do {
		result = next_active(db, version, dbiter, name, nodep);
		if (result == ISC_R_SUCCESS) {