dnssec-signzone.c 47.2 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.122 2000/12/12 20:21:34 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
	isc_result_t result;
Mark Andrews's avatar
Mark Andrews committed
478
	dns_rdata_t rdata = DNS_RDATA_INIT;
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;

Mark Andrews's avatar
Mark Andrews committed
485
		dns_rdata_reset(&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
	dns_db_t *newdb = NULL;
	dns_dbnode_t *newnode = NULL;
	dns_rdataset_t newset, sigset;
Mark Andrews's avatar
Mark Andrews committed
545
	dns_rdata_t rdata = DNS_RDATA_INIT, newrdata = DNS_RDATA_INIT;
546 547
	isc_result_t result;

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

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,