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

    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 <sys/types.h>
19

20
#include "dnssec/error.h"
21 22 23
#include "dnssec/key.h"
#include "dnssec/keytag.h"
#include "dnssec/sign.h"
24
#include "knot/dnssec/key-events.h"
25
#include "knot/dnssec/rrset-sign.h"
26
#include "knot/dnssec/zone-sign.h"
27
#include "libknot/libknot.h"
28
#include "contrib/dynarray.h"
29
#include "contrib/macros.h"
30
#include "contrib/wire_ctx.h"
31

32
typedef struct {
33 34 35 36
	node_t n;
	uint16_t type;
} type_node_t;

37
typedef struct {
38 39 40 41 42
	knot_dname_t *dname;
	knot_dname_t *hashed_dname;
	list_t *type_list;
} signed_info_t;

43 44
/*- private API - common functions -------------------------------------------*/

45 46 47 48 49 50 51 52 53 54 55
/*!
 * \brief Initializes RR set and set owner and rclass from template RR set.
 */
static knot_rrset_t rrset_init_from(const knot_rrset_t *src, uint16_t type)
{
	assert(src);
	knot_rrset_t rrset;
	knot_rrset_init(&rrset, src->owner, type, src->rclass);
	return rrset;
}

56 57 58
/*!
 * \brief Create empty RRSIG RR set for a given RR set to be covered.
 */
59
static knot_rrset_t create_empty_rrsigs_for(const knot_rrset_t *covered)
60
{
61
	assert(!knot_rrset_empty(covered));
62 63 64
	return rrset_init_from(covered, KNOT_RRTYPE_RRSIG);
}

65 66 67 68 69 70 71 72 73 74 75 76 77 78
static bool apex_rr_changed(const zone_node_t *old_apex,
                            const zone_node_t *new_apex,
                            uint16_t type)
{
	assert(old_apex);
	assert(new_apex);
	knot_rrset_t old_rr = node_rrset(old_apex, type);
	knot_rrset_t new_rr = node_rrset(new_apex, type);

	return !knot_rrset_equal(&old_rr, &new_rr, KNOT_RRSET_COMPARE_WHOLE);
}

static bool apex_dnssec_changed(zone_update_t *update)
{
79 80
	if (update->zone->contents == NULL || update->new_cont == NULL) {
		return false;
81
	}
82 83 84 85
	return apex_rr_changed(update->zone->contents->apex,
			       update->new_cont->apex, KNOT_RRTYPE_DNSKEY) ||
	       apex_rr_changed(update->zone->contents->apex,
			       update->new_cont->apex, KNOT_RRTYPE_NSEC3PARAM);
86 87
}

88 89 90 91 92 93 94 95 96 97 98 99 100
/*- 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.
 */
101 102
static bool valid_signature_exists(const knot_rrset_t *covered,
				   const knot_rrset_t *rrsigs,
103 104
				   const dnssec_key_t *key,
				   dnssec_sign_ctx_t *ctx,
105
				   const kdnssec_ctx_t *dnssec_ctx)
106
{
107 108
	assert(key);

109
	if (knot_rrset_empty(rrsigs)) {
110
		return false;
111
	}
112

113
	uint16_t rrsigs_rdata_count = rrsigs->rrs.rr_count;
114
	for (uint16_t i = 0; i < rrsigs_rdata_count; i++) {
115 116 117 118 119
		uint16_t rr_keytag = knot_rrsig_key_tag(&rrsigs->rrs, i);
		uint16_t rr_covered = knot_rrsig_type_covered(&rrsigs->rrs, i);

		uint16_t keytag = dnssec_key_get_keytag(key);
		if (rr_keytag != keytag || rr_covered != covered->type) {
120
			continue;
121
		}
122

123 124 125 126
		if (knot_check_signature(covered, rrsigs, i, key, ctx,
		                         dnssec_ctx) == KNOT_EOK) {
			return true;
		}
127
	}
128

129 130
	return false;
}
131

132
/*!
133
 * \brief Check if key can be used to sign given RR.
134
 *
135 136
 * \param key      Zone key.
 * \param covered  RR to be checked.
137 138 139
 *
 * \return The RR should be signed.
 */
140
static bool use_key(const zone_key_t *key, const knot_rrset_t *covered)
141 142
{
	assert(key);
143
	assert(covered);
144

145
	if (!key->is_active) {
146 147 148
		return false;
	}

149 150
	bool is_apex = knot_dname_is_equal(covered->owner,
	                                   dnssec_key_get_dname(key->key));
151

152
	bool is_zone_key = is_apex && covered->type == KNOT_RRTYPE_DNSKEY;
153

154
	return (key->is_ksk && is_zone_key) || (key->is_zsk && !is_zone_key);
155 156
}

157
/*!
158 159 160 161 162 163 164 165
 * \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.
166
 */
167
static bool all_signatures_exist(const knot_rrset_t *covered,
Lubos Slovak's avatar
Lubos Slovak committed
168
                                 const knot_rrset_t *rrsigs,
169
                                 const zone_keyset_t *zone_keys,
170
                                 const kdnssec_ctx_t *dnssec_ctx)
171
{
172
	assert(!knot_rrset_empty(covered));
173 174
	assert(zone_keys);

175
	for (int i = 0; i < zone_keys->count; i++) {
176
		zone_key_t *key = &zone_keys->keys[i];
177
		if (!use_key(key, covered)) {
178
			continue;
179
		}
180

181
		if (!valid_signature_exists(covered, rrsigs, key->key,
182
		                            key->ctx, dnssec_ctx)) {
Lubos Slovak's avatar
Lubos Slovak committed
183
			return false;
184 185 186
		}
	}

Lubos Slovak's avatar
Lubos Slovak committed
187
	return true;
188
}
189

190
/*!
191
 * \brief Get zone key for given RRSIG (checks key tag only).
192
 *
193 194 195 196
 * \param rrsigs  RR set with RRSIGs.
 * \param pos     Number of RR in RR set.
 * \param keys    Zone keys.
 *
197
 * \return Dynarray of such keys.
198
 */
199
static keyptr_dynarray_t get_matching_zone_keys(const knot_rrset_t *rrsigs,
200
                                                     size_t pos, const zone_keyset_t *keys)
201
{
202 203 204
	assert(rrsigs && rrsigs->type == KNOT_RRTYPE_RRSIG);
	assert(keys);

205
	uint16_t keytag = knot_rrsig_key_tag(&rrsigs->rrs, pos);
206

207
	return get_zone_keys(keys, keytag);
208 209
}

210 211 212 213 214 215 216 217
/*!
 * \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,
218
                                     knot_time_t *expires_at)
Jan Kadlec's avatar
Jan Kadlec committed
219
{
220 221 222
	assert(rrsigs);
	assert(expires_at);

223 224 225
	uint32_t curr_rdata = knot_rrsig_sig_expiration(&rrsigs->rrs, pos);
	knot_time_t current = knot_time_from_u32(curr_rdata);
	*expires_at = knot_time_min(current, *expires_at);
Jan Kadlec's avatar
Jan Kadlec committed
226 227
}

228 229 230
/*!
 * \brief Add expired or invalid RRSIGs into the changeset for removal.
 *
231 232 233 234 235 236
 * \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.
237 238 239
 *
 * \return Error code, KNOT_EOK if successful.
 */
240
static int remove_expired_rrsigs(const knot_rrset_t *covered,
Jan Kadlec's avatar
Jan Kadlec committed
241
                                 const knot_rrset_t *rrsigs,
242
                                 const zone_keyset_t *zone_keys,
243
                                 const kdnssec_ctx_t *dnssec_ctx,
244
                                 changeset_t *changeset,
245
                                 knot_time_t *expires_at)
246
{
247 248
	assert(changeset);

249
	if (knot_rrset_empty(rrsigs)) {
250
		return KNOT_EOK;
251
	}
252 253

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

255 256
	knot_rrset_t to_remove;
	knot_rrset_init_empty(&to_remove);
257
	int result = KNOT_EOK;
258

259
	knot_rrset_t synth_rrsig = rrset_init_from(rrsigs, KNOT_RRTYPE_RRSIG);
260
	result = knot_synth_rrsig(covered->type, &rrsigs->rrs, &synth_rrsig.rrs, NULL);
261
	if (result != KNOT_EOK) {
262 263 264 265
		if (result != KNOT_ENOENT) {
			return result;
		}
		return KNOT_EOK;
266 267
	}

268
	uint16_t rrsig_rdata_count = synth_rrsig.rrs.rr_count;
269
	for (uint16_t i = 0; i < rrsig_rdata_count; i++) {
270 271
		struct keyptr_dynarray keys = get_matching_zone_keys(&synth_rrsig, i, zone_keys);
		int endloop = 0; // 1 - continue; 2 - break
272

273
		dynarray_foreach(keyptr, zone_key_t *, key, keys) {
274
			if (!(*key)->is_active) {
275 276
				continue;
			}
277
			result = knot_check_signature(covered, &synth_rrsig, i,
278
			                              (*key)->key, (*key)->ctx, dnssec_ctx);
279
			if (result == KNOT_EOK) {
280
				// valid signature
281
				note_earliest_expiration(&synth_rrsig, i, expires_at);
282
				endloop = 1;
283
				break;
284 285
			} else if (result != DNSSEC_INVALID_SIGNATURE) {
				endloop = 2;
286
				break;
287
			}
288 289
		}
		keyptr_dynarray_free(&keys);
290

291 292 293 294
		if (endloop == 2) {
			break;
		} else if (endloop == 1) {
			continue;
295
		}
296

297
		if (knot_rrset_empty(&to_remove)) {
298
			to_remove = create_empty_rrsigs_for(&synth_rrsig);
299
		}
300

301
		knot_rdata_t *rr_rem = knot_rdataset_at(&synth_rrsig.rrs, i);
302
		result = knot_rdataset_add(&to_remove.rrs, rr_rem, NULL);
303
		if (result != KNOT_EOK) {
304
			break;
305
		}
306
	}
307

308
	if (!knot_rrset_empty(&to_remove) && result == KNOT_EOK) {
309
		result = changeset_add_removal(changeset, &to_remove, 0);
310 311
	}

312
	knot_rdataset_clear(&synth_rrsig.rrs, NULL);
313
	knot_rdataset_clear(&to_remove.rrs, NULL);
314

315
	return result;
316 317
}

318 319 320
/*!
 * \brief Add missing RRSIGs into the changeset for adding.
 *
321 322 323 324 325
 * \param covered     RR set with covered records.
 * \param rrsigs      RR set with RRSIGs.
 * \param zone_keys   Zone keys.
 * \param dnssec_ctx  DNSSEC signing context
 * \param changeset   Changeset to be updated.
326 327 328
 *
 * \return Error code, KNOT_EOK if successful.
 */
329 330
static int add_missing_rrsigs(const knot_rrset_t *covered,
                              const knot_rrset_t *rrsigs,
331
                              const zone_keyset_t *zone_keys,
332
                              const kdnssec_ctx_t *dnssec_ctx,
333
                              changeset_t *changeset)
334
{
335
	assert(!knot_rrset_empty(covered));
336 337
	assert(zone_keys);
	assert(changeset);
338 339

	int result = KNOT_EOK;
340 341
	knot_rrset_t to_add;
	knot_rrset_init_empty(&to_add);
342

343
	for (int i = 0; i < zone_keys->count; i++) {
344
		const zone_key_t *key = &zone_keys->keys[i];
345
		if (!use_key(key, covered)) {
346
			continue;
347
		}
348

349
		if (valid_signature_exists(covered, rrsigs, key->key, key->ctx, dnssec_ctx)) {
350
			continue;
351
		}
352

353
		if (knot_rrset_empty(&to_add)) {
354 355 356
			to_add = create_empty_rrsigs_for(covered);
		}

357
		result = knot_sign_rrset(&to_add, covered, key->key, key->ctx, dnssec_ctx, NULL);
358
		if (result != KNOT_EOK) {
359
			break;
360
		}
361 362
	}

363
	if (!knot_rrset_empty(&to_add) && result == KNOT_EOK) {
364
		result = changeset_add_addition(changeset, &to_add, 0);
365 366
	}

367
	knot_rdataset_clear(&to_add.rrs, NULL);
368

369
	return result;
370 371
}

372 373 374 375 376 377 378 379
/*!
 * \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.
 */
380
static int remove_rrset_rrsigs(const knot_dname_t *owner, uint16_t type,
381
                               const knot_rrset_t *rrsigs,
382
                               changeset_t *changeset)
383
{
384
	assert(owner);
385
	assert(changeset);
386 387
	knot_rrset_t synth_rrsig;
	knot_rrset_init(&synth_rrsig, (knot_dname_t *)owner,
388
	                KNOT_RRTYPE_RRSIG, rrsigs->rclass);
389
	int ret = knot_synth_rrsig(type, &rrsigs->rrs, &synth_rrsig.rrs, NULL);
390
	if (ret != KNOT_EOK) {
391 392 393 394
		if (ret != KNOT_ENOENT) {
			return ret;
		}
		return KNOT_EOK;
395 396
	}

397
	ret = changeset_add_removal(changeset, &synth_rrsig, 0);
398
	knot_rdataset_clear(&synth_rrsig.rrs, NULL);
399 400

	return ret;
401 402
}

403 404 405 406 407 408 409 410 411 412 413
/*!
 * \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,
414
                              const knot_rrset_t *rrsigs,
415
                              const zone_keyset_t *zone_keys,
416
                              const kdnssec_ctx_t *dnssec_ctx,
417
                              changeset_t *changeset)
418
{
419
	assert(!knot_rrset_empty(covered));
420

421
	if (!knot_rrset_empty(rrsigs)) {
422 423
		int result = remove_rrset_rrsigs(covered->owner, covered->type,
		                                 rrsigs, changeset);
424 425
		if (result != KNOT_EOK) {
			return result;
426 427 428
		}
	}

429
	return add_missing_rrsigs(covered, NULL, zone_keys, dnssec_ctx, changeset);
430 431
}

432 433 434
/*!
 * \brief Drop all expired and create new RRSIGs for covered records.
 *
435 436 437 438 439
 * \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.
440 441 442 443
 *
 * \return Error code, KNOT_EOK if successful.
 */
static int resign_rrset(const knot_rrset_t *covered,
444
                        const knot_rrset_t *rrsigs,
445
                        const zone_keyset_t *zone_keys,
446
                        const kdnssec_ctx_t *dnssec_ctx,
447
                        changeset_t *changeset,
448
                        knot_time_t *expires_at)
449
{
450
	assert(!knot_rrset_empty(covered));
451

Jan Kadlec's avatar
Jan Kadlec committed
452
	// TODO this function creates some signatures twice (for checking)
453
	int result = remove_expired_rrsigs(covered, rrsigs, zone_keys,
454
	                                   dnssec_ctx, changeset, expires_at);
455 456 457 458
	if (result != KNOT_EOK) {
		return result;
	}

459
	return add_missing_rrsigs(covered, rrsigs, zone_keys, dnssec_ctx,
460
	                          changeset);
461 462
}

463
static int remove_standalone_rrsigs(const zone_node_t *node,
464
                                    const knot_rrset_t *rrsigs,
465
                                    changeset_t *changeset)
466 467 468 469 470
{
	if (rrsigs == NULL) {
		return KNOT_EOK;
	}

471
	uint16_t rrsigs_rdata_count = rrsigs->rrs.rr_count;
472
	for (uint16_t i = 0; i < rrsigs_rdata_count; ++i) {
473
		uint16_t type_covered = knot_rrsig_type_covered(&rrsigs->rrs, i);
474
		if (!node_rrtype_exists(node, type_covered)) {
475 476 477
			knot_rrset_t to_remove;
			knot_rrset_init(&to_remove, rrsigs->owner, rrsigs->type,
			                rrsigs->rclass);
478
			knot_rdata_t *rr_rem = knot_rdataset_at(&rrsigs->rrs, i);
479
			int ret = knot_rdataset_add(&to_remove.rrs, rr_rem, NULL);
480 481 482
			if (ret != KNOT_EOK) {
				return ret;
			}
483
			ret = changeset_add_removal(changeset, &to_remove, 0);
484
			knot_rdataset_clear(&to_remove.rrs, NULL);
485 486 487 488 489 490 491 492 493
			if (ret != KNOT_EOK) {
				return ret;
			}
		}
	}

	return KNOT_EOK;
}

494 495 496
/*!
 * \brief Update RRSIGs in a given node by updating changeset.
 *
497 498 499 500 501
 * \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.
502 503 504
 *
 * \return Error code, KNOT_EOK if successful.
 */
505
static int sign_node_rrsets(const zone_node_t *node,
506
                            const zone_keyset_t *zone_keys,
507
                            const kdnssec_ctx_t *dnssec_ctx,
508
                            changeset_t *changeset,
509
                            knot_time_t *expires_at)
510
{
511
	assert(node);
512
	assert(dnssec_ctx);
513 514

	int result = KNOT_EOK;
515
	knot_rrset_t rrsigs = node_rrset(node, KNOT_RRTYPE_RRSIG);
516

517
	for (int i = 0; i < node->rrset_count; i++) {
518
		knot_rrset_t rrset = node_rrset_at(node, i);
519
		if (rrset.type == KNOT_RRTYPE_RRSIG) {
520 521
			continue;
		}
522 523

		if (!knot_zone_sign_rr_should_be_signed(node, &rrset)) {
Lubos Slovak's avatar
Lubos Slovak committed
524 525 526
			continue;
		}

527 528 529
		if (dnssec_ctx->rrsig_drop_existing) {
			result = force_resign_rrset(&rrset, &rrsigs, zone_keys,
			                            dnssec_ctx, changeset);
530
		} else {
531 532
			result = resign_rrset(&rrset, &rrsigs, zone_keys,
			                      dnssec_ctx, changeset, expires_at);
533
		}
534

535
		if (result != KNOT_EOK) {
536
			return result;
537
		}
538 539
	}

540
	return remove_standalone_rrsigs(node, &rrsigs, changeset);
541
}
542

543 544 545
/*!
 * \brief Struct to carry data for 'sign_data' callback function.
 */
Jan Kadlec's avatar
Jan Kadlec committed
546
typedef struct node_sign_args {
547
	const zone_keyset_t *zone_keys;
548
	const kdnssec_ctx_t *dnssec_ctx;
549
	changeset_t *changeset;
550
	knot_time_t expires_at;
Jan Kadlec's avatar
Jan Kadlec committed
551 552
} node_sign_args_t;

553 554 555 556 557 558
/*!
 * \brief Sign node (callback function).
 *
 * \param node  Node to be signed.
 * \param data  Callback data, node_sign_args_t.
 */
559
static int sign_node(zone_node_t **node, void *data)
560
{
561
	assert(node && *node);
Jan Kadlec's avatar
Jan Kadlec committed
562
	assert(data);
563

564
	node_sign_args_t *args = (node_sign_args_t *)data;
565

Jan Kadlec's avatar
Jan Kadlec committed
566
	if ((*node)->rrset_count == 0) {
567
		return KNOT_EOK;
Jan Kadlec's avatar
Jan Kadlec committed
568 569
	}

570
	if ((*node)->flags & NODE_FLAGS_NONAUTH) {
571
		return KNOT_EOK;
Jan Kadlec's avatar
Jan Kadlec committed
572
	}
573

574
	int result = sign_node_rrsets(*node, args->zone_keys, args->dnssec_ctx,
575
	                              args->changeset, &args->expires_at);
576
	(*node)->flags &= ~NODE_FLAGS_REMOVED_NSEC;
577 578

	return result;
579 580
}

581 582 583
/*!
 * \brief Update RRSIGs in a given zone tree by updating changeset.
 *
Jan Kadlec's avatar
Jan Kadlec committed
584 585 586 587 588
 * \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.
589 590 591
 *
 * \return Error code, KNOT_EOK if successful.
 */
592
static int zone_tree_sign(zone_tree_t *tree,
593
                          const zone_keyset_t *zone_keys,
594
                          const kdnssec_ctx_t *dnssec_ctx,
595
                          changeset_t *changeset,
596
                          knot_time_t *expires_at)
597
{
598
	assert(zone_keys);
599
	assert(dnssec_ctx);
600
	assert(changeset);
601

602 603
	node_sign_args_t args = {
		.zone_keys = zone_keys,
604
		.dnssec_ctx = dnssec_ctx,
605
		.changeset = changeset,
606
		.expires_at = knot_time_add(dnssec_ctx->now, dnssec_ctx->policy->rrsig_lifetime),
607 608
	};

609
	int result = zone_tree_apply(tree, sign_node, &args);
610 611 612
	*expires_at = args.expires_at;

	return result;
613 614
}

615 616 617
/*- private API - signing of NSEC(3) in changeset ----------------------------*/

/*!
618
 * \brief Struct to carry data for changeset signing callback functions.
619
 */
620
typedef struct {
621
	const zone_contents_t *zone;
622
	const zone_keyset_t *zone_keys;
623
	const kdnssec_ctx_t *dnssec_ctx;
624
	changeset_t *changeset;
625
	trie_t *signed_tree;
626
} changeset_signing_data_t;
627

628 629
/*- private API - DNSKEY handling --------------------------------------------*/

630
static int rrset_add_zone_key(knot_rrset_t *rrset,
631
                              zone_key_t *zone_key,
632
                              uint32_t ttl)
633 634 635 636
{
	assert(rrset);
	assert(zone_key);

637 638
	dnssec_binary_t dnskey_rdata = { 0 };
	dnssec_key_get_rdata(zone_key->key, &dnskey_rdata);
639

640 641
	return knot_rrset_add_rdata(rrset, dnskey_rdata.data,
	                            dnskey_rdata.size, ttl, NULL);
642 643
}

644 645 646 647 648 649 650 651 652 653 654 655 656 657
static int rrset_add_zone_ds(knot_rrset_t *rrset,
                             zone_key_t *zone_key,
                             uint32_t ttl)
{
	assert(rrset);
	assert(zone_key);

	dnssec_binary_t cds_rdata = { 0 };
	zone_key_calculate_ds(zone_key, &cds_rdata);

	return knot_rrset_add_rdata(rrset, cds_rdata.data,
	                            cds_rdata.size, ttl, NULL);
}

658 659 660 661 662
/*!
 * \brief Goes through list and looks for RRSet type there.
 *
 * \return True if RR type is in the list, false otherwise.
 */
663 664
static bool rr_type_in_list(const knot_rrset_t *rr, const list_t *l)
{
665
	if (l == NULL || EMPTY_LIST(*l)) {
666 667 668 669
		return false;
	}
	assert(rr);

670
	type_node_t *n = NULL;
671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689
	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) {
		return KNOT_ENOMEM;
	}
690
	n->type = rr->type;
691 692 693 694 695

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

696 697 698 699 700
/*!
 * \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.
 *
701 702
 * \param rrset      RRSet to be checked for.
 * \param tree       Tree with already signed RRs.
Jan Kadlec's avatar
Jan Kadlec committed
703
 * \param rr_signed  Set to true if RR is signed already, false otherwise.
704
 *
705
 * \return KNOT_E*
706
 */
707
static int rr_already_signed(const knot_rrset_t *rrset, trie_t *t,
708
                             bool *rr_signed)
709 710 711
{
	assert(rrset);
	assert(t);
712
	*rr_signed = false;
713 714 715
	// Create a key = RRSet owner converted to sortable format
	uint8_t lf[KNOT_DNAME_MAXLEN];
	knot_dname_lf(lf, rrset->owner, NULL);
716
	trie_val_t stored_info = (signed_info_t *)trie_get_try(t, (char *)lf+1,
717
	                                                      *lf);
718
	if (stored_info == NULL) {
719
		// Create new info struct
720
		signed_info_t *info = malloc(sizeof(signed_info_t));
721
		if (info == NULL) {
722
			return KNOT_ENOMEM;
723
		}
724
		memset(info, 0, sizeof(signed_info_t));
725
		// Store actual dname repr
726
		info->dname = knot_dname_copy(rrset->owner, NULL);
727 728
		if (info->dname == NULL) {
			free(info);
729
			return KNOT_ENOMEM;
730
		}
731
		// Create new list to insert as a value
732
		info->type_list = malloc(sizeof(list_t));
733
		if (info->type_list == NULL) {
734 735
			free(info->dname);
			free(info);
736
			return KNOT_ENOMEM;
737
		}
738
		init_list(info->type_list);
739
		// Insert type to list
740
		int ret = add_rr_type_to_list(rrset, info->type_list);
741
		if (ret != KNOT_EOK) {
742 743 744
			free(info->type_list);
			free(info->dname);
			free(info);
745 746
			return ret;
		}
747
		*trie_get_ins(t, (char *)lf+1, *lf) = info;
748
	} else {
749
		signed_info_t *info = *((signed_info_t **)stored_info);
750
		assert(info->type_list);
751
		// Check whether the type is in the list already
752
		if (rr_type_in_list(rrset, info->type_list)) {
753 754
			*rr_signed = true;
			return KNOT_EOK;
755 756
		}
		// Just update the existing list
757
		int ret = add_rr_type_to_list(rrset, info->type_list);
758
		if (ret != KNOT_EOK) {
759 760
			*rr_signed = false;
			return KNOT_EOK;
761 762 763
		}
	}

764 765
	*rr_signed = false;
	return KNOT_EOK;
766 767
}

768 769 770 771
/*!
 * \brief Wrapper function for changeset signing - to be used with changeset
 *        apply functions.
 *
772 773
 * \param chg_rrset  RRSet to be signed (potentially)
 * \param data       Signing data
774 775 776
 *
 * \return Error code, KNOT_EOK if successful.
 */
777
static int sign_changeset_wrap(knot_rrset_t *chg_rrset, changeset_signing_data_t *args)
778 779
{
	// Find RR's node in zone, find out if we need to sign this RR
780
	const zone_node_t *node =
781
		zone_contents_find_node(args->zone, chg_rrset->owner);
782

783 784
	// If node is not in zone, all its RRSIGs were dropped - no-op
	if (node) {
785 786
		knot_rrset_t zone_rrset = node_rrset(node, chg_rrset->type);
		knot_rrset_t rrsigs = node_rrset(node, KNOT_RRTYPE_RRSIG);
787

788
		bool should_sign = knot_zone_sign_rr_should_be_signed(node, &zone_rrset);
789 790

		// Check for RRSet in the 'already_signed' table
791
		if (args->signed_tree && (should_sign && knot_rrset_empty(&zone_rrset))) {
792 793 794 795 796 797 798 799 800 801 802 803 804
			bool already_signed = false;

			int ret = rr_already_signed(chg_rrset, args->signed_tree,
			                            &already_signed);
			if (ret != KNOT_EOK) {
				return ret;
			}
			if (already_signed) {
				/* Do not sign again. */
				should_sign = false;
			}
		}

805
		if (should_sign) {
806
			return force_resign_rrset(&zone_rrset, &rrsigs,
807
			                          args->zone_keys,
808
			                          args->dnssec_ctx,
809
			                          args->changeset);
810 811
		} else {
			/*
812 813
			 * If RRSet in zone DOES have RRSIGs although we
			 * should not sign it, DDNS-caused change to node/rr
814
			 * occurred and we have to drop all RRSIGs.
815 816 817 818
			 *
			 * OR
			 *
			 * The whole RRSet was removed, but RRSIGs remained in
Jan Kadlec's avatar
Jan Kadlec committed
819
			 * the zone. We need to drop them as well.
820
			 */
821
			return remove_rrset_rrsigs(chg_rrset->owner,
822
			                           chg_rrset->type, &rrsigs,
823
			                           args->changeset);
824
		}
825
	}
Jan Kadlec's avatar
Jan Kadlec committed
826

827
	return KNOT_EOK;
828 829
}

830 831 832 833 834 835
/*!
 * \brief Frees info node about update signing.
 *
 * \param val  Node to free.
 * \param d    Unused.
 */
836
static int free_helper_trie_node(trie_val_t *val, void *d)
837
{
838
	UNUSED(d);
839
	signed_info_t *info = (signed_info_t *)*val;
840 841 842 843
	if (info->type_list && !EMPTY_LIST(*(info->type_list))) {
		WALK_LIST_FREE(*(info->type_list));
	}
	free(info->type_list);
844 845
	knot_dname_free(&info->dname, NULL);
	knot_dname_free(&info->hashed_dname, NULL);
846
	free(info);
847
	return KNOT_EOK;
848 849
}

850 851 852 853 854
/*!
 * \brief Clears trie with info about update signing.
 *
 * \param t  Trie to clear.
 */
855
static void knot_zone_clear_sorted_changes(trie_t *t)
856 857
{
	if (t) {
858
		trie_apply(t, free_helper_trie_node, NULL);
859 860 861
	}
}

862
/*- public API ---------------------------------------------------------------*/
863

864
int knot_zone_sign(zone_update_t *update,
865
                   zone_keyset_t *zone_keys,
866
                   const kdnssec_ctx_t *dnssec_ctx,
867
                   knot_time_t *expire_at)
868
{
869
	if (!update || !zone_keys || !dnssec_ctx || !expire_at) {
870 871
		return KNOT_EINVAL;
	}
872 873 874

	int result;

875 876
	changeset_t ch;
	result = changeset_init(&ch, update->new_cont->apex->owner);
877 878 879 880
	if (result != KNOT_EOK) {
		return result;
	}

881
	knot_time_t normal_expire = 0;
882
	result = zone_tree_sign(update->new_cont->nodes, zone_keys, dnssec_ctx, &ch, &normal_expire);
883
	if (result != KNOT_EOK) {
884
		changeset_clear(&ch);
885
		return result;
886
	}
887

888
	knot_time_t nsec3_expire = 0;
889 890
	result = zone_tree_sign(update->new_cont->nsec3_nodes, zone_keys, dnssec_ctx,
				&ch, &nsec3_expire);
891
	if (result != KNOT_EOK) {
892
		changeset_clear(&ch);
893
		return result;
894
	}
895

896 897 898 899
	*expire_at = knot_time_min(normal_expire, nsec3_expire);

	result = zone_update_apply_changeset(update, &ch); // _fix not needed
	changeset_clear(&ch);
Jan Kadlec's avatar
Jan Kadlec committed
900

901
	return result;
902
}
903

904 905 906
int knot_zone_sign_update_dnskeys(zone_update_t *update,
                                  zone_keyset_t *zone_keys,
                                  const kdnssec_ctx_t *dnssec_ctx)
907
{
908 909 910
	if (update == NULL || zone_keys == NULL || dnssec_ctx == NULL) {
		return KNOT_EINVAL;
	}
911

912 913 914 915 916 917 918 919 920 921
	const zone_node_t *apex = update->new_cont->apex;
	knot_rrset_t dnskeys = node_rrset(apex, KNOT_RRTYPE_DNSKEY);
	knot_rrset_t cdnskeys = node_rrset(apex, KNOT_RRTYPE_CDNSKEY);
	knot_rrset_t cdss = node_rrset(apex, KNOT_RRTYPE_CDS);
	knot_rrset_t *add_dnskeys = NULL;
	knot_rrset_t *add_cdnskeys = NULL;
	knot_rrset_t *add_cdss = NULL;
	uint32_t dnskey_ttl = dnssec_ctx->policy->dnskey_ttl;
	knot_rrset_t soa = node_rrset(apex, KNOT_RRTYPE_SOA);
	if (knot_rrset_empty(&soa)) {
922 923 924
		return KNOT_EINVAL;
	}

925 926 927 928 929
	changeset_t ch;
	int ret = changeset_init(&ch, apex->owner);
	if (ret != KNOT_EOK) {
		return ret;
	}
930

931
#define CHECK_RET if (ret != KNOT_EOK) goto cleanup
932

933 934 935 936 937 938 939
	// remove all. This will cancel out with additions later
	ret = changeset_add_removal(&ch, &dnskeys, 0);
	CHECK_RET;
	ret = changeset_add_removal(&ch, &cdnskeys, 0);
	CHECK_RET;
	ret = changeset_add_removal(&ch, &cdss, 0);
	CHECK_RET;
940

941 942 943 944 945 946 947
	// add DNSKEYs, CDNSKEYs and CDSs
	add_dnskeys = knot_rrset_new(apex->owner, KNOT_RRTYPE_DNSKEY, soa.rclass, NULL);
	add_cdnskeys = knot_rrset_new(apex->owner, KNOT_RRTYPE_CDNSKEY, soa.rclass, NULL);
	add_cdss = knot_rrset_new(apex->owner, KNOT_RRTYPE_CDS, soa.rclass, NULL);
	if (add_dnskeys == NULL || add_cdnskeys == NULL || add_cdss == NULL) {
		ret = KNOT_ENOMEM;
	}
948
	zone_key_t *ksk_for_cds = NULL;
949 950
	unsigned crp = dnssec_ctx->policy->child_records_publish;
	int kfc_prio = (crp == CHILD_RECORDS_ALWAYS ? 0 : (crp == CHILD_RECORDS_ROLLOVER ? 1 : 2));
951
	for (int i = 0; i < zone_keys->count; i++) {
952
		zone_key_t *key = &zone_keys->keys[i];
953 954 955
		if (key->is_public) {
			ret = rrset_add_zone_key(add_dnskeys, key, dnskey_ttl);
			CHECK_RET;
956
		}
957

958 959
		// determine which key (if any) will be the one for CDS/CDNSKEY
		if (key->is_ksk && key->cds_priority > kfc_prio) {
960 961 962 963 964 965 966
			ksk_for_cds = key;
			kfc_prio = key->cds_priority;
		}
	}

	if (ksk_for_cds != NULL) {
		ret = rrset_add_zone_key(add_cdnskeys, ksk_for_cds, 0);
967 968 969
		CHECK_RET;
		ret = rrset_add_zone_ds(add_cdss, ksk_for_cds, 0);
		CHECK_RET;
970
	}
971

972 973 974 975 976 977 978 979 980 981 982
	if (crp == CHILD_RECORDS_EMPTY) {
		const uint8_t cdnskey_empty[5] = { 0, 0, 3, 0, 0 };
		const uint8_t cds_empty[5] = { 0, 0, 0, 0, 0 };
		ret = knot_rrset_add_rdata(add_cdnskeys, cdnskey_empty,
		                           sizeof(cdnskey_empty), 0, NULL);
		CHECK_RET;
		ret = knot_rrset_add_rdata(add_cdss, cds_empty,
		                           sizeof(cds_empty), 0, NULL);
		CHECK_RET;
	}

983 984 985 986
	if (!knot_rrset_empty(add_cdnskeys)) {
		ret = changeset_add_addition(&ch, add_cdnskeys, CHANGESET_CHECK |
		                                                CHANGESET_CHECK_CANCELOUT);
		CHECK_RET;
987 988
	}

989 990 991 992
	if (!knot_rrset_empty(add_cdss)) {
		ret = changeset_add_addition(&ch, add_cdss, CHANGESET_CHECK |
		                                            CHANGESET_CHECK_CANCELOUT);
		CHECK_RET;
993 994 995
	}

	if (!knot_rrset_empty(add_dnskeys)) {
996 997 998
		ret = changeset_add_addition(&ch, add_dnskeys, CHANGESET_CHECK |
		                                               CHANGESET_CHECK_CANCELOUT);
		CHECK_RET;
999
	}
1000

1001 1002 1003
	ret = zone_update_apply_changeset(update, &ch);

#undef CHECK_RET
1004

1005 1006 1007 1008 1009 1010 1011
cleanup:
	knot_rrset_free(&add_dnskeys, NULL);
	knot_rrset_free(&add_cdnskeys, NULL);
	knot_rrset_free(&add_cdss, NULL);
	changeset_clear(&ch);
	return ret;
}
1012

1013
bool knot_zone_sign_soa_expired(const zone_contents_t *zone,
1014
                                const zone_keyset_t *zone_keys,
1015
                                const kdnssec_ctx_t *dnssec_ctx)
1016
{
1017 1018 1019
	assert(zone);
	assert(zone_keys);
	assert(dnssec_ctx);
1020

1021 1022
	knot_rrset_t soa = node_rrset(zone->apex, KNOT_RRTYPE_SOA);
	knot_rrset_t rrsigs = node_rrset(zone->apex, KNOT_RRTYPE_RRSIG);
1023
	assert(!knot_rrset_empty(&soa));
1024
	return !all_signatures_exist(&soa, &rrsigs, zone_keys, dnssec_ctx);
1025
}
1026

1027
static int sign_changeset(const zone_contents_t *zone,
1028 1029
                             const changeset_t *in_ch,
                             changeset_t *out_ch,
1030
                             const zone_keyset_t *zone_keys,
1031
                             const kdnssec_ctx_t *dnssec_ctx)
1032
{
1033
	if (zone == NULL || in_ch == NULL || out_ch == NULL) {
1034 1035 1036
		return KNOT_EINVAL;
	}

1037
	// Create args for wrapper function - trie for duplicate sigs
1038 1039 1040
	changeset_signing_data_t args = {
		.zone = zone,
		.zone_keys = zone_keys,
1041
		.dnssec_ctx = dnssec_ctx,
1042
		.changeset = out_ch,
1043
		.signed_tree = trie_create(NULL)
1044 1045
	};

Jan Kadlec's avatar
Jan Kadlec committed
1046 1047
	if (args.signed_tree == NULL) {
		return KNOT_ENOMEM;
1048 1049

	}
1050
	changeset_iter_t itt;
1051
	changeset_iter_all(&itt, in_ch);
1052

1053
	knot_rrset_t rr = changeset_iter_next(&itt);
1054 1055 1056
	while (!knot_rrset_empty(&rr)) {
		int ret = sign_changeset_wrap(&rr, &args);
		if (ret != KNOT_EOK) {
1057
			changeset_iter_clear(&itt);
1058 1059
			return ret;
		}
1060
		rr = changeset_iter_next(&itt);
1061
	}
1062
	changeset_iter_clear(&itt);
1063

1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076
	if (!knot_rrset_empty(in_ch->soa_from)) {
		int ret = sign_changeset_wrap(in_ch->soa_from, &args);
		if (ret != KNOT_EOK) {
			return ret;
		}
	}
	if (!knot_rrset_empty(in_ch->soa_to)) {
		int ret = sign_changeset_wrap(in_ch->soa_to, &args);
		if (ret != KNOT_EOK) {
			return ret;
		}
	}

1077
	knot_zone_clear_sorted_changes(args.signed_tree);
1078
	trie_free(args.signed_tree);
1079

1080
	return KNOT_EOK;
1081 1082
}

1083
int knot_zone_sign_nsecs_in_changeset(const zone_keyset_t *zone_keys,