dnssec-signzone.c 106 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>
Brian Wellington's avatar
Brian Wellington committed
86

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

89
90
#include "dnssectool.h"

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

David Lawrence's avatar
David Lawrence committed
95
const char *program = "dnssec-signzone";
96
int verbose;
97

98
99
100
101
102
103
104
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)

105
106
#define REVOKE(x) ((dst_key_flags(x) & DNS_KEYFLAG_REVOKE) != 0)

107
#define BUFSIZE 2048
108
#define MAXDSKEYS 8
Brian Wellington's avatar
Brian Wellington committed
109

110
111
112
113
#define SIGNER_EVENTCLASS	ISC_EVENTCLASS(0x4453)
#define SIGNER_EVENT_WRITE	(SIGNER_EVENTCLASS + 0)
#define SIGNER_EVENT_WORK	(SIGNER_EVENTCLASS + 1)

114
115
116
117
#define SOA_SERIAL_KEEP		0
#define SOA_SERIAL_INCREMENT	1
#define SOA_SERIAL_UNIXTIME	2

118
119
120
121
122
123
124
typedef struct signer_event sevent_t;
struct signer_event {
	ISC_EVENT_COMMON(sevent_t);
	dns_fixedname_t *fname;
	dns_dbnode_t *node;
};

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

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

204
205
	if (outputformat != dns_masterformat_text)
		return;
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224

	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
225

226
227
228
229
230
231
232
233
234
235
236
		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;
		}

237
		for (;;) {
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
			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
260
261
}

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

278
	dst_key_format(key, keystr, sizeof(keystr));
279
	vbprintf(1, "\t%s %s\n", logmsg, keystr);
Brian Wellington's avatar
Brian Wellington committed
280

281
282
283
284
285
286
	if (rdataset->type == dns_rdatatype_dnskey)
		expiry = dnskey_endtime;
	else
		expiry = endtime;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

512
	vbprintf(1, "%s/%s:\n", namestr, typestr);
513

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

Brian Wellington's avatar
Brian Wellington committed
530
531
532
	while (result == ISC_R_SUCCESS) {
		isc_boolean_t expired, future;
		isc_boolean_t keep = ISC_FALSE, resign = ISC_FALSE;
533

Brian Wellington's avatar
Brian Wellington committed
534
		dns_rdataset_current(&sigset, &sigrdata);
535

536
		result = dns_rdata_tostruct(&sigrdata, &rrsig, NULL);
Brian Wellington's avatar
Brian Wellington committed
537
		check_result(result, "dns_rdata_tostruct");
538

539
		future = isc_serial_lt(now, rrsig.timesigned);
540

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

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

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

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

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

		if (resign) {
638
639
			INSIST(!keep);

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

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

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

663
		if (!issigningkey(key))
Brian Wellington's avatar
Brian Wellington committed
664
665
			continue;

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

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

	isc_mem_put(mctx, wassignedby, arraysize * sizeof(isc_boolean_t));
	isc_mem_put(mctx, nowsignedby, arraysize * sizeof(isc_boolean_t));
695
696
}

697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
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);
730
731
		if (l->hashbuf == NULL)
			fatal("unable to grow hashlist: out of memory");
732
733
	}
	memset(l->hashbuf + l->entries * l->length, 0, l->length);
734
	memmove(l->hashbuf + l->entries * l->length, hash, len);
735
736
737
738
739
740
741
742
743
744
745
746
747
748
	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;

749
750
	len = isc_iterated_hash(hash, hashalg, iterations,
				salt, (int)salt_length,
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
				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
781
	while (entries > 0U && next[l->length-1] != 0U) {
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
		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])
{
802
	size_t entries = l->entries;
803
804
805
806
807
808
809
810
811
812
813
	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
814
815
	} while (entries-- > 1U);
	INSIST(entries != 0U);
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
	return (next);
}

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

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

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

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

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

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

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

863
864
static void
opendb(const char *prefix, dns_name_t *name, dns_rdataclass_t rdclass,
865
       dns_db_t **dbp)
866
{
867
	char filename[PATH_MAX];
868
869
870
871
	isc_buffer_t b;
	isc_result_t result;

	isc_buffer_init(&b, filename, sizeof(filename));
872
873
874
875
876
877
	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] != '/')
878
879
			isc_buffer_putstr(&b, "/");
	}
880
881
	if (strlen(prefix) > isc_buffer_availablelength(&b))
		fatal("path '%s' is too long", dsdir);
882
	isc_buffer_putstr(&b, prefix);
883
	result = dns_name_tofilenametext(name, ISC_FALSE, &b);
884
	check_result(result, "dns_name_tofilenametext()");
885
886
	if (isc_buffer_availablelength(&b) == 0) {
		char namestr[DNS_NAME_FORMATSIZE];
Andreas Gustafsson's avatar
Andreas Gustafsson committed
887
		dns_name_format(name, namestr, sizeof(namestr));
888
889
		fatal("name '%s' is too long", namestr);
	}
890
891
	isc_buffer_putuint8(&b, 0);

892
	result = dns_db_create(mctx, "rbt", dns_rootname, dns_dbtype_zone,
893
			       rdclass, 0, NULL, dbp);
894
895
	check_result(result, "dns_db_create()");

896
	result = dns_db_load3(*dbp, filename, inputformat, DNS_MASTER_HINT);
897
	if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE)
898
		dns_db_detach(dbp);
899
900
}

901
/*%
902
903
904
 * Load the DS set for a child zone, if a dsset-* file can be found.
 * If not, try to find a keyset-* file from an earlier version of
 * dnssec-signzone, and build DS records from that.
905
 */
906
static isc_result_t
907
loadds(dns_name_t *name, isc_uint32_t ttl, dns_rdataset_t *dsset) {
908
909
910
	dns_db_t *db = NULL;
	dns_dbversion_t *ver = NULL;
	dns_dbnode_t *node = NULL;
911
	isc_result_t result;
912
913
914
915
916
	dns_rdataset_t keyset;
	dns_rdata_t key, ds;
	unsigned char dsbuf[DNS_DS_BUFFERSIZE];
	dns_diff_t diff;
	dns_difftuple_t *tuple = NULL;
917

918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
	opendb("dsset-", name, gclass, &db);
	if (db != NULL) {
		result = dns_db_findnode(db, name, ISC_FALSE, &node);
		if (result == ISC_R_SUCCESS) {
			dns_rdataset_init(dsset);
			result = dns_db_findrdataset(db, node, NULL,
						     dns_rdatatype_ds, 0, 0,
						     dsset, NULL);
			dns_db_detachnode(db, &node);
			if (result == ISC_R_SUCCESS) {
				vbprintf(2, "found DS records\n");
				dsset->ttl = ttl;
				dns_db_detach(&db);
				return (result);
			}
		}
		dns_db_detach(&db);
	}

	/* No DS records found; try again, looking for DNSKEY records */
938
	opendb("keyset-", name, gclass, &db);
939
	if (db == NULL) {
940
		return (ISC_R_NOTFOUND);
941
	}
942

943
944
945
946
947
948
	result = dns_db_findnode(db, name, ISC_FALSE, &node);
	if (result != ISC_R_SUCCESS) {
		dns_db_detach(&db);
		return (result);
	}