dnssec-signzone.c 109 KB
Newer Older
Michael Graff's avatar
Michael Graff committed
1
/*
2
 * Portions Copyright (C) 1999-2017  Internet Systems Consortium, Inc. ("ISC")
Automatic Updater's avatar
Automatic Updater committed
3
 *
4 5 6
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
Automatic Updater's avatar
Automatic Updater committed
7
 *
8
 * Portions Copyright (C) 1995-2000 by Network Associates, Inc.
9
 *
Automatic Updater's avatar
Automatic Updater committed
10
 * Permission to use, copy, modify, and/or distribute this software for any
Michael Graff's avatar
Michael Graff committed
11 12
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
13
 *
Mark Andrews's avatar
Mark Andrews committed
14 15 16 17 18 19 20
 * 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
21
 */
Brian Wellington's avatar
Brian Wellington committed
22

23
/*! \file */
David Lawrence's avatar
David Lawrence committed
24

Brian Wellington's avatar
Brian Wellington committed
25 26 27
#include <config.h>

#include <stdlib.h>
28
#include <time.h>
29
#include <unistd.h>
Brian Wellington's avatar
Brian Wellington committed
30

31
#include <isc/app.h>
32
#include <isc/base32.h>
33
#include <isc/commandline.h>
Brian Wellington's avatar
Brian Wellington committed
34
#include <isc/entropy.h>
35
#include <isc/event.h>
36
#include <isc/file.h>
37
#include <isc/hash.h>
38
#include <isc/hex.h>
Brian Wellington's avatar
Brian Wellington committed
39
#include <isc/mem.h>
40 41
#include <isc/mutex.h>
#include <isc/os.h>
42
#include <isc/print.h>
43
#include <isc/random.h>
44
#include <isc/rwlock.h>
45
#include <isc/serial.h>
46
#include <isc/safe.h>
47
#include <isc/stdio.h>
48
#include <isc/stdlib.h>
49
#include <isc/string.h>
50
#include <isc/task.h>
51
#include <isc/time.h>
52
#include <isc/util.h>
Brian Wellington's avatar
Brian Wellington committed
53 54 55

#include <dns/db.h>
#include <dns/dbiterator.h>
56
#include <dns/diff.h>
57
#include <dns/dnssec.h>
58
#include <dns/ds.h>
59
#include <dns/fixedname.h>
60 61
#include <dns/keyvalues.h>
#include <dns/log.h>
62 63
#include <dns/master.h>
#include <dns/masterdump.h>
64
#include <dns/nsec.h>
65
#include <dns/nsec3.h>
Brian Wellington's avatar
Brian Wellington committed
66
#include <dns/rdata.h>
67
#include <dns/rdatalist.h>
Brian Wellington's avatar
Brian Wellington committed
68
#include <dns/rdataset.h>
69
#include <dns/rdataclass.h>
Brian Wellington's avatar
Brian Wellington committed
70
#include <dns/rdatasetiter.h>
71
#include <dns/rdatastruct.h>
72
#include <dns/rdatatype.h>
Brian Wellington's avatar
Brian Wellington committed
73
#include <dns/result.h>
74
#include <dns/soa.h>
75
#include <dns/time.h>
Evan Hunt's avatar
Evan Hunt committed
76
#include <dns/update.h>
Brian Wellington's avatar
Brian Wellington committed
77

78
#include <dst/dst.h>
Brian Wellington's avatar
Brian Wellington committed
79

80 81 82 83
#ifdef PKCS11CRYPTO
#include <pk11/result.h>
#endif

84 85
#include "dnssectool.h"

Evan Hunt's avatar
Evan Hunt committed
86 87 88 89
#ifndef PATH_MAX
#define PATH_MAX 1024   /* AIX, WIN32, and others don't define this. */
#endif

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

93 94 95 96 97 98 99
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)

100 101
#define REVOKE(x) ((dst_key_flags(x) & DNS_KEYFLAG_REVOKE) != 0)

102
#define BUFSIZE 2048
103
#define MAXDSKEYS 8
Brian Wellington's avatar
Brian Wellington committed
104

105 106 107 108
#define SIGNER_EVENTCLASS	ISC_EVENTCLASS(0x4453)
#define SIGNER_EVENT_WRITE	(SIGNER_EVENTCLASS + 0)
#define SIGNER_EVENT_WORK	(SIGNER_EVENTCLASS + 1)

109 110 111
#define SOA_SERIAL_KEEP		0
#define SOA_SERIAL_INCREMENT	1
#define SOA_SERIAL_UNIXTIME	2
Evan Hunt's avatar
Evan Hunt committed
112
#define SOA_SERIAL_DATE		3
113

114 115 116 117 118 119 120
typedef struct signer_event sevent_t;
struct signer_event {
	ISC_EVENT_COMMON(sevent_t);
	dns_fixedname_t *fname;
	dns_dbnode_t *node;
};

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

#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
191 192
static void
dumpnode(dns_name_t *name, dns_dbnode_t *node) {
193 194 195 196
	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
197
	isc_result_t result;
198
	unsigned bufsize = 4096;
Brian Wellington's avatar
Brian Wellington committed
199

200 201
	if (outputformat != dns_masterformat_text)
		return;
202 203 204

	if (!output_dnssec_only) {
		result = dns_master_dumpnodetostream(mctx, gdb, gversion, node,
205
						     name, masterstyle, outfp);
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
		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
221

222 223 224 225 226 227 228 229 230 231 232
		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;
		}

233
		for (;;) {
234 235 236 237 238 239 240 241 242 243 244 245 246
			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);
247
		result = isc_stdio_write(r.base, 1, r.length, outfp, NULL);
248 249 250 251 252 253 254 255
		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
256 257
}

258
/*%
Mark Andrews's avatar
Mark Andrews committed
259
 * Sign the given RRset with given key, and add the signature record to the
260 261
 * given tuple.
 */
262
static void
263 264
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
265 266
{
	isc_result_t result;
267
	isc_stdtime_t jendtime, expiry;
268
	char keystr[DST_KEY_FORMATSIZE];
269
	dns_rdata_t trdata = DNS_RDATA_INIT;
270 271
	unsigned char array[BUFSIZE];
	isc_buffer_t b;
272 273
	dns_difftuple_t *tuple;

274
	dst_key_format(key, keystr, sizeof(keystr));
275
	vbprintf(1, "\t%s %s\n", logmsg, keystr);
Brian Wellington's avatar
Brian Wellington committed
276

277 278 279 280 281 282
	if (rdataset->type == dns_rdatatype_dnskey)
		expiry = dnskey_endtime;
	else
		expiry = endtime;

	jendtime = (jitter != 0) ? isc_random_jitter(expiry, jitter) : expiry;
283
	isc_buffer_init(&b, array, sizeof(array));
284
	result = dns_dnssec_sign(name, rdataset, key, &starttime, &jendtime,
285
				 mctx, &b, &trdata);
286
	isc_entropy_stopcallbacksources(ectx);
287
	if (result != ISC_R_SUCCESS) {
288
		fatal("dnskey '%s' failed to sign data: %s",
289 290
		      keystr, isc_result_totext(result));
	}
291
	INCSTAT(nsigned);
292

293
	if (tryverify) {
294
		result = dns_dnssec_verify(name, rdataset, key,
295
					   ISC_TRUE, mctx, &trdata);
296
		if (result == ISC_R_SUCCESS) {
297
			vbprintf(3, "\tsignature verified\n");
298
			INCSTAT(nverified);
299
		} else {
300
			vbprintf(3, "\tsignature failed to verify\n");
301
			INCSTAT(nverifyfailed);
302
		}
303
	}
304 305

	tuple = NULL;
306 307
	result = dns_difftuple_create(mctx, DNS_DIFFOP_ADDRESIGN,
				      name, ttl, &trdata, &tuple);
308 309
	check_result(result, "dns_difftuple_create");
	dns_diff_append(add, &tuple);
310
}
Brian Wellington's avatar
Brian Wellington committed
311

312
static inline isc_boolean_t
313 314
issigningkey(dns_dnsseckey_t *key) {
	return (key->force_sign || key->hint_sign);
Brian Wellington's avatar
Brian Wellington committed
315 316
}

317 318 319 320 321 322
static inline isc_boolean_t
ispublishedkey(dns_dnsseckey_t *key) {
	return ((key->force_publish || key->hint_publish) &&
		!key->hint_remove);
}

323
static inline isc_boolean_t
324
iszonekey(dns_dnsseckey_t *key) {
325
	return (ISC_TF(dns_name_equal(dst_key_name(key->key), gorigin) &&
326
		       dst_key_iszonekey(key->key)));
327 328
}

329 330 331 332 333 334 335 336 337 338
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);
}

339
/*%
340 341 342
 * Find the key that generated an RRSIG, if it is in the key list.  If
 * so, return a pointer to it, otherwise return NULL.
 *
343
 * No locking is performed here, this must be done by the caller.
344
 */
345
static dns_dnsseckey_t *
346
keythatsigned_unlocked(dns_rdata_rrsig_t *rrsig) {
347
	dns_dnsseckey_t *key;
348

349 350 351
	for (key = ISC_LIST_HEAD(keylist);
	     key != NULL;
	     key = ISC_LIST_NEXT(key, link)) {
352 353 354
		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)))
355
			return (key);
356
	}
357 358 359 360 361 362 363
	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.
 */
364
static dns_dnsseckey_t *
365 366 367
keythatsigned(dns_rdata_rrsig_t *rrsig) {
	isc_result_t result;
	dst_key_t *pubkey = NULL, *privkey = NULL;
368
	dns_dnsseckey_t *key = NULL;
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388

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

390 391
	result = dst_key_fromfile(&rrsig->signer, rrsig->keyid,
				  rrsig->algorithm, DST_TYPE_PUBLIC,
392
				  directory, mctx, &pubkey);
393 394
	if (result != ISC_R_SUCCESS) {
		isc_rwlock_unlock(&keylist_lock, isc_rwlocktype_write);
395
		return (NULL);
396
	}
397

398 399
	result = dst_key_fromfile(&rrsig->signer, rrsig->keyid,
				  rrsig->algorithm,
400
				  DST_TYPE_PUBLIC | DST_TYPE_PRIVATE,
401
				  directory, mctx, &privkey);
402
	if (result == ISC_R_SUCCESS) {
403
		dst_key_free(&pubkey);
404 405 406
		result = dns_dnsseckey_create(mctx, &privkey, &key);
	} else
		result = dns_dnsseckey_create(mctx, &pubkey, &key);
407

408
	if (result == ISC_R_SUCCESS) {
409
		key->force_publish = ISC_FALSE;
410
		key->force_sign = ISC_FALSE;
411
		key->index = keycount++;
412 413
		ISC_LIST_APPEND(keylist, key, link);
	}
414 415

	isc_rwlock_unlock(&keylist_lock, isc_rwlocktype_write);
416
	return (key);
417 418
}

419
/*%
420 421
 * 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.
422 423
 * I'm not sure if this is completely correct, but it seems to work.
 */
424
static isc_boolean_t
425
expecttofindkey(dns_name_t *name) {
426
	unsigned int options = DNS_DBFIND_NOWILD;
427
	dns_fixedname_t fname;
428
	isc_result_t result;
429
	char namestr[DNS_NAME_FORMATSIZE];
430

431
	dns_fixedname_init(&fname);
432
	result = dns_db_find(gdb, name, gversion, dns_rdatatype_dnskey, options,
433
			     0, NULL, dns_fixedname_name(&fname), NULL, NULL);
434
	switch (result) {
435 436 437 438 439 440 441 442
	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);
443
	}
Andreas Gustafsson's avatar
Andreas Gustafsson committed
444
	dns_name_format(name, namestr, sizeof(namestr));
445
	fatal("failure looking for '%s DNSKEY' in database: %s",
446
	      namestr, isc_result_totext(result));
Evan Hunt's avatar
Evan Hunt committed
447
	/* NOTREACHED */
448
	return (ISC_FALSE); /* removes a warning */
449 450
}

451
static inline isc_boolean_t
452
setverifies(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
453
	    dns_rdata_t *rrsig)
454
{
455
	isc_result_t result;
456
	result = dns_dnssec_verify(name, set, key, ISC_FALSE, mctx, rrsig);
457
	if (result == ISC_R_SUCCESS) {
458
		INCSTAT(nverified);
459 460
		return (ISC_TRUE);
	} else {
461
		INCSTAT(nverifyfailed);
462 463
		return (ISC_FALSE);
	}
464 465
}

466
/*%
467
 * Signs a set.  Goes through contortions to decide if each RRSIG should
468 469 470
 * be dropped or retained, and then determines if any new SIGs need to
 * be generated.
 */
471
static void
472
signset(dns_diff_t *del, dns_diff_t *add, dns_dbnode_t *node, dns_name_t *name,
473
	dns_rdataset_t *set)
474
{
475
	dns_rdataset_t sigset;
476
	dns_rdata_t sigrdata = DNS_RDATA_INIT;
477
	dns_rdata_rrsig_t rrsig;
478
	dns_dnsseckey_t *key;
479
	isc_result_t result;
480 481 482
	isc_boolean_t nosigs = ISC_FALSE;
	isc_boolean_t *wassignedby, *nowsignedby;
	int arraysize;
483 484
	dns_difftuple_t *tuple;
	dns_ttl_t ttl;
485
	int i;
486 487 488 489
	char namestr[DNS_NAME_FORMATSIZE];
	char typestr[TYPE_FORMATSIZE];
	char sigstr[SIG_FORMATSIZE];

Andreas Gustafsson's avatar
Andreas Gustafsson committed
490 491
	dns_name_format(name, namestr, sizeof(namestr));
	type_format(set->type, typestr, sizeof(typestr));
492

493
	ttl = ISC_MIN(set->ttl, endtime - starttime);
494

495
	dns_rdataset_init(&sigset);
496
	result = dns_db_findrdataset(gdb, node, gversion, dns_rdatatype_rrsig,
497
				     set->type, 0, &sigset, NULL);
498
	if (result == ISC_R_NOTFOUND) {
499 500
		vbprintf(2, "no existing signatures for %s/%s\n",
			 namestr, typestr);
501 502 503
		result = ISC_R_SUCCESS;
		nosigs = ISC_TRUE;
	}
504
	if (result != ISC_R_SUCCESS)
505
		fatal("failed while looking for '%s RRSIG %s': %s",
506
		      namestr, typestr, isc_result_totext(result));
507

508
	vbprintf(1, "%s/%s:\n", namestr, typestr);
509

510 511 512
	arraysize = keycount;
	if (!nosigs)
		arraysize += dns_rdataset_count(&sigset);
513 514
	wassignedby = isc_mem_get(mctx, arraysize * sizeof(isc_boolean_t));
	nowsignedby = isc_mem_get(mctx, arraysize * sizeof(isc_boolean_t));
515 516 517 518 519 520
	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
521 522 523
	if (nosigs)
		result = ISC_R_NOMORE;
	else
524
		result = dns_rdataset_first(&sigset);
525

Brian Wellington's avatar
Brian Wellington committed
526 527 528
	while (result == ISC_R_SUCCESS) {
		isc_boolean_t expired, future;
		isc_boolean_t keep = ISC_FALSE, resign = ISC_FALSE;
529

Brian Wellington's avatar
Brian Wellington committed
530
		dns_rdataset_current(&sigset, &sigrdata);
531

532
		result = dns_rdata_tostruct(&sigrdata, &rrsig, NULL);
Brian Wellington's avatar
Brian Wellington committed
533
		check_result(result, "dns_rdata_tostruct");
534

535
		future = isc_serial_lt(now, rrsig.timesigned);
536

537 538
		key = keythatsigned(&rrsig);
		sig_format(&rrsig, sigstr, sizeof(sigstr));
539
		if (key != NULL && issigningkey(key))
540
			expired = isc_serial_gt(now + cycle, rrsig.timeexpire);
541
		else
542
			expired = isc_serial_gt(now, rrsig.timeexpire);
Brian Wellington's avatar
Brian Wellington committed
543

544 545 546
		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
547
				 "invalid validity period\n",
548
				 sigstr);
Brian Wellington's avatar
Brian Wellington committed
549
		} else if (key == NULL && !future &&
Francis Dupont's avatar
Francis Dupont committed
550
			   expecttofindkey(&rrsig.signer)) {
551 552 553
			/* rrsig is dropped and not replaced */
			vbprintf(2, "\trrsig by %s dropped - "
				 "private dnskey not found\n",
554
				 sigstr);
Brian Wellington's avatar
Brian Wellington committed
555
		} else if (key == NULL || future) {
Evan Hunt's avatar
Evan Hunt committed
556
			keep = (!expired && !remove_orphansigs);
557
			vbprintf(2, "\trrsig by %s %s - dnskey not found\n",
558
				 keep ? "retained" : "dropped", sigstr);
Evan Hunt's avatar
Evan Hunt committed
559 560 561 562 563
		} 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
564
		} else if (issigningkey(key)) {
565 566
			wassignedby[key->index] = ISC_TRUE;

567 568
			if (!expired && rrsig.originalttl == set->ttl &&
			    setverifies(name, set, key->key, &sigrdata)) {
569
				vbprintf(2, "\trrsig by %s retained\n", sigstr);
570
				keep = ISC_TRUE;
571
			} else {
572
				vbprintf(2, "\trrsig by %s dropped - %s\n",
573 574 575
					 sigstr, expired ? "expired" :
					 rrsig.originalttl != set->ttl ?
					 "ttl change" : "failed to verify");
Brian Wellington's avatar
Brian Wellington committed
576
				resign = ISC_TRUE;
577
			}
Evan Hunt's avatar
Evan Hunt committed
578
		} else if (!ispublishedkey(key) && remove_orphansigs) {
579 580
			vbprintf(2, "\trrsig by %s dropped - dnskey removed\n",
				 sigstr);
581
		} else if (iszonekey(key)) {
582 583
			wassignedby[key->index] = ISC_TRUE;

584 585
			if (!expired && rrsig.originalttl == set->ttl &&
			    setverifies(name, set, key->key, &sigrdata)) {
586
				vbprintf(2, "\trrsig by %s retained\n", sigstr);
Brian Wellington's avatar
Brian Wellington committed
587 588
				keep = ISC_TRUE;
			} else {
589
				vbprintf(2, "\trrsig by %s dropped - %s\n",
590 591 592
					 sigstr, expired ? "expired" :
					 rrsig.originalttl != set->ttl ?
					 "ttl change" : "failed to verify");
593
			}
Brian Wellington's avatar
Brian Wellington committed
594
		} else if (!expired) {
595
			vbprintf(2, "\trrsig by %s retained\n", sigstr);
Brian Wellington's avatar
Brian Wellington committed
596 597
			keep = ISC_TRUE;
		} else {
598
			vbprintf(2, "\trrsig by %s expired\n", sigstr);
Brian Wellington's avatar
Brian Wellington committed
599
		}
600

601
		if (keep) {
602 603
			if (key != NULL)
				nowsignedby[key->index] = ISC_TRUE;
604
			INCSTAT(nretained);
605 606 607 608
			if (sigset.ttl != ttl) {
				vbprintf(2, "\tfixing ttl %s\n", sigstr);
				tuple = NULL;
				result = dns_difftuple_create(mctx,
609 610 611
						      DNS_DIFFOP_DELRESIGN,
						      name, sigset.ttl,
						      &sigrdata, &tuple);
612 613 614
				check_result(result, "dns_difftuple_create");
				dns_diff_append(del, &tuple);
				result = dns_difftuple_create(mctx,
615 616 617
						      DNS_DIFFOP_ADDRESIGN,
						      name, ttl,
						      &sigrdata, &tuple);
618 619 620
				check_result(result, "dns_difftuple_create");
				dns_diff_append(add, &tuple);
			}
621
		} else {
Brian Wellington's avatar
Brian Wellington committed
622
			tuple = NULL;
623
			vbprintf(2, "removing signature by %s\n", sigstr);
624 625
			result = dns_difftuple_create(mctx,
						      DNS_DIFFOP_DELRESIGN,
626 627
						      name, sigset.ttl,
						      &sigrdata, &tuple);
Brian Wellington's avatar
Brian Wellington committed
628
			check_result(result, "dns_difftuple_create");
629
			dns_diff_append(del, &tuple);
630
			INCSTAT(ndropped);
Brian Wellington's avatar
Brian Wellington committed
631 632 633
		}

		if (resign) {
634 635
			INSIST(!keep);

636 637
			signwithkey(name, set, key->key, ttl, add,
				    "resigning with dnskey");
638
			nowsignedby[key->index] = ISC_TRUE;
639
		}
Brian Wellington's avatar
Brian Wellington committed
640

641
		dns_rdata_reset(&sigrdata);
642
		dns_rdata_freestruct(&rrsig);
Brian Wellington's avatar
Brian Wellington committed
643
		result = dns_rdataset_next(&sigset);
Brian Wellington's avatar
Brian Wellington committed
644
	}
Brian Wellington's avatar
Brian Wellington committed
645 646 647 648 649 650
	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);
651

Brian Wellington's avatar
Brian Wellington committed
652 653 654 655
	for (key = ISC_LIST_HEAD(keylist);
	     key != NULL;
	     key = ISC_LIST_NEXT(key, link))
	{
656
		if (nowsignedby[key->index])
657 658
			continue;

659
		if (!issigningkey(key))
Brian Wellington's avatar
Brian Wellington committed
660 661
			continue;

662 663 664
		if ((set->type == dns_rdatatype_cds ||
		     set->type == dns_rdatatype_cdnskey ||
		     set->type == dns_rdatatype_dnskey) &&
665
		     dns_name_equal(name, gorigin)) {
Evan Hunt's avatar
Evan Hunt committed
666
			isc_boolean_t have_ksk;
667 668
			dns_dnsseckey_t *tmpkey;

Automatic Updater's avatar
Automatic Updater committed
669
			have_ksk = isksk(key);
670 671 672 673 674 675 676 677 678 679 680 681 682 683 684
			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");
685
		} else if (iszsk(key)) {
686 687
			signwithkey(name, set, key->key, ttl, add,
				    "signing with dnskey");
688
		}
689
	}
690 691 692

	isc_mem_put(mctx, wassignedby, arraysize * sizeof(isc_boolean_t));
	isc_mem_put(mctx, nowsignedby, arraysize * sizeof(isc_boolean_t));
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
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;
	}
}

719 720 721 722 723 724 725 726 727 728 729
static void
hashlist_free(hashlist_t *l) {
	if (l->hashbuf) {
		free(l->hashbuf);
		l->hashbuf = NULL;
		l->entries = 0;
		l->length = 0;
		l->size = 0;
	}
}

730 731 732 733 734 735 736 737 738
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);
739 740
		if (l->hashbuf == NULL)
			fatal("unable to grow hashlist: out of memory");
741 742
	}
	memset(l->hashbuf + l->entries * l->length, 0, l->length);
743
	memmove(l->hashbuf + l->entries * l->length, hash, len);
744 745 746 747 748 749
	l->entries++;
}

static void
hashlist_add_dns_name(hashlist_t *l, /*const*/ dns_name_t *name,
		      unsigned int hashalg, unsigned int iterations,
750
		      const unsigned char *salt, size_t salt_len,
751 752 753 754 755 756 757
		      isc_boolean_t speculative)
{
	char nametext[DNS_NAME_FORMATSIZE];
	unsigned char hash[NSEC3_MAX_HASH_LENGTH + 1];
	unsigned int len;
	size_t i;

758
	len = isc_iterated_hash(hash, hashalg, iterations,
759
				salt, (int)salt_len,
760 761 762 763 764 765 766 767 768 769 770 771 772
				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) {
773
	return (isc_safe_memcompare(a, b, hash_length + 1));
774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789
}

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
790
	while (entries > 0U && next[l->length-1] != 0U) {
791 792 793 794 795 796 797 798 799
		next += l->length;
		entries--;
	}

	current = next;
	while (entries-- > 1U) {
		next += l->length;
		if (next[l->length-1] != 0)
			continue;
800
		if (isc_safe_memequal(current, next, l->length - 1))
801 802 803 804 805 806 807 808 809 810
			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])
{
811
	size_t entries = l->entries;
812 813 814 815 816 817 818 819 820 821 822
	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;
Mark Andrews's avatar
Mark Andrews committed
823 824
	} while (entries-- >