mirror of
https://github.com/NLnetLabs/unbound.git
synced 2025-12-20 23:00:56 -05:00
178 lines
5.9 KiB
C
178 lines
5.9 KiB
C
/*
|
|
* validator/val_nsec.c - validator NSEC denial of existance functions.
|
|
*
|
|
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
|
*
|
|
* This software is open source.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* Neither the name of the NLNET LABS nor the names of its contributors may
|
|
* be used to endorse or promote products derived from this software without
|
|
* specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/**
|
|
* \file
|
|
*
|
|
* This file contains helper functions for the validator module.
|
|
* The functions help with NSEC checking, the different NSEC proofs
|
|
* for denial of existance, and proofs for presence of types.
|
|
*/
|
|
#include "config.h"
|
|
#include "validator/val_nsec.h"
|
|
#include "validator/val_utils.h"
|
|
#include "util/data/msgreply.h"
|
|
#include "util/data/dname.h"
|
|
|
|
/** Check type present in NSEC typemap with bitmap arg */
|
|
static int
|
|
nsec_has_type_rdata(uint8_t* bitmap, size_t len, uint16_t type)
|
|
{
|
|
/* bitmasks for determining type-lowerbits presence */
|
|
uint8_t masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
|
|
uint8_t type_window = type>>8;
|
|
uint8_t type_low = type&0xff;
|
|
uint8_t win, winlen;
|
|
/* read each of the type bitmap windows and see if the searched
|
|
* type is amongst it */
|
|
while(len > 0) {
|
|
if(len < 3) /* bad window, at least window# winlen bitmap */
|
|
return 0;
|
|
win = *bitmap++;
|
|
winlen = *bitmap++;
|
|
len -= 2;
|
|
if(len < winlen || winlen < 1 || winlen > 32)
|
|
return 0; /* bad window length */
|
|
if(win == type_window) {
|
|
/* search window bitmap for the correct byte */
|
|
/* mybyte is 0 if we need the first byte */
|
|
size_t mybyte = type_low>>3;
|
|
if(winlen <= mybyte)
|
|
return 0; /* window too short */
|
|
return bitmap[mybyte] & masks[type_low&0x7];
|
|
} else {
|
|
/* not the window we are looking for */
|
|
bitmap += winlen;
|
|
len -= winlen;
|
|
}
|
|
}
|
|
/* end of bitmap reached, no type found */
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
unitest_nsec_has_type_rdata(char* bitmap, size_t len, uint16_t type)
|
|
{
|
|
return nsec_has_type_rdata((uint8_t*)bitmap, len, type);
|
|
}
|
|
|
|
/**
|
|
* Check if type is present in the NSEC typemap
|
|
* @param nsec: the nsec RRset.
|
|
* If there are multiple RRs, then each must have the same typemap,
|
|
* since the typemap represents the types at this domain node.
|
|
* @param type: type to check for, host order.
|
|
* @return true if present
|
|
*/
|
|
static int
|
|
nsec_has_type(struct ub_packed_rrset_key* nsec, uint16_t type)
|
|
{
|
|
struct packed_rrset_data* d = (struct packed_rrset_data*)nsec->
|
|
entry.data;
|
|
size_t len;
|
|
if(!d || d->count == 0 || d->rr_len[0] < 2+1)
|
|
return 0;
|
|
len = dname_valid(d->rr_data[0]+2, d->rr_len[0]-2);
|
|
if(!len)
|
|
return 0;
|
|
nsec_has_type_rdata(d->rr_data[0]+2+len, d->rr_len[0]-2-len, type);
|
|
}
|
|
|
|
/**
|
|
* For an NSEC that matches the DS queried for, check absence of DS type.
|
|
*
|
|
* @param nsec: NSEC for proof, must be trusted.
|
|
* @param qinfo: what is queried for.
|
|
* @return if secure the nsec proves that no DS is present, or
|
|
* insecure if it proves it is not a delegation point.
|
|
* or bogus if something was wrong.
|
|
*/
|
|
enum sec_status
|
|
val_nsec_proves_no_ds(struct ub_packed_rrset_key* nsec,
|
|
struct query_info* qinfo)
|
|
{
|
|
log_assert(qinfo->qtype == LDNS_RR_TYPE_DS);
|
|
log_assert(ntohs(nsec->rk.type) == LDNS_RR_TYPE_NSEC);
|
|
/* this proof may also work if qname is a subdomain */
|
|
log_assert(query_dname_compare(nsec->rk.dname, qinfo->qname) == 0);
|
|
|
|
return sec_status_bogus;
|
|
}
|
|
|
|
enum sec_status
|
|
val_nsec_prove_nodata_ds(struct module_env* env, struct val_env* ve,
|
|
struct query_info* qinfo, struct reply_info* rep,
|
|
struct key_entry_key* kkey, uint32_t* proof_ttl)
|
|
{
|
|
struct ub_packed_rrset_key* nsec = reply_find_rrset_section_ns(
|
|
rep, qinfo->qname, qinfo->qname_len, LDNS_RR_TYPE_NSEC,
|
|
qinfo->qclass);
|
|
|
|
/* If we have a NSEC at the same name, it must prove one
|
|
* of two things
|
|
* --
|
|
* 1) this is a delegation point and there is no DS
|
|
* 2) this is not a delegation point */
|
|
if(nsec) {
|
|
enum sec_status sec = val_verify_rrset_entry(env, ve, nsec,
|
|
kkey);
|
|
if(sec != sec_status_secure) {
|
|
verbose(VERB_ALGO, "NSEC RRset for the "
|
|
"referral did not verify.");
|
|
return sec_status_bogus;
|
|
}
|
|
sec = val_nsec_proves_no_ds(nsec, qinfo);
|
|
if(sec == sec_status_bogus) {
|
|
/* something was wrong. */
|
|
return sec;
|
|
} else if(sec == sec_status_insecure) {
|
|
/* this wasn't a delegation point. */
|
|
return sec;
|
|
} else if(sec == sec_status_secure) {
|
|
/* this proved no DS. */
|
|
*proof_ttl = ub_packed_rrset_ttl(nsec);
|
|
return sec;
|
|
}
|
|
/* if unchecked, fall through to next proof */
|
|
}
|
|
|
|
/* Otherwise, there is no NSEC at qname. This could be an ENT.
|
|
* (ENT=empty non terminal). If not, this is broken. */
|
|
|
|
/* verify NSEC rrsets in auth section, call */
|
|
/* ValUtils.nsecProvesNodata, if so: NULL entry */
|
|
|
|
return sec_status_bogus;
|
|
}
|