dnssec-signzone.c 82.9 KB
Newer Older
Michael Graff's avatar
Michael Graff committed
1
/*
Automatic Updater's avatar
Automatic Updater committed
2
 * Portions Copyright (C) 2004-2009  Internet Systems Consortium, Inc. ("ISC")
Mark Andrews's avatar
Mark Andrews committed
3
 * Portions Copyright (C) 1999-2003  Internet Software Consortium.
Automatic Updater's avatar
Automatic Updater committed
4 5 6 7 8 9 10 11 12 13 14 15 16
 *
 * Permission to use, copy, modify, and/or 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.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC 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.
 *
17
 * Portions Copyright (C) 1995-2000 by Network Associates, Inc.
18
 *
Automatic Updater's avatar
Automatic Updater committed
19
 * Permission to use, copy, modify, and/or distribute this software for any
Michael Graff's avatar
Michael Graff committed
20 21
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
22
 *
Mark Andrews's avatar
Mark Andrews committed
23 24 25 26 27 28 29
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC 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
30
 */
Brian Wellington's avatar
Brian Wellington committed
31

Automatic Updater's avatar
Automatic Updater committed
32
/* $Id: dnssec-signzone.c,v 1.211 2009/01/06 23:47:56 tbox Exp $ */
33 34

/*! \file */
David Lawrence's avatar
David Lawrence committed
35

Brian Wellington's avatar
Brian Wellington committed
36 37 38
#include <config.h>

#include <stdlib.h>
39
#include <time.h>
Brian Wellington's avatar
Brian Wellington committed
40

41
#include <isc/app.h>
42
#include <isc/base32.h>
43
#include <isc/commandline.h>
Brian Wellington's avatar
Brian Wellington committed
44
#include <isc/entropy.h>
45
#include <isc/event.h>
46
#include <isc/file.h>
47
#include <isc/hash.h>
48
#include <isc/hex.h>
Brian Wellington's avatar
Brian Wellington committed
49
#include <isc/mem.h>
50 51
#include <isc/mutex.h>
#include <isc/os.h>
52
#include <isc/print.h>
53
#include <isc/random.h>
54
#include <isc/serial.h>
55
#include <isc/stdio.h>
56
#include <isc/stdlib.h>
57
#include <isc/string.h>
58
#include <isc/task.h>
59
#include <isc/time.h>
60
#include <isc/util.h>
Brian Wellington's avatar
Brian Wellington committed
61 62 63

#include <dns/db.h>
#include <dns/dbiterator.h>
64
#include <dns/diff.h>
65
#include <dns/dnssec.h>
66
#include <dns/ds.h>
67
#include <dns/fixedname.h>
68 69
#include <dns/keyvalues.h>
#include <dns/log.h>
70 71
#include <dns/master.h>
#include <dns/masterdump.h>
72
#include <dns/nsec.h>
73
#include <dns/nsec3.h>
Brian Wellington's avatar
Brian Wellington committed
74
#include <dns/rdata.h>
75
#include <dns/rdatalist.h>
Brian Wellington's avatar
Brian Wellington committed
76
#include <dns/rdataset.h>
77
#include <dns/rdataclass.h>
Brian Wellington's avatar
Brian Wellington committed
78
#include <dns/rdatasetiter.h>
79
#include <dns/rdatastruct.h>
80
#include <dns/rdatatype.h>
Brian Wellington's avatar
Brian Wellington committed
81
#include <dns/result.h>
82
#include <dns/soa.h>
83
#include <dns/time.h>
Brian Wellington's avatar
Brian Wellington committed
84

85
#include <dst/dst.h>
Brian Wellington's avatar
Brian Wellington committed
86

87 88
#include "dnssectool.h"

David Lawrence's avatar
David Lawrence committed
89
const char *program = "dnssec-signzone";
90
int verbose;
91

92 93 94 95 96 97 98
typedef struct hashlist hashlist_t;

static int nsec_datatype = dns_rdatatype_nsec;

#define IS_NSEC3	(nsec_datatype == dns_rdatatype_nsec3)
#define OPTOUT(x)	(((x) & DNS_NSEC3FLAG_OPTOUT) != 0)

99
#define BUFSIZE 2048
100
#define MAXDSKEYS 8
Brian Wellington's avatar
Brian Wellington committed
101

102 103 104 105
typedef struct signer_key_struct signer_key_t;

struct signer_key_struct {
	dst_key_t *key;
106 107 108
	isc_boolean_t issigningkey;
	isc_boolean_t isdsk;
	isc_boolean_t isksk;
109
	unsigned int position;
110 111 112
	ISC_LINK(signer_key_t) link;
};

113 114 115 116
#define SIGNER_EVENTCLASS	ISC_EVENTCLASS(0x4453)
#define SIGNER_EVENT_WRITE	(SIGNER_EVENTCLASS + 0)
#define SIGNER_EVENT_WORK	(SIGNER_EVENTCLASS + 1)

117 118 119 120
#define SOA_SERIAL_KEEP		0
#define SOA_SERIAL_INCREMENT	1
#define SOA_SERIAL_UNIXTIME	2

121 122 123 124 125 126 127
typedef struct signer_event sevent_t;
struct signer_event {
	ISC_EVENT_COMMON(sevent_t);
	dns_fixedname_t *fname;
	dns_dbnode_t *node;
};

128
static ISC_LIST(signer_key_t) keylist;
129
static unsigned int keycount = 0;
130 131
static isc_stdtime_t starttime = 0, endtime = 0, now;
static int cycle = -1;
132
static int jitter = 0;
133
static isc_boolean_t tryverify = ISC_FALSE;
134
static isc_boolean_t printstats = ISC_FALSE;
Brian Wellington's avatar
Brian Wellington committed
135
static isc_mem_t *mctx = NULL;
Brian Wellington's avatar
Brian Wellington committed
136
static isc_entropy_t *ectx = NULL;
137
static dns_ttl_t zonettl;
138
static FILE *fp;
139
static char *tempfile = NULL;
Danny Mayer's avatar
Danny Mayer committed
140
static const dns_master_style_t *masterstyle;
141 142
static dns_masterformat_t inputformat = dns_masterformat_text;
static dns_masterformat_t outputformat = dns_masterformat_text;
143 144
static unsigned int nsigned = 0, nretained = 0, ndropped = 0;
static unsigned int nverified = 0, nverifyfailed = 0;
145
static const char *directory;
146 147 148 149 150
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 */
151
static dns_rdataclass_t gclass;		/* The class */
152
static dns_name_t *gorigin;		/* The database origin */
153
static int nsec3flags = 0;
154 155 156
static isc_task_t *master = NULL;
static unsigned int ntasks = 0;
static isc_boolean_t shuttingdown = ISC_FALSE, finished = ISC_FALSE;
157
static isc_boolean_t nokeys = ISC_FALSE;
158
static isc_boolean_t removefile = ISC_FALSE;
159
static isc_boolean_t generateds = ISC_FALSE;
160
static isc_boolean_t ignoreksk = ISC_FALSE;
161 162 163
static dns_name_t *dlv = NULL;
static dns_fixedname_t dlv_fixed;
static dns_master_style_t *dsstyle = NULL;
164
static unsigned int serialformat = SOA_SERIAL_KEEP;
165 166
static unsigned int hash_length = 0;
static isc_boolean_t unknownalg = ISC_FALSE;
167 168 169 170 171 172 173 174 175 176 177

#define INCSTAT(counter)		\
	if (printstats) {		\
		LOCK(&statslock);	\
		counter++;		\
		UNLOCK(&statslock);	\
	}

static void
sign(isc_task_t *task, isc_event_t *event);

178 179
static isc_boolean_t
nsec3only(dns_dbnode_t *node);
Brian Wellington's avatar
Brian Wellington committed
180

Brian Wellington's avatar
Brian Wellington committed
181 182 183 184
static void
dumpnode(dns_name_t *name, dns_dbnode_t *node) {
	isc_result_t result;

185 186
	if (outputformat != dns_masterformat_text)
		return;
Brian Wellington's avatar
Brian Wellington committed
187 188 189 190 191
	result = dns_master_dumpnodetostream(mctx, gdb, gversion, node, name,
					     masterstyle, fp);
	check_result(result, "dns_master_dumpnodetostream");
}

192
static signer_key_t *
193
newkeystruct(dst_key_t *dstkey, isc_boolean_t signwithkey) {
194 195 196 197 198 199
	signer_key_t *key;

	key = isc_mem_get(mctx, sizeof(signer_key_t));
	if (key == NULL)
		fatal("out of memory");
	key->key = dstkey;
200 201 202 203 204 205 206 207 208
	if ((dst_key_flags(dstkey) & DNS_KEYFLAG_KSK) != 0) {
		key->issigningkey = signwithkey;
		key->isksk = ISC_TRUE;
		key->isdsk = ISC_FALSE;
	} else {
		key->issigningkey = signwithkey;
		key->isksk = ISC_FALSE;
		key->isdsk = ISC_TRUE;
	}
209 210 211 212 213
	key->position = keycount++;
	ISC_LINK_INIT(key, link);
	return (key);
}

214 215 216
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
217 218
{
	isc_result_t result;
219
	isc_stdtime_t jendtime;
Brian Wellington's avatar
Brian Wellington committed
220

221 222
	jendtime = (jitter != 0) ? isc_random_jitter(endtime, jitter) : endtime;
	result = dns_dnssec_sign(name, rdataset, key, &starttime, &jendtime,
223
				 mctx, b, rdata);
224
	isc_entropy_stopcallbacksources(ectx);
225 226
	if (result != ISC_R_SUCCESS) {
		char keystr[KEY_FORMATSIZE];
Andreas Gustafsson's avatar
Andreas Gustafsson committed
227
		key_format(key, keystr, sizeof(keystr));
228
		fatal("dnskey '%s' failed to sign data: %s",
229 230
		      keystr, isc_result_totext(result));
	}
231
	INCSTAT(nsigned);
232

233
	if (tryverify) {
234 235
		result = dns_dnssec_verify(name, rdataset, key,
					   ISC_TRUE, mctx, rdata);
236
		if (result == ISC_R_SUCCESS) {
237
			vbprintf(3, "\tsignature verified\n");
238
			INCSTAT(nverified);
239
		} else {
240
			vbprintf(3, "\tsignature failed to verify\n");
241
			INCSTAT(nverifyfailed);
242
		}
243
	}
244
}
Brian Wellington's avatar
Brian Wellington committed
245

246 247
static inline isc_boolean_t
issigningkey(signer_key_t *key) {
248
	return (key->issigningkey);
Brian Wellington's avatar
Brian Wellington committed
249 250
}

251
static inline isc_boolean_t
252 253
iszonekey(signer_key_t *key) {
	return (ISC_TF(dns_name_equal(dst_key_name(key->key), gorigin) &&
254
		       dst_key_iszonekey(key->key)));
255 256
}

257
/*%
258
 * Finds the key that generated a RRSIG, if possible.  First look at the keys
259 260
 * that we've loaded already, and then see if there's a key on disk.
 */
261
static signer_key_t *
262
keythatsigned(dns_rdata_rrsig_t *rrsig) {
263
	isc_result_t result;
264 265 266 267 268
	dst_key_t *pubkey = NULL, *privkey = NULL;
	signer_key_t *key;

	key = ISC_LIST_HEAD(keylist);
	while (key != NULL) {
269 270 271
		if (rrsig->keyid == dst_key_id(key->key) &&
		    rrsig->algorithm == dst_key_alg(key->key) &&
		    dns_name_equal(&rrsig->signer, dst_key_name(key->key)))
272
			return key;
273
		key = ISC_LIST_NEXT(key, link);
274
	}
275

276 277 278
	result = dst_key_fromfile(&rrsig->signer, rrsig->keyid,
				  rrsig->algorithm, DST_TYPE_PUBLIC,
				  NULL, mctx, &pubkey);
279 280
	if (result != ISC_R_SUCCESS)
		return (NULL);
281

282 283
	result = dst_key_fromfile(&rrsig->signer, rrsig->keyid,
				  rrsig->algorithm,
284 285
				  DST_TYPE_PUBLIC | DST_TYPE_PRIVATE,
				  NULL, mctx, &privkey);
286
	if (result == ISC_R_SUCCESS) {
287
		dst_key_free(&pubkey);
288
		key = newkeystruct(privkey, ISC_FALSE);
289
	} else
290
		key = newkeystruct(pubkey, ISC_FALSE);
291 292
	ISC_LIST_APPEND(keylist, key, link);
	return (key);
293 294
}

295
/*%
296 297
 * Check to see if we expect to find a key at this name.  If we see a RRSIG
 * and can't find the signing key that we expect to find, we drop the rrsig.
298 299
 * I'm not sure if this is completely correct, but it seems to work.
 */
300
static isc_boolean_t
301
expecttofindkey(dns_name_t *name) {
302
	unsigned int options = DNS_DBFIND_NOWILD;
303
	dns_fixedname_t fname;
304
	isc_result_t result;
305
	char namestr[DNS_NAME_FORMATSIZE];
306

307
	dns_fixedname_init(&fname);
308
	result = dns_db_find(gdb, name, gversion, dns_rdatatype_dnskey, options,
309
			     0, NULL, dns_fixedname_name(&fname), NULL, NULL);
310
	switch (result) {
311 312 313 314 315 316 317 318
	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);
319
	}
Andreas Gustafsson's avatar
Andreas Gustafsson committed
320
	dns_name_format(name, namestr, sizeof(namestr));
321
	fatal("failure looking for '%s DNSKEY' in database: %s",
322 323
	      namestr, isc_result_totext(result));
	return (ISC_FALSE); /* removes a warning */
324 325
}

326
static inline isc_boolean_t
327
setverifies(dns_name_t *name, dns_rdataset_t *set, signer_key_t *key,
328
	    dns_rdata_t *rrsig)
329
{
330
	isc_result_t result;
331
	result = dns_dnssec_verify(name, set, key->key, ISC_FALSE, mctx, rrsig);
332
	if (result == ISC_R_SUCCESS) {
333
		INCSTAT(nverified);
334 335
		return (ISC_TRUE);
	} else {
336
		INCSTAT(nverifyfailed);
337 338
		return (ISC_FALSE);
	}
339 340
}

341
/*%
342
 * Signs a set.  Goes through contortions to decide if each RRSIG should
343 344 345
 * be dropped or retained, and then determines if any new SIGs need to
 * be generated.
 */
346
static void
347
signset(dns_diff_t *del, dns_diff_t *add, dns_dbnode_t *node, dns_name_t *name,
348
	dns_rdataset_t *set)
349
{
350
	dns_rdataset_t sigset;
351
	dns_rdata_t sigrdata = DNS_RDATA_INIT;
352
	dns_rdata_rrsig_t rrsig;
353
	signer_key_t *key;
354
	isc_result_t result;
355 356 357
	isc_boolean_t nosigs = ISC_FALSE;
	isc_boolean_t *wassignedby, *nowsignedby;
	int arraysize;
358 359
	dns_difftuple_t *tuple;
	dns_ttl_t ttl;
360
	int i;
361 362 363 364
	char namestr[DNS_NAME_FORMATSIZE];
	char typestr[TYPE_FORMATSIZE];
	char sigstr[SIG_FORMATSIZE];

Andreas Gustafsson's avatar
Andreas Gustafsson committed
365 366
	dns_name_format(name, namestr, sizeof(namestr));
	type_format(set->type, typestr, sizeof(typestr));
367

368
	ttl = ISC_MIN(set->ttl, endtime - starttime);
369

370
	dns_rdataset_init(&sigset);
371
	result = dns_db_findrdataset(gdb, node, gversion, dns_rdatatype_rrsig,
372
				     set->type, 0, &sigset, NULL);
373 374 375 376
	if (result == ISC_R_NOTFOUND) {
		result = ISC_R_SUCCESS;
		nosigs = ISC_TRUE;
	}
377
	if (result != ISC_R_SUCCESS)
378
		fatal("failed while looking for '%s RRSIG %s': %s",
379
		      namestr, typestr, isc_result_totext(result));
380

381
	vbprintf(1, "%s/%s:\n", namestr, typestr);
382

383 384 385
	arraysize = keycount;
	if (!nosigs)
		arraysize += dns_rdataset_count(&sigset);
386 387
	wassignedby = isc_mem_get(mctx, arraysize * sizeof(isc_boolean_t));
	nowsignedby = isc_mem_get(mctx, arraysize * sizeof(isc_boolean_t));
388 389 390 391 392 393
	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
394 395 396
	if (nosigs)
		result = ISC_R_NOMORE;
	else
397
		result = dns_rdataset_first(&sigset);
398

Brian Wellington's avatar
Brian Wellington committed
399 400 401
	while (result == ISC_R_SUCCESS) {
		isc_boolean_t expired, future;
		isc_boolean_t keep = ISC_FALSE, resign = ISC_FALSE;
402

Brian Wellington's avatar
Brian Wellington committed
403
		dns_rdataset_current(&sigset, &sigrdata);
404

405
		result = dns_rdata_tostruct(&sigrdata, &rrsig, NULL);
Brian Wellington's avatar
Brian Wellington committed
406
		check_result(result, "dns_rdata_tostruct");
407

408
		future = isc_serial_lt(now, rrsig.timesigned);
409

410 411
		key = keythatsigned(&rrsig);
		sig_format(&rrsig, sigstr, sizeof(sigstr));
412
		if (key != NULL && issigningkey(key))
413
			expired = isc_serial_gt(now + cycle, rrsig.timeexpire);
414
		else
415
			expired = isc_serial_gt(now, rrsig.timeexpire);
Brian Wellington's avatar
Brian Wellington committed
416

417 418 419
		if (isc_serial_gt(rrsig.timesigned, rrsig.timeexpire)) {
			/* rrsig is dropped and not replaced */
			vbprintf(2, "\trrsig by %s dropped - "
Brian Wellington's avatar
Brian Wellington committed
420
				 "invalid validity period\n",
421
				 sigstr);
Brian Wellington's avatar
Brian Wellington committed
422
		} else if (key == NULL && !future &&
423
			 expecttofindkey(&rrsig.signer))
Brian Wellington's avatar
Brian Wellington committed
424
		{
425 426 427
			/* rrsig is dropped and not replaced */
			vbprintf(2, "\trrsig by %s dropped - "
				 "private dnskey not found\n",
428
				 sigstr);
Brian Wellington's avatar
Brian Wellington committed
429
		} else if (key == NULL || future) {
430
			vbprintf(2, "\trrsig by %s %s - dnskey not found\n",
431
				 expired ? "retained" : "dropped", sigstr);
Brian Wellington's avatar
Brian Wellington committed
432 433 434 435
			if (!expired)
				keep = ISC_TRUE;
		} else if (issigningkey(key)) {
			if (!expired && setverifies(name, set, key, &sigrdata))
436
			{
437
				vbprintf(2, "\trrsig by %s retained\n", sigstr);
438
				keep = ISC_TRUE;
439 440
				wassignedby[key->position] = ISC_TRUE;
				nowsignedby[key->position] = ISC_TRUE;
441
			} else {
442
				vbprintf(2, "\trrsig by %s dropped - %s\n",
443
					 sigstr,
Brian Wellington's avatar
Brian Wellington committed
444 445
					 expired ? "expired" :
						   "failed to verify");
446
				wassignedby[key->position] = ISC_TRUE;
Brian Wellington's avatar
Brian Wellington committed
447
				resign = ISC_TRUE;
448
			}
449
		} else if (iszonekey(key)) {
Brian Wellington's avatar
Brian Wellington committed
450 451
			if (!expired && setverifies(name, set, key, &sigrdata))
			{
452
				vbprintf(2, "\trrsig by %s retained\n", sigstr);
Brian Wellington's avatar
Brian Wellington committed
453
				keep = ISC_TRUE;
454 455
				wassignedby[key->position] = ISC_TRUE;
				nowsignedby[key->position] = ISC_TRUE;
Brian Wellington's avatar
Brian Wellington committed
456
			} else {
457
				vbprintf(2, "\trrsig by %s dropped - %s\n",
458
					 sigstr,
Brian Wellington's avatar
Brian Wellington committed
459 460
					 expired ? "expired" :
						   "failed to verify");
461
				wassignedby[key->position] = ISC_TRUE;
462
			}
Brian Wellington's avatar
Brian Wellington committed
463
		} else if (!expired) {
464
			vbprintf(2, "\trrsig by %s retained\n", sigstr);
Brian Wellington's avatar
Brian Wellington committed
465 466
			keep = ISC_TRUE;
		} else {
467
			vbprintf(2, "\trrsig by %s expired\n", sigstr);
Brian Wellington's avatar
Brian Wellington committed
468
		}
469

470
		if (keep) {
471
			nowsignedby[key->position] = ISC_TRUE;
472
			INCSTAT(nretained);
473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
			if (sigset.ttl != ttl) {
				vbprintf(2, "\tfixing ttl %s\n", sigstr);
				tuple = NULL;
				result = dns_difftuple_create(mctx,
							      DNS_DIFFOP_DEL,
							      name, sigset.ttl,
							      &sigrdata,
							      &tuple);
				check_result(result, "dns_difftuple_create");
				dns_diff_append(del, &tuple);
				result = dns_difftuple_create(mctx,
							      DNS_DIFFOP_ADD,
							      name, ttl,
							      &sigrdata,
							      &tuple);
				check_result(result, "dns_difftuple_create");
				dns_diff_append(add, &tuple);
			}
491
		} else {
Brian Wellington's avatar
Brian Wellington committed
492 493
			tuple = NULL;
			result = dns_difftuple_create(mctx, DNS_DIFFOP_DEL,
494 495
						      name, sigset.ttl,
						      &sigrdata, &tuple);
Brian Wellington's avatar
Brian Wellington committed
496
			check_result(result, "dns_difftuple_create");
497
			dns_diff_append(del, &tuple);
498
			INCSTAT(ndropped);
Brian Wellington's avatar
Brian Wellington committed
499 500 501 502
		}

		if (resign) {
			isc_buffer_t b;
503
			dns_rdata_t trdata = DNS_RDATA_INIT;
Brian Wellington's avatar
Brian Wellington committed
504
			unsigned char array[BUFSIZE];
505
			char keystr[KEY_FORMATSIZE];
506

507 508
			INSIST(!keep);

Andreas Gustafsson's avatar
Andreas Gustafsson committed
509
			key_format(key->key, keystr, sizeof(keystr));
510
			vbprintf(1, "\tresigning with dnskey %s\n", keystr);
Brian Wellington's avatar
Brian Wellington committed
511 512
			isc_buffer_init(&b, array, sizeof(array));
			signwithkey(name, set, &trdata, key->key, &b);
513
			nowsignedby[key->position] = ISC_TRUE;
Brian Wellington's avatar
Brian Wellington committed
514 515 516 517 518
			tuple = NULL;
			result = dns_difftuple_create(mctx, DNS_DIFFOP_ADD,
						      name, ttl, &trdata,
						      &tuple);
			check_result(result, "dns_difftuple_create");
519
			dns_diff_append(add, &tuple);
520
		}
Brian Wellington's avatar
Brian Wellington committed
521

522
		dns_rdata_reset(&sigrdata);
523
		dns_rdata_freestruct(&rrsig);
Brian Wellington's avatar
Brian Wellington committed
524
		result = dns_rdataset_next(&sigset);
Brian Wellington's avatar
Brian Wellington committed
525
	}
Brian Wellington's avatar
Brian Wellington committed
526 527 528 529 530 531
	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);
532

Brian Wellington's avatar
Brian Wellington committed
533 534 535 536 537 538 539 540
	for (key = ISC_LIST_HEAD(keylist);
	     key != NULL;
	     key = ISC_LIST_NEXT(key, link))
	{
		isc_buffer_t b;
		dns_rdata_t trdata;
		unsigned char array[BUFSIZE];
		char keystr[KEY_FORMATSIZE];
541

542 543 544
		if (nowsignedby[key->position])
			continue;

545 546 547 548
		if (!key->issigningkey)
			continue;
		if (!(ignoreksk || key->isdsk ||
		      (key->isksk &&
549
		       set->type == dns_rdatatype_dnskey &&
550
		       dns_name_equal(name, gorigin))))
Brian Wellington's avatar
Brian Wellington committed
551 552
			continue;

Andreas Gustafsson's avatar
Andreas Gustafsson committed
553
		key_format(key->key, keystr, sizeof(keystr));
554
		vbprintf(1, "\tsigning with dnskey %s\n", keystr);
Brian Wellington's avatar
Brian Wellington committed
555 556 557 558 559 560 561
		dns_rdata_init(&trdata);
		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");
562
		dns_diff_append(add, &tuple);
563
	}
564 565 566

	isc_mem_put(mctx, wassignedby, arraysize * sizeof(isc_boolean_t));
	isc_mem_put(mctx, nowsignedby, arraysize * sizeof(isc_boolean_t));
567 568
}

569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649
struct hashlist {
	unsigned char *hashbuf;
	size_t entries;
	size_t size;
	size_t length;
};

static void
hashlist_init(hashlist_t *l, unsigned int nodes, unsigned int length) {

	l->entries = 0;
	l->length = length + 1;

	if (nodes != 0) {
		l->size = nodes;
		l->hashbuf = malloc(l->size * l->length);
		if (l->hashbuf == NULL)
			l->size = 0;
	} else {
		l->size = 0;
		l->hashbuf = NULL;
	}
}

static void
hashlist_add(hashlist_t *l, const unsigned char *hash, size_t len)
{

	REQUIRE(len <= l->length);

	if (l->entries == l->size) {
		l->size = l->size * 2 + 100;
		l->hashbuf = realloc(l->hashbuf, l->size * l->length);
	}
	memset(l->hashbuf + l->entries * l->length, 0, l->length);
	memcpy(l->hashbuf + l->entries * l->length, hash, len);
	l->entries++;
}

static void
hashlist_add_dns_name(hashlist_t *l, /*const*/ dns_name_t *name,
		      unsigned int hashalg, unsigned int iterations,
		      const unsigned char *salt, size_t salt_length,
		      isc_boolean_t speculative)
{
	char nametext[DNS_NAME_FORMATSIZE];
	unsigned char hash[NSEC3_MAX_HASH_LENGTH + 1];
	unsigned int len;
	size_t i;

	len = isc_iterated_hash(hash, hashalg, iterations, salt, salt_length,
				name->ndata, name->length);
	if (verbose) {
		dns_name_format(name, nametext, sizeof nametext);
		for (i = 0 ; i < len; i++)
			fprintf(stderr, "%02x", hash[i]);
		fprintf(stderr, " %s\n", nametext);
	}
	hash[len++] = speculative ? 1 : 0;
	hashlist_add(l, hash, len);
}

static int
hashlist_comp(const void *a, const void *b) {
	return (memcmp(a, b, hash_length + 1));
}

static void
hashlist_sort(hashlist_t *l) {
	qsort(l->hashbuf, l->entries, l->length, hashlist_comp);
}

static isc_boolean_t
hashlist_hasdup(hashlist_t *l) {
	unsigned char *current;
	unsigned char *next = l->hashbuf;
	size_t entries = l->entries;

	/*
	 * Skip initial speculative wild card hashs.
	 */
Mark Andrews's avatar
Mark Andrews committed
650
	while (entries > 0U && next[l->length-1] != 0U) {
651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731
		next += l->length;
		entries--;
	}

	current = next;
	while (entries-- > 1U) {
		next += l->length;
		if (next[l->length-1] != 0)
			continue;
		if (memcmp(current, next, l->length - 1) == 0)
			return (ISC_TRUE);
		current = next;
	}
	return (ISC_FALSE);
}

static const unsigned char *
hashlist_findnext(const hashlist_t *l,
		  const unsigned char hash[NSEC3_MAX_HASH_LENGTH])
{
	unsigned int entries = l->entries;
	const unsigned char *next = bsearch(hash, l->hashbuf, l->entries,
					    l->length, hashlist_comp);
	INSIST(next != NULL);

	do {
		if (next < l->hashbuf + (l->entries - 1) * l->length)
			next += l->length;
		else
			next = l->hashbuf;
		if (next[l->length - 1] == 0)
			break;
	} while (entries-- > 1);
	INSIST(entries != 0);
	return (next);
}

static isc_boolean_t
hashlist_exists(const hashlist_t *l,
		const unsigned char hash[NSEC3_MAX_HASH_LENGTH])
{
	if (bsearch(hash, l->hashbuf, l->entries, l->length, hashlist_comp))
		return (ISC_TRUE);
	else
		return (ISC_FALSE);
}

static void
addnowildcardhash(hashlist_t *l, /*const*/ dns_name_t *name,
		  unsigned int hashalg, unsigned int iterations,
		  const unsigned char *salt, size_t salt_length)
{
	dns_fixedname_t fixed;
	dns_name_t *wild;
	dns_dbnode_t *node = NULL;
	isc_result_t result;
	char namestr[DNS_NAME_FORMATSIZE];

	dns_fixedname_init(&fixed);
	wild = dns_fixedname_name(&fixed);

	result = dns_name_concatenate(dns_wildcardname, name, wild, NULL);
	if (result == ISC_R_NOSPACE)
		return;
	check_result(result,"addnowildcardhash: dns_name_concatenate()");

	result = dns_db_findnode(gdb, wild, ISC_FALSE, &node);
	if (result == ISC_R_SUCCESS) {
		dns_db_detachnode(gdb, &node);
		return;
	}

	if (verbose) {
		dns_name_format(wild, namestr, sizeof(namestr));
		fprintf(stderr, "adding no-wildcardhash for %s\n", namestr);
	}

	hashlist_add_dns_name(l, wild, hashalg, iterations, salt, salt_length,
			      ISC_TRUE);
}

732 733
static void
opendb(const char *prefix, dns_name_t *name, dns_rdataclass_t rdclass,
734
       dns_db_t **dbp)
735 736 737 738 739 740
{
	char filename[256];
	isc_buffer_t b;
	isc_result_t result;

	isc_buffer_init(&b, filename, sizeof(filename));
741 742 743 744 745
	if (directory != NULL) {
		isc_buffer_putstr(&b, directory);
		if (directory[strlen(directory) - 1] != '/')
			isc_buffer_putstr(&b, "/");
	}
746
	isc_buffer_putstr(&b, prefix);
747
	result = dns_name_tofilenametext(name, ISC_FALSE, &b);
748
	check_result(result, "dns_name_tofilenametext()");
749 750
	if (isc_buffer_availablelength(&b) == 0) {
		char namestr[DNS_NAME_FORMATSIZE];
Andreas Gustafsson's avatar
Andreas Gustafsson committed
751
		dns_name_format(name, namestr, sizeof(namestr));
752 753
		fatal("name '%s' is too long", namestr);
	}
754 755
	isc_buffer_putuint8(&b, 0);

756
	result = dns_db_create(mctx, "rbt", dns_rootname, dns_dbtype_zone,
757
			       rdclass, 0, NULL, dbp);
758 759
	check_result(result, "dns_db_create()");

760
	result = dns_db_load(*dbp, filename);
761
	if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE)
762
		dns_db_detach(dbp);
763 764
}

765
/*%
766
 * Loads the key set for a child zone, if there is one, and builds DS records.
767
 */
768
static isc_result_t
769
loadds(dns_name_t *name, isc_uint32_t ttl, dns_rdataset_t *dsset) {
770 771 772
	dns_db_t *db = NULL;
	dns_dbversion_t *ver = NULL;
	dns_dbnode_t *node = NULL;
773
	isc_result_t result;
774 775 776 777 778
	dns_rdataset_t keyset;
	dns_rdata_t key, ds;
	unsigned char dsbuf[DNS_DS_BUFFERSIZE];
	dns_diff_t diff;
	dns_difftuple_t *tuple = NULL;
779

780 781 782
	opendb("keyset-", name, gclass, &db);
	if (db == NULL)
		return (ISC_R_NOTFOUND);
783

784 785 786 787
	result = dns_db_findnode(db, name, ISC_FALSE, &node);
	if (result != ISC_R_SUCCESS) {
		dns_db_detach(&db);
		return (DNS_R_BADDB);
788
	}
789
	dns_rdataset_init(&keyset);
790
	result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_dnskey, 0, 0,
791 792 793 794 795
				     &keyset, NULL);
	if (result != ISC_R_SUCCESS) {
		dns_db_detachnode(db, &node);
		dns_db_detach(&db);
		return (result);
796
	}
797

798
	vbprintf(2, "found DNSKEY records\n");
799

800 801
	result = dns_db_newversion(db, &ver);
	check_result(result, "dns_db_newversion");
802

803
	dns_diff_init(mctx, &diff);
804

805 806 807 808 809 810 811 812 813 814
	for (result = dns_rdataset_first(&keyset);
	     result == ISC_R_SUCCESS;
	     result = dns_rdataset_next(&keyset))
	{
		dns_rdata_init(&key);
		dns_rdata_init(&ds);
		dns_rdataset_current(&keyset, &key);
		result = dns_ds_buildrdata(name, &key, DNS_DSDIGEST_SHA1,
					   dsbuf, &ds);
		check_result(result, "dns_ds_buildrdata");
815

816
		result = dns_difftuple_create(mctx, DNS_DIFFOP_ADD, name,
817
					      ttl, &ds, &tuple);
818 819
		check_result(result, "dns_difftuple_create");
		dns_diff_append(&diff, &tuple);
820 821 822 823 824 825 826 827 828 829

		dns_rdata_reset(&ds);
		result = dns_ds_buildrdata(name, &key, DNS_DSDIGEST_SHA256,
					   dsbuf, &ds);
		check_result(result, "dns_ds_buildrdata");

		result = dns_difftuple_create(mctx, DNS_DIFFOP_ADD, name,
					      ttl, &ds, &tuple);
		check_result(result, "dns_difftuple_create");
		dns_diff_append(&diff, &tuple);
830
	}
831 832 833
	result = dns_diff_apply(&diff, db, ver);
	check_result(result, "dns_diff_apply");
	dns_diff_clear(&diff);
834