dnssec-signzone.c 47.1 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.120 2000/12/11 22:55:25 bwelling Exp $ */
David Lawrence's avatar
David Lawrence committed
21

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

#include <stdlib.h>
25
#include <time.h>
Brian Wellington's avatar
Brian Wellington committed
26

27
#include <isc/app.h>
28
#include <isc/commandline.h>
Brian Wellington's avatar
Brian Wellington committed
29
#include <isc/entropy.h>
30
#include <isc/event.h>
Brian Wellington's avatar
Brian Wellington committed
31
#include <isc/mem.h>
32 33
#include <isc/mutex.h>
#include <isc/os.h>
34
#include <isc/stdio.h>
35
#include <isc/string.h>
36
#include <isc/task.h>
Bob Halley's avatar
Bob Halley committed
37
#include <isc/util.h>
Brian Wellington's avatar
Brian Wellington committed
38 39 40

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

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

63 64
#include "dnssectool.h"

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

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

70 71 72 73 74
typedef struct signer_key_struct signer_key_t;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

810
		dns_rdataset_init(&nsset);
811
		result = dns_db_findrdataset(gdb, node, gversion,