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

Brian Wellington's avatar
Brian Wellington committed
20
/* $Id: dnssec-signzone.c,v 1.119 2000/12/11 19:15:50 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/diff.h>
41
#include <dns/dnssec.h>
42
#include <dns/fixedname.h>
43
#include <dns/journal.h>
44 45
#include <dns/keyvalues.h>
#include <dns/log.h>
46 47
#include <dns/master.h>
#include <dns/masterdump.h>
48
#include <dns/nxt.h>
Brian Wellington's avatar
Brian Wellington committed
49 50
#include <dns/rdata.h>
#include <dns/rdataset.h>
51
#include <dns/rdataclass.h>
Brian Wellington's avatar
Brian Wellington committed
52
#include <dns/rdatasetiter.h>
53
#include <dns/rdatastruct.h>
54
#include <dns/rdatatype.h>
Brian Wellington's avatar
Brian Wellington committed
55
#include <dns/result.h>
56
#include <dns/secalg.h>
57
#include <dns/time.h>
Brian Wellington's avatar
Brian Wellington committed
58

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

62 63
#include "dnssectool.h"

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

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

69 70 71 72 73
typedef struct signer_key_struct signer_key_t;

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

78 79 80 81 82 83 84 85 86 87 88 89
#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;
};

90
static ISC_LIST(signer_key_t) keylist;
91
static unsigned int keycount = 0;
92 93
static isc_stdtime_t starttime = 0, endtime = 0, now;
static int cycle = -1;
94
static isc_boolean_t tryverify = ISC_FALSE;
95
static isc_boolean_t printstats = ISC_FALSE;
Brian Wellington's avatar
Brian Wellington committed
96
static isc_mem_t *mctx = NULL;
Brian Wellington's avatar
Brian Wellington committed
97
static isc_entropy_t *ectx = NULL;
98
static dns_ttl_t zonettl;
99 100
static FILE *fp;
static const dns_master_style_t *masterstyle = &dns_master_style_explicitttl;
101 102
static unsigned int nsigned = 0, nretained = 0, ndropped = 0;
static unsigned int nverified = 0, nverifyfailed = 0;
103
static const char *directory;
104 105 106 107 108 109 110 111 112 113 114 115
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;
116
static isc_boolean_t nokeys = ISC_FALSE;
117 118 119 120 121 122 123 124 125 126 127

#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
128

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

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

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

142 143 144 145 146 147 148 149 150 151 152 153 154 155
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);
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

320 321 322
	arraysize = keycount;
	if (!nosigs)
		arraysize += dns_rdataset_count(&sigset);
323 324
	wassignedby = isc_mem_get(mctx, arraysize * sizeof(isc_boolean_t));
	nowsignedby = isc_mem_get(mctx, arraysize * sizeof(isc_boolean_t));
325 326 327 328 329 330
	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
331 332 333
	if (nosigs)
		result = ISC_R_NOMORE;
	else
334
		result = dns_rdataset_first(&sigset);
335

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

484
		dns_rdata_init(&rdata);
485
		dns_rdataset_current(rdataset, &rdata);
486 487
		result = dns_dnssec_keyfromrdata(dns_rootname,
						 &rdata, mctx, &key);
488 489
		if (result != ISC_R_SUCCESS)
			fatal("could not convert KEY into internal format");
490 491
		if (dst_key_isnullkey(key))
			found = ISC_TRUE;
Brian Wellington's avatar
Brian Wellington committed
492
		dst_key_free(&key);
493
		if (found == ISC_TRUE)
494
			return (ISC_TRUE);
Brian Wellington's avatar
Brian Wellington committed
495 496 497 498 499
		result = dns_rdataset_next(rdataset);
	}
	if (result != ISC_R_NOMORE)
		fatal("failure looking for null keys");
	return (ISC_FALSE);
500 501
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	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);
663
		result = dns_rdata_tostruct(&sigrdata, &sig, NULL);
664
		if (result != ISC_R_SUCCESS)
665
			goto failure;
666 667
		key = keythatsigned(&sig);
		dns_rdata_freestruct(&sig);
668 669 670 671 672 673 674
		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));
675
			goto failure;
676
		}
677 678 679 680 681
		result = dns_dnssec_verify(name, &set, key->key,
					   ISC_FALSE, mctx, &sigrdata);
		if (result == ISC_R_SUCCESS) {
			found = ISC_TRUE;
			break;
682 683 684 685 686 687
		} 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));
688
		}
689
		dns_rdata_reset(&sigrdata);
690 691
	}

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

705 706 707 708 709 710 711 712
/*
 * 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;
713
	dns_rdata_t rdata = DNS_RDATA_INIT;
714
	dns_rdata_nxt_t nxt;
715 716 717 718

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

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

736 737
	dns_name_format(name, namestr, sizeof namestr);
	vbprintf(2, "adding null key at %s\n", namestr);
Brian Wellington's avatar
Brian Wellington committed
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 767

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

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

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

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

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

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