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

20
/* $Id: dnssec-signzone.c,v 1.115 2000/12/07 01:41:14 bwelling Exp $ */
David Lawrence's avatar
David Lawrence committed
21

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

#include <stdlib.h>

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

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

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

61 62
#include "dnssectool.h"

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

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

68 69 70 71 72
typedef struct signer_key_struct signer_key_t;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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