dnssec.c 13.8 KB
Newer Older
1
/*  Copyright (C) 2015-2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2 3 4 5 6 7 8 9 10 11 12 13

    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
14
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
15 16
 */

17
#include <assert.h>
Vladimír Čunát's avatar
Vladimír Čunát committed
18 19 20 21 22
#include <libdnssec/binary.h>
#include <libdnssec/crypto.h>
#include <libdnssec/error.h>
#include <libdnssec/key.h>
#include <libdnssec/sign.h>
23
#include <libknot/descriptor.h>
24
#include <libknot/packet/wire.h>
25 26 27
#include <libknot/rdataset.h>
#include <libknot/rrset.h>
#include <libknot/rrtype/dnskey.h>
28
#include <libknot/rrtype/nsec.h>
29
#include <libknot/rrtype/rrsig.h>
30
#include <contrib/wire.h>
31 32

#include "lib/defines.h"
33 34
#include "lib/dnssec/nsec.h"
#include "lib/dnssec/nsec3.h"
35
#include "lib/dnssec/signature.h"
36
#include "lib/dnssec.h"
37
#include "lib/resolve.h"
38

39 40 41 42
/* forward */
static int kr_rrset_validate_with_key(kr_rrset_validation_ctx_t *vctx,
	const knot_rrset_t *covered, size_t key_pos, const struct dseckey *key);

43 44 45 46 47 48 49 50 51 52 53 54 55 56
void kr_crypto_init(void)
{
	dnssec_crypto_init();
}

void kr_crypto_cleanup(void)
{
	dnssec_crypto_cleanup();
}

void kr_crypto_reinit(void)
{
	dnssec_crypto_reinit();
}
57

58 59 60 61 62
#define FLG_WILDCARD_EXPANSION 0x01 /**< Possibly generated by using wildcard expansion. */

/**
 * Check the RRSIG RR validity according to RFC4035 5.3.1 .
 * @param flags     The flags are going to be set according to validation result.
63
 * @param cov_labels Covered RRSet owner label count.
64 65 66
 * @param rrsigs    rdata containing the signatures.
 * @param key_owner Associated DNSKEY's owner.
 * @param key_rdata Associated DNSKEY's rdata.
67
 * @param keytag    Used key tag.
68 69 70
 * @param zone_name The name of the zone cut.
 * @param timestamp Validation time.
 */
71
static int validate_rrsig_rr(int *flags, int cov_labels,
72 73 74
                             const knot_rdata_t *rrsigs,
                             const knot_dname_t *key_owner, const knot_rdata_t *key_rdata,
			     uint16_t keytag,
75 76
                             const knot_dname_t *zone_name, uint32_t timestamp,
                             kr_rrset_validation_ctx_t *vctx)
77
{
78
	if (!flags || !rrsigs || !key_owner || !key_rdata || !zone_name) {
79 80
		return kr_error(EINVAL);
	}
81
	/* bullet 5 */
82
	if (knot_rrsig_sig_expiration(rrsigs) < timestamp) {
83
		vctx->rrs_counters.expired++;
84 85
		return kr_error(EINVAL);
	}
86
	/* bullet 6 */
87
	if (knot_rrsig_sig_inception(rrsigs) > timestamp) {
88
		vctx->rrs_counters.notyet++;
89 90
		return kr_error(EINVAL);
	}
91
	/* bullet 2 */
92
	const knot_dname_t *signer_name = knot_rrsig_signer_name(rrsigs);
93
	if (!signer_name || !knot_dname_is_equal(signer_name, zone_name)) {
94
		vctx->rrs_counters.signer_invalid++;
95
		return kr_error(EAGAIN);
96 97
	}
	/* bullet 4 */
98
	{
99
		int rrsig_labels = knot_rrsig_labels(rrsigs);
100
		if (rrsig_labels > cov_labels) {
101
			vctx->rrs_counters.labels_invalid++;
102 103
			return kr_error(EINVAL);
		}
104
		if (rrsig_labels < cov_labels) {
105 106
			*flags |= FLG_WILDCARD_EXPANSION;
		}
107
	}
108

109
	/* bullet 7 */
110 111 112
	if ((!knot_dname_is_equal(key_owner, signer_name)) ||
	    (knot_dnskey_alg(key_rdata) != knot_rrsig_alg(rrsigs)) ||
	    (keytag != knot_rrsig_key_tag(rrsigs))) {
113
		vctx->rrs_counters.key_invalid++;
114 115 116 117 118 119
		return kr_error(EINVAL);
	}
	/* bullet 8 */
	/* Checked somewhere else. */
	/* bullet 9 and 10 */
	/* One of the requirements should be always fulfilled. */
120

121 122 123
	return kr_ok();
}

124 125 126 127 128 129 130
/**
 * Returns the number of labels that have been added by wildcard expansion.
 * @param expanded Expanded wildcard.
 * @param rrsigs   RRSet containing the signatures.
 * @param sig_pos  Specifies the signature within the RRSIG RRSet.
 * @return         Number of added labels, -1 on error.
 */
131 132
static inline int wildcard_radix_len_diff(const knot_dname_t *expanded,
					  const knot_rdata_t *rrsig)
133
{
134
	if (!expanded || !rrsig) {
135 136 137
		return -1;
	}

138
	return knot_dname_labels(expanded, NULL) - knot_rrsig_labels(rrsig);
139 140
}

141
int kr_rrset_validate(kr_rrset_validation_ctx_t *vctx, const knot_rrset_t *covered)
142
{
143 144 145 146
	if (!vctx) {
		return kr_error(EINVAL);
	}
	if (!vctx->pkt || !covered || !vctx->keys || !vctx->zone_name) {
147 148 149
		return kr_error(EINVAL);
	}

150
	memset(&vctx->rrs_counters, 0, sizeof(vctx->rrs_counters));
151
	for (unsigned i = 0; i < vctx->keys->rrs.count; ++i) {
152
		int ret = kr_rrset_validate_with_key(vctx, covered, i, NULL);
153
		if (ret == 0) {
154
			return ret;
155 156 157
		}
	}

158
	return kr_error(ENOENT);
159 160
}

161 162 163 164 165 166 167 168 169 170
/**
 * Validate RRSet using a specific key.
 * @param vctx    Pointer to validation context.
 * @param covered RRSet covered by a signature. It must be in canonical format.
 * @param key_pos Position of the key to be validated with.
 * @param key     Key to be used to validate.
 *		  If NULL, then key from DNSKEY RRSet is used.
 * @return        0 or error code, same as vctx->result.
 */
static int kr_rrset_validate_with_key(kr_rrset_validation_ctx_t *vctx,
171 172
				const knot_rrset_t *covered,
				size_t key_pos, const struct dseckey *key)
173
{
174 175 176 177 178
	const knot_pkt_t *pkt         = vctx->pkt;
	const knot_rrset_t *keys      = vctx->keys;
	const knot_dname_t *zone_name = vctx->zone_name;
	uint32_t timestamp            = vctx->timestamp;
	bool has_nsec3		      = vctx->has_nsec3;
179
	struct dseckey *created_key = NULL;
180 181 182

	/* It's just caller's approximation that the RR is in that particular zone.
	 * We MUST guard against attempts of zones signing out-of-bailiwick records. */
183
	if (knot_dname_in_bailiwick(covered->owner, zone_name) < 0) {
184 185 186 187
		vctx->result = kr_error(ENOENT);
		return vctx->result;
	}

188
	const knot_rdata_t *key_rdata = knot_rdataset_at(&keys->rrs, key_pos);
189
	if (key == NULL) {
190
		int ret = kr_dnssec_key_from_rdata(&created_key, keys->owner,
191
						   key_rdata->data, key_rdata->len);
192
		if (ret != 0) {
193 194
			vctx->result = ret;
			return vctx->result;
195 196 197
		}
		key = created_key;
	}
198 199 200 201 202 203
	uint16_t keytag = dnssec_key_get_keytag((dnssec_key_t *)key);
	int covered_labels = knot_dname_labels(covered->owner, NULL);
	if (knot_dname_is_wildcard(covered->owner)) {
		/* The asterisk does not count, RFC4034 3.1.3, paragraph 3. */
		--covered_labels;
	}
204

205
	for (uint16_t i = 0; i < vctx->rrs->len; ++i) {
206
		/* Consider every RRSIG that matches owner and covers the class/type. */
207
		const knot_rrset_t *rrsig = vctx->rrs->at[i]->rr;
208
		if (rrsig->type != KNOT_RRTYPE_RRSIG) {
209 210
			continue;
		}
211 212 213
		if ((covered->rclass != rrsig->rclass) || !knot_dname_is_equal(covered->owner, rrsig->owner)) {
			continue;
		}
214 215
		knot_rdata_t *rdata_j = rrsig->rrs.rdata;
		for (uint16_t j = 0; j < rrsig->rrs.count; ++j, rdata_j = knot_rdataset_next(rdata_j)) {
216 217
			int val_flgs = 0;
			int trim_labels = 0;
218
			if (knot_rrsig_type_covered(rdata_j) != covered->type) {
219 220
				continue;
			}
221
			vctx->rrs_counters.matching_name_type++;
222
			int retv = validate_rrsig_rr(&val_flgs, covered_labels, rdata_j,
223
			                      keys->owner, key_rdata, keytag,
224
			                      zone_name, timestamp, vctx);
225
			if (retv == kr_error(EAGAIN)) {
226
				kr_dnssec_key_free(&created_key);
227 228 229
				vctx->result = retv;
				return retv;
			} else if (retv != 0) {
230 231
				continue;
			}
232
			if (val_flgs & FLG_WILDCARD_EXPANSION) {
233
				trim_labels = wildcard_radix_len_diff(covered->owner, rdata_j);
234 235 236 237
				if (trim_labels < 0) {
					break;
				}
			}
238
			if (kr_check_signature(rdata_j, (dnssec_key_t *) key, covered, trim_labels) != 0) {
239
				vctx->rrs_counters.crypto_invalid++;
240 241
				continue;
			}
242
			if (val_flgs & FLG_WILDCARD_EXPANSION) {
243
				int ret = 0;
244 245 246 247
				if (!has_nsec3) {
					ret = kr_nsec_wildcard_answer_response_check(pkt, KNOT_AUTHORITY, covered->owner);
				} else {
					ret = kr_nsec3_wildcard_answer_response_check(pkt, KNOT_AUTHORITY, covered->owner, trim_labels - 1);
248
					if (ret == kr_error(KNOT_ERANGE)) {
249 250 251
						ret = 0;
						vctx->flags |= KR_DNSSEC_VFLG_OPTOUT;
					}
252 253
				}
				if (ret != 0) {
254
					vctx->rrs_counters.nsec_invalid++;
255 256
					continue;
				}
257
				vctx->flags |= KR_DNSSEC_VFLG_WEXPAND;
258
			}
259 260
			/* Validated with current key, OK */
			kr_dnssec_key_free(&created_key);
261 262
			vctx->result = kr_ok();
			return vctx->result;
263
		}
264
	}
265
	/* No applicable key found, cannot be validated. */
266
	kr_dnssec_key_free(&created_key);
267 268
	vctx->result = kr_error(ENOENT);
	return vctx->result;
269 270
}

271 272
static bool kr_ds_algo_support(const knot_rrset_t *ta)
{
273 274 275 276 277
	knot_rdata_t *rdata_i = ta->rrs.rdata;
	for (uint16_t i = 0; i < ta->rrs.count;
			++i, rdata_i = knot_rdataset_next(rdata_i)) {
		if (dnssec_algorithm_digest_support(knot_ds_digest_type(rdata_i))
		    && dnssec_algorithm_key_support(knot_ds_alg(rdata_i))) {
278 279 280 281 282 283
			return true;
		}
	}
	return false;
}

284 285 286 287 288
int kr_dnskeys_trusted(kr_rrset_validation_ctx_t *vctx, const knot_rrset_t *ta)
{
	const knot_pkt_t *pkt         = vctx->pkt;
	const knot_rrset_t *keys      = vctx->keys;

289
	const bool ok = pkt && keys && ta && ta->rrs.count && ta->rrs.rdata
290 291 292
			&& ta->type == KNOT_RRTYPE_DS;
	if (!ok) {
		assert(false);
293 294
		return kr_error(EINVAL);
	}
295

296 297 298 299 300 301
	/* Check if at least one DS has a usable algorithm pair. */
	if (!kr_ds_algo_support(ta)) {
		/* See RFC6840 5.2. */
		return vctx->result = kr_error(DNSSEC_INVALID_DS_ALGORITHM);
	}

302 303
	/* RFC4035 5.2, bullet 1
	 * The supplied DS record has been authenticated.
304 305
	 * It has been validated or is part of a configured trust anchor.
	 */
306
	memset(&vctx->rrs_counters, 0, sizeof(vctx->rrs_counters));
307
	for (uint16_t i = 0; i < keys->rrs.count; ++i) {
308
		/* RFC4035 5.3.1, bullet 8 */ /* ZSK */
Vladimír Čunát's avatar
Vladimír Čunát committed
309
		/* LATER(optim.): more efficient way to iterate than _at() */
310
		const knot_rdata_t *krr = knot_rdataset_at(&keys->rrs, i);
311
		if (!kr_dnssec_key_zsk(krr->data) || kr_dnssec_key_revoked(krr->data)) {
312 313
			continue;
		}
314
		
315
		struct dseckey *key = NULL;
316
		if (kr_dnssec_key_from_rdata(&key, keys->owner, krr->data, krr->len) != 0) {
317 318
			continue;
		}
319
		if (kr_authenticate_referral(ta, (dnssec_key_t *) key) != 0) {
320
			kr_dnssec_key_free(&key);
321 322
			continue;
		}
323
		if (kr_rrset_validate_with_key(vctx, keys, i, key) != 0) {
324 325 326 327
			kr_dnssec_key_free(&key);
			continue;
		}
		kr_dnssec_key_free(&key);
328 329
		assert (vctx->result == 0);
		return vctx->result;
330
	}
331

332
	/* No useable key found */
333 334
	vctx->result = kr_error(ENOENT);
	return vctx->result;
335
}
336

337 338 339 340 341
bool kr_dnssec_key_zsk(const uint8_t *dnskey_rdata)
{
	return wire_read_u16(dnskey_rdata) & 0x0100;
}

342 343 344
bool kr_dnssec_key_ksk(const uint8_t *dnskey_rdata)
{
	return wire_read_u16(dnskey_rdata) & 0x0001;
345
}
346

347 348
/** Return true if the DNSKEY is revoked. */
bool kr_dnssec_key_revoked(const uint8_t *dnskey_rdata)
349
{
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
	return wire_read_u16(dnskey_rdata) & 0x0080;
}

int kr_dnssec_key_tag(uint16_t rrtype, const uint8_t *rdata, size_t rdlen)
{
	if (!rdata || rdlen == 0 || (rrtype != KNOT_RRTYPE_DS && rrtype != KNOT_RRTYPE_DNSKEY)) {
		return kr_error(EINVAL);
	}
	if (rrtype == KNOT_RRTYPE_DS) {
		return wire_read_u16(rdata);
	} else if (rrtype == KNOT_RRTYPE_DNSKEY) {
		struct dseckey *key = NULL;
		int ret = kr_dnssec_key_from_rdata(&key, NULL, rdata, rdlen);
		if (ret != 0) {
			return ret;
		}
		uint16_t keytag = dnssec_key_get_keytag((dnssec_key_t *)key);
		kr_dnssec_key_free(&key);
		return keytag;
	} else {
		return kr_error(EINVAL);
	}
}

374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
int kr_dnssec_key_match(const uint8_t *key_a_rdata, size_t key_a_rdlen,
                        const uint8_t *key_b_rdata, size_t key_b_rdlen)
{
	dnssec_key_t *key_a = NULL, *key_b = NULL;
	int ret = kr_dnssec_key_from_rdata((struct dseckey **)&key_a, NULL, key_a_rdata, key_a_rdlen);
	if (ret != 0) {
		return ret;
	}
	ret = kr_dnssec_key_from_rdata((struct dseckey **)&key_b, NULL, key_b_rdata, key_b_rdlen);
	if (ret != 0) {
		dnssec_key_free(key_a);
		return ret;
	}
	/* If the algorithm and the public key match, we can be sure
	 * that they are the same key. */
	ret = kr_error(ENOENT);
	dnssec_binary_t pk_a, pk_b;
	if (dnssec_key_get_algorithm(key_a) == dnssec_key_get_algorithm(key_b) &&
	    dnssec_key_get_pubkey(key_a, &pk_a) == DNSSEC_EOK &&
	    dnssec_key_get_pubkey(key_b, &pk_b) == DNSSEC_EOK) {
		if (pk_a.size == pk_b.size && memcmp(pk_a.data, pk_b.data, pk_a.size) == 0) {
			ret = 0;
		}
	}
	dnssec_key_free(key_a);
	dnssec_key_free(key_b);
	return ret;
}

403 404 405 406 407
int kr_dnssec_key_from_rdata(struct dseckey **key, const knot_dname_t *kown, const uint8_t *rdata, size_t rdlen)
{
	if (!key || !rdata || rdlen == 0) {
		return kr_error(EINVAL);
	}
408 409

	dnssec_key_t *new_key = NULL;
410 411 412 413
	const dnssec_binary_t binary_key = {
		.size = rdlen,
		.data = (uint8_t *)rdata
	};
414

415
	int ret = dnssec_key_new(&new_key);
416 417 418 419 420 421
	if (ret != DNSSEC_EOK) {
		return kr_error(ENOMEM);
	}
	ret = dnssec_key_set_rdata(new_key, &binary_key);
	if (ret != DNSSEC_EOK) {
		dnssec_key_free(new_key);
422
		return kr_error(ret);
423
	}
424 425 426 427 428 429
	if (kown) {
		ret = dnssec_key_set_dname(new_key, kown);
		if (ret != DNSSEC_EOK) {
			dnssec_key_free(new_key);
			return kr_error(ENOMEM);
		}
430 431 432 433 434 435 436 437 438 439 440 441 442
	}

	*key = (struct dseckey *) new_key;
	return kr_ok();
}

void kr_dnssec_key_free(struct dseckey **key)
{
	assert(key);

	dnssec_key_free((dnssec_key_t *) *key);
	*key = NULL;
}
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473

int kr_dnssec_matches_name_and_type(const ranked_rr_array_t *rrs, uint32_t qry_uid,
				    const knot_dname_t *name, uint16_t type)
{
	int ret = kr_error(ENOENT);
	for (size_t i = 0; i < rrs->len; ++i) {
		const ranked_rr_array_entry_t *entry = rrs->at[i];
		const knot_rrset_t *nsec = entry->rr;
		if (entry->qry_uid != qry_uid || entry->yielded) {
			continue;
		}
		if (nsec->type != KNOT_RRTYPE_NSEC &&
		    nsec->type != KNOT_RRTYPE_NSEC3) {
			continue;
		}
		if (!kr_rank_test(entry->rank, KR_RANK_SECURE)) {
			continue;
		}
		if (nsec->type == KNOT_RRTYPE_NSEC) {
			ret = kr_nsec_matches_name_and_type(nsec, name, type);
		} else {
			ret = kr_nsec3_matches_name_and_type(nsec, name, type);
		}
		if (ret == kr_ok()) {
			return kr_ok();
		} else if (ret != kr_error(ENOENT)) {
			return ret;
		}
	}
	return ret;
}