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

Brian Wellington's avatar
Brian Wellington committed
20
/* $Id: dnssec-signzone.c,v 1.117 2000/12/07 23:00:59 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/app.h>
27
#include <isc/commandline.h>
Brian Wellington's avatar
Brian Wellington committed
28
#include <isc/entropy.h>
29
#include <isc/event.h>
Brian Wellington's avatar
Brian Wellington committed
30
#include <isc/mem.h>
31 32
#include <isc/mutex.h>
#include <isc/os.h>
33
#include <isc/stdio.h>
34
#include <isc/string.h>
35
#include <isc/task.h>
Bob Halley's avatar
Bob Halley committed
36
#include <isc/util.h>
Brian Wellington's avatar
Brian Wellington committed
37 38 39

#include <dns/db.h>
#include <dns/dbiterator.h>
40
#include <dns/dnssec.h>
41
#include <dns/fixedname.h>
42
#include <dns/journal.h>
43 44
#include <dns/keyvalues.h>
#include <dns/log.h>
45 46
#include <dns/master.h>
#include <dns/masterdump.h>
47
#include <dns/nxt.h>
Brian Wellington's avatar
Brian Wellington committed
48 49
#include <dns/rdata.h>
#include <dns/rdataset.h>
50
#include <dns/rdataclass.h>
Brian Wellington's avatar
Brian Wellington committed
51
#include <dns/rdatasetiter.h>
52
#include <dns/rdatastruct.h>
53
#include <dns/rdatatype.h>
Brian Wellington's avatar
Brian Wellington committed
54
#include <dns/result.h>
55
#include <dns/secalg.h>
56
#include <dns/time.h>
Brian Wellington's avatar
Brian Wellington committed
57

58
#include <dst/dst.h>
59
#include <dst/result.h>
Brian Wellington's avatar
Brian Wellington committed
60

61 62
#include "dnssectool.h"

David Lawrence's avatar
David Lawrence committed
63
const char *program = "dnssec-signzone";
64
int verbose;
65

66
#define BUFSIZE 2048
Brian Wellington's avatar
Brian Wellington committed
67

68 69 70 71 72
typedef struct signer_key_struct signer_key_t;

struct signer_key_struct {
	dst_key_t *key;
	isc_boolean_t isdefault;
73
	unsigned int position;
74 75 76
	ISC_LINK(signer_key_t) link;
};

77 78 79 80 81 82 83 84 85 86 87 88
#define SIGNER_EVENTCLASS	ISC_EVENTCLASS(0x4453)
#define SIGNER_EVENT_WRITE	(SIGNER_EVENTCLASS + 0)
#define SIGNER_EVENT_WORK	(SIGNER_EVENTCLASS + 1)

typedef struct signer_event sevent_t;
struct signer_event {
	ISC_EVENT_COMMON(sevent_t);
	dns_fixedname_t *fname;
	dns_fixedname_t *fnextname;
	dns_dbnode_t *node;
};

89
static ISC_LIST(signer_key_t) keylist;
90
static unsigned int keycount = 0;
91 92
static isc_stdtime_t starttime = 0, endtime = 0, now;
static int cycle = -1;
93
static isc_boolean_t tryverify = ISC_FALSE;
94
static isc_boolean_t printstats = ISC_FALSE;
Brian Wellington's avatar
Brian Wellington committed
95
static isc_mem_t *mctx = NULL;
Brian Wellington's avatar
Brian Wellington committed
96
static isc_entropy_t *ectx = NULL;
97
static dns_ttl_t zonettl;
98 99
static FILE *fp;
static const dns_master_style_t *masterstyle = &dns_master_style_explicitttl;
100 101
static unsigned int nsigned = 0, nretained = 0, ndropped = 0;
static unsigned int nverified = 0, nverifyfailed = 0;
102
static const char *directory;
103 104 105 106 107 108 109 110 111 112 113 114
static isc_mutex_t namelock, statslock;
static isc_taskmgr_t *taskmgr = NULL;
static dns_db_t *gdb;			/* The database */
static dns_dbversion_t *gversion;	/* The database version */
static dns_dbiterator_t *gdbiter;	/* The database iterator */
static dns_name_t *gorigin;		/* The database origin */
static dns_dbnode_t *gnode = NULL;	/* The "current" database node */
static dns_name_t *lastzonecut;
static isc_task_t *master = NULL;
static unsigned int ntasks = 0;
static isc_boolean_t shuttingdown = ISC_FALSE, finished = ISC_FALSE;
static unsigned int assigned = 0, completed = 0;
115
static isc_boolean_t nokeys = ISC_FALSE;
116 117 118 119 120 121 122 123 124 125 126

#define INCSTAT(counter)		\
	if (printstats) {		\
		LOCK(&statslock);	\
		counter++;		\
		UNLOCK(&statslock);	\
	}

static void
sign(isc_task_t *task, isc_event_t *event);

Brian Wellington's avatar
Brian Wellington committed
127

128
static inline void
Brian Wellington's avatar
Brian Wellington committed
129
set_bit(unsigned char *array, unsigned int index, unsigned int bit) {
130
	unsigned int shift, mask;
131

Brian Wellington's avatar
Brian Wellington committed
132 133 134
	shift = 7 - (index % 8);
	mask = 1 << shift;

135
	if (bit != 0)
Brian Wellington's avatar
Brian Wellington committed
136 137 138 139 140
		array[index / 8] |= mask;
	else
		array[index / 8] &= (~mask & 0xFF);
}

141 142 143 144 145 146 147 148 149 150 151 152 153 154
static signer_key_t *
newkeystruct(dst_key_t *dstkey, isc_boolean_t isdefault) {
	signer_key_t *key;

	key = isc_mem_get(mctx, sizeof(signer_key_t));
	if (key == NULL)
		fatal("out of memory");
	key->key = dstkey;
	key->isdefault = isdefault;
	key->position = keycount++;
	ISC_LINK_INIT(key, link);
	return (key);
}

155 156 157
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
158 159 160
{
	isc_result_t result;

161
	result = dns_dnssec_sign(name, rdataset, key, &starttime, &endtime,
162
				 mctx, b, rdata);
163
	isc_entropy_stopcallbacksources(ectx);
164 165 166 167 168 169
	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));
	}
170
	INCSTAT(nsigned);
171

172
	if (tryverify) {
173 174
		result = dns_dnssec_verify(name, rdataset, key,
					   ISC_TRUE, mctx, rdata);
175
		if (result == ISC_R_SUCCESS) {
176
			vbprintf(3, "\tsignature verified\n");
177
			INCSTAT(nverified);
178
		} else {
179
			vbprintf(3, "\tsignature failed to verify\n");
180
			INCSTAT(nverifyfailed);
181
		}
182
	}
183
}
Brian Wellington's avatar
Brian Wellington committed
184

185 186 187
static inline isc_boolean_t
issigningkey(signer_key_t *key) {
	return (key->isdefault);
Brian Wellington's avatar
Brian Wellington committed
188 189
}

190
static inline isc_boolean_t
191 192
iszonekey(signer_key_t *key) {
	return (ISC_TF(dns_name_equal(dst_key_name(key->key), gorigin) &&
193
		       dst_key_iszonekey(key->key)));
194 195
}

196 197 198 199
/*
 * 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.
 */
200
static signer_key_t *
201
keythatsigned(dns_rdata_sig_t *sig) {
202
	isc_result_t result;
203 204 205 206 207 208 209
	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) &&
210
		    dns_name_equal(&sig->signer, dst_key_name(key->key)))
211
			return key;
212
		key = ISC_LIST_NEXT(key, link);
213
	}
214

215
	result = dst_key_fromfile(&sig->signer, sig->keyid, sig->algorithm,
Brian Wellington's avatar
Brian Wellington committed
216
				  DST_TYPE_PUBLIC, NULL, mctx, &pubkey);
217 218
	if (result != ISC_R_SUCCESS)
		return (NULL);
219

220
	result = dst_key_fromfile(&sig->signer, sig->keyid, sig->algorithm,
Brian Wellington's avatar
Brian Wellington committed
221
				  DST_TYPE_PRIVATE, NULL, mctx, &privkey);
222
	if (result == ISC_R_SUCCESS) {
223
		dst_key_free(&pubkey);
224 225 226 227 228
		key = newkeystruct(privkey, ISC_FALSE);
	} else
		key = newkeystruct(pubkey, ISC_FALSE);
	ISC_LIST_APPEND(keylist, key, link);
	return (key);
229 230
}

231 232 233 234 235
/*
 * 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.
 */
236
static isc_boolean_t
237
expecttofindkey(dns_name_t *name) {
238
	unsigned int options = DNS_DBFIND_NOWILD;
239
	dns_fixedname_t fname;
240
	isc_result_t result;
241
	char namestr[DNS_NAME_FORMATSIZE];
242

243
	dns_fixedname_init(&fname);
244
	result = dns_db_find(gdb, name, gversion, dns_rdatatype_key, options,
245
			     0, NULL, dns_fixedname_name(&fname), NULL, NULL);
246
	switch (result) {
247 248 249 250 251 252 253 254
	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);
255
	}
256 257 258 259
	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 */
260 261
}

262
static inline isc_boolean_t
263 264
setverifies(dns_name_t *name, dns_rdataset_t *set, signer_key_t *key,
	    dns_rdata_t *sig)
265
{
266 267
	isc_result_t result;
	result = dns_dnssec_verify(name, set, key->key, ISC_FALSE, mctx, sig);
268
	if (result == ISC_R_SUCCESS) {
269
		INCSTAT(nverified);
270 271
		return (ISC_TRUE);
	} else {
272
		INCSTAT(nverifyfailed);
273 274
		return (ISC_FALSE);
	}
275 276
}

277 278 279 280 281
/*
 * 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.
 */
282
static void
283 284
signset(dns_diff_t *diff, dns_dbnode_t *node, dns_name_t *name,
	dns_rdataset_t *set)
285
{
286
	dns_rdataset_t sigset;
287
	dns_rdata_t sigrdata = DNS_RDATA_INIT;
288
	dns_rdata_sig_t sig;
289
	signer_key_t *key;
290
	isc_result_t result;
291 292 293
	isc_boolean_t nosigs = ISC_FALSE;
	isc_boolean_t *wassignedby, *nowsignedby;
	int arraysize;
294 295
	dns_difftuple_t *tuple;
	dns_ttl_t ttl;
296
	int i;
297 298 299 300 301 302
	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);
303

304
	ttl = ISC_MIN(set->ttl, endtime - starttime);
305

306
	dns_rdataset_init(&sigset);
307
	result = dns_db_findrdataset(gdb, node, gversion, dns_rdatatype_sig,
308
				     set->type, 0, &sigset, NULL);
309 310 311 312
	if (result == ISC_R_NOTFOUND) {
		result = ISC_R_SUCCESS;
		nosigs = ISC_TRUE;
	}
313 314
	if (result != ISC_R_SUCCESS)
		fatal("failed while looking for '%s SIG %s': %s",
315
		      namestr, typestr, isc_result_totext(result));
316

317
	vbprintf(1, "%s/%s:\n", namestr, typestr);
318

319 320 321
	arraysize = keycount;
	if (!nosigs)
		arraysize += dns_rdataset_count(&sigset);
322 323
	wassignedby = isc_mem_get(mctx, arraysize * sizeof(isc_boolean_t));
	nowsignedby = isc_mem_get(mctx, arraysize * sizeof(isc_boolean_t));
324 325 326 327 328 329
	if (wassignedby == NULL || nowsignedby == NULL)
		fatal("out of memory");

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

Brian Wellington's avatar
Brian Wellington committed
330 331 332
	if (nosigs)
		result = ISC_R_NOMORE;
	else
333
		result = dns_rdataset_first(&sigset);
334

Brian Wellington's avatar
Brian Wellington committed
335 336 337
	while (result == ISC_R_SUCCESS) {
		isc_boolean_t expired, future;
		isc_boolean_t keep = ISC_FALSE, resign = ISC_FALSE;
338

Brian Wellington's avatar
Brian Wellington committed
339
		dns_rdataset_current(&sigset, &sigrdata);
340

Brian Wellington's avatar
Brian Wellington committed
341 342
		result = dns_rdata_tostruct(&sigrdata, &sig, NULL);
		check_result(result, "dns_rdata_tostruct");
343

Brian Wellington's avatar
Brian Wellington committed
344 345
		expired = ISC_TF(now + cycle > sig.timeexpire);
		future = ISC_TF(now < sig.timesigned);
346

Brian Wellington's avatar
Brian Wellington committed
347
		key = keythatsigned(&sig);
348
		sig_format(&sig, sigstr, sizeof sigstr);
Brian Wellington's avatar
Brian Wellington committed
349 350 351 352 353

		if (sig.timesigned > sig.timeexpire) {
			/* sig is dropped and not replaced */
			vbprintf(2, "\tsig by %s dropped - "
				 "invalid validity period\n",
354
				 sigstr);
Brian Wellington's avatar
Brian Wellington committed
355
		} else if (key == NULL && !future &&
356
			 expecttofindkey(&sig.signer))
Brian Wellington's avatar
Brian Wellington committed
357 358 359 360
		{
			/* sig is dropped and not replaced */
			vbprintf(2, "\tsig by %s dropped - "
				 "private key not found\n",
361
				 sigstr);
Brian Wellington's avatar
Brian Wellington committed
362 363
		} else if (key == NULL || future) {
			vbprintf(2, "\tsig by %s %s - key not found\n",
364
				 expired ? "retained" : "dropped", sigstr);
Brian Wellington's avatar
Brian Wellington committed
365 366 367 368
			if (!expired)
				keep = ISC_TRUE;
		} else if (issigningkey(key)) {
			if (!expired && setverifies(name, set, key, &sigrdata))
369
			{
370
				vbprintf(2, "\tsig by %s retained\n", sigstr);
371
				keep = ISC_TRUE;
372 373
				wassignedby[key->position] = ISC_TRUE;
				nowsignedby[key->position] = ISC_TRUE;
374
			} else {
Brian Wellington's avatar
Brian Wellington committed
375
				vbprintf(2, "\tsig by %s dropped - %s\n",
376
					 sigstr,
Brian Wellington's avatar
Brian Wellington committed
377 378
					 expired ? "expired" :
						   "failed to verify");
379
				wassignedby[key->position] = ISC_TRUE;
Brian Wellington's avatar
Brian Wellington committed
380
				resign = ISC_TRUE;
381
			}
382
		} else if (iszonekey(key)) {
Brian Wellington's avatar
Brian Wellington committed
383 384
			if (!expired && setverifies(name, set, key, &sigrdata))
			{
385
				vbprintf(2, "\tsig by %s retained\n", sigstr);
Brian Wellington's avatar
Brian Wellington committed
386
				keep = ISC_TRUE;
387 388
				wassignedby[key->position] = ISC_TRUE;
				nowsignedby[key->position] = ISC_TRUE;
Brian Wellington's avatar
Brian Wellington committed
389 390
			} else {
				vbprintf(2, "\tsig by %s dropped - %s\n",
391
					 sigstr,
Brian Wellington's avatar
Brian Wellington committed
392 393
					 expired ? "expired" :
						   "failed to verify");
394
				wassignedby[key->position] = ISC_TRUE;
395
			}
Brian Wellington's avatar
Brian Wellington committed
396
		} else if (!expired) {
397
			vbprintf(2, "\tsig by %s retained\n", sigstr);
Brian Wellington's avatar
Brian Wellington committed
398 399
			keep = ISC_TRUE;
		} else {
400
			vbprintf(2, "\tsig by %s expired\n", sigstr);
Brian Wellington's avatar
Brian Wellington committed
401
		}
402

403
		if (keep) {
404
			nowsignedby[key->position] = ISC_TRUE;
405
			INCSTAT(nretained);
406
		} else {
Brian Wellington's avatar
Brian Wellington committed
407 408 409 410 411 412
			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);
413
			INCSTAT(ndropped);
Brian Wellington's avatar
Brian Wellington committed
414 415 416 417
		}

		if (resign) {
			isc_buffer_t b;
418
			dns_rdata_t trdata = DNS_RDATA_INIT;
Brian Wellington's avatar
Brian Wellington committed
419
			unsigned char array[BUFSIZE];
420
			char keystr[KEY_FORMATSIZE];
421

422 423
			key_format(key->key, keystr, sizeof keystr);
			vbprintf(1, "\tresigning with key %s\n", keystr);
Brian Wellington's avatar
Brian Wellington committed
424 425
			isc_buffer_init(&b, array, sizeof(array));
			signwithkey(name, set, &trdata, key->key, &b);
426
			nowsignedby[key->position] = ISC_TRUE;
Brian Wellington's avatar
Brian Wellington committed
427 428 429 430 431 432
			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);
433
		}
Brian Wellington's avatar
Brian Wellington committed
434

435
		dns_rdata_reset(&sigrdata);
Brian Wellington's avatar
Brian Wellington committed
436 437
		dns_rdata_freestruct(&sig);
		result = dns_rdataset_next(&sigset);
Brian Wellington's avatar
Brian Wellington committed
438
	}
Brian Wellington's avatar
Brian Wellington committed
439 440 441 442 443 444
	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);
445 446 447

	key = ISC_LIST_HEAD(keylist);
	while (key != NULL) {
448
		if (key->isdefault && !nowsignedby[key->position]) {
449
			isc_buffer_t b;
450
			dns_rdata_t trdata = DNS_RDATA_INIT;
451
			unsigned char array[BUFSIZE];
452
			char keystr[KEY_FORMATSIZE];
453

454 455
			key_format(key->key, keystr, sizeof keystr);
			vbprintf(1, "\tsigning with key %s\n", keystr);
456 457 458 459 460 461 462 463
			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);
464 465 466
		}
		key = ISC_LIST_NEXT(key, link);
	}
467 468 469

	isc_mem_put(mctx, wassignedby, arraysize * sizeof(isc_boolean_t));
	isc_mem_put(mctx, nowsignedby, arraysize * sizeof(isc_boolean_t));
470 471
}

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

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

483
		dns_rdata_init(&rdata);
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
        return (ISC_FALSE);
}

501 502
static void
opendb(const char *prefix, dns_name_t *name, dns_rdataclass_t rdclass,
503
       dns_db_t **dbp)
504 505 506 507 508 509
{
	char filename[256];
	isc_buffer_t b;
	isc_result_t result;

	isc_buffer_init(&b, filename, sizeof(filename));
510 511 512 513 514
	if (directory != NULL) {
		isc_buffer_putstr(&b, directory);
		if (directory[strlen(directory) - 1] != '/')
			isc_buffer_putstr(&b, "/");
	}
515 516 517
	isc_buffer_putstr(&b, prefix);
	result = dns_name_totext(name, ISC_FALSE, &b);
	check_result(result, "dns_name_totext()");
518 519 520 521 522
	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);
	}
523 524 525
	isc_buffer_putuint8(&b, 0);

	result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone,
526
			       rdclass, 0, NULL, dbp);
527 528
	check_result(result, "dns_db_create()");

529
	result = dns_db_load(*dbp, filename);
530
	if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE)
531
		dns_db_detach(dbp);
532 533
}

534 535 536 537 538
/*
 * Looks for signatures of the zone keys by the parent, and imports them
 * if found.
 */
static void
539
importparentsig(dns_diff_t *diff, dns_name_t *name, dns_rdataset_t *set) {
540 541 542 543 544 545
	dns_db_t *newdb = NULL;
	dns_dbnode_t *newnode = NULL;
	dns_rdataset_t newset, sigset;
	dns_rdata_t rdata, newrdata;
	isc_result_t result;

546 547
	dns_rdataset_init(&newset);
	dns_rdataset_init(&sigset);
548 549
	dns_rdata_init(&rdata);
	dns_rdata_init(&newrdata);
550

551
	opendb("signedkey-", name, dns_db_class(gdb), &newdb);
552 553 554
	if (newdb == NULL)
		return;

555 556 557
	result = dns_db_findnode(newdb, name, ISC_FALSE, &newnode);
	if (result != ISC_R_SUCCESS)
		goto failure;
558 559
	result = dns_db_findrdataset(newdb, newnode, NULL, dns_rdatatype_key,
				     0, 0, &newset, &sigset);
560 561 562
	if (result != ISC_R_SUCCESS)
		goto failure;

563 564 565 566
	if (!dns_rdataset_isassociated(&newset) ||
	    !dns_rdataset_isassociated(&sigset))
		goto failure;

567 568
	if (dns_rdataset_count(set) != dns_rdataset_count(&newset)) {
		result = DNS_R_BADDB;
569
		goto failure;
570
	}
571 572

	result = dns_rdataset_first(set);
573
	check_result(result, "dns_rdataset_first()");
574 575 576
	for (; result == ISC_R_SUCCESS; result = dns_rdataset_next(set)) {
		dns_rdataset_current(set, &rdata);
		result = dns_rdataset_first(&newset);
577
		check_result(result, "dns_rdataset_first()");
578 579 580 581 582 583 584
		for (;
		     result == ISC_R_SUCCESS;
		     result = dns_rdataset_next(&newset))
		{
			dns_rdataset_current(&newset, &newrdata);
			if (dns_rdata_compare(&rdata, &newrdata) == 0)
				break;
585
			dns_rdata_reset(&newrdata);
586 587 588
		}
		if (result != ISC_R_SUCCESS)
			break;
589 590
		dns_rdata_reset(&newrdata);
		dns_rdata_reset(&rdata);
591
	}
592
	if (result != ISC_R_NOMORE)
593 594 595 596
		goto failure;

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

597 598 599 600 601 602 603 604 605 606 607
	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);
	}
608 609
	if (result == ISC_R_NOMORE)
		result = ISC_R_SUCCESS;
610 611

 failure:
612 613 614 615
	if (dns_rdataset_isassociated(&newset))
		dns_rdataset_disassociate(&newset);
	if (dns_rdataset_isassociated(&sigset))
		dns_rdataset_disassociate(&sigset);
616 617 618 619
	if (newnode != NULL)
		dns_db_detachnode(newdb, &newnode);
	if (newdb != NULL)
		dns_db_detach(&newdb);
620 621
	if (result != ISC_R_SUCCESS)
		fatal("zone signedkey file is invalid or does not match zone");
622 623 624
}

/*
625
 * Looks for our signatures of child keys.  If present, inform the caller.
626 627
 */
static isc_boolean_t
628
haschildkey(dns_name_t *name) {
629 630 631
	dns_db_t *newdb = NULL;
	dns_dbnode_t *newnode = NULL;
	dns_rdataset_t set, sigset;
632
	dns_rdata_t sigrdata = DNS_RDATA_INIT;
633 634 635 636 637
	isc_result_t result;
	isc_boolean_t found = ISC_FALSE;
	dns_rdata_sig_t sig;
	signer_key_t *key;

638 639 640
	dns_rdataset_init(&set);
	dns_rdataset_init(&sigset);

641
	opendb("signedkey-", name, dns_db_class(gdb), &newdb);
642 643 644
	if (newdb == NULL)
		return (ISC_FALSE);

645 646 647
	result = dns_db_findnode(newdb, name, ISC_FALSE, &newnode);
	if (result != ISC_R_SUCCESS)
		goto failure;
648 649
	result = dns_db_findrdataset(newdb, newnode, NULL, dns_rdatatype_key,
				     0, 0, &set, &sigset);
650 651 652 653 654
	if (result != ISC_R_SUCCESS)
		goto failure;

	if (!dns_rdataset_isassociated(&set) ||
	    !dns_rdataset_isassociated(&sigset))
655
		goto failure;
656 657 658 659 660 661

	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);
662
		result = dns_rdata_tostruct(&sigrdata, &sig, NULL);
663
		if (result != ISC_R_SUCCESS)
664
			goto failure;
665 666
		key = keythatsigned(&sig);
		dns_rdata_freestruct(&sig);
667 668 669 670 671 672 673
		if (key == NULL) {
			char namestr[DNS_NAME_FORMATSIZE];
			dns_name_format(name, namestr, sizeof namestr);
			fprintf(stderr,
				"creating KEY from signedkey file for %s: "
				"%s\n",
				namestr, isc_result_totext(result));
674
			goto failure;
675
		}
676 677 678 679 680
		result = dns_dnssec_verify(name, &set, key->key,
					   ISC_FALSE, mctx, &sigrdata);
		if (result == ISC_R_SUCCESS) {
			found = ISC_TRUE;
			break;
681 682 683 684 685 686
		} else {
			char namestr[DNS_NAME_FORMATSIZE];
			dns_name_format(name, namestr, sizeof namestr);
			fprintf(stderr,
				"verifying SIG in signedkey file for %s: %s\n",
				namestr, isc_result_totext(result));
687
		}
688
		dns_rdata_reset(&sigrdata);
689 690
	}

691
 failure:
692 693 694 695 696 697 698 699 700 701
	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);
702 703
}

704 705 706 707 708 709 710 711
/*
 * There probably should be a dns_nxt_setbit, but it can get complicated if
 * the length of the bit set needs to be increased.  In this case, since the
 * NXT bit is set and both SIG and KEY are less than NXT, the easy way works.
 */
static void
nxt_setbit(dns_rdataset_t *rdataset, dns_rdatatype_t type) {
	isc_result_t result;
712
	dns_rdata_t rdata = DNS_RDATA_INIT;
713
	dns_rdata_nxt_t nxt;
714 715 716 717

	result = dns_rdataset_first(rdataset);
	check_result(result, "dns_rdataset_first()");
	dns_rdataset_current(rdataset, &rdata);
718 719 720 721
	result = dns_rdata_tostruct(&rdata, &nxt, NULL);
	check_result(result, "dns_rdata_tostruct");
	set_bit(nxt.typebits, type, 1);
	dns_rdata_freestruct(&nxt);
722 723
}

Brian Wellington's avatar
Brian Wellington committed
724 725 726
static void
createnullkey(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name) {
	unsigned char keydata[4];
727
	dns_rdata_t keyrdata = DNS_RDATA_INIT;
Brian Wellington's avatar
Brian Wellington committed
728 729 730 731 732
	dns_rdata_key_t key;
	dns_diff_t diff;
	dns_difftuple_t *tuple = NULL;
	isc_buffer_t b;
	isc_result_t result;
733
	char namestr[DNS_NAME_FORMATSIZE];
Brian Wellington's avatar
Brian Wellington committed
734

735 736
	dns_name_format(name, namestr, sizeof namestr);
	vbprintf(2, "adding null key at %s\n", namestr);
Brian Wellington's avatar
Brian Wellington committed
737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766

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

767 768
/*
 * Signs all records at a name.  This mostly just signs each set individually,
769 770
 * but also adds the SIG bit to any NXTs generated earlier, deals with
 * parent/child KEY signatures, and handles other exceptional cases.
771
 */
Brian Wellington's avatar
Brian Wellington committed
772
static void
773
signname(dns_dbnode_t *node, dns_name_t *name) {
Brian Wellington's avatar
Brian Wellington committed
774
	isc_result_t result;
775
	dns_rdataset_t rdataset;
Brian Wellington's avatar
Brian Wellington committed
776
	dns_rdatasetiter_t *rdsiter;
Brian Wellington's avatar
Brian Wellington committed
777
	isc_boolean_t isdelegation = ISC_FALSE;
778
	isc_boolean_t childkey = ISC_FALSE;
779
	static int warnwild = 0;
780
	isc_boolean_t atorigin;
Brian Wellington's avatar
Brian Wellington committed
781
	isc_boolean_t neednullkey = ISC_FALSE;
782
	dns_diff_t diff;
783 784

	if (dns_name_iswildcard(name)) {
785 786
		char namestr[DNS_NAME_FORMATSIZE];
		dns_name_format(name, namestr, sizeof namestr);
Brian Wellington's avatar
Brian Wellington committed
787 788
		if (warnwild++ == 0) {
			fprintf(stderr, "%s: warning: BIND 9 doesn't properly "
David Lawrence's avatar
David Lawrence committed
789
				"handle wildcards in secure zones:\n",
790
				program);
Brian Wellington's avatar
Brian Wellington committed
791 792 793 794 795 796
			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",
797
			program, namestr);
798
	}
799

800
	atorigin = dns_name_equal(name, gorigin);
801 802 803 804

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

808
		dns_rdataset_init(&nsset);
809
		result = dns_db_findrdataset(gdb, node, gversion,
810 811 812 813 814 815 816 817
					     dns_rdatatype_ns, 0, 0, &nsset,
					     NULL);
		/* Is this a delegation point? */
		if (result == ISC_R_SUCCESS) {
			isdelegation = ISC_TRUE;
			dns_rdataset_disassociate(&nsset);
		}
	}