zone-sign.c 40.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <sys/types.h>
#include <time.h>
22 23

#include "common/debug.h"
24 25
#include "common/descriptor.h"
#include "common/errcode.h"
26
#include "common/hattrie/hat-trie.h"
27
#include "libknot/common.h"
28
#include "libknot/dname.h"
29
#include "libknot/rrset.h"
30
#include "libknot/dnssec/key.h"
31
#include "libknot/dnssec/policy.h"
32
#include "libknot/dnssec/rrset-sign.h"
33
#include "libknot/dnssec/sign.h"
34 35 36
#include "libknot/rrtype/rdname.h"
#include "libknot/rrtype/rrsig.h"
#include "libknot/rrtype/soa.h"
37 38
#include "knot/dnssec/zone-keys.h"
#include "knot/dnssec/zone-sign.h"
39
#include "knot/updates/changesets.h"
40
#include "knot/zone/node.h"
41
#include "knot/zone/contents.h"
42

43 44 45 46 47
/*- private API - common functions -------------------------------------------*/

/*!
 * \brief Create empty RRSIG RR set for a given RR set to be covered.
 */
48
static knot_rrset_t create_empty_rrsigs_for(const knot_rrset_t *covered)
49
{
50
	assert(!knot_rrset_empty(covered));
51 52 53
	knot_rrset_t ret;
	knot_rrset_init(&ret, covered->owner, KNOT_RRTYPE_RRSIG, covered->rclass);
	return ret;
54 55
}

56 57 58 59 60 61 62 63 64 65 66 67 68
/*- private API - signing of in-zone nodes -----------------------------------*/

/*!
 * \brief Check if there is a valid signature for a given RR set and key.
 *
 * \param covered  RR set with covered records.
 * \param rrsigs   RR set with RRSIGs.
 * \param key      Signing key.
 * \param ctx      Signing context.
 * \param policy   DNSSEC policy.
 *
 * \return The signature exists and is valid.
 */
69 70
static bool valid_signature_exists(const knot_rrset_t *covered,
				   const knot_rrset_t *rrsigs,
71
				   const knot_dnssec_key_t *key,
72
				   knot_dnssec_sign_context_t *ctx,
73
				   const knot_dnssec_policy_t *policy)
74
{
75 76
	assert(key);

77
	if (knot_rrset_empty(rrsigs)) {
78
		return false;
79
	}
80

81
	uint16_t rrsigs_rdata_count = rrsigs->rrs.rr_count;
82
	for (uint16_t i = 0; i < rrsigs_rdata_count; i++) {
83 84
		uint16_t keytag = knot_rrsig_key_tag(&rrsigs->rrs, i);
		uint16_t type_covered = knot_rrsig_type_covered(&rrsigs->rrs, i);
85
		if (keytag != key->keytag || type_covered != covered->type) {
86
			continue;
87
		}
88

89 90
		return knot_is_valid_signature(covered, rrsigs, i, key, ctx,
		                               policy) == KNOT_EOK;
91
	}
92

93 94
	return false;
}
95

96
/*!
97
 * \brief Check if key can be used to sign given RR.
98
 *
99 100
 * \param key      Zone key.
 * \param covered  RR to be checked.
101 102 103
 *
 * \return The RR should be signed.
 */
104
static bool use_key(const knot_zone_key_t *key, const knot_rrset_t *covered)
105 106
{
	assert(key);
107
	assert(covered);
108

109 110 111 112
	if (!key->is_active) {
		return false;
	}

113 114 115 116 117 118
	if (key->is_ksk) {
		if (covered->type != KNOT_RRTYPE_DNSKEY) {
			return false;
		}

		// use KSK only in the zone apex
119
		if (!knot_dname_is_equal(key->dnssec_key.name, covered->owner)) {
120 121
			return false;
		}
122 123 124 125 126
	}

	return true;
}

127
/*!
128 129 130 131 132 133 134 135
 * \brief Check if valid signature exist for all keys for a given RR set.
 *
 * \param covered    RR set with covered records.
 * \param rrsigs     RR set with RRSIGs.
 * \param zone_keys  Zone keys.
 * \param policy     DNSSEC policy.
 *
 * \return Valid signature exists for every key.
136
 */
137
static bool all_signatures_exist(const knot_rrset_t *covered,
Lubos Slovak's avatar
Lubos Slovak committed
138 139 140
                                 const knot_rrset_t *rrsigs,
                                 const knot_zone_keys_t *zone_keys,
                                 const knot_dnssec_policy_t *policy)
141
{
142
	assert(!knot_rrset_empty(covered));
143 144
	assert(zone_keys);

145 146 147
	node_t *node = NULL;
	WALK_LIST(node, zone_keys->list) {
		const knot_zone_key_t *key = (knot_zone_key_t *)node;
148
		if (!use_key(key, covered)) {
149
			continue;
150
		}
151

152 153
		if (!valid_signature_exists(covered, rrsigs, &key->dnssec_key,
		                            key->context, policy)) {
Lubos Slovak's avatar
Lubos Slovak committed
154
			return false;
155 156 157
		}
	}

Lubos Slovak's avatar
Lubos Slovak committed
158
	return true;
159
}
160

161
/*!
162
 * \brief Get zone key for given RRSIG (checks key tag only).
163
 *
164 165 166 167 168
 * \param rrsigs  RR set with RRSIGs.
 * \param pos     Number of RR in RR set.
 * \param keys    Zone keys.
 *
 * \return Zone key or NULL if a that key does not exist.
169
 */
170 171
static const knot_zone_key_t *get_matching_zone_key(const knot_rrset_t *rrsigs,
                                      size_t pos, const knot_zone_keys_t *keys)
172
{
173 174 175
	assert(rrsigs && rrsigs->type == KNOT_RRTYPE_RRSIG);
	assert(keys);

176
	uint16_t keytag = knot_rrsig_key_tag(&rrsigs->rrs, pos);
177

178
	return knot_get_zone_key(keys, keytag);
179 180
}

181 182 183 184 185 186 187 188 189
/*!
 * \brief Note earliest expiration of a signature.
 *
 * \param rrsigs      RR set with RRSIGs.
 * \param pos         Position of RR in rrsigs.
 * \param expires_at  Current earliest expiration, will be updated.
 */
static void note_earliest_expiration(const knot_rrset_t *rrsigs, size_t pos,
                                     uint32_t *expires_at)
Jan Kadlec's avatar
Jan Kadlec committed
190
{
191 192 193
	assert(rrsigs);
	assert(expires_at);

194
	const uint32_t current = knot_rrsig_sig_expiration(&rrsigs->rrs, pos);
195 196
	if (current < *expires_at) {
		*expires_at = current;
Jan Kadlec's avatar
Jan Kadlec committed
197 198 199
	}
}

200 201 202
/*!
 * \brief Add expired or invalid RRSIGs into the changeset for removal.
 *
203 204 205 206 207 208
 * \param covered     RR set with covered records.
 * \param rrsigs      RR set with RRSIGs.
 * \param zone_keys   Zone keys.
 * \param policy      DNSSEC policy.
 * \param changeset   Changeset to be updated.
 * \param expires_at  Earliest RRSIG expiration.
209 210 211
 *
 * \return Error code, KNOT_EOK if successful.
 */
212
static int remove_expired_rrsigs(const knot_rrset_t *covered,
Jan Kadlec's avatar
Jan Kadlec committed
213 214 215
                                 const knot_rrset_t *rrsigs,
                                 const knot_zone_keys_t *zone_keys,
                                 const knot_dnssec_policy_t *policy,
216
                                 changeset_t *changeset,
Jan Kadlec's avatar
Jan Kadlec committed
217
                                 uint32_t *expires_at)
218
{
219 220
	assert(changeset);

221
	if (knot_rrset_empty(rrsigs)) {
222
		return KNOT_EOK;
223
	}
224 225

	assert(rrsigs->type == KNOT_RRTYPE_RRSIG);
226

227 228
	knot_rrset_t to_remove;
	knot_rrset_init_empty(&to_remove);
229
	int result = KNOT_EOK;
230

231
	knot_rrset_t synth_rrsig;
232
	knot_rrset_init(&synth_rrsig, rrsigs->owner, KNOT_RRTYPE_RRSIG,
233 234
	                KNOT_CLASS_IN);
	result = knot_synth_rrsig(covered->type, &rrsigs->rrs,
235
	                          &synth_rrsig.rrs, NULL);
236
	if (result != KNOT_EOK) {
237 238 239 240
		if (result != KNOT_ENOENT) {
			return result;
		}
		return KNOT_EOK;
241 242
	}

243
	uint16_t rrsig_rdata_count = synth_rrsig.rrs.rr_count;
244
	for (uint16_t i = 0; i < rrsig_rdata_count; i++) {
245
		const knot_zone_key_t *key;
246
		key = get_matching_zone_key(&synth_rrsig, i, zone_keys);
247

248
		if (key && key->is_active && key->context) {
249
			result = knot_is_valid_signature(covered, &synth_rrsig, i,
250 251
			                                 &key->dnssec_key,
			                                 key->context, policy);
252
			if (result == KNOT_EOK) {
253
				// valid signature
254
				note_earliest_expiration(&synth_rrsig, i, expires_at);
255
				continue;
256
			}
257

258
			if (result != KNOT_DNSSEC_EINVALID_SIGNATURE) {
259
				break;
260
			}
261
		}
262

263
		if (knot_rrset_empty(&to_remove)) {
264
			to_remove = create_empty_rrsigs_for(&synth_rrsig);
265
		}
266

267
		knot_rdata_t *rr_rem = knot_rdataset_at(&synth_rrsig.rrs, i);
268
		result = knot_rdataset_add(&to_remove.rrs, rr_rem, NULL);
269
		if (result != KNOT_EOK) {
270
			break;
271
		}
272
	}
273

274 275
	if (!knot_rrset_empty(&to_remove) && result == KNOT_EOK) {
		result = changeset_rem_rrset(changeset, &to_remove);
276 277
	}

278
	knot_rdataset_clear(&synth_rrsig.rrs, NULL);
279
	knot_rdataset_clear(&to_remove.rrs, NULL);
280

281
	return result;
282 283
}

284 285 286 287 288 289 290 291 292 293 294
/*!
 * \brief Add missing RRSIGs into the changeset for adding.
 *
 * \param covered    RR set with covered records.
 * \param rrsigs     RR set with RRSIGs.
 * \param zone_keys  Zone keys.
 * \param policy     DNSSEC policy.
 * \param changeset  Changeset to be updated.
 *
 * \return Error code, KNOT_EOK if successful.
 */
295 296
static int add_missing_rrsigs(const knot_rrset_t *covered,
                              const knot_rrset_t *rrsigs,
297
                              const knot_zone_keys_t *zone_keys,
298
                              const knot_dnssec_policy_t *policy,
299
                              changeset_t *changeset)
300
{
301
	assert(!knot_rrset_empty(covered));
302 303
	assert(zone_keys);
	assert(changeset);
304 305

	int result = KNOT_EOK;
306 307
	knot_rrset_t to_add;
	knot_rrset_init_empty(&to_add);
308

309 310 311
	node_t *node = NULL;
	WALK_LIST(node, zone_keys->list) {
		const knot_zone_key_t *key = (knot_zone_key_t *)node;
312
		if (!use_key(key, covered)) {
313
			continue;
314
		}
315

316 317
		if (valid_signature_exists(covered, rrsigs, &key->dnssec_key,
		                           key->context, policy)) {
318
			continue;
319
		}
320

321
		if (knot_rrset_empty(&to_add)) {
322 323 324
			to_add = create_empty_rrsigs_for(covered);
		}

325
		result = knot_sign_rrset(&to_add, covered, &key->dnssec_key,
326
		                         key->context, policy);
327
		if (result != KNOT_EOK) {
328
			break;
329
		}
330 331
	}

332 333
	if (!knot_rrset_empty(&to_add) && result == KNOT_EOK) {
		result = changeset_add_rrset(changeset, &to_add);
334 335
	}

336
	knot_rdataset_clear(&to_add.rrs, NULL);
337

338
	return result;
339 340
}

341 342 343 344 345 346 347 348
/*!
 * \brief Add all RRSIGs into the changeset for removal.
 *
 * \param covered    RR set with covered records.
 * \param changeset  Changeset to be updated.
 *
 * \return Error code, KNOT_EOK if successful.
 */
349
static int remove_rrset_rrsigs(const knot_dname_t *owner, uint16_t type,
350
                               const knot_rrset_t *rrsigs,
351
                               changeset_t *changeset)
352
{
353
	assert(owner);
354
	assert(changeset);
355 356 357 358
	knot_rrset_t synth_rrsig;
	knot_rrset_init(&synth_rrsig, (knot_dname_t *)owner,
	                KNOT_RRTYPE_RRSIG, KNOT_CLASS_IN);
	int ret = knot_synth_rrsig(type, &rrsigs->rrs, &synth_rrsig.rrs, NULL);
359
	if (ret != KNOT_EOK) {
360 361 362 363
		if (ret != KNOT_ENOENT) {
			return ret;
		}
		return KNOT_EOK;
364 365
	}

366 367
	ret = changeset_rem_rrset(changeset, &synth_rrsig);
	knot_rdataset_clear(&synth_rrsig.rrs, NULL);
368 369

	return ret;
370 371
}

372 373 374 375 376 377 378 379 380 381 382
/*!
 * \brief Drop all existing and create new RRSIGs for covered records.
 *
 * \param covered    RR set with covered records.
 * \param zone_keys  Zone keys.
 * \param policy     DNSSEC policy.
 * \param changeset  Changeset to be updated.
 *
 * \return Error code, KNOT_EOK if successful.
 */
static int force_resign_rrset(const knot_rrset_t *covered,
383
                              const knot_rrset_t *rrsigs,
384 385
                              const knot_zone_keys_t *zone_keys,
                              const knot_dnssec_policy_t *policy,
386
                              changeset_t *changeset)
387
{
388
	assert(!knot_rrset_empty(covered));
389

390
	if (!knot_rrset_empty(rrsigs)) {
391 392
		int result = remove_rrset_rrsigs(covered->owner, covered->type,
		                                 rrsigs, changeset);
393 394
		if (result != KNOT_EOK) {
			return result;
395 396 397
		}
	}

398
	return add_missing_rrsigs(covered, NULL, zone_keys, policy, changeset);
399 400
}

401 402 403
/*!
 * \brief Drop all expired and create new RRSIGs for covered records.
 *
404 405 406 407 408
 * \param covered     RR set with covered records.
 * \param zone_keys   Zone keys.
 * \param policy      DNSSEC policy.
 * \param changeset   Changeset to be updated.
 * \param expires_at  Current earliest expiration, will be updated.
409 410 411 412
 *
 * \return Error code, KNOT_EOK if successful.
 */
static int resign_rrset(const knot_rrset_t *covered,
413
                        const knot_rrset_t *rrsigs,
414 415
                        const knot_zone_keys_t *zone_keys,
                        const knot_dnssec_policy_t *policy,
416
                        changeset_t *changeset,
Jan Kadlec's avatar
Jan Kadlec committed
417
                        uint32_t *expires_at)
418
{
419
	assert(!knot_rrset_empty(covered));
420

Jan Kadlec's avatar
Jan Kadlec committed
421 422
	// TODO this function creates some signatures twice (for checking)
	// maybe merge the two functions into one
Jan Včelák's avatar
Jan Včelák committed
423 424
	// jvcelak: Not really, maybe for RSA. The digest is computed twice,
	// but the verification process can differ from signature computation.
425 426
	// TODO reuse digest for RSA then, RSA is the most used algo family,
	// and we create all the signatures twice, that is not cool I think.
427 428 429 430 431 432 433 434 435

	int result = remove_expired_rrsigs(covered, rrsigs, zone_keys,
	                                   policy, changeset, expires_at);
	if (result != KNOT_EOK) {
		return result;
	}

	return add_missing_rrsigs(covered, rrsigs, zone_keys, policy,
	                          changeset);
436 437
}

438
static int remove_standalone_rrsigs(const zone_node_t *node,
439
                                    const knot_rrset_t *rrsigs,
440
                                    changeset_t *changeset)
441 442 443 444 445
{
	if (rrsigs == NULL) {
		return KNOT_EOK;
	}

446
	uint16_t rrsigs_rdata_count = rrsigs->rrs.rr_count;
447
	for (uint16_t i = 0; i < rrsigs_rdata_count; ++i) {
448
		uint16_t type_covered = knot_rrsig_type_covered(&rrsigs->rrs, i);
449
		if (!node_rrtype_exists(node, type_covered)) {
450 451 452
			knot_rrset_t to_remove;
			knot_rrset_init(&to_remove, rrsigs->owner, rrsigs->type,
			                rrsigs->rclass);
453
			knot_rdata_t *rr_rem = knot_rdataset_at(&rrsigs->rrs, i);
454
			int ret = knot_rdataset_add(&to_remove.rrs, rr_rem, NULL);
455 456 457
			if (ret != KNOT_EOK) {
				return ret;
			}
458 459
			ret = changeset_rem_rrset(changeset, &to_remove);
			knot_rdataset_clear(&to_remove.rrs, NULL);
460 461 462 463 464 465 466 467 468
			if (ret != KNOT_EOK) {
				return ret;
			}
		}
	}

	return KNOT_EOK;
}

469 470 471
/*!
 * \brief Update RRSIGs in a given node by updating changeset.
 *
472 473 474 475 476
 * \param node        Node to be signed.
 * \param zone_keys   Zone keys.
 * \param policy      DNSSEC policy.
 * \param changeset   Changeset to be updated.
 * \param expires_at  Current earliest expiration, will be updated.
477 478 479
 *
 * \return Error code, KNOT_EOK if successful.
 */
480
static int sign_node_rrsets(const zone_node_t *node,
481 482
                            const knot_zone_keys_t *zone_keys,
                            const knot_dnssec_policy_t *policy,
483
                            changeset_t *changeset,
Jan Kadlec's avatar
Jan Kadlec committed
484
                            uint32_t *expires_at)
485
{
486
	assert(node);
487 488 489
	assert(policy);

	int result = KNOT_EOK;
490
	knot_rrset_t rrsigs = node_rrset(node, KNOT_RRTYPE_RRSIG);
491

492
	for (int i = 0; i < node->rrset_count; i++) {
493
		knot_rrset_t rrset = node_rrset_at(node, i);
494
		if (rrset.type == KNOT_RRTYPE_RRSIG) {
495 496
			continue;
		}
497
		bool should_sign = false;
498
		result = knot_zone_sign_rr_should_be_signed(node, &rrset,
499 500 501 502 503
		                                            &should_sign);
		if (result != KNOT_EOK) {
			return result;
		}
		if (!should_sign) {
Lubos Slovak's avatar
Lubos Slovak committed
504 505 506
			continue;
		}

507
		if (policy->forced_sign) {
508
			result = force_resign_rrset(&rrset, &rrsigs, zone_keys, policy,
509 510
			         changeset);
		} else {
511
			result = resign_rrset(&rrset, &rrsigs, zone_keys, policy,
Jan Kadlec's avatar
Jan Kadlec committed
512
			                      changeset, expires_at);
513
		}
514

515
		if (result != KNOT_EOK) {
516
			return result;
517
		}
518 519
	}

520
	return remove_standalone_rrsigs(node, &rrsigs, changeset);
521
}
522

523 524 525
/*!
 * \brief Struct to carry data for 'sign_data' callback function.
 */
Jan Kadlec's avatar
Jan Kadlec committed
526 527 528
typedef struct node_sign_args {
	const knot_zone_keys_t *zone_keys;
	const knot_dnssec_policy_t *policy;
529
	changeset_t *changeset;
Jan Kadlec's avatar
Jan Kadlec committed
530
	uint32_t expires_at;
Jan Kadlec's avatar
Jan Kadlec committed
531 532
} node_sign_args_t;

533 534 535 536 537 538
/*!
 * \brief Sign node (callback function).
 *
 * \param node  Node to be signed.
 * \param data  Callback data, node_sign_args_t.
 */
539
static int sign_node(zone_node_t **node, void *data)
540
{
541
	assert(node && *node);
Jan Kadlec's avatar
Jan Kadlec committed
542
	assert(data);
543

544
	node_sign_args_t *args = (node_sign_args_t *)data;
545

Jan Kadlec's avatar
Jan Kadlec committed
546
	if ((*node)->rrset_count == 0) {
547
		return KNOT_EOK;
Jan Kadlec's avatar
Jan Kadlec committed
548 549
	}

550
	if ((*node)->flags & NODE_FLAGS_NONAUTH) {
551
		return KNOT_EOK;
Jan Kadlec's avatar
Jan Kadlec committed
552
	}
553

554 555
	int result = sign_node_rrsets(*node, args->zone_keys, args->policy,
	                              args->changeset, &args->expires_at);
556
	(*node)->flags &= ~NODE_FLAGS_REMOVED_NSEC;
557 558

	return result;
559 560
}

561 562 563
/*!
 * \brief Update RRSIGs in a given zone tree by updating changeset.
 *
Jan Kadlec's avatar
Jan Kadlec committed
564 565 566 567 568
 * \param tree        Zone tree to be signed.
 * \param zone_keys   Zone keys.
 * \param policy      DNSSEC policy.
 * \param changeset   Changeset to be updated.
 * \param expires_at  Expiration time of the oldest signature in zone.
569 570 571
 *
 * \return Error code, KNOT_EOK if successful.
 */
572
static int zone_tree_sign(zone_tree_t *tree,
573 574
                          const knot_zone_keys_t *zone_keys,
                          const knot_dnssec_policy_t *policy,
575
                          changeset_t *changeset,
Jan Kadlec's avatar
Jan Kadlec committed
576
                          uint32_t *expires_at)
577
{
578 579 580
	assert(zone_keys);
	assert(policy);
	assert(changeset);
581

582 583 584 585 586 587 588
	node_sign_args_t args = {
		.zone_keys = zone_keys,
		.policy = policy,
		.changeset = changeset,
		.expires_at = time(NULL) + policy->sign_lifetime
	};

589
	int result = zone_tree_apply(tree, sign_node, &args);
590 591 592
	*expires_at = args.expires_at;

	return result;
593 594
}

595 596 597
/*- private API - signing of NSEC(3) in changeset ----------------------------*/

/*!
598
 * \brief Struct to carry data for changeset signing callback functions.
599
 */
600
typedef struct {
601
	const zone_contents_t *zone;
602
	const knot_zone_keys_t *zone_keys;
603
	const knot_dnssec_policy_t *policy;
604
	changeset_t *changeset;
605
	hattrie_t *signed_tree;
606
} changeset_signing_data_t;
607 608


609 610 611
/*- private API - DNSKEY handling --------------------------------------------*/

/*!
612
 * \brief Check if DNSKEY RDATA match with DNSSEC key.
613
 *
614
 * \param zone_key    Zone key.
615 616
 * \param rdata       DNSKEY RDATA.
 * \param rdata_size  DNSKEY RDATA size.
617
 *
618
 * \return DNSKEY RDATA match with DNSSEC key.
619
 */
620
static bool dnskey_rdata_match(const knot_zone_key_t *key,
621
                               const uint8_t *rdata, size_t rdata_size)
622
{
623
	assert(key);
624 625
	assert(rdata);

626 627 628 629
	const knot_dnssec_key_t *dnssec_key = &key->dnssec_key;

	return dnssec_key->dnskey_rdata.size == rdata_size &&
	       memcmp(dnssec_key->dnskey_rdata.data, rdata, rdata_size) == 0;
630 631 632
}

/*!
633 634 635 636 637 638
 * \brief Check if DNSKEY (key struct given) exists in zone.
 *
 * \param dnskeys  DNSKEYS RR set in zone apex.
 * \param key      Key to be searched for.
 *
 * \return DNSKEY exists in the zone.
639
 */
640
static bool dnskey_exists_in_zone(const knot_rrset_t *dnskeys,
641
                                  const knot_zone_key_t *key)
642
{
643
	assert(!knot_rrset_empty(dnskeys));
644 645
	assert(key);

646
	uint16_t dnskeys_rdata_count = dnskeys->rrs.rr_count;
647
	for (uint16_t i = 0; i < dnskeys_rdata_count; i++) {
648 649 650
		const knot_rdata_t *rr_data = knot_rdataset_at(&dnskeys->rrs, i);
		uint8_t *rdata = knot_rdata_data(rr_data);
		uint16_t rdata_size = knot_rdata_rdlen(rr_data);
651
		if (dnskey_rdata_match(key, rdata, rdata_size)) {
652 653 654 655 656 657 658
			return true;
		}
	}

	return false;
}

659
static int rrset_add_zone_key(knot_rrset_t *rrset,
660 661
                              const knot_zone_key_t *zone_key,
                              uint32_t ttl)
662 663 664 665 666 667
{
	assert(rrset);
	assert(zone_key);

	const knot_binary_t *key_rdata = &zone_key->dnssec_key.dnskey_rdata;

668
	return knot_rrset_add_rdata(rrset, key_rdata->data, key_rdata->size, ttl,
669
	                            NULL);
670 671
}

672
/*!
673 674 675 676
 * \brief Remove invalid DNSKEYs from the zone by updating the changeset.
 *
 * Invalid DNSKEY has wrong TTL, or the same keytag as some zone key
 * but different RDATA.
677 678 679 680 681 682 683 684
 *
 * \param soa        RR set with SOA (to get TTL value from).
 * \param dnskeys    RR set with DNSKEYs.
 * \param zone_keys  Zone keys.
 * \param changeset  Changeset to be updated.
 *
 * \return Error code, KNOT_EOK if successful.
 */
685
static int remove_invalid_dnskeys(const knot_rrset_t *soa,
686
                                  const knot_rrset_t *dnskeys,
687
                                  const knot_zone_keys_t *zone_keys,
688
                                  changeset_t *changeset)
689
{
690
	assert(soa->type == KNOT_RRTYPE_SOA);
691
	assert(changeset);
692

693
	if (knot_rrset_empty(dnskeys)) {
694 695 696 697
		return KNOT_EOK;
	}
	assert(dnskeys->type == KNOT_RRTYPE_DNSKEY);

698 699 700
	knot_rrset_t to_remove;
	knot_rrset_init(&to_remove, dnskeys->owner, dnskeys->type,
	                dnskeys->rclass);
701 702
	int result = KNOT_EOK;

703 704 705
	const knot_rdata_t *dnskeys_data = knot_rdataset_at(&dnskeys->rrs, 0);
	const knot_rdata_t *soa_data = knot_rdataset_at(&soa->rrs, 0);
	if (knot_rdata_ttl(dnskeys_data) != knot_rdata_ttl(soa_data)) {
706
		dbg_dnssec_detail("removing DNSKEYs (SOA TTL differs)\n");
707
		result = knot_rdataset_copy(&to_remove.rrs, &dnskeys->rrs, NULL);
708 709 710
		goto done;
	}

711
	uint16_t dnskeys_rdata_count = dnskeys->rrs.rr_count;
712
	for (uint16_t i = 0; i < dnskeys_rdata_count; i++) {
Jan Kadlec's avatar
Jan Kadlec committed
713
		dnskeys_data = knot_rdataset_at(&dnskeys->rrs, i);
714 715
		uint8_t *rdata = knot_rdata_data(dnskeys_data);
		uint16_t rdata_size = knot_rdata_rdlen(dnskeys_data);
716
		uint16_t keytag = knot_keytag(rdata, rdata_size);
717
		const knot_zone_key_t *key = knot_get_zone_key(zone_keys, keytag);
718 719 720 721 722 723
		if (key == NULL) {
			dbg_dnssec_detail("keeping unknown DNSKEY with tag "
			                  "%d\n", keytag);
			continue;
		}

724
		if (dnskey_rdata_match(key, rdata, rdata_size) && key->is_public) {
725 726
			dbg_dnssec_detail("keeping known DNSKEY with tag "
			                  "%d\n", keytag);
727 728 729
			continue;
		}

730
		dbg_dnssec_detail("removing DNSKEY with tag %d\n", keytag);
731

732
		knot_rdata_t *to_rem = knot_rdataset_at(&dnskeys->rrs, i);
733
		result = knot_rdataset_add(&to_remove.rrs, to_rem, NULL);
734 735 736 737 738
		if (result != KNOT_EOK) {
			break;
		}
	}

739 740
done:

741 742
	if (!knot_rrset_empty(&to_remove) && result == KNOT_EOK) {
		result = changeset_rem_rrset(changeset, &to_remove);
743 744
	}

745
	knot_rdataset_clear(&to_remove.rrs, NULL);
746 747 748 749

	return result;
}

750 751 752 753 754 755 756
/*!
 * \brief Create DNSKEY RR set from SOA RR set.
 *
 * \param soa  RR set with zone SOA.
 *
 * \return Empty DNSKEY RR set.
 */
757
static knot_rrset_t create_dnskey_rrset_from_soa(const knot_rrset_t *soa)
758 759
{
	assert(soa);
760 761 762
	knot_rrset_t rrset;
	knot_rrset_init(&rrset, soa->owner, KNOT_RRTYPE_DNSKEY, soa->rclass);
	return rrset;
763 764
}

765 766 767 768 769 770 771 772 773 774
/*!
 * \brief Add missing DNSKEYs into the zone by updating the changeset.
 *
 * \param soa        RR set with SOA (to get TTL value from).
 * \param dnskeys    RR set with DNSKEYs.
 * \param zone_keys  Zone keys.
 * \param changeset  Changeset to be updated.
 *
 * \return Error code, KNOT_EOK if successful.
 */
775 776
static int add_missing_dnskeys(const knot_rrset_t *soa,
                               const knot_rrset_t *dnskeys,
777
                               const knot_zone_keys_t *zone_keys,
778
                               changeset_t *changeset)
779
{
780 781
	assert(soa);
	assert(soa->type == KNOT_RRTYPE_SOA);
782
	assert(knot_rrset_empty(dnskeys) || dnskeys->type == KNOT_RRTYPE_DNSKEY);
783
	assert(zone_keys);
784
	assert(changeset);
785

786 787
	knot_rrset_t to_add;
	knot_rrset_init_empty(&to_add);
788
	int result = KNOT_EOK;
789 790
	const knot_rdata_t *dnskeys_data = knot_rdataset_at(&dnskeys->rrs, 0);
	const knot_rdata_t *soa_data = knot_rdataset_at(&soa->rrs, 0);
791
	bool add_all = (knot_rrset_empty(dnskeys) ||
792
	                knot_rdata_ttl(dnskeys_data) != knot_rdata_ttl(soa_data));
793

794 795 796
	node_t *node = NULL;
	WALK_LIST(node, zone_keys->list) {
		const knot_zone_key_t *key = (knot_zone_key_t *)node;
797
		if (!add_all && dnskey_exists_in_zone(dnskeys, key)) {
798 799 800
			continue;
		}

801 802 803 804
		if (!key->is_public) {
			continue;
		}

805 806
		dbg_dnssec_detail("adding DNSKEY with tag %d\n",
		                  key->dnssec_key.keytag);
807

808
		if (knot_rrset_empty(&to_add)) {
809
			to_add = create_dnskey_rrset_from_soa(soa);
810 811
		}

812
		result = rrset_add_zone_key(&to_add, key, knot_rdata_ttl(soa_data));
813 814 815 816 817
		if (result != KNOT_EOK) {
			break;
		}
	}

818 819
	if (!knot_rrset_empty(&to_add) && result == KNOT_EOK) {
		result = changeset_add_rrset(changeset, &to_add);
820 821
	}

822
	knot_rdataset_clear(&to_add.rrs, NULL);
823 824 825 826

	return result;
}

827 828 829 830 831 832 833 834 835 836 837
/*!
 * \brief Refresh DNSKEY RRSIGs in the zone by updating the changeset.
 *
 * \param dnskeys    RR set with DNSKEYs.
 * \param soa        RR set with SOA.
 * \param zone_keys  Zone keys.
 * \param policy     DNSSEC policy.
 * \param changeset  Changeset to be updated.
 *
 * \return Error code, KNOT_EOK if successful.
 */
838
static int update_dnskeys_rrsigs(const knot_rrset_t *dnskeys,
839
                                 const knot_rrset_t *rrsigs,
840
                                 const knot_rrset_t *soa,
841
                                 const knot_zone_keys_t *zone_keys,
842
                                 const knot_dnssec_policy_t *policy,
843
                                 changeset_t *changeset)
844
{
845
	assert(zone_keys);
846
	assert(changeset);
847 848 849

	int result;

850 851
	// We know how the DNSKEYs in zone should look like after applying
	// the changeset. RRSIGs can be then built easily.
852

853
	knot_rrset_t new_dnskeys = create_dnskey_rrset_from_soa(soa);
854

855
	// add unknown keys from zone
856
	uint16_t dnskeys_rdata_count = dnskeys->rrs.rr_count;
857
	for (uint16_t i = 0; i < dnskeys_rdata_count; i++) {
858 859 860
		const knot_rdata_t *rr_data = knot_rdataset_at(&dnskeys->rrs, i);
		uint8_t *rdata = knot_rdata_data(rr_data);
		uint16_t rdata_size = knot_rdata_rdlen(rr_data);
861
		uint16_t keytag = knot_keytag(rdata, rdata_size);
862
		if (knot_get_zone_key(zone_keys, keytag) != NULL) {
863 864 865
			continue;
		}

866
		knot_rdata_t *to_add = knot_rdataset_at(&dnskeys->rrs, i);
867
		result = knot_rdataset_add(&new_dnskeys.rrs, to_add, NULL);
868 869 870 871 872 873
		if (result != KNOT_EOK) {
			goto fail;
		}
	}

	// add known keys from key database
874 875 876
	node_t *node = NULL;
	WALK_LIST(node, zone_keys->list) {
		const knot_zone_key_t *key = (knot_zone_key_t *)node;
877 878 879 880
		if (!key->is_public) {
			continue;
		}

881
		const knot_rdata_t *soa_data = knot_rdataset_at(&soa->rrs, 0);
882
		result = rrset_add_zone_key(&new_dnskeys, key,
883
		                            knot_rdata_ttl(soa_data));
884 885 886 887 888
		if (result != KNOT_EOK) {
			goto fail;
		}
	}

889
	result = add_missing_rrsigs(&new_dnskeys, NULL, zone_keys, policy,
890
	                            changeset);
891 892 893 894
	if (result != KNOT_EOK) {
		goto fail;
	}

895
	if (!knot_rrset_empty(dnskeys)) {
896 897
		result = remove_rrset_rrsigs(dnskeys->owner, dnskeys->type,
		                             rrsigs, changeset);
898 899 900 901
	}

fail:

902
	knot_rdataset_clear(&new_dnskeys.rrs, NULL);
903 904 905
	return result;
}

906 907 908 909 910 911 912 913 914 915
/*!
 * \brief Update DNSKEY records in the zone by updating the changeset.
 *
 * \param zone       Zone to be updated.
 * \param zone_keys  Zone keys.
 * \param policy     DNSSEC policy.
 * \param changeset  Changeset to be updated.
 *
 * \return Error code, KNOT_EOK if successful.
 */
916
static int update_dnskeys(const zone_contents_t *zone,
917
                          const knot_zone_keys_t *zone_keys,
918
                          const knot_dnssec_policy_t *policy,
919
                          changeset_t *changeset)
920 921 922
{
	assert(zone);
	assert(zone->apex);
923
	assert(changeset);
924

925 926 927 928
	const zone_node_t *apex = zone->apex;
	knot_rrset_t dnskeys = node_rrset(apex, KNOT_RRTYPE_DNSKEY);
	knot_rrset_t soa = node_rrset(apex, KNOT_RRTYPE_SOA);
	knot_rrset_t rrsigs = node_rrset(apex, KNOT_RRTYPE_RRSIG);
929
	if (knot_rrset_empty(&soa)) {
930 931
		return KNOT_EINVAL;
	}
932

933
	int result;
934
	size_t changes_before = changeset_size(changeset);
935

936
	result = remove_invalid_dnskeys(&soa, &dnskeys, zone_keys, changeset);
937 938 939 940
	if (result != KNOT_EOK) {
		return result;
	}

941
	result = add_missing_dnskeys(&soa, &dnskeys, zone_keys, changeset);
942 943 944
	if (result != KNOT_EOK) {
		return result;
	}
945 946 947
	knot_rrset_t dnskey_rrsig;
	knot_rrset_init(&dnskey_rrsig, apex->owner, KNOT_RRTYPE_RRSIG,
	                KNOT_CLASS_IN);
948
	result = knot_synth_rrsig(KNOT_RRTYPE_DNSKEY, &rrsigs.rrs,
949
	                          &dnskey_rrsig.rrs, NULL);
950 951 952 953
	if (result != KNOT_EOK) {
		if (result != KNOT_ENOENT) {
			return result;
		}
954 955
	}

956
	bool modified = (changeset_size(changeset) != changes_before);
957
	bool signatures_exist = (!knot_rrset_empty(&dnskeys) &&
958
	                        all_signatures_exist(&dnskeys, &dnskey_rrsig,
959
	                                             zone_keys, policy));
960
	knot_rdataset_clear(&dnskey_rrsig.rrs, NULL);
961
	if (!modified && signatures_exist) {
962 963
		return KNOT_EOK;
	}
964 965

	dbg_dnssec_detail("Creating new signatures for DNSKEYs\n");
966
	return update_dnskeys_rrsigs(&dnskeys, &rrsigs, &soa, zone_keys, policy, changeset);
967 968
}

969 970 971 972 973
/*!
 * \brief Goes through list and looks for RRSet type there.
 *
 * \return True if RR type is in the list, false otherwise.
 */
974 975
static bool rr_type_in_list(const knot_rrset_t *rr, const list_t *l)
{
976
	if (l == NULL || EMPTY_LIST(*l)) {
977 978 979 980
		return false;
	}
	assert(rr);

981
	type_node_t *n = NULL;
982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001
	WALK_LIST(n, *l) {
		type_node_t *type_node = (type_node_t *)n;
		if (type_node->type == rr->type) {
			return true;
		}
	};

	return false;
}

static int add_rr_type_to_list(const knot_rrset_t *rr, list_t *l)
{
	assert(rr);
	assert(l);

	type_node_t *n = malloc(sizeof(type_node_t));
	if (n == NULL) {
		ERR_ALLOC_FAILED;
		return KNOT_ENOMEM;
	}
1002
	n->type = rr->type;
1003 1004 1005 1006 1007

	add_head(l, (node_t *)n);
	return KNOT_EOK;
}

1008 1009 1010 1011 1012
/*!
 * \brief Checks whether RRSet is not already in the hash table, automatically
 *        stores its pointer to the table if not found, but returns false in
 *        that case.
 *
1013 1014
 * \param rrset      RRSet to be checked for.
 * \param tree       Tree with already signed RRs.
Jan Kadlec's avatar
Jan Kadlec committed
1015
 * \param rr_signed  Set to true if RR is signed already, false otherwise.
1016
 *
1017
 * \return KNOT_E*
1018
 */
1019 1020
static int rr_already_signed(const knot_rrset_t *rrset, hattrie_t *t,
                             bool *rr_signed)
1021 1022 1023
{
	assert(rrset);
	assert(t);
1024
	*rr_signed = false;
1025 1026 1027
	// Create a key = RRSet owner converted to sortable format
	uint8_t lf[KNOT_DNAME_MAXLEN];
	knot_dname_lf(lf, rrset->owner, NULL);
1028
	value_t stored_info = (signed_info_t *)hattrie_tryget(t, (char *)lf+1,
1029
	                                                      *lf);
1030
	if (stored_info == NULL) {
1031
		// Create new info struct
1032
		signed_info_t *info = malloc(sizeof(signed_info_t));
1033 1034
		if (info == NULL) {
			ERR_ALLOC_FAILED;
1035
			return KNOT_ENOMEM;
1036
		}
1037
		memset(info, 0, sizeof(signed_info_t));
1038
		// Store actual dname repr
1039
		info->dname = knot_dname_copy(rrset->owner, NULL);
1040 1041
		if (info->dname == NULL) {
			free(info);
1042
			return KNOT_ENOMEM;
1043
		}
1044
		// Create new list to insert as a value
1045
		info->type_list = malloc(sizeof(list_t));
1046
		if (info->type_list == NULL) {
1047
			ERR_ALLOC_FAILED;
1048 1049
			free(info->dname);
			free(info);
1050
			return KNOT_ENOMEM;
1051
		}
1052
		init_list(info->type_list);
1053
		// Insert type to list
1054
		int ret = add_rr_type_to_list(rrset, info->type_list);
1055
		if (ret != KNOT_EOK) {
1056 1057 1058
			free(info->type_list);
			free(info->dname);
			free(info);
1059 1060
			return ret;
		}
1061
		*hattrie_get(t, (char *)lf+1, *lf) = info;
1062
	} else {
1063
		signed_info_t *info = *((signed_info_t **)stored_info);
1064
		assert(info->type_list);
1065
		// Check whether the type is in the list already
1066
		if (rr_type_in_list(rrset, info->type_list)) {
1067 1068
			*rr_signed = true;
			return KNOT_EOK;
1069 1070
		}
		// Just update the existing list
1071
		int ret = add_rr_type_to_list(rrset, info->type_list);
1072
		if (ret != KNOT_EOK) {
1073 1074
			*rr_signed = false;
			return KNOT_EOK;
1075 1076 1077
		}
	}

1078 1079
	*rr_signed = false;
	return KNOT_EOK;
1080 1081
}

1082 1083 1084 1085
/*!
 * \brief Wrapper function for changeset signing - to be used with changeset
 *        apply functions.
 *
1086 1087
 * \param chg_rrset  RRSet to be signed (potentially)
 * \param data       Signing data
1088 1089 1090
 *
 * \return Error code, KNOT_EOK if successful.
 */
1091
static int sign_changeset_wrap(knot_rrset_t *chg_rrset, changeset_signing_data_t *args)
1092 1093
{
	// Find RR's node in zone, find out if we need to sign this RR
1094
	const zone_node_t *node =
1095
		zone_contents_find_node(args->zone, chg_rrset->owner);
1096

1097 1098
	// If node is not in zone, all its RRSIGs were dropped - no-op
	if (node) {
1099 1100
		knot_rrset_t zone_rrset = node_rrset(node, chg_rrset->type);
		knot_rrset_t rrsigs = node_rrset(node, KNOT_RRTYPE_RRSIG);
1101
		bool should_sign = false;
1102

1103
		int ret = knot_zone_sign_rr_should_be_signed(node, &zone_rrset,
1104 1105 1106 1107
		                                             &should_sign);
		if (ret != KNOT_EOK) {
			return ret;
		}
1108 1109

		// Check for RRSet in the 'already_signed' table
1110
		if (args->signed_tree && (should_sign && knot_rrset_empty(&zone_rrset))) {