1238106Sdes/* 2238106Sdes * validator/val_nsec.c - validator NSEC denial of existance functions. 3238106Sdes * 4238106Sdes * Copyright (c) 2007, NLnet Labs. All rights reserved. 5238106Sdes * 6238106Sdes * This software is open source. 7238106Sdes * 8238106Sdes * Redistribution and use in source and binary forms, with or without 9238106Sdes * modification, are permitted provided that the following conditions 10238106Sdes * are met: 11238106Sdes * 12238106Sdes * Redistributions of source code must retain the above copyright notice, 13238106Sdes * this list of conditions and the following disclaimer. 14238106Sdes * 15238106Sdes * Redistributions in binary form must reproduce the above copyright notice, 16238106Sdes * this list of conditions and the following disclaimer in the documentation 17238106Sdes * and/or other materials provided with the distribution. 18238106Sdes * 19238106Sdes * Neither the name of the NLNET LABS nor the names of its contributors may 20238106Sdes * be used to endorse or promote products derived from this software without 21238106Sdes * specific prior written permission. 22238106Sdes * 23238106Sdes * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24269257Sdes * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25269257Sdes * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26269257Sdes * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27269257Sdes * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28269257Sdes * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 29269257Sdes * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 30269257Sdes * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 31269257Sdes * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 32269257Sdes * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33269257Sdes * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34238106Sdes */ 35238106Sdes 36238106Sdes/** 37238106Sdes * \file 38238106Sdes * 39238106Sdes * This file contains helper functions for the validator module. 40238106Sdes * The functions help with NSEC checking, the different NSEC proofs 41238106Sdes * for denial of existance, and proofs for presence of types. 42238106Sdes */ 43238106Sdes#include "config.h" 44238106Sdes#include "validator/val_nsec.h" 45238106Sdes#include "validator/val_utils.h" 46238106Sdes#include "util/data/msgreply.h" 47238106Sdes#include "util/data/dname.h" 48238106Sdes#include "util/net_help.h" 49238106Sdes#include "util/module.h" 50238106Sdes#include "services/cache/rrset.h" 51238106Sdes 52238106Sdes/** get ttl of rrset */ 53238106Sdesstatic uint32_t 54238106Sdesrrset_get_ttl(struct ub_packed_rrset_key* k) 55238106Sdes{ 56238106Sdes struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data; 57238106Sdes return d->ttl; 58238106Sdes} 59238106Sdes 60238106Sdesint 61238106Sdesnsecbitmap_has_type_rdata(uint8_t* bitmap, size_t len, uint16_t type) 62238106Sdes{ 63238106Sdes /* Check type present in NSEC typemap with bitmap arg */ 64238106Sdes /* bitmasks for determining type-lowerbits presence */ 65238106Sdes uint8_t masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}; 66238106Sdes uint8_t type_window = type>>8; 67238106Sdes uint8_t type_low = type&0xff; 68238106Sdes uint8_t win, winlen; 69238106Sdes /* read each of the type bitmap windows and see if the searched 70238106Sdes * type is amongst it */ 71238106Sdes while(len > 0) { 72238106Sdes if(len < 3) /* bad window, at least window# winlen bitmap */ 73238106Sdes return 0; 74238106Sdes win = *bitmap++; 75238106Sdes winlen = *bitmap++; 76238106Sdes len -= 2; 77238106Sdes if(len < winlen || winlen < 1 || winlen > 32) 78238106Sdes return 0; /* bad window length */ 79238106Sdes if(win == type_window) { 80238106Sdes /* search window bitmap for the correct byte */ 81238106Sdes /* mybyte is 0 if we need the first byte */ 82238106Sdes size_t mybyte = type_low>>3; 83238106Sdes if(winlen <= mybyte) 84238106Sdes return 0; /* window too short */ 85238106Sdes return (int)(bitmap[mybyte] & masks[type_low&0x7]); 86238106Sdes } else { 87238106Sdes /* not the window we are looking for */ 88238106Sdes bitmap += winlen; 89238106Sdes len -= winlen; 90238106Sdes } 91238106Sdes } 92238106Sdes /* end of bitmap reached, no type found */ 93238106Sdes return 0; 94238106Sdes} 95238106Sdes 96238106Sdesint 97238106Sdesnsec_has_type(struct ub_packed_rrset_key* nsec, uint16_t type) 98238106Sdes{ 99238106Sdes struct packed_rrset_data* d = (struct packed_rrset_data*)nsec-> 100238106Sdes entry.data; 101238106Sdes size_t len; 102238106Sdes if(!d || d->count == 0 || d->rr_len[0] < 2+1) 103238106Sdes return 0; 104238106Sdes len = dname_valid(d->rr_data[0]+2, d->rr_len[0]-2); 105238106Sdes if(!len) 106238106Sdes return 0; 107238106Sdes return nsecbitmap_has_type_rdata(d->rr_data[0]+2+len, 108238106Sdes d->rr_len[0]-2-len, type); 109238106Sdes} 110238106Sdes 111238106Sdes/** 112238106Sdes * Get next owner name from nsec record 113238106Sdes * @param nsec: the nsec RRset. 114238106Sdes * If there are multiple RRs, then this will only return one of them. 115238106Sdes * @param nm: the next name is returned. 116238106Sdes * @param ln: length of nm is returned. 117238106Sdes * @return false on a bad NSEC RR (too short, malformed dname). 118238106Sdes */ 119238106Sdesstatic int 120238106Sdesnsec_get_next(struct ub_packed_rrset_key* nsec, uint8_t** nm, size_t* ln) 121238106Sdes{ 122238106Sdes struct packed_rrset_data* d = (struct packed_rrset_data*)nsec-> 123238106Sdes entry.data; 124238106Sdes if(!d || d->count == 0 || d->rr_len[0] < 2+1) { 125238106Sdes *nm = 0; 126238106Sdes *ln = 0; 127238106Sdes return 0; 128238106Sdes } 129238106Sdes *nm = d->rr_data[0]+2; 130238106Sdes *ln = dname_valid(*nm, d->rr_len[0]-2); 131238106Sdes if(!*ln) { 132238106Sdes *nm = 0; 133238106Sdes *ln = 0; 134238106Sdes return 0; 135238106Sdes } 136238106Sdes return 1; 137238106Sdes} 138238106Sdes 139238106Sdes/** 140238106Sdes * For an NSEC that matches the DS queried for, check absence of DS type. 141238106Sdes * 142238106Sdes * @param nsec: NSEC for proof, must be trusted. 143238106Sdes * @param qinfo: what is queried for. 144238106Sdes * @return if secure the nsec proves that no DS is present, or 145238106Sdes * insecure if it proves it is not a delegation point. 146238106Sdes * or bogus if something was wrong. 147238106Sdes */ 148238106Sdesstatic enum sec_status 149238106Sdesval_nsec_proves_no_ds(struct ub_packed_rrset_key* nsec, 150238106Sdes struct query_info* qinfo) 151238106Sdes{ 152238106Sdes log_assert(qinfo->qtype == LDNS_RR_TYPE_DS); 153238106Sdes log_assert(ntohs(nsec->rk.type) == LDNS_RR_TYPE_NSEC); 154238106Sdes 155238106Sdes if(nsec_has_type(nsec, LDNS_RR_TYPE_SOA) && qinfo->qname_len != 1) { 156238106Sdes /* SOA present means that this is the NSEC from the child, 157238106Sdes * not the parent (so it is the wrong one). */ 158238106Sdes return sec_status_bogus; 159238106Sdes } 160238106Sdes if(nsec_has_type(nsec, LDNS_RR_TYPE_DS)) { 161238106Sdes /* DS present means that there should have been a positive 162238106Sdes * response to the DS query, so there is something wrong. */ 163238106Sdes return sec_status_bogus; 164238106Sdes } 165238106Sdes 166238106Sdes if(!nsec_has_type(nsec, LDNS_RR_TYPE_NS)) { 167238106Sdes /* If there is no NS at this point at all, then this 168238106Sdes * doesn't prove anything one way or the other. */ 169238106Sdes return sec_status_insecure; 170238106Sdes } 171238106Sdes /* Otherwise, this proves no DS. */ 172238106Sdes return sec_status_secure; 173238106Sdes} 174238106Sdes 175238106Sdes/** check security status from cache or verify rrset, returns true if secure */ 176238106Sdesstatic int 177238106Sdesnsec_verify_rrset(struct module_env* env, struct val_env* ve, 178238106Sdes struct ub_packed_rrset_key* nsec, struct key_entry_key* kkey, 179238106Sdes char** reason) 180238106Sdes{ 181238106Sdes struct packed_rrset_data* d = (struct packed_rrset_data*) 182238106Sdes nsec->entry.data; 183238106Sdes if(d->security == sec_status_secure) 184238106Sdes return 1; 185238106Sdes rrset_check_sec_status(env->rrset_cache, nsec, *env->now); 186238106Sdes if(d->security == sec_status_secure) 187238106Sdes return 1; 188238106Sdes d->security = val_verify_rrset_entry(env, ve, nsec, kkey, reason); 189238106Sdes if(d->security == sec_status_secure) { 190238106Sdes rrset_update_sec_status(env->rrset_cache, nsec, *env->now); 191238106Sdes return 1; 192238106Sdes } 193238106Sdes return 0; 194238106Sdes} 195238106Sdes 196238106Sdesenum sec_status 197238106Sdesval_nsec_prove_nodata_dsreply(struct module_env* env, struct val_env* ve, 198238106Sdes struct query_info* qinfo, struct reply_info* rep, 199269257Sdes struct key_entry_key* kkey, time_t* proof_ttl, char** reason) 200238106Sdes{ 201238106Sdes struct ub_packed_rrset_key* nsec = reply_find_rrset_section_ns( 202238106Sdes rep, qinfo->qname, qinfo->qname_len, LDNS_RR_TYPE_NSEC, 203238106Sdes qinfo->qclass); 204238106Sdes enum sec_status sec; 205238106Sdes size_t i; 206238106Sdes uint8_t* wc = NULL, *ce = NULL; 207238106Sdes int valid_nsec = 0; 208238106Sdes struct ub_packed_rrset_key* wc_nsec = NULL; 209238106Sdes 210238106Sdes /* If we have a NSEC at the same name, it must prove one 211238106Sdes * of two things 212238106Sdes * -- 213238106Sdes * 1) this is a delegation point and there is no DS 214238106Sdes * 2) this is not a delegation point */ 215238106Sdes if(nsec) { 216238106Sdes if(!nsec_verify_rrset(env, ve, nsec, kkey, reason)) { 217238106Sdes verbose(VERB_ALGO, "NSEC RRset for the " 218238106Sdes "referral did not verify."); 219238106Sdes return sec_status_bogus; 220238106Sdes } 221238106Sdes sec = val_nsec_proves_no_ds(nsec, qinfo); 222238106Sdes if(sec == sec_status_bogus) { 223238106Sdes /* something was wrong. */ 224238106Sdes *reason = "NSEC does not prove absence of DS"; 225238106Sdes return sec; 226238106Sdes } else if(sec == sec_status_insecure) { 227238106Sdes /* this wasn't a delegation point. */ 228238106Sdes return sec; 229238106Sdes } else if(sec == sec_status_secure) { 230238106Sdes /* this proved no DS. */ 231238106Sdes *proof_ttl = ub_packed_rrset_ttl(nsec); 232238106Sdes return sec; 233238106Sdes } 234238106Sdes /* if unchecked, fall through to next proof */ 235238106Sdes } 236238106Sdes 237238106Sdes /* Otherwise, there is no NSEC at qname. This could be an ENT. 238238106Sdes * (ENT=empty non terminal). If not, this is broken. */ 239238106Sdes 240238106Sdes /* verify NSEC rrsets in auth section */ 241238106Sdes for(i=rep->an_numrrsets; i < rep->an_numrrsets+rep->ns_numrrsets; 242238106Sdes i++) { 243238106Sdes if(rep->rrsets[i]->rk.type != htons(LDNS_RR_TYPE_NSEC)) 244238106Sdes continue; 245238106Sdes if(!nsec_verify_rrset(env, ve, rep->rrsets[i], kkey, reason)) { 246238106Sdes verbose(VERB_ALGO, "NSEC for empty non-terminal " 247238106Sdes "did not verify."); 248238106Sdes return sec_status_bogus; 249238106Sdes } 250238106Sdes if(nsec_proves_nodata(rep->rrsets[i], qinfo, &wc)) { 251238106Sdes verbose(VERB_ALGO, "NSEC for empty non-terminal " 252238106Sdes "proved no DS."); 253238106Sdes *proof_ttl = rrset_get_ttl(rep->rrsets[i]); 254238106Sdes if(wc && dname_is_wild(rep->rrsets[i]->rk.dname)) 255238106Sdes wc_nsec = rep->rrsets[i]; 256238106Sdes valid_nsec = 1; 257238106Sdes } 258238106Sdes if(val_nsec_proves_name_error(rep->rrsets[i], qinfo->qname)) { 259238106Sdes ce = nsec_closest_encloser(qinfo->qname, 260238106Sdes rep->rrsets[i]); 261238106Sdes } 262238106Sdes } 263238106Sdes if(wc && !ce) 264238106Sdes valid_nsec = 0; 265238106Sdes else if(wc && ce) { 266238106Sdes /* ce and wc must match */ 267238106Sdes if(query_dname_compare(wc, ce) != 0) 268238106Sdes valid_nsec = 0; 269238106Sdes else if(!wc_nsec) 270238106Sdes valid_nsec = 0; 271238106Sdes } 272238106Sdes if(valid_nsec) { 273238106Sdes if(wc) { 274238106Sdes /* check if this is a delegation */ 275238106Sdes *reason = "NSEC for wildcard does not prove absence of DS"; 276238106Sdes return val_nsec_proves_no_ds(wc_nsec, qinfo); 277238106Sdes } 278238106Sdes /* valid nsec proves empty nonterminal */ 279238106Sdes return sec_status_insecure; 280238106Sdes } 281238106Sdes 282238106Sdes /* NSEC proof did not conlusively point to DS or no DS */ 283238106Sdes return sec_status_unchecked; 284238106Sdes} 285238106Sdes 286238106Sdesint nsec_proves_nodata(struct ub_packed_rrset_key* nsec, 287238106Sdes struct query_info* qinfo, uint8_t** wc) 288238106Sdes{ 289238106Sdes log_assert(wc); 290238106Sdes if(query_dname_compare(nsec->rk.dname, qinfo->qname) != 0) { 291238106Sdes uint8_t* nm; 292238106Sdes size_t ln; 293238106Sdes 294238106Sdes /* empty-non-terminal checking. 295238106Sdes * Done before wildcard, because this is an exact match, 296238106Sdes * and would prevent a wildcard from matching. */ 297238106Sdes 298238106Sdes /* If the nsec is proving that qname is an ENT, the nsec owner 299238106Sdes * will be less than qname, and the next name will be a child 300238106Sdes * domain of the qname. */ 301238106Sdes if(!nsec_get_next(nsec, &nm, &ln)) 302238106Sdes return 0; /* bad nsec */ 303238106Sdes if(dname_strict_subdomain_c(nm, qinfo->qname) && 304238106Sdes dname_canonical_compare(nsec->rk.dname, 305238106Sdes qinfo->qname) < 0) { 306238106Sdes return 1; /* proves ENT */ 307238106Sdes } 308238106Sdes 309238106Sdes /* wildcard checking. */ 310238106Sdes 311238106Sdes /* If this is a wildcard NSEC, make sure that a) it was 312238106Sdes * possible to have generated qname from the wildcard and 313238106Sdes * b) the type map does not contain qtype. Note that this 314238106Sdes * does NOT prove that this wildcard was the applicable 315238106Sdes * wildcard. */ 316238106Sdes if(dname_is_wild(nsec->rk.dname)) { 317238106Sdes /* the purported closest encloser. */ 318238106Sdes uint8_t* ce = nsec->rk.dname; 319238106Sdes size_t ce_len = nsec->rk.dname_len; 320238106Sdes dname_remove_label(&ce, &ce_len); 321238106Sdes 322238106Sdes /* The qname must be a strict subdomain of the 323238106Sdes * closest encloser, for the wildcard to apply 324238106Sdes */ 325238106Sdes if(dname_strict_subdomain_c(qinfo->qname, ce)) { 326238106Sdes /* here we have a matching NSEC for the qname, 327238106Sdes * perform matching NSEC checks */ 328238106Sdes if(nsec_has_type(nsec, LDNS_RR_TYPE_CNAME)) { 329238106Sdes /* should have gotten the wildcard CNAME */ 330238106Sdes return 0; 331238106Sdes } 332238106Sdes if(nsec_has_type(nsec, LDNS_RR_TYPE_NS) && 333238106Sdes !nsec_has_type(nsec, LDNS_RR_TYPE_SOA)) { 334238106Sdes /* wrong parentside (wildcard) NSEC used */ 335238106Sdes return 0; 336238106Sdes } 337238106Sdes if(nsec_has_type(nsec, qinfo->qtype)) { 338238106Sdes return 0; 339238106Sdes } 340238106Sdes *wc = ce; 341238106Sdes return 1; 342238106Sdes } 343238106Sdes } 344238106Sdes 345238106Sdes /* Otherwise, this NSEC does not prove ENT and is not a 346238106Sdes * wildcard, so it does not prove NODATA. */ 347238106Sdes return 0; 348238106Sdes } 349238106Sdes 350238106Sdes /* If the qtype exists, then we should have gotten it. */ 351238106Sdes if(nsec_has_type(nsec, qinfo->qtype)) { 352238106Sdes return 0; 353238106Sdes } 354238106Sdes 355238106Sdes /* if the name is a CNAME node, then we should have gotten the CNAME*/ 356238106Sdes if(nsec_has_type(nsec, LDNS_RR_TYPE_CNAME)) { 357238106Sdes return 0; 358238106Sdes } 359238106Sdes 360238106Sdes /* If an NS set exists at this name, and NOT a SOA (so this is a 361238106Sdes * zone cut, not a zone apex), then we should have gotten a 362238106Sdes * referral (or we just got the wrong NSEC). 363238106Sdes * The reverse of this check is used when qtype is DS, since that 364238106Sdes * must use the NSEC from above the zone cut. */ 365238106Sdes if(qinfo->qtype != LDNS_RR_TYPE_DS && 366238106Sdes nsec_has_type(nsec, LDNS_RR_TYPE_NS) && 367238106Sdes !nsec_has_type(nsec, LDNS_RR_TYPE_SOA)) { 368238106Sdes return 0; 369238106Sdes } else if(qinfo->qtype == LDNS_RR_TYPE_DS && 370238106Sdes nsec_has_type(nsec, LDNS_RR_TYPE_SOA) && 371238106Sdes !dname_is_root(qinfo->qname)) { 372238106Sdes return 0; 373238106Sdes } 374238106Sdes 375238106Sdes return 1; 376238106Sdes} 377238106Sdes 378238106Sdesint 379238106Sdesval_nsec_proves_name_error(struct ub_packed_rrset_key* nsec, uint8_t* qname) 380238106Sdes{ 381238106Sdes uint8_t* owner = nsec->rk.dname; 382238106Sdes uint8_t* next; 383238106Sdes size_t nlen; 384238106Sdes if(!nsec_get_next(nsec, &next, &nlen)) 385238106Sdes return 0; 386238106Sdes 387238106Sdes /* If NSEC owner == qname, then this NSEC proves that qname exists. */ 388238106Sdes if(query_dname_compare(qname, owner) == 0) { 389238106Sdes return 0; 390238106Sdes } 391238106Sdes 392238106Sdes /* If NSEC is a parent of qname, we need to check the type map 393238106Sdes * If the parent name has a DNAME or is a delegation point, then 394238106Sdes * this NSEC is being misused. */ 395238106Sdes if(dname_subdomain_c(qname, owner) && 396238106Sdes (nsec_has_type(nsec, LDNS_RR_TYPE_DNAME) || 397238106Sdes (nsec_has_type(nsec, LDNS_RR_TYPE_NS) 398238106Sdes && !nsec_has_type(nsec, LDNS_RR_TYPE_SOA)) 399238106Sdes )) { 400238106Sdes return 0; 401238106Sdes } 402238106Sdes 403238106Sdes if(query_dname_compare(owner, next) == 0) { 404238106Sdes /* this nsec is the only nsec */ 405238106Sdes /* zone.name NSEC zone.name, disproves everything else */ 406238106Sdes /* but only for subdomains of that zone */ 407238106Sdes if(dname_strict_subdomain_c(qname, next)) 408238106Sdes return 1; 409238106Sdes } 410238106Sdes else if(dname_canonical_compare(owner, next) > 0) { 411238106Sdes /* this is the last nsec, ....(bigger) NSEC zonename(smaller) */ 412238106Sdes /* the names after the last (owner) name do not exist 413238106Sdes * there are no names before the zone name in the zone 414238106Sdes * but the qname must be a subdomain of the zone name(next). */ 415238106Sdes if(dname_canonical_compare(owner, qname) < 0 && 416238106Sdes dname_strict_subdomain_c(qname, next)) 417238106Sdes return 1; 418238106Sdes } else { 419238106Sdes /* regular NSEC, (smaller) NSEC (larger) */ 420238106Sdes if(dname_canonical_compare(owner, qname) < 0 && 421238106Sdes dname_canonical_compare(qname, next) < 0) { 422238106Sdes return 1; 423238106Sdes } 424238106Sdes } 425238106Sdes return 0; 426238106Sdes} 427238106Sdes 428238106Sdesint val_nsec_proves_insecuredelegation(struct ub_packed_rrset_key* nsec, 429238106Sdes struct query_info* qinfo) 430238106Sdes{ 431238106Sdes if(nsec_has_type(nsec, LDNS_RR_TYPE_NS) && 432238106Sdes !nsec_has_type(nsec, LDNS_RR_TYPE_DS) && 433238106Sdes !nsec_has_type(nsec, LDNS_RR_TYPE_SOA)) { 434238106Sdes /* see if nsec signals an insecure delegation */ 435238106Sdes if(qinfo->qtype == LDNS_RR_TYPE_DS) { 436238106Sdes /* if type is DS and qname is equal to nsec, then it 437238106Sdes * is an exact match nsec, result not insecure */ 438238106Sdes if(dname_strict_subdomain_c(qinfo->qname, 439238106Sdes nsec->rk.dname)) 440238106Sdes return 1; 441238106Sdes } else { 442238106Sdes if(dname_subdomain_c(qinfo->qname, nsec->rk.dname)) 443238106Sdes return 1; 444238106Sdes } 445238106Sdes } 446238106Sdes return 0; 447238106Sdes} 448238106Sdes 449238106Sdesuint8_t* 450238106Sdesnsec_closest_encloser(uint8_t* qname, struct ub_packed_rrset_key* nsec) 451238106Sdes{ 452238106Sdes uint8_t* next; 453238106Sdes size_t nlen; 454238106Sdes uint8_t* common1, *common2; 455238106Sdes if(!nsec_get_next(nsec, &next, &nlen)) 456238106Sdes return NULL; 457238106Sdes /* longest common with owner or next name */ 458238106Sdes common1 = dname_get_shared_topdomain(nsec->rk.dname, qname); 459238106Sdes common2 = dname_get_shared_topdomain(next, qname); 460238106Sdes if(dname_count_labels(common1) > dname_count_labels(common2)) 461238106Sdes return common1; 462238106Sdes return common2; 463238106Sdes} 464238106Sdes 465238106Sdesint val_nsec_proves_positive_wildcard(struct ub_packed_rrset_key* nsec, 466238106Sdes struct query_info* qinf, uint8_t* wc) 467238106Sdes{ 468238106Sdes uint8_t* ce; 469238106Sdes /* 1) prove that qname doesn't exist and 470238106Sdes * 2) that the correct wildcard was used 471238106Sdes * nsec has been verified already. */ 472238106Sdes if(!val_nsec_proves_name_error(nsec, qinf->qname)) 473238106Sdes return 0; 474238106Sdes /* check wildcard name */ 475238106Sdes ce = nsec_closest_encloser(qinf->qname, nsec); 476238106Sdes if(!ce) 477238106Sdes return 0; 478238106Sdes if(query_dname_compare(wc, ce) != 0) { 479238106Sdes return 0; 480238106Sdes } 481238106Sdes return 1; 482238106Sdes} 483238106Sdes 484238106Sdesint 485238106Sdesval_nsec_proves_no_wc(struct ub_packed_rrset_key* nsec, uint8_t* qname, 486238106Sdes size_t qnamelen) 487238106Sdes{ 488238106Sdes /* Determine if a NSEC record proves the non-existence of a 489238106Sdes * wildcard that could have produced qname. */ 490238106Sdes int labs; 491238106Sdes int i; 492238106Sdes uint8_t* ce = nsec_closest_encloser(qname, nsec); 493238106Sdes uint8_t* strip; 494238106Sdes size_t striplen; 495238106Sdes uint8_t buf[LDNS_MAX_DOMAINLEN+3]; 496238106Sdes if(!ce) 497238106Sdes return 0; 498238106Sdes /* we can subtract the closest encloser count - since that is the 499238106Sdes * largest shared topdomain with owner and next NSEC name, 500238106Sdes * because the NSEC is no proof for names shorter than the owner 501238106Sdes * and next names. */ 502238106Sdes labs = dname_count_labels(qname) - dname_count_labels(ce); 503238106Sdes 504238106Sdes for(i=labs; i>0; i--) { 505238106Sdes /* i is number of labels to strip off qname, prepend * wild */ 506238106Sdes strip = qname; 507238106Sdes striplen = qnamelen; 508238106Sdes dname_remove_labels(&strip, &striplen, i); 509238106Sdes if(striplen > LDNS_MAX_DOMAINLEN-2) 510238106Sdes continue; /* too long to prepend wildcard */ 511238106Sdes buf[0] = 1; 512238106Sdes buf[1] = (uint8_t)'*'; 513238106Sdes memmove(buf+2, strip, striplen); 514238106Sdes if(val_nsec_proves_name_error(nsec, buf)) { 515238106Sdes return 1; 516238106Sdes } 517238106Sdes } 518238106Sdes return 0; 519238106Sdes} 520238106Sdes 521238106Sdes/** 522238106Sdes * Find shared topdomain that exists 523238106Sdes */ 524238106Sdesstatic void 525238106Sdesdlv_topdomain(struct ub_packed_rrset_key* nsec, uint8_t* qname, 526238106Sdes uint8_t** nm, size_t* nm_len) 527238106Sdes{ 528238106Sdes /* make sure reply is part of nm */ 529238106Sdes /* take shared topdomain with left of NSEC. */ 530238106Sdes 531238106Sdes /* because, if empty nonterminal, then right is subdomain of qname. 532238106Sdes * and any shared topdomain would be empty nonterminals. 533238106Sdes * 534238106Sdes * If nxdomain, then the right is bigger, and could have an 535238106Sdes * interesting shared topdomain, but if it does have one, it is 536238106Sdes * an empty nonterminal. An empty nonterminal shared with the left 537238106Sdes * one. */ 538238106Sdes int n; 539238106Sdes uint8_t* common = dname_get_shared_topdomain(qname, nsec->rk.dname); 540238106Sdes n = dname_count_labels(*nm) - dname_count_labels(common); 541238106Sdes dname_remove_labels(nm, nm_len, n); 542238106Sdes} 543238106Sdes 544238106Sdesint val_nsec_check_dlv(struct query_info* qinfo, 545238106Sdes struct reply_info* rep, uint8_t** nm, size_t* nm_len) 546238106Sdes{ 547238106Sdes uint8_t* next; 548238106Sdes size_t i, nlen; 549238106Sdes int c; 550238106Sdes /* we should now have a NOERROR/NODATA or NXDOMAIN message */ 551238106Sdes if(rep->an_numrrsets != 0) { 552238106Sdes return 0; 553238106Sdes } 554238106Sdes /* is this NOERROR ? */ 555238106Sdes if(FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NOERROR) { 556238106Sdes /* it can be a plain NSEC match - go up one more level. */ 557238106Sdes /* or its an empty nonterminal - go up to nonempty level */ 558238106Sdes for(i=0; i<rep->ns_numrrsets; i++) { 559238106Sdes if(htons(rep->rrsets[i]->rk.type)!=LDNS_RR_TYPE_NSEC || 560238106Sdes !nsec_get_next(rep->rrsets[i], &next, &nlen)) 561238106Sdes continue; 562238106Sdes c = dname_canonical_compare( 563238106Sdes rep->rrsets[i]->rk.dname, qinfo->qname); 564238106Sdes if(c == 0) { 565238106Sdes /* plain match */ 566238106Sdes if(nsec_has_type(rep->rrsets[i], 567238106Sdes LDNS_RR_TYPE_DLV)) 568238106Sdes return 0; 569238106Sdes dname_remove_label(nm, nm_len); 570238106Sdes return 1; 571238106Sdes } else if(c < 0 && 572238106Sdes dname_strict_subdomain_c(next, qinfo->qname)) { 573238106Sdes /* ENT */ 574238106Sdes dlv_topdomain(rep->rrsets[i], qinfo->qname, 575238106Sdes nm, nm_len); 576238106Sdes return 1; 577238106Sdes } 578238106Sdes } 579238106Sdes return 0; 580238106Sdes } 581238106Sdes 582238106Sdes /* is this NXDOMAIN ? */ 583238106Sdes if(FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NXDOMAIN) { 584238106Sdes /* find the qname denial NSEC record. It can tell us 585238106Sdes * a closest encloser name; or that we not need bother */ 586238106Sdes for(i=0; i<rep->ns_numrrsets; i++) { 587238106Sdes if(htons(rep->rrsets[i]->rk.type) != LDNS_RR_TYPE_NSEC) 588238106Sdes continue; 589238106Sdes if(val_nsec_proves_name_error(rep->rrsets[i], 590238106Sdes qinfo->qname)) { 591238106Sdes log_nametypeclass(VERB_ALGO, "topdomain on", 592238106Sdes rep->rrsets[i]->rk.dname, 593238106Sdes ntohs(rep->rrsets[i]->rk.type), 0); 594238106Sdes dlv_topdomain(rep->rrsets[i], qinfo->qname, 595238106Sdes nm, nm_len); 596238106Sdes return 1; 597238106Sdes } 598238106Sdes } 599238106Sdes return 0; 600238106Sdes } 601238106Sdes return 0; 602238106Sdes} 603