dnssec.c 13.3 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
                             const knot_dname_t *zone_name, uint32_t timestamp)
76
{
77
	if (!flags || !rrsigs || !key_owner || !key_rdata || !zone_name) {
78 79
		return kr_error(EINVAL);
	}
80
	/* bullet 5 */
81
	if (knot_rrsig_sig_expiration(rrsigs) < timestamp) {
82 83
		return kr_error(EINVAL);
	}
84
	/* bullet 6 */
85
	if (knot_rrsig_sig_inception(rrsigs) > timestamp) {
86 87
		return kr_error(EINVAL);
	}
88
	/* bullet 2 */
89
	const knot_dname_t *signer_name = knot_rrsig_signer_name(rrsigs);
90
	if (!signer_name || !knot_dname_is_equal(signer_name, zone_name)) {
91
		return kr_error(EAGAIN);
92 93
	}
	/* bullet 4 */
94
	{
95
		int rrsig_labels = knot_rrsig_labels(rrsigs);
96
		if (rrsig_labels > cov_labels) {
97 98
			return kr_error(EINVAL);
		}
99
		if (rrsig_labels < cov_labels) {
100 101
			*flags |= FLG_WILDCARD_EXPANSION;
		}
102
	}
103

104
	/* bullet 7 */
105 106 107
	if ((!knot_dname_is_equal(key_owner, signer_name)) ||
	    (knot_dnskey_alg(key_rdata) != knot_rrsig_alg(rrsigs)) ||
	    (keytag != knot_rrsig_key_tag(rrsigs))) {
108 109 110 111 112 113
		return kr_error(EINVAL);
	}
	/* bullet 8 */
	/* Checked somewhere else. */
	/* bullet 9 and 10 */
	/* One of the requirements should be always fulfilled. */
114

115 116 117
	return kr_ok();
}

118 119 120 121 122 123 124
/**
 * 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.
 */
125 126
static inline int wildcard_radix_len_diff(const knot_dname_t *expanded,
					  const knot_rdata_t *rrsig)
127
{
128
	if (!expanded || !rrsig) {
129 130 131
		return -1;
	}

132
	return knot_dname_labels(expanded, NULL) - knot_rrsig_labels(rrsig);
133 134
}

135
int kr_rrset_validate(kr_rrset_validation_ctx_t *vctx, const knot_rrset_t *covered)
136
{
137 138 139 140
	if (!vctx) {
		return kr_error(EINVAL);
	}
	if (!vctx->pkt || !covered || !vctx->keys || !vctx->zone_name) {
141 142 143
		return kr_error(EINVAL);
	}

144
	for (unsigned i = 0; i < vctx->keys->rrs.count; ++i) {
145
		int ret = kr_rrset_validate_with_key(vctx, covered, i, NULL);
146
		if (ret == 0) {
147
			return ret;
148 149 150
		}
	}

151
	return kr_error(ENOENT);
152 153
}

154 155 156 157 158 159 160 161 162 163
/**
 * 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,
164 165
				const knot_rrset_t *covered,
				size_t key_pos, const struct dseckey *key)
166
{
167 168 169 170 171
	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;
172
	struct dseckey *created_key = NULL;
173 174 175

	/* 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. */
176
	if (knot_dname_in_bailiwick(covered->owner, zone_name) < 0) {
177 178 179 180
		vctx->result = kr_error(ENOENT);
		return vctx->result;
	}

181
	const knot_rdata_t *key_rdata = knot_rdataset_at(&keys->rrs, key_pos);
182
	if (key == NULL) {
183
		int ret = kr_dnssec_key_from_rdata(&created_key, keys->owner,
184
						   key_rdata->data, key_rdata->len);
185
		if (ret != 0) {
186 187
			vctx->result = ret;
			return vctx->result;
188 189 190
		}
		key = created_key;
	}
191 192 193 194 195 196
	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;
	}
197

198
	for (uint16_t i = 0; i < vctx->rrs->len; ++i) {
199
		/* Consider every RRSIG that matches owner and covers the class/type. */
200
		const knot_rrset_t *rrsig = vctx->rrs->at[i]->rr;
201
		if (rrsig->type != KNOT_RRTYPE_RRSIG) {
202 203
			continue;
		}
204 205 206
		if ((covered->rclass != rrsig->rclass) || !knot_dname_is_equal(covered->owner, rrsig->owner)) {
			continue;
		}
207 208
		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)) {
209 210
			int val_flgs = 0;
			int trim_labels = 0;
211
			if (knot_rrsig_type_covered(rdata_j) != covered->type) {
212 213
				continue;
			}
214 215
			int ret = validate_rrsig_rr(&val_flgs, covered_labels, rdata_j,
			                      keys->owner, key_rdata, keytag,
216 217 218 219 220 221
			                      zone_name, timestamp);
			if (ret == kr_error(EAGAIN)) {
				kr_dnssec_key_free(&created_key);
				vctx->result = ret;
				return ret;
			} else if (ret != 0) {
222 223
				continue;
			}
224
			if (val_flgs & FLG_WILDCARD_EXPANSION) {
225
				trim_labels = wildcard_radix_len_diff(covered->owner, rdata_j);
226 227 228 229
				if (trim_labels < 0) {
					break;
				}
			}
230
			if (kr_check_signature(rdata_j, (dnssec_key_t *) key, covered, trim_labels) != 0) {
231 232
				continue;
			}
233
			if (val_flgs & FLG_WILDCARD_EXPANSION) {
234
				int ret = 0;
235 236 237 238
				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);
239
					if (ret == kr_error(KNOT_ERANGE)) {
240 241 242
						ret = 0;
						vctx->flags |= KR_DNSSEC_VFLG_OPTOUT;
					}
243 244
				}
				if (ret != 0) {
245 246
					continue;
				}
247
				vctx->flags |= KR_DNSSEC_VFLG_WEXPAND;
248
			}
249 250
			/* Validated with current key, OK */
			kr_dnssec_key_free(&created_key);
251 252
			vctx->result = kr_ok();
			return vctx->result;
253
		}
254
	}
255
	/* No applicable key found, cannot be validated. */
256
	kr_dnssec_key_free(&created_key);
257 258
	vctx->result = kr_error(ENOENT);
	return vctx->result;
259 260
}

261 262
static bool kr_ds_algo_support(const knot_rrset_t *ta)
{
263 264 265 266 267
	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))) {
268 269 270 271 272 273
			return true;
		}
	}
	return false;
}

274 275 276 277 278
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;

279
	const bool ok = pkt && keys && ta && ta->rrs.count && ta->rrs.rdata
280 281 282
			&& ta->type == KNOT_RRTYPE_DS;
	if (!ok) {
		assert(false);
283 284
		return kr_error(EINVAL);
	}
285

286 287 288 289 290 291
	/* 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);
	}

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

321
	/* No useable key found */
322 323
	vctx->result = kr_error(ENOENT);
	return vctx->result;
324
}
325

326 327 328 329 330
bool kr_dnssec_key_zsk(const uint8_t *dnskey_rdata)
{
	return wire_read_u16(dnskey_rdata) & 0x0100;
}

331 332 333
bool kr_dnssec_key_ksk(const uint8_t *dnskey_rdata)
{
	return wire_read_u16(dnskey_rdata) & 0x0001;
334
}
335

336 337
/** Return true if the DNSKEY is revoked. */
bool kr_dnssec_key_revoked(const uint8_t *dnskey_rdata)
338
{
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
	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);
	}
}

363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
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;
}

392 393 394 395 396
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);
	}
397 398

	dnssec_key_t *new_key = NULL;
399 400 401 402
	const dnssec_binary_t binary_key = {
		.size = rdlen,
		.data = (uint8_t *)rdata
	};
403

404
	int ret = dnssec_key_new(&new_key);
405 406 407 408 409 410
	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);
411
		return kr_error(ret);
412
	}
413 414 415 416 417 418
	if (kown) {
		ret = dnssec_key_set_dname(new_key, kown);
		if (ret != DNSSEC_EOK) {
			dnssec_key_free(new_key);
			return kr_error(ENOMEM);
		}
419 420 421 422 423 424 425 426 427 428 429 430 431
	}

	*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;
}
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462

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;
}