onlinesign.c 13.9 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

    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>
18
#include <stddef.h>
19
#include <string.h>
20

21
#include "contrib/string.h"
22 23 24 25
#include "dnssec/error.h"
#include "knot/include/module.h"
#include "knot/modules/onlinesign/nsec_next.h"
// Next dependencies force static module!
26
#include "knot/dnssec/key-events.h"
27
#include "knot/dnssec/rrset-sign.h"
28 29
#include "knot/dnssec/zone-keys.h"
#include "knot/dnssec/zone-events.h"
30 31
#include "knot/nameserver/query_module.h"
#include "knot/nameserver/process_query.h"
32

33
#define MOD_POLICY	"\x06""policy"
34

35 36 37 38 39 40 41 42 43 44 45 46
int policy_check(knotd_conf_check_args_t *args)
{
	int ret = knotd_conf_check_ref(args);
	if (ret != KNOT_EOK && strcmp((const char *)args->data, "default") == 0) {
		return KNOT_EOK;
	}

	return ret;
}

const yp_item_t online_sign_conf[] = {
	{ MOD_POLICY, YP_TREF, YP_VREF = { C_POLICY }, YP_FNONE, { policy_check } },
47 48
	{ NULL }
};
49

50
#define RRSIG_LIFETIME (25*60*60)
51

52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
/*
 * TODO:
 *
 * - Fix delegation signing:
 *   + The NSEC proof can decsend into the child zone.
 *   + Out-of-zone glue records can be signed.
 *
 * - Fix CNAME handling:
 *   + Owner name of synthesized records can be incorrect.
 *   + Combination with wildcards results in invalid signatures.
 *
 * - Add support for CDS/CDSKEY synthesis.
 *
 */

67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
/*!
 * \brief RR types to force in synthesised NSEC maps.
 *
 * We cannot determine the true NSEC bitmap because of dynamic modules which
 * can synthesize some types on-the-fly. The base NSEC map will be determined
 * from zone content and this list of types.
 *
 * The types in the NSEC bitmap really don't have to exist. Only the QTYPE
 * must not be present. This will make the validation work with resolvers
 * performing negative caching.
 *
 * This list should contain all RR types, which can be potentionally
 * synthesized by other modules.
 */
static const uint16_t NSEC_FORCE_TYPES[] = {
	KNOT_RRTYPE_A,
	KNOT_RRTYPE_AAAA,
	0
};

87
typedef struct {
88
	dnssec_key_t *key;
89
	uint32_t rrsig_lifetime;
90
} online_sign_ctx_t;
91

92
static bool want_dnssec(knotd_qdata_t *qdata)
93 94 95 96
{
	return knot_pkt_has_dnssec(qdata->query);
}

97
static uint32_t dnskey_ttl(knotd_qdata_t *qdata)
98
{
99
	knot_rrset_t soa = knotd_qdata_zone_apex_rrset(qdata, KNOT_RRTYPE_SOA);
100 101 102
	return knot_rrset_ttl(&soa);
}

103
static uint32_t nsec_ttl(knotd_qdata_t *qdata)
104
{
105
	knot_rrset_t soa = knotd_qdata_zone_apex_rrset(qdata, KNOT_RRTYPE_SOA);
106 107 108
	return knot_soa_minimum(&soa.rrs);
}

109 110 111 112
/*!
 * \brief Add bitmap records synthesized by online-signing.
 */
static void bitmap_add_synth(dnssec_nsec_bitmap_t *map, bool is_apex)
113
{
114 115 116 117 118 119 120
	dnssec_nsec_bitmap_add(map, KNOT_RRTYPE_NSEC);
	dnssec_nsec_bitmap_add(map, KNOT_RRTYPE_RRSIG);
	if (is_apex) {
		dnssec_nsec_bitmap_add(map, KNOT_RRTYPE_DNSKEY);
		//dnssec_nsec_bitmap_add(map, KNOT_RRTYPE_CDS);
	}
}
121

122 123 124 125 126 127 128
/*!
 * \brief Add bitmap records present in the zone.
 */
static void bitmap_add_zone(dnssec_nsec_bitmap_t *map, const zone_node_t *node)
{
	if (!node) {
		return;
129 130
	}

131 132 133 134
	for (int i = 0; i < node->rrset_count; i++) {
		dnssec_nsec_bitmap_add(map, node->rrs[i].type);
	}
}
135

136 137 138 139 140 141 142
/*!
 * \brief Add bitmap records which can be synthesized by other modules.
 *
 * \param qtype  Current QTYPE, will never be added into the map.
 */
static void bitmap_add_forced(dnssec_nsec_bitmap_t *map, uint16_t qtype)
{
143 144
	for (const uint16_t *type = NSEC_FORCE_TYPES; *type; type += 1) {
		if (*type != qtype) {
145
			dnssec_nsec_bitmap_add(map, *type);
146 147
		}
	}
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
}

/*!
 * \brief Add bitmap records from the actual response.
 */
static void bitmap_add_section(dnssec_nsec_bitmap_t *map, const knot_pktsection_t *answer)
{
	for (int i = 0; i < answer->count; i++) {
		const knot_rrset_t *rr = knot_pkt_rr(answer, i);
		dnssec_nsec_bitmap_add(map, rr->type);
	}
}

/*!
 * \brief Synthesize NSEC type bitmap.
 *
 * - The bitmap will contain types synthesized by this module.
 * - For ANY query, the bitmap will contain types from the actual response.
 * - For non-ANY query, the bitmap will contain types from zone and forced
 *   types which can be potentionally synthesized by other query modules.
 */
169
static dnssec_nsec_bitmap_t *synth_bitmap(knot_pkt_t *pkt, const knotd_qdata_t *qdata)
170 171 172 173 174 175 176
{
	dnssec_nsec_bitmap_t *map = dnssec_nsec_bitmap_new();
	if (!map) {
		return NULL;
	}

	uint16_t qtype = knot_pkt_qtype(qdata->query);
177 178 179
	bool is_apex = qdata->extra->zone
	               && qdata->extra->zone->contents
	               && qdata->extra->node == qdata->extra->zone->contents->apex;
180 181 182 183 184 185 186

	bitmap_add_synth(map, is_apex);

	if (qtype == KNOT_RRTYPE_ANY) {
		const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER);
		bitmap_add_section(map, answer);
	} else {
187 188
		bitmap_add_zone(map, qdata->extra->node);
		if (!node_rrtype_exists(qdata->extra->node, KNOT_RRTYPE_CNAME)) {
189 190
			bitmap_add_forced(map, qtype);
		}
191
	}
192

193
	return map;
194 195
}

196
static knot_rrset_t *synth_nsec(knot_pkt_t *pkt, knotd_qdata_t *qdata, knot_mm_t *mm)
197
{
198 199
	knot_rrset_t *nsec = knot_rrset_new(qdata->name, KNOT_RRTYPE_NSEC,
	                                    KNOT_CLASS_IN, mm);
200 201 202 203
	if (!nsec) {
		return NULL;
	}

204
	knot_dname_t *next = online_nsec_next(qdata->name, knotd_qdata_zone_name(qdata));
205 206 207 208 209
	if (!next) {
		knot_rrset_free(&nsec, mm);
		return NULL;
	}

210
	dnssec_nsec_bitmap_t *bitmap = synth_bitmap(pkt, qdata);
211 212 213
	if (!bitmap) {
		free(next);
		knot_rrset_free(&nsec, mm);
214
		return NULL;
215 216 217 218 219 220 221 222 223 224 225
	}

	size_t size = knot_dname_size(next) + dnssec_nsec_bitmap_size(bitmap);
	uint8_t rdata[size];

	int written = knot_dname_to_wire(rdata, next, size);
	dnssec_nsec_bitmap_write(bitmap, rdata + written);

	knot_dname_free(&next, NULL);
	dnssec_nsec_bitmap_free(bitmap);

226
	uint32_t ttl = nsec_ttl(qdata);
227 228

	if (knot_rrset_add_rdata(nsec, rdata, size, ttl, mm) != KNOT_EOK) {
229 230 231 232 233 234 235
		knot_rrset_free(&nsec, mm);
		return NULL;
	}

	return nsec;
}

236 237
static knot_rrset_t *sign_rrset(const knot_dname_t *owner,
                                const knot_rrset_t *cover,
238 239
                                online_sign_ctx_t *module_ctx,
                                dnssec_sign_ctx_t *sign_ctx,
240
                                knot_mm_t *mm)
241
{
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
	// copy of RR set with replaced owner name

	knot_rrset_t *copy = knot_rrset_new(owner, cover->type, cover->rclass, NULL);
	if (!copy) {
		return NULL;
	}

	if (knot_rdataset_copy(&copy->rrs, &cover->rrs, NULL) != KNOT_EOK) {
		knot_rrset_free(&copy, NULL);
		return NULL;
	}

	// resulting RRSIG

	knot_rrset_t *rrsig = knot_rrset_new(owner, KNOT_RRTYPE_RRSIG, copy->rclass, mm);
257
	if (!rrsig) {
258
		knot_rrset_free(&copy, NULL);
259 260 261 262 263
		return NULL;
	}

	// compatible context

264
	knot_kasp_policy_t policy = {
265
		.rrsig_lifetime = module_ctx->rrsig_lifetime
266 267 268 269 270 271 272
	};

	kdnssec_ctx_t ksign_ctx = {
		.now = time(NULL),
		.policy = &policy
	};

273 274 275 276
	int r = knot_sign_rrset(rrsig, copy, module_ctx->key, sign_ctx, &ksign_ctx, mm);

	knot_rrset_free(&copy, NULL);

277 278 279 280 281 282 283 284
	if (r != KNOT_EOK) {
		knot_rrset_free(&rrsig, mm);
		return NULL;
	}

	return rrsig;
}

285 286
static knotd_in_state_t sign_section(knotd_in_state_t state, knot_pkt_t *pkt,
                                     knotd_qdata_t *qdata, knotd_mod_t *mod)
287
{
288
	online_sign_ctx_t *module_ctx = knotd_mod_ctx(mod);
289 290 291 292 293 294 295 296

	if (!want_dnssec(qdata)) {
		return state;
	}

	dnssec_sign_ctx_t *sign_ctx = NULL;
	int r = dnssec_sign_new(&sign_ctx, module_ctx->key);
	if (r != DNSSEC_EOK) {
297
		return KNOTD_IN_STATE_ERROR;
298 299 300 301 302 303 304 305
	}

	const knot_pktsection_t *section = knot_pkt_section(pkt, pkt->current);
	assert(section);

	uint16_t count_unsigned = section->count;
	for (int i = 0; i < count_unsigned; i++) {
		const knot_rrset_t *rr = knot_pkt_rr(section, i);
306 307 308 309 310 311 312
		uint16_t rr_pos = knot_pkt_rr_offset(section, i);

		uint8_t owner[KNOT_DNAME_MAXLEN] = { 0 };
		knot_dname_unpack(owner, pkt->wire + rr_pos, sizeof(owner), pkt->wire);
		knot_dname_to_lower(owner);

		knot_rrset_t *rrsig = sign_rrset(owner, rr, module_ctx, sign_ctx, &pkt->mm);
313
		if (!rrsig) {
314
			state = KNOTD_IN_STATE_ERROR;
315 316 317 318 319 320
			break;
		}

		r = knot_pkt_put(pkt, KNOT_COMPR_HINT_NONE, rrsig, KNOT_PF_FREE);
		if (r != KNOT_EOK) {
			knot_rrset_free(&rrsig, &pkt->mm);
321
			state = KNOTD_IN_STATE_ERROR;
322 323 324 325 326 327 328 329 330
			break;
		}
	}

	dnssec_sign_free(sign_ctx);

	return state;
}

331 332
static knotd_in_state_t synth_authority(knotd_in_state_t state, knot_pkt_t *pkt,
                                        knotd_qdata_t *qdata, knotd_mod_t *mod)
333
{
334
	if (state == KNOTD_IN_STATE_HIT) {
335 336 337 338 339 340
		return state;
	}

	// synthesise NSEC

	if (want_dnssec(qdata)) {
341
		knot_rrset_t *nsec = synth_nsec(pkt, qdata, &pkt->mm);
342
		int r = knot_pkt_put(pkt, KNOT_COMPR_HINT_NONE, nsec, KNOT_PF_FREE);
343 344
		if (r != DNSSEC_EOK) {
			knot_rrset_free(&nsec, &pkt->mm);
345
			return KNOTD_IN_STATE_ERROR;
346 347 348 349 350
		}
	}

	// promote NXDOMAIN to NODATA

351
	if (state == KNOTD_IN_STATE_MISS) {
352 353
		//! \todo Override RCODE set in solver_authority. Review.
		qdata->rcode = KNOT_RCODE_NOERROR;
354
		return KNOTD_IN_STATE_NODATA;
355 356 357
	}

	return state;
358 359
}

360
static knot_rrset_t *synth_dnskey(knotd_qdata_t *qdata, const dnssec_key_t *key,
361
                                  knot_mm_t *mm)
362
{
363 364
	knot_rrset_t *dnskey = knot_rrset_new(knotd_qdata_zone_name(qdata),
	                                      KNOT_RRTYPE_DNSKEY, KNOT_CLASS_IN, mm);
365 366 367 368 369 370 371 372
	if (!dnskey) {
		return 0;
	}

	dnssec_binary_t rdata = { 0 };
	dnssec_key_get_rdata(key, &rdata);
	assert(rdata.size > 0 && rdata.data);

373
	uint32_t ttl = dnskey_ttl(qdata);
374 375 376 377 378 379 380 381 382 383

	int r = knot_rrset_add_rdata(dnskey, rdata.data, rdata.size, ttl, mm);
	if (r != KNOT_EOK) {
		knot_rrset_free(&dnskey, mm);
		return NULL;
	}

	return dnskey;
}

384
static bool qtype_match(knotd_qdata_t *qdata, uint16_t type)
385 386 387 388 389
{
	uint16_t qtype = knot_pkt_qtype(qdata->query);
	return (qtype == KNOT_RRTYPE_ANY || qtype == type);
}

390
static bool is_apex_query(knotd_qdata_t *qdata)
391
{
392
	return knot_dname_is_equal(qdata->name, knotd_qdata_zone_name(qdata));
393 394
}

395 396
static knotd_in_state_t synth_answer(knotd_in_state_t state, knot_pkt_t *pkt,
                                     knotd_qdata_t *qdata, knotd_mod_t *mod)
397
{
398
	online_sign_ctx_t *ctx = knotd_mod_ctx(mod);
399

400
	// disallowed queries
401

402
	if (knot_pkt_qtype(pkt) == KNOT_RRTYPE_RRSIG) {
403
		qdata->rcode = KNOT_RCODE_REFUSED;
404
		return KNOTD_IN_STATE_ERROR;
405 406
	}

407 408
	// synthesized DNSSEC answers

409
	if (qtype_match(qdata, KNOT_RRTYPE_DNSKEY) && is_apex_query(qdata)) {
410
		knot_rrset_t *dnskey = synth_dnskey(qdata, ctx->key, &pkt->mm);
411
		if (!dnskey) {
412
			return KNOTD_IN_STATE_ERROR;
413 414 415
		}

		int r = knot_pkt_put(pkt, KNOT_COMPR_HINT_QNAME, dnskey, KNOT_PF_FREE);
416
		if (r != DNSSEC_EOK) {
417
			knot_rrset_free(&dnskey, &pkt->mm);
418
			return KNOTD_IN_STATE_ERROR;
419
		}
420

421
		state = KNOTD_IN_STATE_HIT;
422 423
	}

424
	if (qtype_match(qdata, KNOT_RRTYPE_NSEC)) {
425
		knot_rrset_t *nsec = synth_nsec(pkt, qdata, &pkt->mm);
426
		if (!nsec) {
427
			return KNOTD_IN_STATE_ERROR;
428 429 430 431 432
		}

		int r = knot_pkt_put(pkt, KNOT_COMPR_HINT_QNAME, nsec, KNOT_PF_FREE);
		if (r != DNSSEC_EOK) {
			knot_rrset_free(&nsec, &pkt->mm);
433
			return KNOTD_IN_STATE_ERROR;
434 435
		}

436
		state = KNOTD_IN_STATE_HIT;
437 438
	}

439
	return state;
440 441
}

442
static int get_online_key(dnssec_key_t **key_ptr, knotd_mod_t *mod)
443
{
444
	kdnssec_ctx_t kctx = { 0 };
445

446
	int r = kdnssec_ctx_init(mod->config, &kctx, mod->zone, mod->id);
447
	if (r != DNSSEC_EOK) {
448
		return r;
449 450
	}

451 452
	// Force Singe-Type signing scheme.
	kctx.policy->singe_type_signing = true;
453

454 455 456
	zone_sign_reschedule_t ignore_out = { 0 };
	ignore_out.allow_rollover = true;
	r = knot_dnssec_key_rollover(&kctx, &ignore_out);
457 458
	if (r != DNSSEC_EOK) {
		goto fail;
459 460
	}

461
	assert(kctx.zone->num_keys > 0);
462
	knot_kasp_key_t *kasp_key = &kctx.zone->keys[0];
463
	assert(kasp_key);
464

465
	dnssec_key_t *key = dnssec_key_dup(kasp_key->key);
466 467 468
	if (!key) {
		r = KNOT_ENOMEM;
		goto fail;
469 470
	}

471
	r = dnssec_key_import_keystore(key, kctx.keystore, kasp_key->id);
472 473
	if (r != DNSSEC_EOK) {
		dnssec_key_free(key);
474
		goto fail;
475 476 477 478
	}

	*key_ptr = key;

479
fail:
480
	kdnssec_ctx_deinit(&kctx);
481 482

	return r;
483 484
}

485 486 487 488 489 490 491
static void online_sign_ctx_free(online_sign_ctx_t *ctx)
{
	dnssec_key_free(ctx->key);

	free(ctx);
}

492
static int online_sign_ctx_new(online_sign_ctx_t **ctx_ptr, knotd_mod_t *mod)
493 494 495 496 497 498
{
	online_sign_ctx_t *ctx = calloc(1, sizeof(*ctx));
	if (!ctx) {
		return KNOT_ENOMEM;
	}

499
	int r = get_online_key(&ctx->key, mod);
500 501 502 503 504
	if (r != KNOT_EOK) {
		online_sign_ctx_free(ctx);
		return r;
	}

505
	ctx->rrsig_lifetime = RRSIG_LIFETIME;
506 507 508 509 510
	knotd_conf_t policy = knotd_conf_mod(mod, MOD_POLICY);
	if (policy.count != 0) {
		knotd_conf_t conf = knotd_conf(mod, C_POLICY, C_RRSIG_LIFETIME, &policy);
		if (conf.count != 0) {
			ctx->rrsig_lifetime = conf.single.integer;
511 512 513
		}
	}

514 515 516 517 518
	*ctx_ptr = ctx;

	return KNOT_EOK;
}

519
int online_sign_load(knotd_mod_t *mod)
520
{
521 522 523 524
	knotd_conf_t conf = knotd_conf_zone(mod, C_DNSSEC_SIGNING,
	                                    knotd_mod_zone(mod));
	if (conf.single.boolean) {
		knotd_mod_log(mod, LOG_ERR, "incompatible with automatic signing");
525 526 527
		return KNOT_ENOTSUP;
	}

528
	online_sign_ctx_t *ctx = NULL;
529
	int r = online_sign_ctx_new(&ctx, mod);
530
	if (r != KNOT_EOK) {
531
		knotd_mod_log(mod, LOG_ERR, "failed to initialize signing key (%s)",
532
		              knot_strerror(r));
533 534 535
		return KNOT_ERROR;
	}

536
	knotd_mod_ctx_set(mod, ctx);
537

538 539
	knotd_mod_in_hook(mod, KNOTD_STAGE_ANSWER, synth_answer);
	knotd_mod_in_hook(mod, KNOTD_STAGE_ANSWER, sign_section);
540

541 542
	knotd_mod_in_hook(mod, KNOTD_STAGE_AUTHORITY, synth_authority);
	knotd_mod_in_hook(mod, KNOTD_STAGE_AUTHORITY, sign_section);
543

544
	knotd_mod_in_hook(mod, KNOTD_STAGE_ADDITIONAL, sign_section);
545

546 547 548
	return KNOT_EOK;
}

549
void online_sign_unload(knotd_mod_t *mod)
550
{
551
	online_sign_ctx_free(knotd_mod_ctx(mod));
552
}
553 554 555

KNOTD_MOD_API(onlinesign, KNOTD_MOD_FLAG_SCOPE_ZONE | KNOTD_MOD_FLAG_OPT_CONF,
              online_sign_load, online_sign_unload, online_sign_conf, NULL);