dnssec-signzone.c 107 KB
Newer Older
Michael Graff's avatar
Michael Graff committed
1
/*
2
 * Portions Copyright (C) 2004-2017  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
/*! \file */
David Lawrence's avatar
David Lawrence committed
33

Brian Wellington's avatar
Brian Wellington committed
34
35
36
#include <config.h>

#include <stdlib.h>
37
#include <time.h>
38
#include <unistd.h>
Brian Wellington's avatar
Brian Wellington committed
39

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

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

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

88
89
90
91
#ifdef PKCS11CRYPTO
#include <pk11/result.h>
#endif

92
93
#include "dnssectool.h"

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

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

101
102
103
104
105
106
107
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)

108
109
#define REVOKE(x) ((dst_key_flags(x) & DNS_KEYFLAG_REVOKE) != 0)

110
#define BUFSIZE 2048
111
#define MAXDSKEYS 8
Brian Wellington's avatar
Brian Wellington committed
112

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

#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
198
199
static void
dumpnode(dns_name_t *name, dns_dbnode_t *node) {
200
201
202
203
	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
204
	isc_result_t result;
205
	unsigned bufsize = 4096;
Brian Wellington's avatar
Brian Wellington committed
206

207
208
	if (outputformat != dns_masterformat_text)
		return;
209
210
211

	if (!output_dnssec_only) {
		result = dns_master_dumpnodetostream(mctx, gdb, gversion, node,
212
						     name, masterstyle, outfp);
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
		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
228

229
230
231
232
233
234
235
236
237
238
239
		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;
		}

240
		for (;;) {
241
242
243
244
245
246
247
248
249
250
251
252
253
			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);
254
		result = isc_stdio_write(r.base, 1, r.length, outfp, NULL);
255
256
257
258
259
260
261
262
		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
263
264
}

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

281
	dst_key_format(key, keystr, sizeof(keystr));
282
	vbprintf(1, "\t%s %s\n", logmsg, keystr);
Brian Wellington's avatar
Brian Wellington committed
283

284
285
286
287
288
289
	if (rdataset->type == dns_rdatatype_dnskey)
		expiry = dnskey_endtime;
	else
		expiry = endtime;

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

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

	tuple = NULL;
313
314
	result = dns_difftuple_create(mctx, DNS_DIFFOP_ADDRESIGN,
				      name, ttl, &trdata, &tuple);
315
316
	check_result(result, "dns_difftuple_create");
	dns_diff_append(add, &tuple);
317
}
Brian Wellington's avatar
Brian Wellington committed
318

319
static inline isc_boolean_t
320
321
issigningkey(dns_dnsseckey_t *key) {
	return (key->force_sign || key->hint_sign);
Brian Wellington's avatar
Brian Wellington committed
322
323
}

324
325
326
327
328
329
static inline isc_boolean_t
ispublishedkey(dns_dnsseckey_t *key) {
	return ((key->force_publish || key->hint_publish) &&
		!key->hint_remove);
}

330
static inline isc_boolean_t
331
iszonekey(dns_dnsseckey_t *key) {
332
	return (ISC_TF(dns_name_equal(dst_key_name(key->key), gorigin) &&
333
		       dst_key_iszonekey(key->key)));
334
335
}

336
337
338
339
340
341
342
343
344
345
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);
}

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

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

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

397
398
	result = dst_key_fromfile(&rrsig->signer, rrsig->keyid,
				  rrsig->algorithm, DST_TYPE_PUBLIC,
399
				  directory, mctx, &pubkey);
400
401
	if (result != ISC_R_SUCCESS) {
		isc_rwlock_unlock(&keylist_lock, isc_rwlocktype_write);
402
		return (NULL);
403
	}
404

405
406
	result = dst_key_fromfile(&rrsig->signer, rrsig->keyid,
				  rrsig->algorithm,
407
				  DST_TYPE_PUBLIC | DST_TYPE_PRIVATE,
408
				  directory, mctx, &privkey);
409
	if (result == ISC_R_SUCCESS) {
410
		dst_key_free(&pubkey);
411
412
413
		result = dns_dnsseckey_create(mctx, &privkey, &key);
	} else
		result = dns_dnsseckey_create(mctx, &pubkey, &key);
414

415
	if (result == ISC_R_SUCCESS) {
416
		key->force_publish = ISC_FALSE;
417
		key->force_sign = ISC_FALSE;
418
		key->index = keycount++;
419
420
		ISC_LIST_APPEND(keylist, key, link);
	}
421
422

	isc_rwlock_unlock(&keylist_lock, isc_rwlocktype_write);
423
	return (key);
424
425
}

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

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

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

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

Andreas Gustafsson's avatar
Andreas Gustafsson committed
497
498
	dns_name_format(name, namestr, sizeof(namestr));
	type_format(set->type, typestr, sizeof(typestr));
499

500
	ttl = ISC_MIN(set->ttl, endtime - starttime);
501

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

515
	vbprintf(1, "%s/%s:\n", namestr, typestr);
516

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

Brian Wellington's avatar
Brian Wellington committed
533
534
535
	while (result == ISC_R_SUCCESS) {
		isc_boolean_t expired, future;
		isc_boolean_t keep = ISC_FALSE, resign = ISC_FALSE;
536

Brian Wellington's avatar
Brian Wellington committed
537
		dns_rdataset_current(&sigset, &sigrdata);
538

539
		result = dns_rdata_tostruct(&sigrdata, &rrsig, NULL);
Brian Wellington's avatar
Brian Wellington committed
540
		check_result(result, "dns_rdata_tostruct");
541

542
		future = isc_serial_lt(now, rrsig.timesigned);
543

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

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

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

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

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

		if (resign) {
641
642
			INSIST(!keep);

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

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

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

666
		if (!issigningkey(key))
Brian Wellington's avatar
Brian Wellington committed
667
668
			continue;

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

Automatic Updater's avatar
Automatic Updater committed
674
			have_ksk = isksk(key);
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
			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");
690
691
692
		} else if (set->type == dns_rdatatype_cds ||
			   set->type == dns_rdatatype_cdnskey ||
			   iszsk(key)) {
693
694
			signwithkey(name, set, key->key, ttl, add,
				    "signing with dnskey");
695
		}
696
	}
697
698
699

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

726
727
728
729
730
731
732
733
734
735
736
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;
	}
}

737
738
739
740
741
742
743
744
745
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);
746
747
		if (l->hashbuf == NULL)
			fatal("unable to grow hashlist: out of memory");
748
749
	}
	memset(l->hashbuf + l->entries * l->length, 0, l->length);
750
	memmove(l->hashbuf + l->entries * l->length, hash, len);
751
752
753
754
755
756
	l->entries++;
}

static void
hashlist_add_dns_name(hashlist_t *l, /*const*/ dns_name_t *name,
		      unsigned int hashalg, unsigned int iterations,
757
		      const unsigned char *salt, size_t salt_len,
758
759
760
761
762
763
764
		      isc_boolean_t speculative)
{
	char nametext[DNS_NAME_FORMATSIZE];
	unsigned char hash[NSEC3_MAX_HASH_LENGTH + 1];
	unsigned int len;
	size_t i;

765
	len = isc_iterated_hash(hash, hashalg, iterations,
766
				salt, (int)salt_len,
767
768
769
770
771
772
773
774
775
776
777
778
779
				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) {
780
	return (isc_safe_memcompare(a, b, hash_length + 1));
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
}

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
797
	while (entries > 0U && next[l->length-1] != 0U) {
798
799
800
801
802
803
804
805
806
		next += l->length;
		entries--;
	}

	current = next;
	while (entries-- > 1U) {
		next += l->length;
		if (next[l->length-1] != 0)
			continue;
807
		if (isc_safe_memequal(current, next, l->length - 1))
808
809
810
811
812
813
814
815
816
817
			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])
{
818
	size_t entries = l->entries;
819
820
821
822
823
824
825
826
827
828
829
	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
830
831
	} while (entries-- > 1U);
	INSIST(entries != 0U);
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
	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,
848
		  const unsigned char *salt, size_t salt_len)
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
{
	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);
	}

875
	hashlist_add_dns_name(l, wild, hashalg, iterations, salt, salt_len,
876
877
878
			      ISC_TRUE);
}

879
880
static void
opendb(const char *prefix, dns_name_t *name, dns_rdataclass_t rdclass,
881
       dns_db_t **dbp)
882
{
883
	char filename[PATH_MAX];
884
885
886
887
	isc_buffer_t b;
	isc_result_t result;

	isc_buffer_init(&b, filename, sizeof(filename));
888
889
890
891
892
893
	if (dsdir != NULL) {
		/* allow room for a trailing slash */
		if (strlen(dsdir) >= isc_buffer_availablelength(&b))
			fatal("path '%s' is too long", dsdir);
		isc_buffer_putstr(&b, dsdir);
		if (dsdir[strlen(dsdir) - 1] != '/')
894
895
			isc_buffer_putstr(&b, "/");
	}
896
897
	if (strlen(prefix) > isc_buffer_availablelength(&b))
		fatal("path '%s' is too long", dsdir);
898
	isc_buffer_putstr(&b, prefix);
899
	result = dns_name_tofilenametext(name, ISC_FALSE, &b);
900
	check_result(result, "dns_name_tofilenametext()");
901
902
	if (isc_buffer_availablelength(&b) == 0) {
		char namestr[DNS_NAME_FORMATSIZE];
Andreas Gustafsson's avatar
Andreas Gustafsson committed
903
		dns_name_format(name, namestr, sizeof(namestr));
904
905
		fatal("name '%s' is too long", namestr);
	}
906
907
	isc_buffer_putuint8(&b, 0);

908
	result = dns_db_create(mctx, "rbt", dns_rootname, dns_dbtype_zone,
909
			       rdclass, 0, NULL, dbp);
910
911
	check_result(result, "dns_db_create()");

912
	result = dns_db_load3(*dbp, filename, inputformat, DNS_MASTER_HINT);
913
	if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE)
914
		dns_db_detach(dbp);