dnssec-signzone.c 113 KB
Newer Older
Michael Graff's avatar
Michael Graff committed
1
/*
Automatic Updater's avatar
Automatic Updater committed
2
 * Portions Copyright (C) 2004-2009  Internet Systems Consortium, Inc. ("ISC")
Mark Andrews's avatar
Mark Andrews committed
3
 * Portions Copyright (C) 1999-2003  Internet Software Consortium.
Automatic Updater's avatar
Automatic Updater committed
4 5 6 7 8 9 10 11 12 13 14 15 16
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
 * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
17
 * Portions Copyright (C) 1995-2000 by Network Associates, Inc.
18
 *
Automatic Updater's avatar
Automatic Updater committed
19
 * Permission to use, copy, modify, and/or distribute this software for any
Michael Graff's avatar
Michael Graff committed
20 21
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
22
 *
Mark Andrews's avatar
Mark Andrews committed
23 24 25 26 27 28 29
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
 * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Michael Graff's avatar
Michael Graff committed
30
 */
Brian Wellington's avatar
Brian Wellington committed
31

Francis Dupont's avatar
Francis Dupont committed
32
/* $Id: dnssec-signzone.c,v 1.238 2009/09/29 15:06:06 fdupont Exp $ */
33 34

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

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

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

41
#include <isc/app.h>
42
#include <isc/base32.h>
43
#include <isc/commandline.h>
Brian Wellington's avatar
Brian Wellington committed
44
#include <isc/entropy.h>
45
#include <isc/event.h>
46
#include <isc/file.h>
47
#include <isc/hash.h>
48
#include <isc/hex.h>
Brian Wellington's avatar
Brian Wellington committed
49
#include <isc/mem.h>
50 51
#include <isc/mutex.h>
#include <isc/os.h>
52
#include <isc/print.h>
53
#include <isc/random.h>
54
#include <isc/rwlock.h>
55
#include <isc/serial.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
#include "dnssectool.h"

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

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

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

104
#define BUFSIZE 2048
105
#define MAXDSKEYS 8
Brian Wellington's avatar
Brian Wellington committed
106

107 108 109 110
#define SIGNER_EVENTCLASS	ISC_EVENTCLASS(0x4453)
#define SIGNER_EVENT_WRITE	(SIGNER_EVENTCLASS + 0)
#define SIGNER_EVENT_WORK	(SIGNER_EVENTCLASS + 1)

111 112 113 114
#define SOA_SERIAL_KEEP		0
#define SOA_SERIAL_INCREMENT	1
#define SOA_SERIAL_UNIXTIME	2

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

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

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

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

183 184 185
#define check_dns_dbiterator_current(result) \
	check_result((result == DNS_R_NEWORIGIN) ? ISC_R_SUCCESS : result, \
		     "dns_dbiterator_current()")
Brian Wellington's avatar
Brian Wellington committed
186

Brian Wellington's avatar
Brian Wellington committed
187 188 189 190
static void
dumpnode(dns_name_t *name, dns_dbnode_t *node) {
	isc_result_t result;

191 192
	if (outputformat != dns_masterformat_text)
		return;
Brian Wellington's avatar
Brian Wellington committed
193 194 195 196 197
	result = dns_master_dumpnodetostream(mctx, gdb, gversion, node, name,
					     masterstyle, fp);
	check_result(result, "dns_master_dumpnodetostream");
}

198
/*%
Mark Andrews's avatar
Mark Andrews committed
199
 * Sign the given RRset with given key, and add the signature record to the
200 201
 * given tuple.
 */
202
static void
203 204
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
205 206
{
	isc_result_t result;
207
	isc_stdtime_t jendtime;
208 209
	char keystr[KEY_FORMATSIZE];
	dns_rdata_t trdata = DNS_RDATA_INIT;
210 211
	unsigned char array[BUFSIZE];
	isc_buffer_t b;
212 213 214 215
	dns_difftuple_t *tuple;

	key_format(key, keystr, sizeof(keystr));
	vbprintf(1, "\t%s %s\n", logmsg, keystr);
Brian Wellington's avatar
Brian Wellington committed
216

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

230
	if (tryverify) {
231
		result = dns_dnssec_verify(name, rdataset, key,
232
					   ISC_TRUE, mctx, &trdata);
233
		if (result == ISC_R_SUCCESS) {
234
			vbprintf(3, "\tsignature verified\n");
235
			INCSTAT(nverified);
236
		} else {
237
			vbprintf(3, "\tsignature failed to verify\n");
238
			INCSTAT(nverifyfailed);
239
		}
240
	}
241 242 243 244 245 246

	tuple = NULL;
	result = dns_difftuple_create(mctx, DNS_DIFFOP_ADD, name, ttl, &trdata,
				      &tuple);
	check_result(result, "dns_difftuple_create");
	dns_diff_append(add, &tuple);
247
}
Brian Wellington's avatar
Brian Wellington committed
248

249
static inline isc_boolean_t
250 251
issigningkey(dns_dnsseckey_t *key) {
	return (key->force_sign || key->hint_sign);
Brian Wellington's avatar
Brian Wellington committed
252 253
}

254
static inline isc_boolean_t
255
iszonekey(dns_dnsseckey_t *key) {
256
	return (ISC_TF(dns_name_equal(dst_key_name(key->key), gorigin) &&
257
		       dst_key_iszonekey(key->key)));
258 259
}

260 261 262 263 264 265 266 267 268 269
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);
}

270
/*%
271 272 273
 * Find the key that generated an RRSIG, if it is in the key list.  If
 * so, return a pointer to it, otherwise return NULL.
 *
274
 * No locking is performed here, this must be done by the caller.
275
 */
276
static dns_dnsseckey_t *
277
keythatsigned_unlocked(dns_rdata_rrsig_t *rrsig) {
278
	dns_dnsseckey_t *key;
279

280 281 282
	for (key = ISC_LIST_HEAD(keylist);
	     key != NULL;
	     key = ISC_LIST_NEXT(key, link)) {
283 284 285
		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)))
286
			return (key);
287
	}
288 289 290 291 292 293 294
	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.
 */
295
static dns_dnsseckey_t *
296 297 298
keythatsigned(dns_rdata_rrsig_t *rrsig) {
	isc_result_t result;
	dst_key_t *pubkey = NULL, *privkey = NULL;
299
	dns_dnsseckey_t *key = NULL;
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319

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

321 322
	result = dst_key_fromfile(&rrsig->signer, rrsig->keyid,
				  rrsig->algorithm, DST_TYPE_PUBLIC,
323
				  directory, mctx, &pubkey);
324 325
	if (result != ISC_R_SUCCESS) {
		isc_rwlock_unlock(&keylist_lock, isc_rwlocktype_write);
326
		return (NULL);
327
	}
328

329 330
	result = dst_key_fromfile(&rrsig->signer, rrsig->keyid,
				  rrsig->algorithm,
331
				  DST_TYPE_PUBLIC | DST_TYPE_PRIVATE,
332
				  directory, mctx, &privkey);
333
	if (result == ISC_R_SUCCESS) {
334
		dst_key_free(&pubkey);
335 336 337 338
		dns_dnsseckey_create(mctx, &privkey, &key);
	} else {
		dns_dnsseckey_create(mctx, &pubkey, &key);
	}
339 340
	key->force_publish = ISC_TRUE;
	key->force_sign = ISC_FALSE;
341
	ISC_LIST_APPEND(keylist, key, link);
342 343

	isc_rwlock_unlock(&keylist_lock, isc_rwlocktype_write);
344
	return (key);
345 346
}

347
/*%
348 349
 * 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.
350 351
 * I'm not sure if this is completely correct, but it seems to work.
 */
352
static isc_boolean_t
353
expecttofindkey(dns_name_t *name) {
354
	unsigned int options = DNS_DBFIND_NOWILD;
355
	dns_fixedname_t fname;
356
	isc_result_t result;
357
	char namestr[DNS_NAME_FORMATSIZE];
358

359
	dns_fixedname_init(&fname);
360
	result = dns_db_find(gdb, name, gversion, dns_rdatatype_dnskey, options,
361
			     0, NULL, dns_fixedname_name(&fname), NULL, NULL);
362
	switch (result) {
363 364 365 366 367 368 369 370
	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);
371
	}
Andreas Gustafsson's avatar
Andreas Gustafsson committed
372
	dns_name_format(name, namestr, sizeof(namestr));
373
	fatal("failure looking for '%s DNSKEY' in database: %s",
374 375
	      namestr, isc_result_totext(result));
	return (ISC_FALSE); /* removes a warning */
376 377
}

378
static inline isc_boolean_t
379
setverifies(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
380
	    dns_rdata_t *rrsig)
381
{
382
	isc_result_t result;
383
	result = dns_dnssec_verify(name, set, key, ISC_FALSE, mctx, rrsig);
384
	if (result == ISC_R_SUCCESS) {
385
		INCSTAT(nverified);
386 387
		return (ISC_TRUE);
	} else {
388
		INCSTAT(nverifyfailed);
389 390
		return (ISC_FALSE);
	}
391 392
}

393
/*%
394
 * Signs a set.  Goes through contortions to decide if each RRSIG should
395 396 397
 * be dropped or retained, and then determines if any new SIGs need to
 * be generated.
 */
398
static void
399
signset(dns_diff_t *del, dns_diff_t *add, dns_dbnode_t *node, dns_name_t *name,
400
	dns_rdataset_t *set)
401
{
402
	dns_rdataset_t sigset;
403
	dns_rdata_t sigrdata = DNS_RDATA_INIT;
404
	dns_rdata_rrsig_t rrsig;
405
	dns_dnsseckey_t *key;
406
	isc_result_t result;
407 408 409
	isc_boolean_t nosigs = ISC_FALSE;
	isc_boolean_t *wassignedby, *nowsignedby;
	int arraysize;
410 411
	dns_difftuple_t *tuple;
	dns_ttl_t ttl;
412
	int i;
413 414 415 416
	char namestr[DNS_NAME_FORMATSIZE];
	char typestr[TYPE_FORMATSIZE];
	char sigstr[SIG_FORMATSIZE];

Andreas Gustafsson's avatar
Andreas Gustafsson committed
417 418
	dns_name_format(name, namestr, sizeof(namestr));
	type_format(set->type, typestr, sizeof(typestr));
419

420
	ttl = ISC_MIN(set->ttl, endtime - starttime);
421

422
	dns_rdataset_init(&sigset);
423
	result = dns_db_findrdataset(gdb, node, gversion, dns_rdatatype_rrsig,
424
				     set->type, 0, &sigset, NULL);
425 426 427 428
	if (result == ISC_R_NOTFOUND) {
		result = ISC_R_SUCCESS;
		nosigs = ISC_TRUE;
	}
429
	if (result != ISC_R_SUCCESS)
430
		fatal("failed while looking for '%s RRSIG %s': %s",
431
		      namestr, typestr, isc_result_totext(result));
432

433
	vbprintf(1, "%s/%s:\n", namestr, typestr);
434

435 436 437
	arraysize = keycount;
	if (!nosigs)
		arraysize += dns_rdataset_count(&sigset);
438 439
	wassignedby = isc_mem_get(mctx, arraysize * sizeof(isc_boolean_t));
	nowsignedby = isc_mem_get(mctx, arraysize * sizeof(isc_boolean_t));
440 441 442 443 444 445
	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
446 447 448
	if (nosigs)
		result = ISC_R_NOMORE;
	else
449
		result = dns_rdataset_first(&sigset);
450

Brian Wellington's avatar
Brian Wellington committed
451 452 453
	while (result == ISC_R_SUCCESS) {
		isc_boolean_t expired, future;
		isc_boolean_t keep = ISC_FALSE, resign = ISC_FALSE;
454

Brian Wellington's avatar
Brian Wellington committed
455
		dns_rdataset_current(&sigset, &sigrdata);
456

457
		result = dns_rdata_tostruct(&sigrdata, &rrsig, NULL);
Brian Wellington's avatar
Brian Wellington committed
458
		check_result(result, "dns_rdata_tostruct");
459

460
		future = isc_serial_lt(now, rrsig.timesigned);
461

462 463
		key = keythatsigned(&rrsig);
		sig_format(&rrsig, sigstr, sizeof(sigstr));
464
		if (key != NULL && issigningkey(key))
465
			expired = isc_serial_gt(now + cycle, rrsig.timeexpire);
466
		else
467
			expired = isc_serial_gt(now, rrsig.timeexpire);
Brian Wellington's avatar
Brian Wellington committed
468

469 470 471
		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
472
				 "invalid validity period\n",
473
				 sigstr);
Brian Wellington's avatar
Brian Wellington committed
474
		} else if (key == NULL && !future &&
Francis Dupont's avatar
Francis Dupont committed
475
			   expecttofindkey(&rrsig.signer)) {
476 477 478
			/* rrsig is dropped and not replaced */
			vbprintf(2, "\trrsig by %s dropped - "
				 "private dnskey not found\n",
479
				 sigstr);
Brian Wellington's avatar
Brian Wellington committed
480
		} else if (key == NULL || future) {
481
			vbprintf(2, "\trrsig by %s %s - dnskey not found\n",
482
				 expired ? "retained" : "dropped", sigstr);
Brian Wellington's avatar
Brian Wellington committed
483 484 485
			if (!expired)
				keep = ISC_TRUE;
		} else if (issigningkey(key)) {
486 487
			if (!expired && setverifies(name, set, key->key,
						    &sigrdata)) {
488
				vbprintf(2, "\trrsig by %s retained\n", sigstr);
489
				keep = ISC_TRUE;
490 491
				wassignedby[key->index] = ISC_TRUE;
				nowsignedby[key->index] = ISC_TRUE;
492
			} else {
493
				vbprintf(2, "\trrsig by %s dropped - %s\n",
494
					 sigstr,
Brian Wellington's avatar
Brian Wellington committed
495 496
					 expired ? "expired" :
						   "failed to verify");
497
				wassignedby[key->index] = ISC_TRUE;
Brian Wellington's avatar
Brian Wellington committed
498
				resign = ISC_TRUE;
499
			}
500
		} else if (iszonekey(key)) {
501 502
			if (!expired && setverifies(name, set, key->key,
						    &sigrdata)) {
503
				vbprintf(2, "\trrsig by %s retained\n", sigstr);
Brian Wellington's avatar
Brian Wellington committed
504
				keep = ISC_TRUE;
505 506
				wassignedby[key->index] = ISC_TRUE;
				nowsignedby[key->index] = ISC_TRUE;
Brian Wellington's avatar
Brian Wellington committed
507
			} else {
508
				vbprintf(2, "\trrsig by %s dropped - %s\n",
509
					 sigstr,
Brian Wellington's avatar
Brian Wellington committed
510 511
					 expired ? "expired" :
						   "failed to verify");
512
				wassignedby[key->index] = ISC_TRUE;
513
			}
Brian Wellington's avatar
Brian Wellington committed
514
		} else if (!expired) {
515
			vbprintf(2, "\trrsig by %s retained\n", sigstr);
Brian Wellington's avatar
Brian Wellington committed
516 517
			keep = ISC_TRUE;
		} else {
518
			vbprintf(2, "\trrsig by %s expired\n", sigstr);
Brian Wellington's avatar
Brian Wellington committed
519
		}
520

521
		if (keep) {
522
			nowsignedby[key->index] = ISC_TRUE;
523
			INCSTAT(nretained);
524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541
			if (sigset.ttl != ttl) {
				vbprintf(2, "\tfixing ttl %s\n", sigstr);
				tuple = NULL;
				result = dns_difftuple_create(mctx,
							      DNS_DIFFOP_DEL,
							      name, sigset.ttl,
							      &sigrdata,
							      &tuple);
				check_result(result, "dns_difftuple_create");
				dns_diff_append(del, &tuple);
				result = dns_difftuple_create(mctx,
							      DNS_DIFFOP_ADD,
							      name, ttl,
							      &sigrdata,
							      &tuple);
				check_result(result, "dns_difftuple_create");
				dns_diff_append(add, &tuple);
			}
542
		} else {
Brian Wellington's avatar
Brian Wellington committed
543 544
			tuple = NULL;
			result = dns_difftuple_create(mctx, DNS_DIFFOP_DEL,
545 546
						      name, sigset.ttl,
						      &sigrdata, &tuple);
Brian Wellington's avatar
Brian Wellington committed
547
			check_result(result, "dns_difftuple_create");
548
			dns_diff_append(del, &tuple);
549
			INCSTAT(ndropped);
Brian Wellington's avatar
Brian Wellington committed
550 551 552
		}

		if (resign) {
553 554
			INSIST(!keep);

555 556
			signwithkey(name, set, key->key, ttl, add,
				    "resigning with dnskey");
557
			nowsignedby[key->index] = ISC_TRUE;
558
		}
Brian Wellington's avatar
Brian Wellington committed
559

560
		dns_rdata_reset(&sigrdata);
561
		dns_rdata_freestruct(&rrsig);
Brian Wellington's avatar
Brian Wellington committed
562
		result = dns_rdataset_next(&sigset);
Brian Wellington's avatar
Brian Wellington committed
563
	}
Brian Wellington's avatar
Brian Wellington committed
564 565 566 567 568 569
	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);
570

Brian Wellington's avatar
Brian Wellington committed
571 572 573 574
	for (key = ISC_LIST_HEAD(keylist);
	     key != NULL;
	     key = ISC_LIST_NEXT(key, link))
	{
575
		if (nowsignedby[key->index])
576 577
			continue;

578
		if (!issigningkey(key))
Brian Wellington's avatar
Brian Wellington committed
579 580
			continue;

581 582 583
		if (iszsk(key) ||
		    (isksk(key) && set->type == dns_rdatatype_dnskey &&
		     dns_name_equal(name, gorigin))) {
584 585
			signwithkey(name, set, key->key, ttl, add,
				    "signing with dnskey");
586
		}
587
	}
588 589 590

	isc_mem_put(mctx, wassignedby, arraysize * sizeof(isc_boolean_t));
	isc_mem_put(mctx, nowsignedby, arraysize * sizeof(isc_boolean_t));
591 592
}

593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673
struct hashlist {
	unsigned char *hashbuf;
	size_t entries;
	size_t size;
	size_t length;
};

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

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

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

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

	REQUIRE(len <= l->length);

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

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

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

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

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

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

	/*
	 * Skip initial speculative wild card hashs.
	 */
Mark Andrews's avatar
Mark Andrews committed
674
	while (entries > 0U && next[l->length-1] != 0U) {
675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755
		next += l->length;
		entries--;
	}

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

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

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

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

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

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

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

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

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

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

756 757
static void
opendb(const char *prefix, dns_name_t *name, dns_rdataclass_t rdclass,
758
       dns_db_t **dbp)
759
{
760
	char filename[PATH_MAX];
761 762 763 764
	isc_buffer_t b;
	isc_result_t result;

	isc_buffer_init(&b, filename, sizeof(filename));
765 766 767 768 769 770
	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] != '/')
771 772
			isc_buffer_putstr(&b, "/");
	}
773 774
	if (strlen(prefix) > isc_buffer_availablelength(&b))
		fatal("path '%s' is too long", dsdir);
775
	isc_buffer_putstr(&b, prefix);
776
	result = dns_name_tofilenametext(name, ISC_FALSE, &b);
777
	check_result(result, "dns_name_tofilenametext()");
778 779
	if (isc_buffer_availablelength(&b) == 0) {
		char namestr[DNS_NAME_FORMATSIZE];
Andreas Gustafsson's avatar
Andreas Gustafsson committed
780
		dns_name_format(name, namestr, sizeof(namestr));
781 782
		fatal("name '%s' is too long", namestr);
	}
783 784
	isc_buffer_putuint8(&b, 0);

785
	result = dns_db_create(mctx, "rbt", dns_rootname, dns_dbtype_zone,
786
			       rdclass, 0, NULL, dbp);
787 788
	check_result(result, "dns_db_create()");

789
	result = dns_db_load3(*dbp, filename, inputformat, DNS_MASTER_HINT);
790
	if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE)
791
		dns_db_detach(dbp);
792 793
}

794
/*%
795 796 797
 * 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.
798
 */
799
static isc_result_t
800
loadds(dns_name_t *name, isc_uint32_t ttl, dns_rdataset_t *dsset) {
801 802 803
	dns_db_t *db = NULL;
	dns_dbversion_t *ver = NULL;
	dns_dbnode_t *node = NULL;
804
	isc_result_t result;
805 806 807 808 809
	dns_rdataset_t keyset;
	dns_rdata_t key, ds;
	unsigned char dsbuf[DNS_DS_BUFFERSIZE];
	dns_diff_t diff;
	dns_difftuple_t *tuple = NULL;
810

811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830
	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 */
831
	opendb("keyset-", name, gclass, &db);
832
	if (db == NULL) {
833
		return (ISC_R_NOTFOUND);
834
	}
835

836 837 838 839 840 841
	result = dns_db_findnode(db, name, ISC_FALSE, &node);
	if (result != ISC_R_SUCCESS) {
		dns_db_detach(&db);
		return (result);
	}

842
	dns_rdataset_init(&keyset);