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

    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 18
#include <assert.h>
#include <dnssec/binary.h>
19
#include <dnssec/crypto.h>
20 21
#include <dnssec/error.h>
#include <dnssec/key.h>
22
#include <dnssec/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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
#include "lib/dnssec.h"

#define DEBUG_MSG(fmt...) fprintf(stderr, fmt)

void kr_crypto_init(void)
{
	dnssec_crypto_init();
}

void kr_crypto_cleanup(void)
{
	dnssec_crypto_cleanup();
}

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

55 56 57 58 59
#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.
60
 * @param cov_labels Covered RRSet owner label count.
61 62 63 64
 * @param rrsigs    RRSet containing the signatures.
 * @param sig_pos   Specifies the signature within the RRSIG RRSet.
 * @param keys      Associated DNSKEY RRSet.
 * @param key_pos   Specifies the key within the DNSKEY RRSet,
65
 * @param keytag    Used key tag.
66 67 68
 * @param zone_name The name of the zone cut.
 * @param timestamp Validation time.
 */
69
static int validate_rrsig_rr(int *flags, int cov_labels,
70
                             const knot_rrset_t *rrsigs, size_t sig_pos,
71
                             const knot_rrset_t *keys, size_t key_pos, uint16_t keytag,
72
                             const knot_dname_t *zone_name, uint32_t timestamp)
73
{
74
	if (!flags || !rrsigs || !keys || !zone_name) {
75 76
		return kr_error(EINVAL);
	}
77 78
	/* bullet 5 */
	if (knot_rrsig_sig_expiration(&rrsigs->rrs, sig_pos) < timestamp) {
79 80
		return kr_error(EINVAL);
	}
81 82
	/* bullet 6 */
	if (knot_rrsig_sig_inception(&rrsigs->rrs, sig_pos) > timestamp) {
83 84
		return kr_error(EINVAL);
	}
85 86 87
	/* bullet 2 */
	const knot_dname_t *signer_name = knot_rrsig_signer_name(&rrsigs->rrs, sig_pos);
	if (!signer_name || !knot_dname_is_equal(signer_name, zone_name)) {
88 89 90
		return kr_error(EINVAL);
	}
	/* bullet 4 */
91 92
	{
		int rrsig_labels = knot_rrsig_labels(&rrsigs->rrs, sig_pos);
93
		if (rrsig_labels > cov_labels) {
94 95
			return kr_error(EINVAL);
		}
96
		if (rrsig_labels < cov_labels) {
97 98
			*flags |= FLG_WILDCARD_EXPANSION;
		}
99
	}
100

101
	/* bullet 7 */
102
	if ((!knot_dname_is_equal(keys->owner, signer_name)) ||
103
	    (knot_dnskey_alg(&keys->rrs, key_pos) != knot_rrsig_algorithm(&rrsigs->rrs, sig_pos)) ||
104
	    (keytag != knot_rrsig_key_tag(&rrsigs->rrs, sig_pos))) {
105 106 107 108 109 110
		return kr_error(EINVAL);
	}
	/* bullet 8 */
	/* Checked somewhere else. */
	/* bullet 9 and 10 */
	/* One of the requirements should be always fulfilled. */
111

112 113 114
	return kr_ok();
}

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

	return knot_dname_labels(expanded, NULL) - knot_rrsig_labels(&rrsigs->rrs, sig_pos);
}

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

141 142
	for (unsigned i = 0; i < vctx->keys->rrs.rr_count; ++i) {
		int ret = kr_rrset_validate_with_key(vctx, covered, i, NULL);
143
		if (ret == 0) {
144
			return ret;
145 146 147
		}
	}

148
	return kr_error(ENOENT);
149 150
}

151 152 153
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)
154
{
155 156 157 158 159 160
	const knot_pkt_t *pkt         = vctx->pkt;
	knot_section_t section_id     = vctx->section_id;
	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;
161 162 163
	struct dseckey *created_key = NULL;
	if (key == NULL) {
		const knot_rdata_t *krr = knot_rdataset_at(&keys->rrs, key_pos);
164
		int ret = kr_dnssec_key_from_rdata(&created_key, keys->owner,
165
			                       knot_rdata_data(krr), knot_rdata_rdlen(krr));
166
		if (ret != 0) {
167 168
			vctx->result = ret;
			return vctx->result;
169 170 171
		}
		key = created_key;
	}
172 173 174 175 176 177
	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;
	}
178

179
	const knot_pktsection_t *sec = knot_pkt_section(pkt, section_id);
180
	for (unsigned i = 0; i < sec->count; ++i) {
181
		/* Consider every RRSIG that matches owner and covers the class/type. */
182 183
		const knot_rrset_t *rrsig = knot_pkt_rr(sec, i);
		if (rrsig->type != KNOT_RRTYPE_RRSIG) {
184 185
			continue;
		}
186 187 188
		if ((covered->rclass != rrsig->rclass) || !knot_dname_is_equal(covered->owner, rrsig->owner)) {
			continue;
		}
189
		for (uint16_t j = 0; j < rrsig->rrs.rr_count; ++j) {
190 191 192 193 194 195 196
			int val_flgs = 0;
			int trim_labels = 0;
			if (knot_rrsig_type_covered(&rrsig->rrs, j) != covered->type) {
				continue;
			}
			if (validate_rrsig_rr(&val_flgs, covered_labels, rrsig, j,
			                      keys, key_pos, keytag,
197
			                      zone_name, timestamp) != 0) {
198 199
				continue;
			}
200 201 202 203 204 205
			if (val_flgs & FLG_WILDCARD_EXPANSION) {
				trim_labels = wildcard_radix_len_diff(covered->owner, rrsig, j);
				if (trim_labels < 0) {
					break;
				}
			}
206
			if (kr_check_signature(rrsig, j, (dnssec_key_t *) key, covered, trim_labels) != 0) {
207 208
				continue;
			}
209
			if (val_flgs & FLG_WILDCARD_EXPANSION) {
210
				int ret = 0;
211 212 213 214 215 216
				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);
				}
				if (ret != 0) {
217 218
					continue;
				}
219
				vctx->flags |= KR_DNSSEC_VFLG_WEXPAND;
220
			}
221 222
			/* Validated with current key, OK */
			kr_dnssec_key_free(&created_key);
223 224
			vctx->result = kr_ok();
			return vctx->result;
225
		}
226
	}
227
	/* No applicable key found, cannot be validated. */
228
	kr_dnssec_key_free(&created_key);
229 230
	vctx->result = kr_error(ENOENT);
	return vctx->result;
231 232
}

233 234 235 236 237
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;

238
	if (!pkt || !keys || !ta) {
239 240
		return kr_error(EINVAL);
	}
241

242 243
	/* RFC4035 5.2, bullet 1
	 * The supplied DS record has been authenticated.
244 245 246
	 * It has been validated or is part of a configured trust anchor.
	 */
	for (uint16_t i = 0; i < keys->rrs.rr_count; ++i) {
247
		/* RFC4035 5.3.1, bullet 8 */ /* ZSK */
248 249
		const knot_rdata_t *krr = knot_rdataset_at(&keys->rrs, i);
		const uint8_t *key_data = knot_rdata_data(krr);
250
		if (!kr_dnssec_key_zsk(key_data) || kr_dnssec_key_revoked(key_data)) {
251 252
			continue;
		}
253
		
254
		struct dseckey *key;
255
		if (kr_dnssec_key_from_rdata(&key, keys->owner, key_data, knot_rdata_rdlen(krr)) != 0) {
256 257
			continue;
		}
258
		if (kr_authenticate_referral(ta, (dnssec_key_t *) key) != 0) {
259
			kr_dnssec_key_free(&key);
260 261
			continue;
		}
262
		if (kr_rrset_validate_with_key(vctx, keys, i, key) != 0) {
263 264 265 266
			kr_dnssec_key_free(&key);
			continue;
		}
		kr_dnssec_key_free(&key);
267 268
		assert (vctx->result == 0);
		return vctx->result;
269
	}
270
	/* No useable key found */
271 272
	vctx->result = kr_error(ENOENT);
	return vctx->result;
273
}
274

275 276 277 278 279
bool kr_dnssec_key_zsk(const uint8_t *dnskey_rdata)
{
	return wire_read_u16(dnskey_rdata) & 0x0100;
}

280 281 282
bool kr_dnssec_key_ksk(const uint8_t *dnskey_rdata)
{
	return wire_read_u16(dnskey_rdata) & 0x0001;
283
}
284

285 286
/** Return true if the DNSKEY is revoked. */
bool kr_dnssec_key_revoked(const uint8_t *dnskey_rdata)
287
{
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
	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);
	}
}

312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
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;
}

341 342 343 344 345
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);
	}
346 347

	dnssec_key_t *new_key = NULL;
348 349 350 351
	const dnssec_binary_t binary_key = {
		.size = rdlen,
		.data = (uint8_t *)rdata
	};
352

353
	int ret = dnssec_key_new(&new_key);
354 355 356 357 358 359 360 361
	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);
		return kr_error(ENOMEM);
	}
362 363 364 365 366 367
	if (kown) {
		ret = dnssec_key_set_dname(new_key, kown);
		if (ret != DNSSEC_EOK) {
			dnssec_key_free(new_key);
			return kr_error(ENOMEM);
		}
368 369 370 371 372 373 374 375 376 377 378 379 380
	}

	*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;
}
381 382 383

#undef DEBUG_MSG