zone-sign.c 35.5 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

Daniel Salzman's avatar
Daniel Salzman committed
32
typedef struct {
33 34 35 36
	node_t n;
	uint16_t type;
} type_node_t;

Daniel Salzman's avatar
Daniel Salzman committed
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,
Jan Včelák's avatar
Jan Včelák committed
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;
Jan Včelák's avatar
Jan Včelák committed
111
	}
112

113
	uint16_t rrsigs_rdata_count = rrsigs->rrs.rr_count;
114
	for (uint16_t i = 0; i < rrsigs_rdata_count; i++) {
Jan Včelák's avatar
Jan Včelák committed
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;
Jan Včelák's avatar
Jan Včelák committed
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.
 */
Jan Včelák's avatar
Jan Včelák committed
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
}

Jan Včelák's avatar
Jan Včelák committed
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.
Jan Včelák's avatar
Jan Včelák committed
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,
Jan Včelák's avatar
Jan Včelák committed
169
                                 const zone_keyset_t *zone_keys,
170
                                 const kdnssec_ctx_t *dnssec_ctx)
Jan Kadlec's avatar
Jan Kadlec committed
171
{
172
	assert(!knot_rrset_empty(covered));
173 174
	assert(zone_keys);

Jan Kadlec's avatar
Jan Kadlec committed
175
	for (int i = 0; i < zone_keys->count; i++) {
Jan Včelák's avatar
Jan Včelák committed
176
		zone_key_t *key = &zone_keys->keys[i];
177
		if (!use_key(key, covered)) {
Jan Kadlec's avatar
Jan Kadlec committed
178
			continue;
179
		}
Jan Kadlec's avatar
Jan Kadlec committed
180

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

Lubos Slovak's avatar
Lubos Slovak committed
187
	return true;
Jan Kadlec's avatar
Jan Kadlec committed
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)
Jan Kadlec's avatar
Jan Kadlec committed
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);
Jan Kadlec's avatar
Jan Kadlec committed
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.
 */
Jan Kadlec's avatar
Jan Kadlec committed
240
static int remove_expired_rrsigs(const knot_rrset_t *covered,
Jan Kadlec's avatar
Jan Kadlec committed
241
                                 const knot_rrset_t *rrsigs,
Jan Včelák's avatar
Jan Včelák committed
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.
 *
Libor Peltan's avatar
Libor Peltan committed
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,
Jan Včelák's avatar
Jan Včelák committed
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++) {
Jan Včelák's avatar
Jan Včelák committed
344
		const zone_key_t *key = &zone_keys->keys[i];
345
		if (!use_key(key, covered)) {
346
			continue;
Jan Včelák's avatar
Jan Včelák committed
347
		}
348

349
		if (valid_signature_exists(covered, rrsigs, key->key, key->ctx, dnssec_ctx)) {
350
			continue;
Jan Včelák's avatar
Jan Včelák committed
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);
Jan Včelák's avatar
Jan Včelák committed
358
		if (result != KNOT_EOK) {
359
			break;
Jan Včelák's avatar
Jan Včelák committed
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)
Lubos Slovak's avatar
Lubos Slovak committed
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;
Lubos Slovak's avatar
Lubos Slovak committed
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,
Jan Včelák's avatar
Jan Včelák committed
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,
Jan Včelák's avatar
Jan Včelák committed
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,
Jan Včelák's avatar
Jan Včelák committed
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 {
Jan Včelák's avatar
Jan Včelák committed
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,
Jan Včelák's avatar
Jan Včelák committed
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;
Jan Včelák's avatar
Jan Včelák committed
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 630
/*- private API - DNSKEY handling --------------------------------------------*/

/*!
631
 * \brief Check if DNSKEY RDATA match with DNSSEC key.
632
 *
633
 * \param zone_key    Zone key.
634
 * \param rdata       DNSKEY RDATA.
635
 *
636
 * \return DNSKEY RDATA match with DNSSEC key.
637
 */
638
static bool dnskey_rdata_match(zone_key_t *key,
639
                               const dnssec_binary_t *rdata)
640
{
641
	assert(key);
642 643
	assert(rdata);

Jan Včelák's avatar
Jan Včelák committed
644 645
	dnssec_binary_t dnskey_rdata = { 0 };
	dnssec_key_get_rdata(key->key, &dnskey_rdata);
646

647
	return dnssec_binary_cmp(&dnskey_rdata, rdata) == 0;
648 649
}

650 651 652 653 654 655 656 657 658 659 660
static bool cds_rdata_match(zone_key_t *key,
                            const dnssec_binary_t *rdata)
{
        assert(key);
        assert(rdata);
        dnssec_binary_t cds_rdata = { 0 };
        int ret = zone_key_calculate_ds(key, &cds_rdata);
        int res = dnssec_binary_cmp(&cds_rdata, rdata);
        return (ret == KNOT_EOK && res == 0);
}

661 662 663 664 665 666 667 668 669
bool knot_match_key_ds(zone_key_t *key, const knot_rdata_t *rdata)
{
        dnssec_binary_t rdata_bin = {
                .data = knot_rdata_data(rdata),
		.size = knot_rdata_rdlen(rdata)
	};
        return cds_rdata_match(key, &rdata_bin);
}

670
/*!
671
 * \brief Check if DNSKEY/DS is present in public zone key set.
672
 */
673 674 675
static bool is_from_keyset(zone_keyset_t *keyset,
                           const knot_rdata_t *record,
                           bool is_ds, // otherwise, it's DNSKEY
Libor Peltan's avatar
Libor Peltan committed
676
                           bool is_cds_cdnskey, // in this case we match only ready keys
677
                           zone_key_t **matching_key) // out, optional
678 679
{
	assert(keyset);
680
	assert(record);
681 682

	dnssec_binary_t rdata = {
683 684
		.data = knot_rdata_data(record),
		.size = knot_rdata_rdlen(record)
685 686 687
	};

	uint16_t tag = 0;
688 689 690 691 692 693 694 695 696
	bool (*match_fce)(zone_key_t *, const dnssec_binary_t *);
	if (is_ds) {
		wire_ctx_t wrdata = wire_ctx_init(rdata.data, rdata.size);
		tag = wire_ctx_read_u16(&wrdata); // key tag in DS is just at beginning of wire
		match_fce = cds_rdata_match;
	} else {
		dnssec_keytag(&rdata, &tag);
		match_fce = dnskey_rdata_match;
	}
697 698 699 700 701
	bool found = false;

	struct keyptr_dynarray keys = get_zone_keys(keyset, tag);

	for (size_t i = 0; i < keys.size; i++) {
702
		bool usekey = (is_cds_cdnskey ? (keys.arr(&keys)[i]->cds_priority > 1) : keys.arr(&keys)[i]->is_public);
703
		if (usekey && match_fce(keys.arr(&keys)[i], &rdata)) {
704
			found = true;
705
			if (matching_key != NULL) {
706
				*matching_key = keys.arr(&keys)[i];
707
			}
708 709 710 711
			break;
		}
	}
	keyptr_dynarray_free(&keys);
712

713
	return found;
714 715
}

716
static int rrset_add_zone_key(knot_rrset_t *rrset,
717
                              zone_key_t *zone_key,
Jan Kadlec's avatar
Jan Kadlec committed
718
                              uint32_t ttl)
719 720 721 722
{
	assert(rrset);
	assert(zone_key);

Jan Včelák's avatar
Jan Včelák committed
723 724
	dnssec_binary_t dnskey_rdata = { 0 };
	dnssec_key_get_rdata(zone_key->key, &dnskey_rdata);
725

Jan Včelák's avatar
Jan Včelák committed
726 727
	return knot_rrset_add_rdata(rrset, dnskey_rdata.data,
	                            dnskey_rdata.size, ttl, NULL);
728 729
}

730 731 732 733 734 735 736 737 738 739 740 741 742 743
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);
}

744 745 746 747 748
/*!
 * \brief Goes through list and looks for RRSet type there.
 *
 * \return True if RR type is in the list, false otherwise.
 */
749 750
static bool rr_type_in_list(const knot_rrset_t *rr, const list_t *l)
{
751
	if (l == NULL || EMPTY_LIST(*l)) {
752 753 754 755
		return false;
	}
	assert(rr);

756
	type_node_t *n = NULL;
757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775
	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;
	}
776
	n->type = rr->type;
777 778 779 780 781

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

782 783 784 785 786
/*!
 * \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.
 *
787 788
 * \param rrset      RRSet to be checked for.
 * \param tree       Tree with already signed RRs.
Jan Kadlec's avatar
Jan Kadlec committed
789
 * \param rr_signed  Set to true if RR is signed already, false otherwise.
790
 *
791
 * \return KNOT_E*
792
 */
793
static int rr_already_signed(const knot_rrset_t *rrset, trie_t *t,
794
                             bool *rr_signed)
795 796 797
{
	assert(rrset);
	assert(t);
798
	*rr_signed = false;
799 800 801
	// Create a key = RRSet owner converted to sortable format
	uint8_t lf[KNOT_DNAME_MAXLEN];
	knot_dname_lf(lf, rrset->owner, NULL);
802
	trie_val_t stored_info = (signed_info_t *)trie_get_try(t, (char *)lf+1,
803
	                                                      *lf);
804
	if (stored_info == NULL) {
805
		// Create new info struct
806
		signed_info_t *info = malloc(sizeof(signed_info_t));
807
		if (info == NULL) {
808
			return KNOT_ENOMEM;
809
		}
810
		memset(info, 0, sizeof(signed_info_t));
811
		// Store actual dname repr
812
		info->dname = knot_dname_copy(rrset->owner, NULL);
813 814
		if (info->dname == NULL) {
			free(info);
815
			return KNOT_ENOMEM;
816
		}
817
		// Create new list to insert as a value
818
		info->type_list = malloc(sizeof(list_t));
819
		if (info->type_list == NULL) {
820 821
			free(info->dname);
			free(info);
822
			return KNOT_ENOMEM;
823
		}