dnssec-signzone.c 48.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.126 2000/12/15 19:26:54 gson 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>
38
#include <isc/time.h>
Brian Wellington's avatar
Brian Wellington committed
39 40 41

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

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

64 65
#include "dnssectool.h"

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

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

71 72 73 74 75
typedef struct signer_key_struct signer_key_t;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

475
/* Determine if a KEY set contains a null key */
476
static isc_boolean_t
477
hasnullkey(dns_rdataset_t *rdataset) {
478
	isc_result_t result;
Mark Andrews's avatar
Mark Andrews committed
479
	dns_rdata_t rdata = DNS_RDATA_INIT;
480
	isc_boolean_t found = ISC_FALSE;
481

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

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

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

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

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

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

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

549 550 551
	dns_rdataset_init(&newset);
	dns_rdataset_init(&sigset);

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
		dns_rdata_reset(&newrdata);
		dns_rdata_reset(&rdata);
590 591
		if (result != ISC_R_SUCCESS)
			break;
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
	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
		dns_rdata_reset(&rdata);
609
	}
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) {
Brian Wellington's avatar