dnssec-signzone.c 107 KB
Newer Older
Michael Graff's avatar
Michael Graff committed
1
/*
Tinderbox User's avatar
Tinderbox User committed
2
 * Portions Copyright (C) 2004-2014  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

32
/* $Id: dnssec-signzone.c,v 1.285.32.1 2012/02/07 00:44:12 each 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>
40
#include <unistd.h>
Brian Wellington's avatar
Brian Wellington committed
41

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

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

88
#include <dst/dst.h>
Brian Wellington's avatar
Brian Wellington committed
89

90 91 92 93
#ifdef PKCS11CRYPTO
#include <pk11/result.h>
#endif

94 95
#include "dnssectool.h"

Evan Hunt's avatar
Evan Hunt committed
96 97 98 99
#ifndef PATH_MAX
#define PATH_MAX 1024   /* AIX, WIN32, and others don't define this. */
#endif

David Lawrence's avatar
David Lawrence committed
100
const char *program = "dnssec-signzone";
101
int verbose;
102

103 104 105 106 107 108 109
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)

110 111
#define REVOKE(x) ((dst_key_flags(x) & DNS_KEYFLAG_REVOKE) != 0)

112
#define BUFSIZE 2048
113
#define MAXDSKEYS 8
Brian Wellington's avatar
Brian Wellington committed
114

115 116 117 118
#define SIGNER_EVENTCLASS	ISC_EVENTCLASS(0x4453)
#define SIGNER_EVENT_WRITE	(SIGNER_EVENTCLASS + 0)
#define SIGNER_EVENT_WORK	(SIGNER_EVENTCLASS + 1)

119 120 121
#define SOA_SERIAL_KEEP		0
#define SOA_SERIAL_INCREMENT	1
#define SOA_SERIAL_UNIXTIME	2
Evan Hunt's avatar
Evan Hunt committed
122
#define SOA_SERIAL_DATE		3
123

124 125 126 127 128 129 130
typedef struct signer_event sevent_t;
struct signer_event {
	ISC_EVENT_COMMON(sevent_t);
	dns_fixedname_t *fname;
	dns_dbnode_t *node;
};

131
static dns_dnsseckeylist_t keylist;
132
static unsigned int keycount = 0;
133
isc_rwlock_t keylist_lock;
134
static isc_stdtime_t starttime = 0, endtime = 0, dnskey_endtime = 0, now;
135
static int cycle = -1;
136
static int jitter = 0;
137
static isc_boolean_t tryverify = ISC_FALSE;
138
static isc_boolean_t printstats = ISC_FALSE;
Brian Wellington's avatar
Brian Wellington committed
139
static isc_mem_t *mctx = NULL;
Brian Wellington's avatar
Brian Wellington committed
140
static isc_entropy_t *ectx = NULL;
141
static dns_ttl_t zone_soa_min_ttl;
142
static dns_ttl_t soa_ttl;
143
static FILE *fp = NULL;
144
static char *tempfile = NULL;
Danny Mayer's avatar
Danny Mayer committed
145
static const dns_master_style_t *masterstyle;
146 147
static dns_masterformat_t inputformat = dns_masterformat_text;
static dns_masterformat_t outputformat = dns_masterformat_text;
148 149
static isc_uint32_t rawversion = 1, serialnum = 0;
static isc_boolean_t snset = ISC_FALSE;
150 151
static unsigned int nsigned = 0, nretained = 0, ndropped = 0;
static unsigned int nverified = 0, nverifyfailed = 0;
152
static const char *directory = NULL, *dsdir = NULL;
153 154 155 156 157
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 */
158
static dns_rdataclass_t gclass;		/* The class */
159
static dns_name_t *gorigin;		/* The database origin */
160
static int nsec3flags = 0;
161
static dns_iterations_t nsec3iter = 10U;
162 163 164
static unsigned char saltbuf[255];
static unsigned char *salt = saltbuf;
static size_t salt_length = 0;
165 166 167
static isc_task_t *master = NULL;
static unsigned int ntasks = 0;
static isc_boolean_t shuttingdown = ISC_FALSE, finished = ISC_FALSE;
168
static isc_boolean_t nokeys = ISC_FALSE;
169
static isc_boolean_t removefile = ISC_FALSE;
170
static isc_boolean_t generateds = ISC_FALSE;
171
static isc_boolean_t ignore_kskflag = ISC_FALSE;
172
static isc_boolean_t keyset_kskonly = ISC_FALSE;
173 174 175
static dns_name_t *dlv = NULL;
static dns_fixedname_t dlv_fixed;
static dns_master_style_t *dsstyle = NULL;
176
static unsigned int serialformat = SOA_SERIAL_KEEP;
177 178
static unsigned int hash_length = 0;
static isc_boolean_t unknownalg = ISC_FALSE;
179
static isc_boolean_t disable_zone_check = ISC_FALSE;
180
static isc_boolean_t update_chain = ISC_FALSE;
181 182
static isc_boolean_t set_keyttl = ISC_FALSE;
static dns_ttl_t keyttl;
183
static isc_boolean_t smartsign = ISC_FALSE;
Evan Hunt's avatar
Evan Hunt committed
184 185
static isc_boolean_t remove_orphansigs = ISC_FALSE;
static isc_boolean_t remove_inactkeysigs = ISC_FALSE;
186
static isc_boolean_t output_dnssec_only = ISC_FALSE;
187
static isc_boolean_t output_stdout = ISC_FALSE;
Evan Hunt's avatar
Evan Hunt committed
188 189
isc_boolean_t set_maxttl = ISC_FALSE;
static dns_ttl_t maxttl = 0;
190 191 192 193 194 195 196 197 198 199 200

#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
201 202
static void
dumpnode(dns_name_t *name, dns_dbnode_t *node) {
203 204 205 206
	dns_rdataset_t rds;
	dns_rdatasetiter_t *iter = NULL;
	isc_buffer_t *buffer = NULL;
	isc_region_t r;
Brian Wellington's avatar
Brian Wellington committed
207
	isc_result_t result;
208
	unsigned bufsize = 4096;
Brian Wellington's avatar
Brian Wellington committed
209

210 211
	if (outputformat != dns_masterformat_text)
		return;
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230

	if (!output_dnssec_only) {
		result = dns_master_dumpnodetostream(mctx, gdb, gversion, node,
						     name, masterstyle, fp);
		check_result(result, "dns_master_dumpnodetostream");
		return;
	}

	result = dns_db_allrdatasets(gdb, node, gversion, 0, &iter);
	check_result(result, "dns_db_allrdatasets");

	dns_rdataset_init(&rds);

	result = isc_buffer_allocate(mctx, &buffer, bufsize);
	check_result(result, "isc_buffer_allocate");

	for (result = dns_rdatasetiter_first(iter);
	     result == ISC_R_SUCCESS;
	     result = dns_rdatasetiter_next(iter)) {
Automatic Updater's avatar
Automatic Updater committed
231

232 233 234 235 236 237 238 239 240 241 242
		dns_rdatasetiter_current(iter, &rds);

		if (rds.type != dns_rdatatype_rrsig &&
		    rds.type != dns_rdatatype_nsec &&
		    rds.type != dns_rdatatype_nsec3 &&
		    rds.type != dns_rdatatype_nsec3param &&
		    (!smartsign || rds.type != dns_rdatatype_dnskey)) {
			dns_rdataset_disassociate(&rds);
			continue;
		}

243
		for (;;) {
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
			result = dns_master_rdatasettotext(name, &rds,
							   masterstyle, buffer);
			if (result != ISC_R_NOSPACE)
				break;

			bufsize <<= 1;
			isc_buffer_free(&buffer);
			result = isc_buffer_allocate(mctx, &buffer, bufsize);
			check_result(result, "isc_buffer_allocate");
		}
		check_result(result, "dns_master_rdatasettotext");

		isc_buffer_usedregion(buffer, &r);
		result = isc_stdio_write(r.base, 1, r.length, fp, NULL);
		check_result(result, "isc_stdio_write");
		isc_buffer_clear(buffer);

		dns_rdataset_disassociate(&rds);
	}

	isc_buffer_free(&buffer);
	dns_rdatasetiter_destroy(&iter);
Brian Wellington's avatar
Brian Wellington committed
266 267
}

268
/*%
Mark Andrews's avatar
Mark Andrews committed
269
 * Sign the given RRset with given key, and add the signature record to the
270 271
 * given tuple.
 */
272
static void
273 274
signwithkey(dns_name_t *name, dns_rdataset_t *rdataset, dst_key_t *key,
	    dns_ttl_t ttl, dns_diff_t *add, const char *logmsg)
Brian Wellington's avatar
Brian Wellington committed
275 276
{
	isc_result_t result;
277
	isc_stdtime_t jendtime, expiry;
278
	char keystr[DST_KEY_FORMATSIZE];
279
	dns_rdata_t trdata = DNS_RDATA_INIT;
280 281
	unsigned char array[BUFSIZE];
	isc_buffer_t b;
282 283
	dns_difftuple_t *tuple;

284
	dst_key_format(key, keystr, sizeof(keystr));
285
	vbprintf(1, "\t%s %s\n", logmsg, keystr);
Brian Wellington's avatar
Brian Wellington committed
286

287 288 289 290 291 292
	if (rdataset->type == dns_rdatatype_dnskey)
		expiry = dnskey_endtime;
	else
		expiry = endtime;

	jendtime = (jitter != 0) ? isc_random_jitter(expiry, jitter) : expiry;
293
	isc_buffer_init(&b, array, sizeof(array));
294
	result = dns_dnssec_sign(name, rdataset, key, &starttime, &jendtime,
295
				 mctx, &b, &trdata);
296
	isc_entropy_stopcallbacksources(ectx);
297
	if (result != ISC_R_SUCCESS) {
298 299
		char keystr[DST_KEY_FORMATSIZE];
		dst_key_format(key, keystr, sizeof(keystr));
300
		fatal("dnskey '%s' failed to sign data: %s",
301 302
		      keystr, isc_result_totext(result));
	}
303
	INCSTAT(nsigned);
304

305
	if (tryverify) {
306
		result = dns_dnssec_verify(name, rdataset, key,
307
					   ISC_TRUE, mctx, &trdata);
308
		if (result == ISC_R_SUCCESS) {
309
			vbprintf(3, "\tsignature verified\n");
310
			INCSTAT(nverified);
311
		} else {
312
			vbprintf(3, "\tsignature failed to verify\n");
313
			INCSTAT(nverifyfailed);
314
		}
315
	}
316 317

	tuple = NULL;
318 319
	result = dns_difftuple_create(mctx, DNS_DIFFOP_ADDRESIGN,
				      name, ttl, &trdata, &tuple);
320 321
	check_result(result, "dns_difftuple_create");
	dns_diff_append(add, &tuple);
322
}
Brian Wellington's avatar
Brian Wellington committed
323

324
static inline isc_boolean_t
325 326
issigningkey(dns_dnsseckey_t *key) {
	return (key->force_sign || key->hint_sign);
Brian Wellington's avatar
Brian Wellington committed
327 328
}

329 330 331 332 333 334
static inline isc_boolean_t
ispublishedkey(dns_dnsseckey_t *key) {
	return ((key->force_publish || key->hint_publish) &&
		!key->hint_remove);
}

335
static inline isc_boolean_t
336
iszonekey(dns_dnsseckey_t *key) {
337
	return (ISC_TF(dns_name_equal(dst_key_name(key->key), gorigin) &&
338
		       dst_key_iszonekey(key->key)));
339 340
}

341 342 343 344 345 346 347 348 349 350
static inline isc_boolean_t
isksk(dns_dnsseckey_t *key) {
	return (key->ksk);
}

static inline isc_boolean_t
iszsk(dns_dnsseckey_t *key) {
	return (ignore_kskflag || !key->ksk);
}

351
/*%
352 353 354
 * Find the key that generated an RRSIG, if it is in the key list.  If
 * so, return a pointer to it, otherwise return NULL.
 *
355
 * No locking is performed here, this must be done by the caller.
356
 */
357
static dns_dnsseckey_t *
358
keythatsigned_unlocked(dns_rdata_rrsig_t *rrsig) {
359
	dns_dnsseckey_t *key;
360

361 362 363
	for (key = ISC_LIST_HEAD(keylist);
	     key != NULL;
	     key = ISC_LIST_NEXT(key, link)) {
364 365 366
		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)))
367
			return (key);
368
	}
369 370 371 372 373 374 375
	return (NULL);
}

/*%
 * Finds the key that generated a RRSIG, if possible.  First look at the keys
 * that we've loaded already, and then see if there's a key on disk.
 */
376
static dns_dnsseckey_t *
377 378 379
keythatsigned(dns_rdata_rrsig_t *rrsig) {
	isc_result_t result;
	dst_key_t *pubkey = NULL, *privkey = NULL;
380
	dns_dnsseckey_t *key = NULL;
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400

	isc_rwlock_lock(&keylist_lock, isc_rwlocktype_read);
	key = keythatsigned_unlocked(rrsig);
	isc_rwlock_unlock(&keylist_lock, isc_rwlocktype_read);
	if (key != NULL)
		return (key);

	/*
	 * We did not find the key in our list.  Get a write lock now, since
	 * we may be modifying the bits.  We could do the tryupgrade() dance,
	 * but instead just get a write lock and check once again to see if
	 * it is on our list.  It's possible someone else may have added it
	 * after all.
	 */
	isc_rwlock_lock(&keylist_lock, isc_rwlocktype_write);
	key = keythatsigned_unlocked(rrsig);
	if (key != NULL) {
		isc_rwlock_unlock(&keylist_lock, isc_rwlocktype_write);
		return (key);
	}
401

402 403
	result = dst_key_fromfile(&rrsig->signer, rrsig->keyid,
				  rrsig->algorithm, DST_TYPE_PUBLIC,
404
				  directory, mctx, &pubkey);
405 406
	if (result != ISC_R_SUCCESS) {
		isc_rwlock_unlock(&keylist_lock, isc_rwlocktype_write);
407
		return (NULL);
408
	}
409

410 411
	result = dst_key_fromfile(&rrsig->signer, rrsig->keyid,
				  rrsig->algorithm,
412
				  DST_TYPE_PUBLIC | DST_TYPE_PRIVATE,
413
				  directory, mctx, &privkey);
414
	if (result == ISC_R_SUCCESS) {
415
		dst_key_free(&pubkey);
416 417 418
		result = dns_dnsseckey_create(mctx, &privkey, &key);
	} else
		result = dns_dnsseckey_create(mctx, &pubkey, &key);
419

420
	if (result == ISC_R_SUCCESS) {
421
		key->force_publish = ISC_FALSE;
422
		key->force_sign = ISC_FALSE;
423
		key->index = keycount++;
424 425
		ISC_LIST_APPEND(keylist, key, link);
	}
426 427

	isc_rwlock_unlock(&keylist_lock, isc_rwlocktype_write);
428
	return (key);
429 430
}

431
/*%
432 433
 * 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.
434 435
 * I'm not sure if this is completely correct, but it seems to work.
 */
436
static isc_boolean_t
437
expecttofindkey(dns_name_t *name) {
438
	unsigned int options = DNS_DBFIND_NOWILD;
439
	dns_fixedname_t fname;
440
	isc_result_t result;
441
	char namestr[DNS_NAME_FORMATSIZE];
442

443
	dns_fixedname_init(&fname);
444
	result = dns_db_find(gdb, name, gversion, dns_rdatatype_dnskey, options,
445
			     0, NULL, dns_fixedname_name(&fname), NULL, NULL);
446
	switch (result) {
447 448 449 450 451 452 453 454
	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);
455
	}
Andreas Gustafsson's avatar
Andreas Gustafsson committed
456
	dns_name_format(name, namestr, sizeof(namestr));
457
	fatal("failure looking for '%s DNSKEY' in database: %s",
458
	      namestr, isc_result_totext(result));
Evan Hunt's avatar
Evan Hunt committed
459
	/* NOTREACHED */
460
	return (ISC_FALSE); /* removes a warning */
461 462
}

463
static inline isc_boolean_t
464
setverifies(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
465
	    dns_rdata_t *rrsig)
466
{
467
	isc_result_t result;
468
	result = dns_dnssec_verify(name, set, key, ISC_FALSE, mctx, rrsig);
469
	if (result == ISC_R_SUCCESS) {
470
		INCSTAT(nverified);
471 472
		return (ISC_TRUE);
	} else {
473
		INCSTAT(nverifyfailed);
474 475
		return (ISC_FALSE);
	}
476 477
}

478
/*%
479
 * Signs a set.  Goes through contortions to decide if each RRSIG should
480 481 482
 * be dropped or retained, and then determines if any new SIGs need to
 * be generated.
 */
483
static void
484
signset(dns_diff_t *del, dns_diff_t *add, dns_dbnode_t *node, dns_name_t *name,
485
	dns_rdataset_t *set)
486
{
487
	dns_rdataset_t sigset;
488
	dns_rdata_t sigrdata = DNS_RDATA_INIT;
489
	dns_rdata_rrsig_t rrsig;
490
	dns_dnsseckey_t *key;
491
	isc_result_t result;
492 493 494
	isc_boolean_t nosigs = ISC_FALSE;
	isc_boolean_t *wassignedby, *nowsignedby;
	int arraysize;
495 496
	dns_difftuple_t *tuple;
	dns_ttl_t ttl;
497
	int i;
498 499 500 501
	char namestr[DNS_NAME_FORMATSIZE];
	char typestr[TYPE_FORMATSIZE];
	char sigstr[SIG_FORMATSIZE];

Andreas Gustafsson's avatar
Andreas Gustafsson committed
502 503
	dns_name_format(name, namestr, sizeof(namestr));
	type_format(set->type, typestr, sizeof(typestr));
504

505
	ttl = ISC_MIN(set->ttl, endtime - starttime);
506

507
	dns_rdataset_init(&sigset);
508
	result = dns_db_findrdataset(gdb, node, gversion, dns_rdatatype_rrsig,
509
				     set->type, 0, &sigset, NULL);
510 511 512 513
	if (result == ISC_R_NOTFOUND) {
		result = ISC_R_SUCCESS;
		nosigs = ISC_TRUE;
	}
514
	if (result != ISC_R_SUCCESS)
515
		fatal("failed while looking for '%s RRSIG %s': %s",
516
		      namestr, typestr, isc_result_totext(result));
517

518
	vbprintf(1, "%s/%s:\n", namestr, typestr);
519

520 521 522
	arraysize = keycount;
	if (!nosigs)
		arraysize += dns_rdataset_count(&sigset);
523 524
	wassignedby = isc_mem_get(mctx, arraysize * sizeof(isc_boolean_t));
	nowsignedby = isc_mem_get(mctx, arraysize * sizeof(isc_boolean_t));
525 526 527 528 529 530
	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
531 532 533
	if (nosigs)
		result = ISC_R_NOMORE;
	else
534
		result = dns_rdataset_first(&sigset);
535

Brian Wellington's avatar
Brian Wellington committed
536 537 538
	while (result == ISC_R_SUCCESS) {
		isc_boolean_t expired, future;
		isc_boolean_t keep = ISC_FALSE, resign = ISC_FALSE;
539

Brian Wellington's avatar
Brian Wellington committed
540
		dns_rdataset_current(&sigset, &sigrdata);
541

542
		result = dns_rdata_tostruct(&sigrdata, &rrsig, NULL);
Brian Wellington's avatar
Brian Wellington committed
543
		check_result(result, "dns_rdata_tostruct");
544

545
		future = isc_serial_lt(now, rrsig.timesigned);
546

547 548
		key = keythatsigned(&rrsig);
		sig_format(&rrsig, sigstr, sizeof(sigstr));
549
		if (key != NULL && issigningkey(key))
550
			expired = isc_serial_gt(now + cycle, rrsig.timeexpire);
551
		else
552
			expired = isc_serial_gt(now, rrsig.timeexpire);
Brian Wellington's avatar
Brian Wellington committed
553

554 555 556
		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
557
				 "invalid validity period\n",
558
				 sigstr);
Brian Wellington's avatar
Brian Wellington committed
559
		} else if (key == NULL && !future &&
Francis Dupont's avatar
Francis Dupont committed
560
			   expecttofindkey(&rrsig.signer)) {
561 562 563
			/* rrsig is dropped and not replaced */
			vbprintf(2, "\trrsig by %s dropped - "
				 "private dnskey not found\n",
564
				 sigstr);
Brian Wellington's avatar
Brian Wellington committed
565
		} else if (key == NULL || future) {
Evan Hunt's avatar
Evan Hunt committed
566
			keep = (!expired && !remove_orphansigs);
567
			vbprintf(2, "\trrsig by %s %s - dnskey not found\n",
568
				 keep ? "retained" : "dropped", sigstr);
Evan Hunt's avatar
Evan Hunt committed
569 570 571 572 573
		} else if (!dns_dnssec_keyactive(key->key, now) &&
			   remove_inactkeysigs) {
			keep = ISC_FALSE;
			vbprintf(2, "\trrsig by %s dropped - key inactive\n",
				 sigstr);
Brian Wellington's avatar
Brian Wellington committed
574
		} else if (issigningkey(key)) {
575 576
			wassignedby[key->index] = ISC_TRUE;

577 578
			if (!expired && rrsig.originalttl == set->ttl &&
			    setverifies(name, set, key->key, &sigrdata)) {
579
				vbprintf(2, "\trrsig by %s retained\n", sigstr);
580
				keep = ISC_TRUE;
581
			} else {
582
				vbprintf(2, "\trrsig by %s dropped - %s\n",
583 584 585
					 sigstr, expired ? "expired" :
					 rrsig.originalttl != set->ttl ?
					 "ttl change" : "failed to verify");
Brian Wellington's avatar
Brian Wellington committed
586
				resign = ISC_TRUE;
587
			}
Evan Hunt's avatar
Evan Hunt committed
588
		} else if (!ispublishedkey(key) && remove_orphansigs) {
589 590
			vbprintf(2, "\trrsig by %s dropped - dnskey removed\n",
				 sigstr);
591
		} else if (iszonekey(key)) {
592 593
			wassignedby[key->index] = ISC_TRUE;

594 595
			if (!expired && rrsig.originalttl == set->ttl &&
			    setverifies(name, set, key->key, &sigrdata)) {
596
				vbprintf(2, "\trrsig by %s retained\n", sigstr);
Brian Wellington's avatar
Brian Wellington committed
597 598
				keep = ISC_TRUE;
			} else {
599
				vbprintf(2, "\trrsig by %s dropped - %s\n",
600 601 602
					 sigstr, expired ? "expired" :
					 rrsig.originalttl != set->ttl ?
					 "ttl change" : "failed to verify");
603
			}
Brian Wellington's avatar
Brian Wellington committed
604
		} else if (!expired) {
605
			vbprintf(2, "\trrsig by %s retained\n", sigstr);
Brian Wellington's avatar
Brian Wellington committed
606 607
			keep = ISC_TRUE;
		} else {
608
			vbprintf(2, "\trrsig by %s expired\n", sigstr);
Brian Wellington's avatar
Brian Wellington committed
609
		}
610

611
		if (keep) {
612 613
			if (key != NULL)
				nowsignedby[key->index] = ISC_TRUE;
614
			INCSTAT(nretained);
615 616 617 618
			if (sigset.ttl != ttl) {
				vbprintf(2, "\tfixing ttl %s\n", sigstr);
				tuple = NULL;
				result = dns_difftuple_create(mctx,
619 620 621
						      DNS_DIFFOP_DELRESIGN,
						      name, sigset.ttl,
						      &sigrdata, &tuple);
622 623 624
				check_result(result, "dns_difftuple_create");
				dns_diff_append(del, &tuple);
				result = dns_difftuple_create(mctx,
625 626 627
						      DNS_DIFFOP_ADDRESIGN,
						      name, ttl,
						      &sigrdata, &tuple);
628 629 630
				check_result(result, "dns_difftuple_create");
				dns_diff_append(add, &tuple);
			}
631
		} else {
Brian Wellington's avatar
Brian Wellington committed
632
			tuple = NULL;
633
			vbprintf(2, "removing signature by %s\n", sigstr);
634 635
			result = dns_difftuple_create(mctx,
						      DNS_DIFFOP_DELRESIGN,
636 637
						      name, sigset.ttl,
						      &sigrdata, &tuple);
Brian Wellington's avatar
Brian Wellington committed
638
			check_result(result, "dns_difftuple_create");
639
			dns_diff_append(del, &tuple);
640
			INCSTAT(ndropped);
Brian Wellington's avatar
Brian Wellington committed
641 642 643
		}

		if (resign) {
644 645
			INSIST(!keep);

646 647
			signwithkey(name, set, key->key, ttl, add,
				    "resigning with dnskey");
648
			nowsignedby[key->index] = ISC_TRUE;
649
		}
Brian Wellington's avatar
Brian Wellington committed
650

651
		dns_rdata_reset(&sigrdata);
652
		dns_rdata_freestruct(&rrsig);
Brian Wellington's avatar
Brian Wellington committed
653
		result = dns_rdataset_next(&sigset);
Brian Wellington's avatar
Brian Wellington committed
654
	}
Brian Wellington's avatar
Brian Wellington committed
655 656 657 658 659 660
	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);
661

Brian Wellington's avatar
Brian Wellington committed
662 663 664 665
	for (key = ISC_LIST_HEAD(keylist);
	     key != NULL;
	     key = ISC_LIST_NEXT(key, link))
	{
666
		if (nowsignedby[key->index])
667 668
			continue;

669
		if (!issigningkey(key))
Brian Wellington's avatar
Brian Wellington committed
670 671
			continue;

672 673
		if (set->type == dns_rdatatype_dnskey &&
		     dns_name_equal(name, gorigin)) {
Evan Hunt's avatar
Evan Hunt committed
674
			isc_boolean_t have_ksk;
675 676
			dns_dnsseckey_t *tmpkey;

Automatic Updater's avatar
Automatic Updater committed
677
			have_ksk = isksk(key);
678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
			for (tmpkey = ISC_LIST_HEAD(keylist);
			     tmpkey != NULL;
			     tmpkey = ISC_LIST_NEXT(tmpkey, link)) {
				if (dst_key_alg(key->key) !=
				    dst_key_alg(tmpkey->key))
					continue;
				if (REVOKE(tmpkey->key))
					continue;
				if (isksk(tmpkey))
					have_ksk = ISC_TRUE;
			}
			if (isksk(key) || !have_ksk ||
			    (iszsk(key) && !keyset_kskonly))
				signwithkey(name, set, key->key, ttl, add,
					    "signing with dnskey");
		} else if (iszsk(key)) {
694 695
			signwithkey(name, set, key->key, ttl, add,
				    "signing with dnskey");
696
		}
697
	}
698 699 700

	isc_mem_put(mctx, wassignedby, arraysize * sizeof(isc_boolean_t));
	isc_mem_put(mctx, nowsignedby, arraysize * sizeof(isc_boolean_t));
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 732 733 734 735
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);
736 737
		if (l->hashbuf == NULL)
			fatal("unable to grow hashlist: out of memory");
738 739
	}
	memset(l->hashbuf + l->entries * l->length, 0, l->length);
740
	memmove(l->hashbuf + l->entries * l->length, hash, len);
741 742 743 744 745 746 747 748 749 750 751 752 753 754
	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;

755 756
	len = isc_iterated_hash(hash, hashalg, iterations,
				salt, (int)salt_length,
757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786
				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
787
	while (entries > 0U && next[l->length-1] != 0U) {
788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807
		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])
{
808
	size_t entries = l->entries;
809 810 811 812 813 814 815 816 817 818 819
	const unsigned