dnssec-signzone.c 38.6 KB
Newer Older
Michael Graff's avatar
Michael Graff committed
1
/*
2 3
 * Portions Copyright (C) 1999, 2000  Internet Software Consortium.
 * Portions Copyright (C) 1995-2000 by Network Associates, Inc.
4
 *
Michael Graff's avatar
Michael Graff committed
5 6 7
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
8
 *
9 10 11 12 13 14 15 16 17
 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM AND
 * NETWORK ASSOCIATES DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
 * SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE CONSORTIUM OR NETWORK
 * ASSOCIATES BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
Michael Graff's avatar
Michael Graff committed
18
 */
Brian Wellington's avatar
Brian Wellington committed
19

20
/* $Id: dnssec-signzone.c,v 1.94 2000/08/14 04:43:15 bwelling Exp $ */
David Lawrence's avatar
David Lawrence committed
21

Brian Wellington's avatar
Brian Wellington committed
22 23 24 25
#include <config.h>

#include <stdlib.h>

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

#include <dns/db.h>
#include <dns/dbiterator.h>
35
#include <dns/dnssec.h>
36
#include <dns/fixedname.h>
37
#include <dns/journal.h>
38 39
#include <dns/keyvalues.h>
#include <dns/log.h>
40 41
#include <dns/master.h>
#include <dns/masterdump.h>
42
#include <dns/nxt.h>
Brian Wellington's avatar
Brian Wellington committed
43 44 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 105 106 107 108
	if (result != ISC_R_SUCCESS) {
		char keystr[KEY_FORMATSIZE];
		key_format(key, keystr, sizeof keystr);
		fatal("key '%s' failed to sign data: %s",
		      keystr, isc_result_totext(result));
	}
109

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

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

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

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

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

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

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

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

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

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

213 214 215 216 217
/*
 * 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.
 */
218
static void
219 220
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)
221
{
222 223
	dns_rdataset_t sigset;
	dns_rdata_t sigrdata;
224
	dns_rdata_sig_t sig;
225
	signer_key_t *key;
226
	isc_result_t result;
227
	isc_boolean_t notsigned = ISC_TRUE, nosigs = ISC_FALSE;
228
	isc_boolean_t wassignedby[256], nowsignedby[256];
229 230
	dns_difftuple_t *tuple;
	dns_ttl_t ttl;
231
	int i;
232 233 234 235 236 237
	char namestr[DNS_NAME_FORMATSIZE];
	char typestr[TYPE_FORMATSIZE];
	char sigstr[SIG_FORMATSIZE];

	dns_name_format(name, namestr, sizeof namestr);
	type_format(set->type, typestr, sizeof typestr);
238

239
	ttl = ISC_MIN(set->ttl, endtime - starttime);
240 241 242 243

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

244
	dns_rdataset_init(&sigset);
245
	result = dns_db_findrdataset(db, node, version, dns_rdatatype_sig,
246
				     set->type, 0, &sigset, NULL);
247 248 249 250
	if (result == ISC_R_NOTFOUND) {
		result = ISC_R_SUCCESS;
		nosigs = ISC_TRUE;
	}
251 252
	if (result != ISC_R_SUCCESS)
		fatal("failed while looking for '%s SIG %s': %s",
253
		      namestr, typestr, isc_result_totext(result));
254

255
	vbprintf(1, "%s/%s:\n", namestr, typestr);
256

Brian Wellington's avatar
Brian Wellington committed
257 258 259
	if (nosigs)
		result = ISC_R_NOMORE;
	else
260
		result = dns_rdataset_first(&sigset);
261

Brian Wellington's avatar
Brian Wellington committed
262 263 264
	while (result == ISC_R_SUCCESS) {
		isc_boolean_t expired, future;
		isc_boolean_t keep = ISC_FALSE, resign = ISC_FALSE;
265

Brian Wellington's avatar
Brian Wellington committed
266
		dns_rdataset_current(&sigset, &sigrdata);
267

Brian Wellington's avatar
Brian Wellington committed
268 269
		result = dns_rdata_tostruct(&sigrdata, &sig, NULL);
		check_result(result, "dns_rdata_tostruct");
270

Brian Wellington's avatar
Brian Wellington committed
271 272
		expired = ISC_TF(now + cycle > sig.timeexpire);
		future = ISC_TF(now < sig.timesigned);
273

Brian Wellington's avatar
Brian Wellington committed
274
		key = keythatsigned(&sig);
275
		sig_format(&sig, sigstr, sizeof sigstr);
Brian Wellington's avatar
Brian Wellington committed
276 277 278 279 280

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

Brian Wellington's avatar
Brian Wellington committed
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
		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];
346
			char keystr[KEY_FORMATSIZE];
347

348 349
			key_format(key->key, keystr, sizeof keystr);
			vbprintf(1, "\tresigning with key %s\n", keystr);
Brian Wellington's avatar
Brian Wellington committed
350 351 352 353 354 355 356 357 358
			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);
359
		}
Brian Wellington's avatar
Brian Wellington committed
360 361 362

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

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

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

388 389
			key_format(key->key, keystr, sizeof keystr);
			vbprintf(1, "\tsigning with key %s\n", keystr);
390 391 392 393 394 395 396 397
			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);
398 399 400 401 402
		}
		key = ISC_LIST_NEXT(key, link);
	}
}

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

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

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

431 432 433 434 435 436 437 438 439 440 441 442
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()");
443 444 445 446 447
	if (isc_buffer_availablelength(&b) == 0) {
		char namestr[DNS_NAME_FORMATSIZE];
		dns_name_format(name, namestr, sizeof namestr);
		fatal("name '%s' is too long", namestr);
	}
448 449 450 451 452 453 454 455 456 457 458
	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);
}

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

473 474 475
	dns_rdataset_init(&newset);
	dns_rdataset_init(&sigset);

476 477 478 479
	opendb("signedkey-", name, dns_db_class(db), &newdb);
	if (newdb == NULL)
		return;

480 481 482
	result = dns_db_findnode(newdb, name, ISC_FALSE, &newnode);
	if (result != ISC_R_SUCCESS)
		goto failure;
483 484
	result = dns_db_findrdataset(newdb, newnode, NULL, dns_rdatatype_key,
				     0, 0, &newset, &sigset);
485 486 487
	if (result != ISC_R_SUCCESS)
		goto failure;

488 489 490 491
	if (!dns_rdataset_isassociated(&newset) ||
	    !dns_rdataset_isassociated(&sigset))
		goto failure;

492 493 494 495 496 497 498
	if (dns_rdataset_count(set) != dns_rdataset_count(&newset))
		goto failure;

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

	result = dns_rdataset_first(set);
499
	check_result(result, "dns_rdataset_first()");
500 501 502
	for (; result == ISC_R_SUCCESS; result = dns_rdataset_next(set)) {
		dns_rdataset_current(set, &rdata);
		result = dns_rdataset_first(&newset);
503
		check_result(result, "dns_rdataset_first()");
504 505 506 507 508 509 510 511 512 513
		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;
514
	}
515
	if (result != ISC_R_NOMORE)
516 517 518 519
		goto failure;

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

520 521 522 523 524 525 526 527 528 529 530
	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);
	}
531 532

 failure:
533 534 535 536
	if (dns_rdataset_isassociated(&newset))
		dns_rdataset_disassociate(&newset);
	if (dns_rdataset_isassociated(&sigset))
		dns_rdataset_disassociate(&sigset);
537 538 539 540 541 542 543
	if (newnode != NULL)
		dns_db_detachnode(newdb, &newnode);
	if (newdb != NULL)
		dns_db_detach(&newdb);
}

/*
544
 * Looks for our signatures of child keys.  If present, inform the caller.
545 546 547 548 549 550 551 552 553 554 555 556
 */
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;

557 558 559
	dns_rdataset_init(&set);
	dns_rdataset_init(&sigset);

560 561 562 563
	opendb("signedkey-", name, dns_db_class(db), &newdb);
	if (newdb == NULL)
		return (ISC_FALSE);

564 565 566
	result = dns_db_findnode(newdb, name, ISC_FALSE, &newnode);
	if (result != ISC_R_SUCCESS)
		goto failure;
567 568
	result = dns_db_findrdataset(newdb, newnode, NULL, dns_rdatatype_key,
				     0, 0, &set, &sigset);
569 570 571 572 573
	if (result != ISC_R_SUCCESS)
		goto failure;

	if (!dns_rdataset_isassociated(&set) ||
	    !dns_rdataset_isassociated(&sigset))
574
		goto failure;
575 576 577 578 579 580

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

596
 failure:
597 598 599 600 601 602 603 604 605 606
	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);
607 608
}

609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632
/*
 * 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
633 634 635 636 637 638 639 640 641
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;
642
	char namestr[DNS_NAME_FORMATSIZE];
Brian Wellington's avatar
Brian Wellington committed
643

644 645
	dns_name_format(name, namestr, sizeof namestr);
	vbprintf(2, "adding null key at %s\n", namestr);
Brian Wellington's avatar
Brian Wellington committed
646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675

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

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

	if (dns_name_iswildcard(name)) {
696 697
		char namestr[DNS_NAME_FORMATSIZE];
		dns_name_format(name, namestr, sizeof namestr);
Brian Wellington's avatar
Brian Wellington committed
698 699
		if (warnwild++ == 0) {
			fprintf(stderr, "%s: warning: BIND 9 doesn't properly "
David Lawrence's avatar
David Lawrence committed
700
				"handle wildcards in secure zones:\n",
701
				program);
Brian Wellington's avatar
Brian Wellington committed
702 703 704 705 706 707
			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",
708
			program, namestr);
709
	}
710

711
	atorigin = dns_name_equal(name, dns_db_origin(db));
712 713 714 715

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

719 720 721 722 723 724 725 726 727 728
		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);
		}
	}
729 730 731 732 733 734 735 736 737

	/*
	 * 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
738
		neednullkey = ISC_TRUE;
739 740 741 742 743

		dns_rdataset_init(&keyset);
		result = dns_db_findrdataset(db, node, version,
					     dns_rdatatype_key, 0, 0, &keyset,
					     NULL);
744 745 746
		if (result == ISC_R_SUCCESS && childkey) {
			char namestr[DNS_NAME_FORMATSIZE];
			dns_name_format(name, namestr, sizeof namestr);
747
			fatal("%s has both a signedkey file and KEY "
748 749
			      "records in the zone.  Aborting.", namestr);
		}
750 751 752 753 754
		else if (result == ISC_R_SUCCESS) {
			if (hasnullkey(&keyset))
				neednullkey = ISC_FALSE;
			dns_rdataset_disassociate(&keyset);
		} else if (childkey) {
755 756 757
			char namestr[DNS_NAME_FORMATSIZE];
			dns_name_format(name, namestr, sizeof namestr);
			vbprintf(2, "child key for %s found\n", namestr);
758 759 760
			neednullkey = ISC_FALSE;
		}

Brian Wellington's avatar
Brian Wellington committed
761 762
		if (neednullkey)
			createnullkey(db, version, name);
763 764 765 766 767 768
	}

	/*
	 * Now iterate through the rdatasets.
	 */
	dns_diff_init(mctx, &diff);
Brian Wellington's avatar
Brian Wellington committed
769 770 771 772 773 774 775
	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
776

777 778 779
		/* If this is a SIG set, skip it. */
		if (rdataset.type == dns_rdatatype_sig)
			goto skip;
Brian Wellington's avatar
Brian Wellington committed
780

781 782 783
		/*
		 * If this is a KEY set at the apex, look for a signedkey file.
		 */
Brian Wellington's avatar
Brian Wellington committed
784
		if (atorigin && rdataset.type == dns_rdatatype_key) {
785
			importparentsig(db, &diff, name, &rdataset);
786
			goto skip;
787
		}
788 789 790

		/*
		 * If this name is a delegation point, skip all records
791
		 * except an NXT set a KEY set containing a null key.
792 793
		 */
		if (isdelegation) {
Brian Wellington's avatar
Brian Wellington committed
794 795 796
			if (!(rdataset.type == dns_rdatatype_nxt ||
			      (rdataset.type == dns_rdatatype_key &&
			       hasnullkey(&rdataset))))
797
				goto skip;
798 799
		}

800
		if (rdataset.type == dns_rdatatype_nxt) {
801 802 803
			nxt_setbit(&rdataset, dns_rdatatype_sig);
			if (neednullkey)
				nxt_setbit(&rdataset, dns_rdatatype_key);
Brian Wellington's avatar
Brian Wellington committed
804
		}
Brian Wellington's avatar
Brian Wellington committed
805

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

808
 skip:
Brian Wellington's avatar
Brian Wellington committed
809 810 811
		dns_rdataset_disassociate(&rdataset);
		result = dns_rdatasetiter_next(rdsiter);
	}
812 813 814
	if (result != ISC_R_NOMORE) {
		char namestr[DNS_NAME_FORMATSIZE];
		dns_name_format(name, namestr, sizeof namestr);
815
		fatal("rdataset iteration for name '%s' failed: %s",
816 817
		      namestr, isc_result_totext(result));
	}
Brian Wellington's avatar
Brian Wellington committed
818
	dns_rdatasetiter_destroy(&rdsiter);
819 820

	result = dns_diff_apply(&diff, db, version);
821 822 823 824 825
	if (result != ISC_R_SUCCESS) {
		char namestr[DNS_NAME_FORMATSIZE];
		dns_name_format(name, namestr, sizeof namestr);
		fatal("failed to add SIGs at node %s", namestr);
	}
826
	dns_diff_clear(&diff);
Brian Wellington's avatar
Brian Wellington committed
827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848
}

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
849
			result = ISC_R_NOMORE;
Brian Wellington's avatar
Brian Wellington committed
850
	}
851
	if (result != ISC_R_NOMORE)
852 853
		fatal("rdataset iteration failed: %s",
		      isc_result_totext(result));
Brian Wellington's avatar
Brian Wellington committed
854 855 856 857 858 859 860
	dns_rdatasetiter_destroy(&rdsiter);

	if (!active) {
		/*
		 * Make sure there is no NXT record for this node.
		 */
		result = dns_db_deleterdataset(db, node, version,
861
					       dns_rdatatype_nxt, 0);
Brian Wellington's avatar
Brian Wellington committed
862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891
		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);
}

892 893
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
894 895
	    dns_name_t *name, dns_dbnode_t **nodep, dns_name_t *origin,
	    dns_name_t *lastcut)
896
{
897
	isc_result_t result, dresult;
898 899 900 901

	do {
		result = next_active(db, version, dbiter, name, nodep);
		if (result == ISC_R_SUCCESS) {
Brian Wellington's avatar
Brian Wellington committed
902 903 904
			if (dns_name_issubdomain(name, origin) &&
			    (lastcut == NULL ||
			     !dns_name_issubdomain(name, lastcut)))
905
				return (ISC_R_SUCCESS);
906 907 908 909 910
			dresult = dns_master_dumpnodetostream(mctx, db,
							      version,
							      *nodep, name,
							      masterstyle, fp);
			check_result(dresult, "dns_master_dumpnodetostream");
911 912 913 914 915 916 917
			dns_db_detachnode(db, nodep);
			result = dns_dbiterator_next(dbiter);
		}
	} while (result == ISC_R_SUCCESS);
	return (result);
}

918
/*
Brian Wellington's avatar