1933707f3Ssthen /* 24bfc71b0Ssthen * validator/val_nsec.c - validator NSEC denial of existence functions. 3933707f3Ssthen * 4933707f3Ssthen * Copyright (c) 2007, NLnet Labs. All rights reserved. 5933707f3Ssthen * 6933707f3Ssthen * This software is open source. 7933707f3Ssthen * 8933707f3Ssthen * Redistribution and use in source and binary forms, with or without 9933707f3Ssthen * modification, are permitted provided that the following conditions 10933707f3Ssthen * are met: 11933707f3Ssthen * 12933707f3Ssthen * Redistributions of source code must retain the above copyright notice, 13933707f3Ssthen * this list of conditions and the following disclaimer. 14933707f3Ssthen * 15933707f3Ssthen * Redistributions in binary form must reproduce the above copyright notice, 16933707f3Ssthen * this list of conditions and the following disclaimer in the documentation 17933707f3Ssthen * and/or other materials provided with the distribution. 18933707f3Ssthen * 19933707f3Ssthen * Neither the name of the NLNET LABS nor the names of its contributors may 20933707f3Ssthen * be used to endorse or promote products derived from this software without 21933707f3Ssthen * specific prior written permission. 22933707f3Ssthen * 23933707f3Ssthen * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 245d76a658Ssthen * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 255d76a658Ssthen * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 265d76a658Ssthen * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 275d76a658Ssthen * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 285d76a658Ssthen * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 295d76a658Ssthen * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 305d76a658Ssthen * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 315d76a658Ssthen * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 325d76a658Ssthen * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 335d76a658Ssthen * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34933707f3Ssthen */ 35933707f3Ssthen 36933707f3Ssthen /** 37933707f3Ssthen * \file 38933707f3Ssthen * 39933707f3Ssthen * This file contains helper functions for the validator module. 40933707f3Ssthen * The functions help with NSEC checking, the different NSEC proofs 414bfc71b0Ssthen * for denial of existence, and proofs for presence of types. 42933707f3Ssthen */ 43933707f3Ssthen #include "config.h" 44933707f3Ssthen #include "validator/val_nsec.h" 45933707f3Ssthen #include "validator/val_utils.h" 46933707f3Ssthen #include "util/data/msgreply.h" 47933707f3Ssthen #include "util/data/dname.h" 48933707f3Ssthen #include "util/net_help.h" 49933707f3Ssthen #include "util/module.h" 50933707f3Ssthen #include "services/cache/rrset.h" 51933707f3Ssthen 52933707f3Ssthen /** get ttl of rrset */ 53933707f3Ssthen static uint32_t 54933707f3Ssthen rrset_get_ttl(struct ub_packed_rrset_key* k) 55933707f3Ssthen { 56933707f3Ssthen struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data; 57933707f3Ssthen return d->ttl; 58933707f3Ssthen } 59933707f3Ssthen 60933707f3Ssthen int 61933707f3Ssthen nsecbitmap_has_type_rdata(uint8_t* bitmap, size_t len, uint16_t type) 62933707f3Ssthen { 63933707f3Ssthen /* Check type present in NSEC typemap with bitmap arg */ 64933707f3Ssthen /* bitmasks for determining type-lowerbits presence */ 65933707f3Ssthen uint8_t masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}; 66933707f3Ssthen uint8_t type_window = type>>8; 67933707f3Ssthen uint8_t type_low = type&0xff; 68933707f3Ssthen uint8_t win, winlen; 69933707f3Ssthen /* read each of the type bitmap windows and see if the searched 70933707f3Ssthen * type is amongst it */ 71933707f3Ssthen while(len > 0) { 72933707f3Ssthen if(len < 3) /* bad window, at least window# winlen bitmap */ 73933707f3Ssthen return 0; 74933707f3Ssthen win = *bitmap++; 75933707f3Ssthen winlen = *bitmap++; 76933707f3Ssthen len -= 2; 77933707f3Ssthen if(len < winlen || winlen < 1 || winlen > 32) 78933707f3Ssthen return 0; /* bad window length */ 79933707f3Ssthen if(win == type_window) { 80933707f3Ssthen /* search window bitmap for the correct byte */ 81933707f3Ssthen /* mybyte is 0 if we need the first byte */ 82933707f3Ssthen size_t mybyte = type_low>>3; 83933707f3Ssthen if(winlen <= mybyte) 84933707f3Ssthen return 0; /* window too short */ 85933707f3Ssthen return (int)(bitmap[mybyte] & masks[type_low&0x7]); 86933707f3Ssthen } else { 87933707f3Ssthen /* not the window we are looking for */ 88933707f3Ssthen bitmap += winlen; 89933707f3Ssthen len -= winlen; 90933707f3Ssthen } 91933707f3Ssthen } 92933707f3Ssthen /* end of bitmap reached, no type found */ 93933707f3Ssthen return 0; 94933707f3Ssthen } 95933707f3Ssthen 96933707f3Ssthen int 97933707f3Ssthen nsec_has_type(struct ub_packed_rrset_key* nsec, uint16_t type) 98933707f3Ssthen { 99933707f3Ssthen struct packed_rrset_data* d = (struct packed_rrset_data*)nsec-> 100933707f3Ssthen entry.data; 101933707f3Ssthen size_t len; 102933707f3Ssthen if(!d || d->count == 0 || d->rr_len[0] < 2+1) 103933707f3Ssthen return 0; 104933707f3Ssthen len = dname_valid(d->rr_data[0]+2, d->rr_len[0]-2); 105933707f3Ssthen if(!len) 106933707f3Ssthen return 0; 107933707f3Ssthen return nsecbitmap_has_type_rdata(d->rr_data[0]+2+len, 108933707f3Ssthen d->rr_len[0]-2-len, type); 109933707f3Ssthen } 110933707f3Ssthen 111933707f3Ssthen /** 112933707f3Ssthen * Get next owner name from nsec record 113933707f3Ssthen * @param nsec: the nsec RRset. 114933707f3Ssthen * If there are multiple RRs, then this will only return one of them. 115933707f3Ssthen * @param nm: the next name is returned. 116933707f3Ssthen * @param ln: length of nm is returned. 117933707f3Ssthen * @return false on a bad NSEC RR (too short, malformed dname). 118933707f3Ssthen */ 119933707f3Ssthen static int 120933707f3Ssthen nsec_get_next(struct ub_packed_rrset_key* nsec, uint8_t** nm, size_t* ln) 121933707f3Ssthen { 122933707f3Ssthen struct packed_rrset_data* d = (struct packed_rrset_data*)nsec-> 123933707f3Ssthen entry.data; 124933707f3Ssthen if(!d || d->count == 0 || d->rr_len[0] < 2+1) { 125933707f3Ssthen *nm = 0; 126933707f3Ssthen *ln = 0; 127933707f3Ssthen return 0; 128933707f3Ssthen } 129933707f3Ssthen *nm = d->rr_data[0]+2; 130933707f3Ssthen *ln = dname_valid(*nm, d->rr_len[0]-2); 131933707f3Ssthen if(!*ln) { 132933707f3Ssthen *nm = 0; 133933707f3Ssthen *ln = 0; 134933707f3Ssthen return 0; 135933707f3Ssthen } 136933707f3Ssthen return 1; 137933707f3Ssthen } 138933707f3Ssthen 139933707f3Ssthen /** 140933707f3Ssthen * For an NSEC that matches the DS queried for, check absence of DS type. 141933707f3Ssthen * 142933707f3Ssthen * @param nsec: NSEC for proof, must be trusted. 143933707f3Ssthen * @param qinfo: what is queried for. 144933707f3Ssthen * @return if secure the nsec proves that no DS is present, or 145933707f3Ssthen * insecure if it proves it is not a delegation point. 146933707f3Ssthen * or bogus if something was wrong. 147933707f3Ssthen */ 148933707f3Ssthen static enum sec_status 149933707f3Ssthen val_nsec_proves_no_ds(struct ub_packed_rrset_key* nsec, 150933707f3Ssthen struct query_info* qinfo) 151933707f3Ssthen { 152933707f3Ssthen log_assert(qinfo->qtype == LDNS_RR_TYPE_DS); 153933707f3Ssthen log_assert(ntohs(nsec->rk.type) == LDNS_RR_TYPE_NSEC); 154933707f3Ssthen 155933707f3Ssthen if(nsec_has_type(nsec, LDNS_RR_TYPE_SOA) && qinfo->qname_len != 1) { 156933707f3Ssthen /* SOA present means that this is the NSEC from the child, 157933707f3Ssthen * not the parent (so it is the wrong one). */ 158933707f3Ssthen return sec_status_bogus; 159933707f3Ssthen } 160933707f3Ssthen if(nsec_has_type(nsec, LDNS_RR_TYPE_DS)) { 161933707f3Ssthen /* DS present means that there should have been a positive 162933707f3Ssthen * response to the DS query, so there is something wrong. */ 163933707f3Ssthen return sec_status_bogus; 164933707f3Ssthen } 165933707f3Ssthen 166933707f3Ssthen if(!nsec_has_type(nsec, LDNS_RR_TYPE_NS)) { 167933707f3Ssthen /* If there is no NS at this point at all, then this 168933707f3Ssthen * doesn't prove anything one way or the other. */ 169933707f3Ssthen return sec_status_insecure; 170933707f3Ssthen } 171933707f3Ssthen /* Otherwise, this proves no DS. */ 172933707f3Ssthen return sec_status_secure; 173933707f3Ssthen } 174933707f3Ssthen 175933707f3Ssthen /** check security status from cache or verify rrset, returns true if secure */ 176933707f3Ssthen static int 177933707f3Ssthen nsec_verify_rrset(struct module_env* env, struct val_env* ve, 178933707f3Ssthen struct ub_packed_rrset_key* nsec, struct key_entry_key* kkey, 1798b7325afSsthen char** reason, sldns_ede_code* reason_bogus, 180*98bc733bSsthen struct module_qstate* qstate, char* reasonbuf, size_t reasonlen) 181933707f3Ssthen { 182933707f3Ssthen struct packed_rrset_data* d = (struct packed_rrset_data*) 183933707f3Ssthen nsec->entry.data; 184817bdb8fSflorian int verified = 0; 185191f22c6Ssthen if(!d) return 0; 186933707f3Ssthen if(d->security == sec_status_secure) 187933707f3Ssthen return 1; 188933707f3Ssthen rrset_check_sec_status(env->rrset_cache, nsec, *env->now); 189933707f3Ssthen if(d->security == sec_status_secure) 190933707f3Ssthen return 1; 191bdfc4d55Sflorian d->security = val_verify_rrset_entry(env, ve, nsec, kkey, reason, 192*98bc733bSsthen reason_bogus, LDNS_SECTION_AUTHORITY, qstate, &verified, 193*98bc733bSsthen reasonbuf, reasonlen); 194933707f3Ssthen if(d->security == sec_status_secure) { 195933707f3Ssthen rrset_update_sec_status(env->rrset_cache, nsec, *env->now); 196933707f3Ssthen return 1; 197933707f3Ssthen } 198933707f3Ssthen return 0; 199933707f3Ssthen } 200933707f3Ssthen 201933707f3Ssthen enum sec_status 202933707f3Ssthen val_nsec_prove_nodata_dsreply(struct module_env* env, struct val_env* ve, 203933707f3Ssthen struct query_info* qinfo, struct reply_info* rep, 204bdfc4d55Sflorian struct key_entry_key* kkey, time_t* proof_ttl, char** reason, 205*98bc733bSsthen sldns_ede_code* reason_bogus, struct module_qstate* qstate, 206*98bc733bSsthen char* reasonbuf, size_t reasonlen) 207933707f3Ssthen { 208933707f3Ssthen struct ub_packed_rrset_key* nsec = reply_find_rrset_section_ns( 209933707f3Ssthen rep, qinfo->qname, qinfo->qname_len, LDNS_RR_TYPE_NSEC, 210933707f3Ssthen qinfo->qclass); 211933707f3Ssthen enum sec_status sec; 212933707f3Ssthen size_t i; 213933707f3Ssthen uint8_t* wc = NULL, *ce = NULL; 214933707f3Ssthen int valid_nsec = 0; 215933707f3Ssthen struct ub_packed_rrset_key* wc_nsec = NULL; 216933707f3Ssthen 217933707f3Ssthen /* If we have a NSEC at the same name, it must prove one 218933707f3Ssthen * of two things 219933707f3Ssthen * -- 220933707f3Ssthen * 1) this is a delegation point and there is no DS 221933707f3Ssthen * 2) this is not a delegation point */ 222933707f3Ssthen if(nsec) { 2238b7325afSsthen if(!nsec_verify_rrset(env, ve, nsec, kkey, reason, 224*98bc733bSsthen reason_bogus, qstate, reasonbuf, reasonlen)) { 225933707f3Ssthen verbose(VERB_ALGO, "NSEC RRset for the " 226933707f3Ssthen "referral did not verify."); 227933707f3Ssthen return sec_status_bogus; 228933707f3Ssthen } 229933707f3Ssthen sec = val_nsec_proves_no_ds(nsec, qinfo); 230933707f3Ssthen if(sec == sec_status_bogus) { 231933707f3Ssthen /* something was wrong. */ 232933707f3Ssthen *reason = "NSEC does not prove absence of DS"; 2338b7325afSsthen *reason_bogus = LDNS_EDE_DNSSEC_BOGUS; 234933707f3Ssthen return sec; 235933707f3Ssthen } else if(sec == sec_status_insecure) { 236933707f3Ssthen /* this wasn't a delegation point. */ 237933707f3Ssthen return sec; 238933707f3Ssthen } else if(sec == sec_status_secure) { 239933707f3Ssthen /* this proved no DS. */ 240933707f3Ssthen *proof_ttl = ub_packed_rrset_ttl(nsec); 241933707f3Ssthen return sec; 242933707f3Ssthen } 243933707f3Ssthen /* if unchecked, fall through to next proof */ 244933707f3Ssthen } 245933707f3Ssthen 246933707f3Ssthen /* Otherwise, there is no NSEC at qname. This could be an ENT. 247933707f3Ssthen * (ENT=empty non terminal). If not, this is broken. */ 248933707f3Ssthen 249933707f3Ssthen /* verify NSEC rrsets in auth section */ 250933707f3Ssthen for(i=rep->an_numrrsets; i < rep->an_numrrsets+rep->ns_numrrsets; 251933707f3Ssthen i++) { 252933707f3Ssthen if(rep->rrsets[i]->rk.type != htons(LDNS_RR_TYPE_NSEC)) 253933707f3Ssthen continue; 254bdfc4d55Sflorian if(!nsec_verify_rrset(env, ve, rep->rrsets[i], kkey, reason, 255*98bc733bSsthen reason_bogus, qstate, reasonbuf, reasonlen)) { 256933707f3Ssthen verbose(VERB_ALGO, "NSEC for empty non-terminal " 257933707f3Ssthen "did not verify."); 2588b7325afSsthen *reason = "NSEC for empty non-terminal " 2598b7325afSsthen "did not verify."; 260933707f3Ssthen return sec_status_bogus; 261933707f3Ssthen } 262933707f3Ssthen if(nsec_proves_nodata(rep->rrsets[i], qinfo, &wc)) { 263933707f3Ssthen verbose(VERB_ALGO, "NSEC for empty non-terminal " 264933707f3Ssthen "proved no DS."); 265933707f3Ssthen *proof_ttl = rrset_get_ttl(rep->rrsets[i]); 266933707f3Ssthen if(wc && dname_is_wild(rep->rrsets[i]->rk.dname)) 267933707f3Ssthen wc_nsec = rep->rrsets[i]; 268933707f3Ssthen valid_nsec = 1; 269933707f3Ssthen } 270933707f3Ssthen if(val_nsec_proves_name_error(rep->rrsets[i], qinfo->qname)) { 271933707f3Ssthen ce = nsec_closest_encloser(qinfo->qname, 272933707f3Ssthen rep->rrsets[i]); 273933707f3Ssthen } 274933707f3Ssthen } 275933707f3Ssthen if(wc && !ce) 276933707f3Ssthen valid_nsec = 0; 277933707f3Ssthen else if(wc && ce) { 278933707f3Ssthen /* ce and wc must match */ 279933707f3Ssthen if(query_dname_compare(wc, ce) != 0) 280933707f3Ssthen valid_nsec = 0; 281933707f3Ssthen else if(!wc_nsec) 282933707f3Ssthen valid_nsec = 0; 283933707f3Ssthen } 284933707f3Ssthen if(valid_nsec) { 285933707f3Ssthen if(wc) { 286933707f3Ssthen /* check if this is a delegation */ 287933707f3Ssthen *reason = "NSEC for wildcard does not prove absence of DS"; 288933707f3Ssthen return val_nsec_proves_no_ds(wc_nsec, qinfo); 289933707f3Ssthen } 290933707f3Ssthen /* valid nsec proves empty nonterminal */ 291933707f3Ssthen return sec_status_insecure; 292933707f3Ssthen } 293933707f3Ssthen 2944bfc71b0Ssthen /* NSEC proof did not conclusively point to DS or no DS */ 295933707f3Ssthen return sec_status_unchecked; 296933707f3Ssthen } 297933707f3Ssthen 298933707f3Ssthen int nsec_proves_nodata(struct ub_packed_rrset_key* nsec, 299933707f3Ssthen struct query_info* qinfo, uint8_t** wc) 300933707f3Ssthen { 301933707f3Ssthen log_assert(wc); 302933707f3Ssthen if(query_dname_compare(nsec->rk.dname, qinfo->qname) != 0) { 303933707f3Ssthen uint8_t* nm; 304933707f3Ssthen size_t ln; 305933707f3Ssthen 306933707f3Ssthen /* empty-non-terminal checking. 307933707f3Ssthen * Done before wildcard, because this is an exact match, 308933707f3Ssthen * and would prevent a wildcard from matching. */ 309933707f3Ssthen 310933707f3Ssthen /* If the nsec is proving that qname is an ENT, the nsec owner 311933707f3Ssthen * will be less than qname, and the next name will be a child 312933707f3Ssthen * domain of the qname. */ 313933707f3Ssthen if(!nsec_get_next(nsec, &nm, &ln)) 314933707f3Ssthen return 0; /* bad nsec */ 315933707f3Ssthen if(dname_strict_subdomain_c(nm, qinfo->qname) && 316933707f3Ssthen dname_canonical_compare(nsec->rk.dname, 317933707f3Ssthen qinfo->qname) < 0) { 318933707f3Ssthen return 1; /* proves ENT */ 319933707f3Ssthen } 320933707f3Ssthen 321933707f3Ssthen /* wildcard checking. */ 322933707f3Ssthen 323933707f3Ssthen /* If this is a wildcard NSEC, make sure that a) it was 324933707f3Ssthen * possible to have generated qname from the wildcard and 325933707f3Ssthen * b) the type map does not contain qtype. Note that this 326933707f3Ssthen * does NOT prove that this wildcard was the applicable 327933707f3Ssthen * wildcard. */ 328933707f3Ssthen if(dname_is_wild(nsec->rk.dname)) { 329933707f3Ssthen /* the purported closest encloser. */ 330933707f3Ssthen uint8_t* ce = nsec->rk.dname; 331933707f3Ssthen size_t ce_len = nsec->rk.dname_len; 332933707f3Ssthen dname_remove_label(&ce, &ce_len); 333933707f3Ssthen 334933707f3Ssthen /* The qname must be a strict subdomain of the 335933707f3Ssthen * closest encloser, for the wildcard to apply 336933707f3Ssthen */ 337933707f3Ssthen if(dname_strict_subdomain_c(qinfo->qname, ce)) { 338933707f3Ssthen /* here we have a matching NSEC for the qname, 339933707f3Ssthen * perform matching NSEC checks */ 340933707f3Ssthen if(nsec_has_type(nsec, LDNS_RR_TYPE_CNAME)) { 341933707f3Ssthen /* should have gotten the wildcard CNAME */ 342933707f3Ssthen return 0; 343933707f3Ssthen } 344933707f3Ssthen if(nsec_has_type(nsec, LDNS_RR_TYPE_NS) && 345933707f3Ssthen !nsec_has_type(nsec, LDNS_RR_TYPE_SOA)) { 346933707f3Ssthen /* wrong parentside (wildcard) NSEC used */ 347933707f3Ssthen return 0; 348933707f3Ssthen } 349933707f3Ssthen if(nsec_has_type(nsec, qinfo->qtype)) { 350933707f3Ssthen return 0; 351933707f3Ssthen } 352933707f3Ssthen *wc = ce; 353933707f3Ssthen return 1; 354933707f3Ssthen } 35524893edcSsthen } else { 35624893edcSsthen /* See if the next owner name covers a wildcard 35724893edcSsthen * empty non-terminal. */ 35877079be7Ssthen while (dname_canonical_compare(nsec->rk.dname, nm) < 0) { 35924893edcSsthen /* wildcard does not apply if qname below 36024893edcSsthen * the name that exists under the '*' */ 36124893edcSsthen if (dname_subdomain_c(qinfo->qname, nm)) 36224893edcSsthen break; 36324893edcSsthen /* but if it is a wildcard and qname is below 36424893edcSsthen * it, then the wildcard applies. The wildcard 36524893edcSsthen * is an empty nonterminal. nodata proven. */ 36624893edcSsthen if (dname_is_wild(nm)) { 36724893edcSsthen size_t ce_len = ln; 36824893edcSsthen uint8_t* ce = nm; 36924893edcSsthen dname_remove_label(&ce, &ce_len); 37024893edcSsthen if(dname_strict_subdomain_c(qinfo->qname, ce)) { 37124893edcSsthen *wc = ce; 37224893edcSsthen return 1; 37324893edcSsthen } 37424893edcSsthen } 37524893edcSsthen dname_remove_label(&nm, &ln); 37624893edcSsthen } 377933707f3Ssthen } 378933707f3Ssthen 379933707f3Ssthen /* Otherwise, this NSEC does not prove ENT and is not a 380933707f3Ssthen * wildcard, so it does not prove NODATA. */ 381933707f3Ssthen return 0; 382933707f3Ssthen } 383933707f3Ssthen 384933707f3Ssthen /* If the qtype exists, then we should have gotten it. */ 385933707f3Ssthen if(nsec_has_type(nsec, qinfo->qtype)) { 386933707f3Ssthen return 0; 387933707f3Ssthen } 388933707f3Ssthen 389933707f3Ssthen /* if the name is a CNAME node, then we should have gotten the CNAME*/ 390933707f3Ssthen if(nsec_has_type(nsec, LDNS_RR_TYPE_CNAME)) { 391933707f3Ssthen return 0; 392933707f3Ssthen } 393933707f3Ssthen 394933707f3Ssthen /* If an NS set exists at this name, and NOT a SOA (so this is a 395933707f3Ssthen * zone cut, not a zone apex), then we should have gotten a 396933707f3Ssthen * referral (or we just got the wrong NSEC). 397933707f3Ssthen * The reverse of this check is used when qtype is DS, since that 398933707f3Ssthen * must use the NSEC from above the zone cut. */ 399933707f3Ssthen if(qinfo->qtype != LDNS_RR_TYPE_DS && 400933707f3Ssthen nsec_has_type(nsec, LDNS_RR_TYPE_NS) && 401933707f3Ssthen !nsec_has_type(nsec, LDNS_RR_TYPE_SOA)) { 402933707f3Ssthen return 0; 403933707f3Ssthen } else if(qinfo->qtype == LDNS_RR_TYPE_DS && 404d8d14d0cSsthen nsec_has_type(nsec, LDNS_RR_TYPE_SOA) && 405d8d14d0cSsthen !dname_is_root(qinfo->qname)) { 406933707f3Ssthen return 0; 407933707f3Ssthen } 408933707f3Ssthen 409933707f3Ssthen return 1; 410933707f3Ssthen } 411933707f3Ssthen 412933707f3Ssthen int 413933707f3Ssthen val_nsec_proves_name_error(struct ub_packed_rrset_key* nsec, uint8_t* qname) 414933707f3Ssthen { 415933707f3Ssthen uint8_t* owner = nsec->rk.dname; 416933707f3Ssthen uint8_t* next; 417933707f3Ssthen size_t nlen; 418933707f3Ssthen if(!nsec_get_next(nsec, &next, &nlen)) 419933707f3Ssthen return 0; 420933707f3Ssthen 421933707f3Ssthen /* If NSEC owner == qname, then this NSEC proves that qname exists. */ 422933707f3Ssthen if(query_dname_compare(qname, owner) == 0) { 423933707f3Ssthen return 0; 424933707f3Ssthen } 425933707f3Ssthen 426933707f3Ssthen /* If NSEC is a parent of qname, we need to check the type map 427933707f3Ssthen * If the parent name has a DNAME or is a delegation point, then 428933707f3Ssthen * this NSEC is being misused. */ 429933707f3Ssthen if(dname_subdomain_c(qname, owner) && 430933707f3Ssthen (nsec_has_type(nsec, LDNS_RR_TYPE_DNAME) || 431933707f3Ssthen (nsec_has_type(nsec, LDNS_RR_TYPE_NS) 432933707f3Ssthen && !nsec_has_type(nsec, LDNS_RR_TYPE_SOA)) 433933707f3Ssthen )) { 434933707f3Ssthen return 0; 435933707f3Ssthen } 436933707f3Ssthen 437933707f3Ssthen if(query_dname_compare(owner, next) == 0) { 438933707f3Ssthen /* this nsec is the only nsec */ 439933707f3Ssthen /* zone.name NSEC zone.name, disproves everything else */ 440933707f3Ssthen /* but only for subdomains of that zone */ 441933707f3Ssthen if(dname_strict_subdomain_c(qname, next)) 442933707f3Ssthen return 1; 443933707f3Ssthen } 444933707f3Ssthen else if(dname_canonical_compare(owner, next) > 0) { 445933707f3Ssthen /* this is the last nsec, ....(bigger) NSEC zonename(smaller) */ 446933707f3Ssthen /* the names after the last (owner) name do not exist 447933707f3Ssthen * there are no names before the zone name in the zone 448933707f3Ssthen * but the qname must be a subdomain of the zone name(next). */ 449933707f3Ssthen if(dname_canonical_compare(owner, qname) < 0 && 450933707f3Ssthen dname_strict_subdomain_c(qname, next)) 451933707f3Ssthen return 1; 452933707f3Ssthen } else { 453933707f3Ssthen /* regular NSEC, (smaller) NSEC (larger) */ 454933707f3Ssthen if(dname_canonical_compare(owner, qname) < 0 && 455933707f3Ssthen dname_canonical_compare(qname, next) < 0) { 456933707f3Ssthen return 1; 457933707f3Ssthen } 458933707f3Ssthen } 459933707f3Ssthen return 0; 460933707f3Ssthen } 461933707f3Ssthen 462933707f3Ssthen int val_nsec_proves_insecuredelegation(struct ub_packed_rrset_key* nsec, 463933707f3Ssthen struct query_info* qinfo) 464933707f3Ssthen { 465933707f3Ssthen if(nsec_has_type(nsec, LDNS_RR_TYPE_NS) && 466933707f3Ssthen !nsec_has_type(nsec, LDNS_RR_TYPE_DS) && 467933707f3Ssthen !nsec_has_type(nsec, LDNS_RR_TYPE_SOA)) { 468933707f3Ssthen /* see if nsec signals an insecure delegation */ 469933707f3Ssthen if(qinfo->qtype == LDNS_RR_TYPE_DS) { 470933707f3Ssthen /* if type is DS and qname is equal to nsec, then it 471933707f3Ssthen * is an exact match nsec, result not insecure */ 472933707f3Ssthen if(dname_strict_subdomain_c(qinfo->qname, 473933707f3Ssthen nsec->rk.dname)) 474933707f3Ssthen return 1; 475933707f3Ssthen } else { 476933707f3Ssthen if(dname_subdomain_c(qinfo->qname, nsec->rk.dname)) 477933707f3Ssthen return 1; 478933707f3Ssthen } 479933707f3Ssthen } 480933707f3Ssthen return 0; 481933707f3Ssthen } 482933707f3Ssthen 483933707f3Ssthen uint8_t* 484933707f3Ssthen nsec_closest_encloser(uint8_t* qname, struct ub_packed_rrset_key* nsec) 485933707f3Ssthen { 486933707f3Ssthen uint8_t* next; 487933707f3Ssthen size_t nlen; 488933707f3Ssthen uint8_t* common1, *common2; 489933707f3Ssthen if(!nsec_get_next(nsec, &next, &nlen)) 490933707f3Ssthen return NULL; 491933707f3Ssthen /* longest common with owner or next name */ 492933707f3Ssthen common1 = dname_get_shared_topdomain(nsec->rk.dname, qname); 493933707f3Ssthen common2 = dname_get_shared_topdomain(next, qname); 494933707f3Ssthen if(dname_count_labels(common1) > dname_count_labels(common2)) 495933707f3Ssthen return common1; 496933707f3Ssthen return common2; 497933707f3Ssthen } 498933707f3Ssthen 499933707f3Ssthen int val_nsec_proves_positive_wildcard(struct ub_packed_rrset_key* nsec, 500933707f3Ssthen struct query_info* qinf, uint8_t* wc) 501933707f3Ssthen { 502933707f3Ssthen uint8_t* ce; 503933707f3Ssthen /* 1) prove that qname doesn't exist and 504933707f3Ssthen * 2) that the correct wildcard was used 505933707f3Ssthen * nsec has been verified already. */ 506933707f3Ssthen if(!val_nsec_proves_name_error(nsec, qinf->qname)) 507933707f3Ssthen return 0; 508933707f3Ssthen /* check wildcard name */ 509933707f3Ssthen ce = nsec_closest_encloser(qinf->qname, nsec); 510933707f3Ssthen if(!ce) 511933707f3Ssthen return 0; 512933707f3Ssthen if(query_dname_compare(wc, ce) != 0) { 513933707f3Ssthen return 0; 514933707f3Ssthen } 515933707f3Ssthen return 1; 516933707f3Ssthen } 517933707f3Ssthen 518933707f3Ssthen int 519933707f3Ssthen val_nsec_proves_no_wc(struct ub_packed_rrset_key* nsec, uint8_t* qname, 520933707f3Ssthen size_t qnamelen) 521933707f3Ssthen { 522933707f3Ssthen /* Determine if a NSEC record proves the non-existence of a 523933707f3Ssthen * wildcard that could have produced qname. */ 524933707f3Ssthen int labs; 525933707f3Ssthen uint8_t* ce = nsec_closest_encloser(qname, nsec); 526933707f3Ssthen uint8_t* strip; 527933707f3Ssthen size_t striplen; 528933707f3Ssthen uint8_t buf[LDNS_MAX_DOMAINLEN+3]; 529933707f3Ssthen if(!ce) 530933707f3Ssthen return 0; 531933707f3Ssthen /* we can subtract the closest encloser count - since that is the 532933707f3Ssthen * largest shared topdomain with owner and next NSEC name, 533933707f3Ssthen * because the NSEC is no proof for names shorter than the owner 534933707f3Ssthen * and next names. */ 535933707f3Ssthen labs = dname_count_labels(qname) - dname_count_labels(ce); 536933707f3Ssthen 537938a3a5eSflorian if(labs > 0) { 538933707f3Ssthen /* i is number of labels to strip off qname, prepend * wild */ 539933707f3Ssthen strip = qname; 540933707f3Ssthen striplen = qnamelen; 541938a3a5eSflorian dname_remove_labels(&strip, &striplen, labs); 542933707f3Ssthen if(striplen > LDNS_MAX_DOMAINLEN-2) 543938a3a5eSflorian return 0; /* too long to prepend wildcard */ 544933707f3Ssthen buf[0] = 1; 545933707f3Ssthen buf[1] = (uint8_t)'*'; 546933707f3Ssthen memmove(buf+2, strip, striplen); 547933707f3Ssthen if(val_nsec_proves_name_error(nsec, buf)) { 548933707f3Ssthen return 1; 549933707f3Ssthen } 550933707f3Ssthen } 551933707f3Ssthen return 0; 552933707f3Ssthen } 553