nsec3.c 21.4 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 18 19
#include <assert.h>
#include <string.h>

Vladimír Čunát's avatar
Vladimír Čunát committed
20 21 22
#include <libdnssec/binary.h>
#include <libdnssec/error.h>
#include <libdnssec/nsec.h>
23
#include <libknot/descriptor.h>
24
#include <contrib/base32hex.h>
25 26 27 28
#include <libknot/rrset.h>
#include <libknot/rrtype/nsec3.h>

#include "lib/defines.h"
29
#include "lib/dnssec/nsec.h"
30
#include "lib/dnssec/nsec3.h"
31 32 33

#define OPT_OUT_BIT 0x01

34 35 36 37 38 39
//#define FLG_CLOSEST_ENCLOSER (1 << 0)
#define FLG_CLOSEST_PROVABLE_ENCLOSER (1 << 1)
#define FLG_NAME_COVERED (1 << 2)
#define FLG_NAME_MATCHED (1 << 3)
#define FLG_TYPE_BIT_MISSING (1 << 4)
#define FLG_CNAME_BIT_MISSING (1 << 5)
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54

/**
 * Obtains NSEC3 parameters from RR.
 * @param params NSEC3 parameters structure to be set.
 * @param nsec3  NSEC3 RR containing the parameters.
 * @return       0 or error code.
 */
static int nsec3_parameters(dnssec_nsec3_params_t *params, const knot_rrset_t *nsec3)
{
	assert(params && nsec3);

	const knot_rdata_t *rr = knot_rdataset_at(&nsec3->rrs, 0);
	assert(rr);

	/* Every NSEC3 RR contains data from NSEC3PARAMS. */
55
	const size_t SALT_OFFSET = 5; /* First 5 octets contain { Alg, Flags, Iterations, Salt length } */
56
	dnssec_binary_t rdata = {
57
		.size = SALT_OFFSET + (size_t)knot_nsec3_salt_len(nsec3->rrs.rdata),
Vladimír Čunát's avatar
Vladimír Čunát committed
58
		.data = /*const-cast*/(uint8_t *)rr->data,
59
	};
Vladimír Čunát's avatar
Vladimír Čunát committed
60
	if (rdata.size > rr->len)
61
		return kr_error(EMSGSIZE);
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80

	int ret = dnssec_nsec3_params_from_rdata(params, &rdata);
	if (ret != DNSSEC_EOK) {
		return kr_error(EINVAL);
	}

	return kr_ok();
}

/**
 * Computes a hash of a given domain name.
 * @param hash   Resulting hash, must be freed.
 * @param params NSEC3 parameters.
 * @param name   Domain name to be hashed.
 * @return       0 or error code.
 */
static int hash_name(dnssec_binary_t *hash, const dnssec_nsec3_params_t *params,
                     const knot_dname_t *name)
{
81 82 83
	assert(hash && params);
	if (!name)
		return kr_error(EINVAL);
84

85 86 87 88
	dnssec_binary_t dname = {
		.size = knot_dname_size(name),
		.data = (uint8_t *) name,
	};
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111

	int ret = dnssec_nsec3_hash(&dname, params, hash);
	if (ret != DNSSEC_EOK) {
		return kr_error(EINVAL);
	}

	return kr_ok();
}

/**
 * Read hash from NSEC3 owner name and store its binary form.
 * @param hash          Buffer to be written.
 * @param max_hash_size Maximal has size.
 * @param nsec3         NSEC3 RR.
 * @return              0 or error code.
 */
static int read_owner_hash(dnssec_binary_t *hash, size_t max_hash_size, const knot_rrset_t *nsec3)
{
	assert(hash && nsec3);
	assert(hash->data);

	int32_t ret = base32hex_decode(nsec3->owner + 1, nsec3->owner[0], hash->data, max_hash_size);
	if (ret < 0) {
112
		return kr_error(EILSEQ);
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
	}
	hash->size = ret;

	return kr_ok();
}

#define MAX_HASH_BYTES 64
/**
 * Closest (provable) encloser match (RFC5155 7.2.1, bullet 1).
 * @param flags   Flags to be set according to check outcome.
 * @param nsec3   NSEC3 RR.
 * @param name    Name to be checked.
 * @param skipped Number of skipped labels to find closest (provable) match.
 * @return        0 or error code.
 */
static int closest_encloser_match(int *flags, const knot_rrset_t *nsec3,
                                  const knot_dname_t *name, unsigned *skipped)
{
	assert(flags && nsec3 && name && skipped);

	uint8_t hash_data[MAX_HASH_BYTES] = {0, };
134 135 136
	dnssec_binary_t owner_hash = { 0, hash_data };
	dnssec_nsec3_params_t params = { 0, };
	dnssec_binary_t name_hash = { 0, };
137 138 139 140 141 142 143 144 145 146 147

	int ret = read_owner_hash(&owner_hash, MAX_HASH_BYTES, nsec3);
	if (ret != 0) {
		goto fail;
	}

	ret = nsec3_parameters(&params, nsec3);
	if (ret != 0) {
		goto fail;
	}

148 149 150 151 152 153
	/* Root label has no encloser */
	if (!name[0]) {
		ret = kr_error(ENOENT);
		goto fail;
	}

154 155 156
	const knot_dname_t *encloser = knot_wire_next_label(name, NULL);
	*skipped = 1;

157
	while(encloser) {
158 159 160 161 162 163 164 165 166 167 168 169 170 171
		ret = hash_name(&name_hash, &params, encloser);
		if (ret != 0) {
			goto fail;
		}

		if ((owner_hash.size == name_hash.size) &&
		    (memcmp(owner_hash.data, name_hash.data, owner_hash.size) == 0)) {
			dnssec_binary_free(&name_hash);
			*flags |= FLG_CLOSEST_PROVABLE_ENCLOSER;
			break;
		}

		dnssec_binary_free(&name_hash);

172 173
		if (!encloser[0])
			break;
174 175
		encloser = knot_wire_next_label(encloser, NULL);
		++(*skipped);
176
	}
177 178 179 180 181 182 183 184 185 186 187 188 189 190

	ret = kr_ok();

fail:
	if (params.salt.data) {
		dnssec_nsec3_params_free(&params);
	}
	if (name_hash.data) {
		dnssec_binary_free(&name_hash);
	}
	return ret;
}

/**
191
 * Checks whether NSEC3 RR covers the supplied name (RFC5155 7.2.1, bullet 2).
192 193 194 195 196
 * @param flags Flags to be set according to check outcome.
 * @param nsec3 NSEC3 RR.
 * @param name  Name to be checked.
 * @return      0 or error code.
 */
197
static int covers_name(int *flags, const knot_rrset_t *nsec3, const knot_dname_t *name)
198 199 200
{
	assert(flags && nsec3 && name);

201 202 203 204
	uint8_t hash_data[MAX_HASH_BYTES] = { 0, };
	dnssec_binary_t owner_hash = { 0, hash_data };
	dnssec_nsec3_params_t params = { 0, };
	dnssec_binary_t name_hash = { 0, };
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220

	int ret = read_owner_hash(&owner_hash, MAX_HASH_BYTES, nsec3);
	if (ret != 0) {
		goto fail;
	}

	ret = nsec3_parameters(&params, nsec3);
	if (ret != 0) {
		goto fail;
	}

	ret = hash_name(&name_hash, &params, name);
	if (ret != 0) {
		goto fail;
	}

221 222
	uint8_t next_size = knot_nsec3_next_len(nsec3->rrs.rdata);
	const uint8_t *next_hash = knot_nsec3_next(nsec3->rrs.rdata);
223

224
	if ((next_size > 0) && (owner_hash.size == next_size) && (name_hash.size == next_size)) {
225 226 227 228 229 230 231
		/* All hash lengths must be same. */
		const uint8_t *ownrd = owner_hash.data;
		const uint8_t *nextd = next_hash;
		int covered = 0;
		int greater_then_owner = (memcmp(ownrd, name_hash.data, next_size) < 0);
		int less_then_next = (memcmp(name_hash.data, nextd, next_size) < 0);
		if (memcmp(ownrd, nextd, next_size) < 0) {
232
			/*
233 234 235 236 237
			 * 0 (...) owner ... next (...) MAX
			 *                ^
			 *                name
			 * ==>
			 * (owner < name) && (name < next)
238
			 */
239 240
			covered = ((greater_then_owner) && (less_then_next));
		} else {
241
			/*
242 243 244 245 246
			 * owner ... MAX, 0 ... next
			 *        ^     ^    ^
			 *        name  name name
			 * =>
			 * (owner < name) || (name < next)
247
			 */
248
			covered = ((greater_then_owner) || (less_then_next));
249 250
		}

251 252
		if (covered) {
			*flags |= FLG_NAME_COVERED;
253

254
			uint8_t nsec3_flags = knot_nsec3_flags(nsec3->rrs.rdata);
255 256 257 258 259 260 261
			if (nsec3_flags & ~OPT_OUT_BIT) {
				/* RFC5155 3.1.2 */
				ret = kr_error(EINVAL);
			} else {
				ret = kr_ok();
			}
		}
262 263 264 265 266 267 268 269 270 271 272 273
	}

fail:
	if (params.salt.data) {
		dnssec_nsec3_params_free(&params);
	}
	if (name_hash.data) {
		dnssec_binary_free(&name_hash);
	}
	return ret;
}

274 275 276 277 278 279 280 281 282 283 284 285 286
/**
 * Checks whether NSEC3 RR has the opt-out bit set.
 * @param flags Flags to be set according to check outcome.
 * @param nsec3 NSEC3 RR.
 * @param name  Name to be checked.
 * @return      0 or error code.
 */
static bool has_optout(const knot_rrset_t *nsec3)
{
	if (!nsec3) {
		return false;
	}

287
	uint8_t nsec3_flags = knot_nsec3_flags(nsec3->rrs.rdata);
288 289 290 291 292 293 294 295
	if (nsec3_flags & ~OPT_OUT_BIT) {
		/* RFC5155 3.1.2 */
		return false;
	}

	return nsec3_flags & OPT_OUT_BIT;
}

296 297 298 299 300
/**
 * Checks whether NSEC3 RR matches the supplied name.
 * @param flags Flags to be set according to check outcome.
 * @param nsec3 NSEC3 RR.
 * @param name  Name to be checked.
301
 * @return      0 if matching, >0 if not (abs(ENOENT)), or error code (<0).
302
 */
303
static int matches_name(const knot_rrset_t *nsec3, const knot_dname_t *name)
304
{
305
	assert(nsec3 && name);
306

307 308 309 310
	uint8_t hash_data[MAX_HASH_BYTES] = { 0, };
	dnssec_binary_t owner_hash = { 0, hash_data };
	dnssec_nsec3_params_t params = { 0, };
	dnssec_binary_t name_hash = { 0, };
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326

	int ret = read_owner_hash(&owner_hash, MAX_HASH_BYTES, nsec3);
	if (ret != 0) {
		goto fail;
	}

	ret = nsec3_parameters(&params, nsec3);
	if (ret != 0) {
		goto fail;
	}

	ret = hash_name(&name_hash, &params, name);
	if (ret != 0) {
		goto fail;
	}

327 328 329
	if ((owner_hash.size == name_hash.size) &&
	    (memcmp(owner_hash.data, name_hash.data, owner_hash.size) == 0)) {
		ret = kr_ok();
330 331
	} else {
		ret = abs(ENOENT);
332 333
	}

334 335 336 337 338 339 340 341 342 343 344
fail:
	if (params.salt.data) {
		dnssec_nsec3_params_free(&params);
	}
	if (name_hash.data) {
		dnssec_binary_free(&name_hash);
	}
	return ret;
}
#undef MAX_HASH_BYTES

345 346 347 348 349
/**
 * Prepends an asterisk label to given name.
 *
 * @param tgt  Target buffer to write domain name into.
 * @param name Name to be added to the asterisk.
350
 * @return     Size of the resulting name or error code.
351
 */
352
static int prepend_asterisk(uint8_t *tgt, size_t maxlen, const knot_dname_t *name)
353
{
354 355 356
	assert(maxlen >= 3);
	memcpy(tgt, "\1*", 3);
	return knot_dname_to_wire(tgt + 2, name, maxlen - 2);
357 358
}

359 360 361
/**
 * Closest encloser proof (RFC5155 7.2.1).
 * @note No RRSIGs are validated.
362 363 364 365 366 367 368
 * @param pkt                     Packet structure to be processed.
 * @param section_id              Packet section to be processed.
 * @param sname                   Name to be checked.
 * @param encloser_name           Returned matching encloser name, if found.
 * @param matching_encloser_nsec3 Pointer to matching encloser NSEC RRSet.
 * @param covering_next_nsec3     Pointer to covering next closer NSEC3 RRSet.
 * @return                        0 or error code.
369
 */
370 371 372 373 374 375
static int closest_encloser_proof(const knot_pkt_t *pkt,
				  knot_section_t section_id,
				  const knot_dname_t *sname,
				  const knot_dname_t **encloser_name,
				  const knot_rrset_t **matching_encloser_nsec3,
				  const knot_rrset_t **covering_next_nsec3)
376 377
{
	const knot_pktsection_t *sec = knot_pkt_section(pkt, section_id);
378
	if (!sec || !sname) {
379 380 381
		return kr_error(EINVAL);
	}

382 383 384
	const knot_rrset_t *matching = NULL;
	const knot_rrset_t *covering = NULL;

385
	int flags = 0;
386 387 388 389 390 391
	const knot_dname_t *next_closer = NULL;
	for (unsigned i = 0; i < sec->count; ++i) {
		const knot_rrset_t *rrset = knot_pkt_rr(sec, i);
		if (rrset->type != KNOT_RRTYPE_NSEC3) {
			continue;
		}
392 393 394 395 396
		/* Also skip the NSEC3-to-match an ancestor of sname if it's
		 * a parent-side delegation, as that would mean the owner
		 * does not really exist (authoritatively in this zone,
		 * even in case of opt-out).
		 */
397 398
		const uint8_t *bm = knot_nsec3_bitmap(rrset->rrs.rdata);
		uint16_t bm_size = knot_nsec3_bitmap_len(rrset->rrs.rdata);
399 400 401 402
		if (kr_nsec_children_in_zone_check(bm, bm_size) != 0) {
			continue; /* no fatal errors from bad RRs */
		}
		/* Match the NSEC3 to sname or one of its ancestors. */
403 404
		unsigned skipped = 0;
		flags = 0;
405
		int ret = closest_encloser_match(&flags, rrset, sname, &skipped);
406 407 408 409 410 411
		if (ret != 0) {
			return ret;
		}
		if (!(flags & FLG_CLOSEST_PROVABLE_ENCLOSER)) {
			continue;
		}
412
		matching = rrset;
413
		/* Construct the next closer name and try to cover it. */
414 415 416
		--skipped;
		next_closer = sname;
		for (unsigned j = 0; j < skipped; ++j) {
417
			assert(next_closer[0]);
418 419 420 421 422 423 424
			next_closer = knot_wire_next_label(next_closer, NULL);
		}
		for (unsigned j = 0; j < sec->count; ++j) {
			const knot_rrset_t *rrset = knot_pkt_rr(sec, j);
			if (rrset->type != KNOT_RRTYPE_NSEC3) {
				continue;
			}
425
			ret = covers_name(&flags, rrset, next_closer);
426 427 428
			if (ret != 0) {
				return ret;
			}
429
			if (flags & FLG_NAME_COVERED) {
430
				covering = rrset;
431 432 433
				break;
			}
		}
434
		if (flags & FLG_NAME_COVERED) {
435 436
			break;
		}
437
		flags = 0; //
438 439
	}

440
	if ((flags & FLG_CLOSEST_PROVABLE_ENCLOSER) && (flags & FLG_NAME_COVERED) && next_closer) {
441
		if (encloser_name && next_closer[0]) {
442
			*encloser_name = knot_wire_next_label(next_closer, NULL);
443
		}
444 445
		if (matching_encloser_nsec3) {
			*matching_encloser_nsec3 = matching;
446 447 448
		}
		if (covering_next_nsec3) {
			*covering_next_nsec3 = covering;
449
		}
450 451 452
		return kr_ok();
	}

453
	return kr_error(ENOENT);
454 455
}

456 457 458 459 460
/**
 * Check whether any NSEC3 RR covers a wildcard RR at the closer encloser.
 * @param pkt        Packet structure to be processed.
 * @param section_id Packet section to be processed.
 * @param encloser   Closest (provable) encloser domain name.
461
 * @return           0 or error code:
462
 *                   KNOT_ERANGE - NSEC3 RR (that covers a wildcard)
463 464
 *                   has been found, but has opt-out flag set;
 *                   otherwise - error.
465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
 */
static int covers_closest_encloser_wildcard(const knot_pkt_t *pkt, knot_section_t section_id,
                                            const knot_dname_t *encloser)
{
	const knot_pktsection_t *sec = knot_pkt_section(pkt, section_id);
	if (!sec || !encloser) {
		return kr_error(EINVAL);
	}

	uint8_t wildcard[KNOT_DNAME_MAXLEN];
	wildcard[0] = 1;
	wildcard[1] = '*';
	int encloser_len = knot_dname_size(encloser);
	if (encloser_len < 0) {
		return encloser_len;
	}
	memcpy(wildcard + 2, encloser, encloser_len);

	int flags = 0;
	for (unsigned i = 0; i < sec->count; ++i) {
		const knot_rrset_t *rrset = knot_pkt_rr(sec, i);
		if (rrset->type != KNOT_RRTYPE_NSEC3) {
			continue;
		}
		int ret = covers_name(&flags, rrset, wildcard);
		if (ret != 0) {
			return ret;
		}
		if (flags & FLG_NAME_COVERED) {
494
			return has_optout(rrset) ?
495
			       kr_error(KNOT_ERANGE) : kr_ok();
496 497 498
		}
	}

499
	return kr_error(ENOENT);
500 501
}

502
int kr_nsec3_name_error_response_check(const knot_pkt_t *pkt, knot_section_t section_id,
503
                                       const knot_dname_t *sname)
504 505
{
	const knot_dname_t *encloser = NULL;
506 507 508
	const knot_rrset_t *covering_next_nsec3 = NULL;
	int ret = closest_encloser_proof(pkt, section_id, sname,
					 &encloser, NULL, &covering_next_nsec3);
509 510 511
	if (ret != 0) {
		return ret;
	}
512 513 514 515 516 517 518 519 520 521
	ret = covers_closest_encloser_wildcard(pkt, section_id, encloser);
	if (ret != 0) {
		/* OK, but NSEC3 for wildcard at encloser has opt-out;
		 * or error */
		return ret;
	}
	/* Closest encloser proof is OK and
	 * NSEC3 for wildcard has been found and optout flag is not set.
	 * Now check if NSEC3 that covers next closer name has opt-out. */
	return has_optout(covering_next_nsec3) ?
522
	       kr_error(KNOT_ERANGE) : kr_ok();
523
}
524

525
/**
526
 * Search the packet section for a matching NSEC3 with nodata-proving bitmap.
527 528 529 530 531
 * @param pkt        Packet structure to be processed.
 * @param section_id Packet section to be processed.
 * @param sname      Name to be checked.
 * @param stype      Type to be checked.
 * @return           0 or error code.
532 533
 * @note             This does NOT check the opt-out case if type is DS;
 *                   see RFC 5155 8.6.
534
 */
535 536
static int nodata_find(const knot_pkt_t *pkt, knot_section_t section_id,
			const knot_dname_t *name, const uint16_t type)
537 538
{
	const knot_pktsection_t *sec = knot_pkt_section(pkt, section_id);
539
	if (!sec || !name) {
540 541 542 543
		return kr_error(EINVAL);
	}

	for (unsigned i = 0; i < sec->count; ++i) {
544 545 546 547
		const knot_rrset_t *nsec3 = knot_pkt_rr(sec, i);
		/* Records causing any errors are simply skipped. */
		if (nsec3->type != KNOT_RRTYPE_NSEC3
		    || matches_name(nsec3, name) != kr_ok()) {
548
			continue;
549
			/* LATER(optim.): we repeatedly recompute the hash of `name` */
550 551
		}

552 553
		const uint8_t *bm = knot_nsec3_bitmap(nsec3->rrs.rdata);
		uint16_t bm_size = knot_nsec3_bitmap_len(nsec3->rrs.rdata);
Marek Vavruša's avatar
Marek Vavruša committed
554
		if (kr_nsec_bitmap_nodata_check(bm, bm_size, type, nsec3->owner) == kr_ok()) {
555
			return kr_ok();
556 557 558 559 560 561
		}
	}

	return kr_error(ENOENT);
}

562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
/**
 * Check whether NSEC3 RR matches a wildcard at the closest encloser and has given type bit missing.
 * @param pkt        Packet structure to be processed.
 * @param section_id Packet section to be processed.
 * @param encloser   Closest (provable) encloser domain name.
 * @param stype      Type to be checked.
 * @return           0 or error code.
 */
static int matches_closest_encloser_wildcard(const knot_pkt_t *pkt, knot_section_t section_id,
                                             const knot_dname_t *encloser, uint16_t stype)
{
	const knot_pktsection_t *sec = knot_pkt_section(pkt, section_id);
	if (!sec || !encloser) {
		return kr_error(EINVAL);
	}

578
	uint8_t wildcard[KNOT_DNAME_MAXLEN]; /**< the source of synthesis */
579
	int ret = prepend_asterisk(wildcard, sizeof(wildcard), encloser);
580
	if (ret < 0) {
581
		return ret;
582
	}
583
	assert(ret >= 3);
584
	return nodata_find(pkt, section_id, wildcard, stype);
585 586
}

587 588 589 590 591 592 593 594 595 596
int kr_nsec3_wildcard_answer_response_check(const knot_pkt_t *pkt, knot_section_t section_id,
                                            const knot_dname_t *sname, int trim_to_next)
{
	const knot_pktsection_t *sec = knot_pkt_section(pkt, section_id);
	if (!sec || !sname) {
		return kr_error(EINVAL);
	}

	/* Compute the next closer name. */
	for (int i = 0; i < trim_to_next; ++i) {
597
		assert(sname[0]);
598 599 600 601 602 603 604 605 606 607 608 609 610 611
		sname = knot_wire_next_label(sname, NULL);
	}

	int flags = 0;
	for (unsigned i = 0; i < sec->count; ++i) {
		const knot_rrset_t *rrset = knot_pkt_rr(sec, i);
		if (rrset->type != KNOT_RRTYPE_NSEC3) {
			continue;
		}
		int ret = covers_name(&flags, rrset, sname);
		if (ret != 0) {
			return ret;
		}
		if (flags & FLG_NAME_COVERED) {
612
			return has_optout(rrset) ?
613
			       kr_error(KNOT_ERANGE) : kr_ok();
614 615 616 617 618
		}
	}

	return kr_error(ENOENT);
}
619

620

621
int kr_nsec3_no_data(const knot_pkt_t *pkt, knot_section_t section_id,
622 623
                     const knot_dname_t *sname, uint16_t stype)
{
624
	/* DS record may be also matched by an existing NSEC3 RR. */
625
	int ret = nodata_find(pkt, section_id, sname, stype);
626
	if (ret == 0) {
627
		/* Satisfies RFC5155 8.5 and 8.6, both first paragraph. */
628 629 630 631 632 633 634 635 636 637
		return ret;
	}

	/* Find closest provable encloser. */
	const knot_dname_t *encloser_name = NULL;
	const knot_rrset_t *covering_next_nsec3 = NULL;
	ret = closest_encloser_proof(pkt, section_id, sname, &encloser_name,
                                     NULL, &covering_next_nsec3);
	if (ret != 0) {
		return ret;
638 639
	}

640 641
	assert(encloser_name && covering_next_nsec3);
	ret = matches_closest_encloser_wildcard(pkt, section_id,
642
	                                         encloser_name, stype);
643 644
	if (ret == 0) {
		/* Satisfies RFC5155 8.7 */
645 646 647 648 649 650
		if (has_optout(covering_next_nsec3)) {
			/* Opt-out is detected.
			 * Despite the fact that all records
			 * in the packet can be properly signed,
			 * AD bit must not be set due to rfc5155 9.2.
			 * Return appropriate code to the caller */
651
			ret = kr_error(KNOT_ERANGE);
652
		}
653 654 655
		return ret;
	}

656 657 658 659
	if (!has_optout(covering_next_nsec3)) {
		/* Bogus */
		ret = kr_error(ENOENT);
	} else {
660
		/* 
661 662 663 664
		 * Satisfies RFC5155 8.6 (QTYPE == DS), 2nd paragraph.
		 * Also satisfies ERRATA 3441 8.5 (QTYPE != DS), 3rd paragraph.
		 * - (wildcard) empty nonterminal
		 * derived from unsecure delegation.
665
		 * Denial of existence can not be proven.
666 667
		 * Set error code to proceed unsecure.
		 */
668
		ret = kr_error(KNOT_ERANGE);
669
	}
670

671
	return ret;
672
}
673

674 675 676 677 678 679 680 681 682 683 684 685 686 687
int kr_nsec3_ref_to_unsigned(const knot_pkt_t *pkt)
{
	const knot_pktsection_t *sec = knot_pkt_section(pkt, KNOT_AUTHORITY);
	if (!sec) {
		return kr_error(EINVAL);
	}
	for (unsigned i = 0; i < sec->count; ++i) {
		const knot_rrset_t *ns = knot_pkt_rr(sec, i);
		if (ns->type == KNOT_RRTYPE_DS) {
			return kr_error(EEXIST);
		}
		if (ns->type != KNOT_RRTYPE_NS) {
			continue;
		}
688 689

		int flags = 0;
Grigorii Demidov's avatar
Grigorii Demidov committed
690
		bool nsec3_found = false;
691 692 693 694 695 696 697 698
		for (unsigned j = 0; j < sec->count; ++j) {
			const knot_rrset_t *nsec3 = knot_pkt_rr(sec, j);
			if (nsec3->type == KNOT_RRTYPE_DS) {
				return kr_error(EEXIST);
			}
			if (nsec3->type != KNOT_RRTYPE_NSEC3) {
				continue;
			}
Grigorii Demidov's avatar
Grigorii Demidov committed
699
			nsec3_found = true;
700 701 702
			/* nsec3 found, check if owner name matches the delegation name.
			 * Just skip in case of *any* errors. */
			if (matches_name(nsec3, ns->owner) != kr_ok()) {
703 704
				continue;
			}
705

706 707
			const uint8_t *bm = knot_nsec3_bitmap(nsec3->rrs.rdata);
			uint16_t bm_size = knot_nsec3_bitmap_len(nsec3->rrs.rdata);
708 709 710
			if (!bm) {
				return kr_error(EINVAL);
			}
711
			if (dnssec_nsec_bitmap_contains(bm, bm_size,
712
							  KNOT_RRTYPE_NS) &&
713
			    !dnssec_nsec_bitmap_contains(bm, bm_size,
714
							  KNOT_RRTYPE_DS) &&
715
			    !dnssec_nsec_bitmap_contains(bm, bm_size,
716 717 718 719 720
							  KNOT_RRTYPE_SOA)) {
				/* Satisfies rfc5155, 8.9. paragraph 2 */
				return kr_ok();
			}
		}
Grigorii Demidov's avatar
Grigorii Demidov committed
721 722 723
		if (!nsec3_found) {
			return kr_error(DNSSEC_NOT_FOUND);
		}
724 725 726 727 728 729 730 731 732 733 734 735 736
		if (flags & FLG_NAME_MATCHED) {
			/* nsec3 which owner matches
			 * the delegation name was found,
			 * but nsec3 type bitmap contains wrong types
			 */
			return kr_error(EINVAL);
		}
		/* nsec3 that matches the delegation was not found.
		 * Check rfc5155, 8.9. paragraph 4.
		 * Find closest provable encloser.
		 */
		const knot_dname_t *encloser_name = NULL;
		const knot_rrset_t *covering_next_nsec3 = NULL;
737 738
		int ret = closest_encloser_proof(pkt, KNOT_AUTHORITY, ns->owner,
				&encloser_name, NULL, &covering_next_nsec3);
739 740 741 742 743
		if (ret != 0) {
			return kr_error(EINVAL);
		}

		if (has_optout(covering_next_nsec3)) {
744
			return kr_error(KNOT_ERANGE);
745 746 747 748 749 750 751
		} else {
			return kr_error(EINVAL);
		}
	}
	return kr_error(EINVAL);
}

752 753 754
int kr_nsec3_matches_name_and_type(const knot_rrset_t *nsec3,
				   const knot_dname_t *name, uint16_t type)
{
755 756 757 758 759 760 761 762 763 764 765
	/* It's not secure enough to just check a single bit for (some) other types,
	 * but we don't (currently) only use this API for NS.  See RFC 6840 sec. 4.
	 */
	if (type != KNOT_RRTYPE_NS) {
		assert(!EINVAL);
		return kr_error(EINVAL);
	}
	int ret = matches_name(nsec3, name);
	if (ret) {
		return kr_error(ret);
	}
766 767
	const uint8_t *bm = knot_nsec3_bitmap(nsec3->rrs.rdata);
	uint16_t bm_size = knot_nsec3_bitmap_len(nsec3->rrs.rdata);
768 769 770
	if (!bm) {
		return kr_error(EINVAL);
	}
771
	if (dnssec_nsec_bitmap_contains(bm, bm_size, type)) {
772 773 774
		return kr_ok();
	} else {
		return kr_error(ENOENT);
775 776
	}
}