1ae8c6e27Sflorian /* 2ae8c6e27Sflorian * validator/val_nsec.c - validator NSEC denial of existence functions. 3ae8c6e27Sflorian * 4ae8c6e27Sflorian * Copyright (c) 2007, NLnet Labs. All rights reserved. 5ae8c6e27Sflorian * 6ae8c6e27Sflorian * This software is open source. 7ae8c6e27Sflorian * 8ae8c6e27Sflorian * Redistribution and use in source and binary forms, with or without 9ae8c6e27Sflorian * modification, are permitted provided that the following conditions 10ae8c6e27Sflorian * are met: 11ae8c6e27Sflorian * 12ae8c6e27Sflorian * Redistributions of source code must retain the above copyright notice, 13ae8c6e27Sflorian * this list of conditions and the following disclaimer. 14ae8c6e27Sflorian * 15ae8c6e27Sflorian * Redistributions in binary form must reproduce the above copyright notice, 16ae8c6e27Sflorian * this list of conditions and the following disclaimer in the documentation 17ae8c6e27Sflorian * and/or other materials provided with the distribution. 18ae8c6e27Sflorian * 19ae8c6e27Sflorian * Neither the name of the NLNET LABS nor the names of its contributors may 20ae8c6e27Sflorian * be used to endorse or promote products derived from this software without 21ae8c6e27Sflorian * specific prior written permission. 22ae8c6e27Sflorian * 23ae8c6e27Sflorian * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24ae8c6e27Sflorian * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25ae8c6e27Sflorian * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26ae8c6e27Sflorian * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27ae8c6e27Sflorian * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28ae8c6e27Sflorian * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 29ae8c6e27Sflorian * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 30ae8c6e27Sflorian * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 31ae8c6e27Sflorian * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 32ae8c6e27Sflorian * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33ae8c6e27Sflorian * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34ae8c6e27Sflorian */ 35ae8c6e27Sflorian 36ae8c6e27Sflorian /** 37ae8c6e27Sflorian * \file 38ae8c6e27Sflorian * 39ae8c6e27Sflorian * This file contains helper functions for the validator module. 40ae8c6e27Sflorian * The functions help with NSEC checking, the different NSEC proofs 41ae8c6e27Sflorian * for denial of existence, and proofs for presence of types. 42ae8c6e27Sflorian */ 43ae8c6e27Sflorian #include "config.h" 44ae8c6e27Sflorian #include "validator/val_nsec.h" 45ae8c6e27Sflorian #include "validator/val_utils.h" 46ae8c6e27Sflorian #include "util/data/msgreply.h" 47ae8c6e27Sflorian #include "util/data/dname.h" 48ae8c6e27Sflorian #include "util/net_help.h" 49ae8c6e27Sflorian #include "util/module.h" 50ae8c6e27Sflorian #include "services/cache/rrset.h" 51ae8c6e27Sflorian 52ae8c6e27Sflorian /** get ttl of rrset */ 53ae8c6e27Sflorian static uint32_t 54ae8c6e27Sflorian rrset_get_ttl(struct ub_packed_rrset_key* k) 55ae8c6e27Sflorian { 56ae8c6e27Sflorian struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data; 57ae8c6e27Sflorian return d->ttl; 58ae8c6e27Sflorian } 59ae8c6e27Sflorian 60ae8c6e27Sflorian int 61ae8c6e27Sflorian nsecbitmap_has_type_rdata(uint8_t* bitmap, size_t len, uint16_t type) 62ae8c6e27Sflorian { 63ae8c6e27Sflorian /* Check type present in NSEC typemap with bitmap arg */ 64ae8c6e27Sflorian /* bitmasks for determining type-lowerbits presence */ 65ae8c6e27Sflorian uint8_t masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}; 66ae8c6e27Sflorian uint8_t type_window = type>>8; 67ae8c6e27Sflorian uint8_t type_low = type&0xff; 68ae8c6e27Sflorian uint8_t win, winlen; 69ae8c6e27Sflorian /* read each of the type bitmap windows and see if the searched 70ae8c6e27Sflorian * type is amongst it */ 71ae8c6e27Sflorian while(len > 0) { 72ae8c6e27Sflorian if(len < 3) /* bad window, at least window# winlen bitmap */ 73ae8c6e27Sflorian return 0; 74ae8c6e27Sflorian win = *bitmap++; 75ae8c6e27Sflorian winlen = *bitmap++; 76ae8c6e27Sflorian len -= 2; 77ae8c6e27Sflorian if(len < winlen || winlen < 1 || winlen > 32) 78ae8c6e27Sflorian return 0; /* bad window length */ 79ae8c6e27Sflorian if(win == type_window) { 80ae8c6e27Sflorian /* search window bitmap for the correct byte */ 81ae8c6e27Sflorian /* mybyte is 0 if we need the first byte */ 82ae8c6e27Sflorian size_t mybyte = type_low>>3; 83ae8c6e27Sflorian if(winlen <= mybyte) 84ae8c6e27Sflorian return 0; /* window too short */ 85ae8c6e27Sflorian return (int)(bitmap[mybyte] & masks[type_low&0x7]); 86ae8c6e27Sflorian } else { 87ae8c6e27Sflorian /* not the window we are looking for */ 88ae8c6e27Sflorian bitmap += winlen; 89ae8c6e27Sflorian len -= winlen; 90ae8c6e27Sflorian } 91ae8c6e27Sflorian } 92ae8c6e27Sflorian /* end of bitmap reached, no type found */ 93ae8c6e27Sflorian return 0; 94ae8c6e27Sflorian } 95ae8c6e27Sflorian 96ae8c6e27Sflorian int 97ae8c6e27Sflorian nsec_has_type(struct ub_packed_rrset_key* nsec, uint16_t type) 98ae8c6e27Sflorian { 99ae8c6e27Sflorian struct packed_rrset_data* d = (struct packed_rrset_data*)nsec-> 100ae8c6e27Sflorian entry.data; 101ae8c6e27Sflorian size_t len; 102ae8c6e27Sflorian if(!d || d->count == 0 || d->rr_len[0] < 2+1) 103ae8c6e27Sflorian return 0; 104ae8c6e27Sflorian len = dname_valid(d->rr_data[0]+2, d->rr_len[0]-2); 105ae8c6e27Sflorian if(!len) 106ae8c6e27Sflorian return 0; 107ae8c6e27Sflorian return nsecbitmap_has_type_rdata(d->rr_data[0]+2+len, 108ae8c6e27Sflorian d->rr_len[0]-2-len, type); 109ae8c6e27Sflorian } 110ae8c6e27Sflorian 111ae8c6e27Sflorian /** 112ae8c6e27Sflorian * Get next owner name from nsec record 113ae8c6e27Sflorian * @param nsec: the nsec RRset. 114ae8c6e27Sflorian * If there are multiple RRs, then this will only return one of them. 115ae8c6e27Sflorian * @param nm: the next name is returned. 116ae8c6e27Sflorian * @param ln: length of nm is returned. 117ae8c6e27Sflorian * @return false on a bad NSEC RR (too short, malformed dname). 118ae8c6e27Sflorian */ 119ae8c6e27Sflorian static int 120ae8c6e27Sflorian nsec_get_next(struct ub_packed_rrset_key* nsec, uint8_t** nm, size_t* ln) 121ae8c6e27Sflorian { 122ae8c6e27Sflorian struct packed_rrset_data* d = (struct packed_rrset_data*)nsec-> 123ae8c6e27Sflorian entry.data; 124ae8c6e27Sflorian if(!d || d->count == 0 || d->rr_len[0] < 2+1) { 125ae8c6e27Sflorian *nm = 0; 126ae8c6e27Sflorian *ln = 0; 127ae8c6e27Sflorian return 0; 128ae8c6e27Sflorian } 129ae8c6e27Sflorian *nm = d->rr_data[0]+2; 130ae8c6e27Sflorian *ln = dname_valid(*nm, d->rr_len[0]-2); 131ae8c6e27Sflorian if(!*ln) { 132ae8c6e27Sflorian *nm = 0; 133ae8c6e27Sflorian *ln = 0; 134ae8c6e27Sflorian return 0; 135ae8c6e27Sflorian } 136ae8c6e27Sflorian return 1; 137ae8c6e27Sflorian } 138ae8c6e27Sflorian 139ae8c6e27Sflorian /** 140ae8c6e27Sflorian * For an NSEC that matches the DS queried for, check absence of DS type. 141ae8c6e27Sflorian * 142ae8c6e27Sflorian * @param nsec: NSEC for proof, must be trusted. 143ae8c6e27Sflorian * @param qinfo: what is queried for. 144ae8c6e27Sflorian * @return if secure the nsec proves that no DS is present, or 145ae8c6e27Sflorian * insecure if it proves it is not a delegation point. 146ae8c6e27Sflorian * or bogus if something was wrong. 147ae8c6e27Sflorian */ 148ae8c6e27Sflorian static enum sec_status 149ae8c6e27Sflorian val_nsec_proves_no_ds(struct ub_packed_rrset_key* nsec, 150ae8c6e27Sflorian struct query_info* qinfo) 151ae8c6e27Sflorian { 152ae8c6e27Sflorian log_assert(qinfo->qtype == LDNS_RR_TYPE_DS); 153ae8c6e27Sflorian log_assert(ntohs(nsec->rk.type) == LDNS_RR_TYPE_NSEC); 154ae8c6e27Sflorian 155ae8c6e27Sflorian if(nsec_has_type(nsec, LDNS_RR_TYPE_SOA) && qinfo->qname_len != 1) { 156ae8c6e27Sflorian /* SOA present means that this is the NSEC from the child, 157ae8c6e27Sflorian * not the parent (so it is the wrong one). */ 158ae8c6e27Sflorian return sec_status_bogus; 159ae8c6e27Sflorian } 160ae8c6e27Sflorian if(nsec_has_type(nsec, LDNS_RR_TYPE_DS)) { 161ae8c6e27Sflorian /* DS present means that there should have been a positive 162ae8c6e27Sflorian * response to the DS query, so there is something wrong. */ 163ae8c6e27Sflorian return sec_status_bogus; 164ae8c6e27Sflorian } 165ae8c6e27Sflorian 166ae8c6e27Sflorian if(!nsec_has_type(nsec, LDNS_RR_TYPE_NS)) { 167ae8c6e27Sflorian /* If there is no NS at this point at all, then this 168ae8c6e27Sflorian * doesn't prove anything one way or the other. */ 169ae8c6e27Sflorian return sec_status_insecure; 170ae8c6e27Sflorian } 171ae8c6e27Sflorian /* Otherwise, this proves no DS. */ 172ae8c6e27Sflorian return sec_status_secure; 173ae8c6e27Sflorian } 174ae8c6e27Sflorian 175ae8c6e27Sflorian /** check security status from cache or verify rrset, returns true if secure */ 176ae8c6e27Sflorian static int 177ae8c6e27Sflorian nsec_verify_rrset(struct module_env* env, struct val_env* ve, 178ae8c6e27Sflorian struct ub_packed_rrset_key* nsec, struct key_entry_key* kkey, 179d500c338Sflorian char** reason, sldns_ede_code* reason_bogus, 180*7037e34cSflorian struct module_qstate* qstate, char* reasonbuf, size_t reasonlen) 181ae8c6e27Sflorian { 182ae8c6e27Sflorian struct packed_rrset_data* d = (struct packed_rrset_data*) 183ae8c6e27Sflorian nsec->entry.data; 184fed3efa7Sflorian int verified = 0; 185411c5950Sflorian if(!d) return 0; 186ae8c6e27Sflorian if(d->security == sec_status_secure) 187ae8c6e27Sflorian return 1; 188ae8c6e27Sflorian rrset_check_sec_status(env->rrset_cache, nsec, *env->now); 189ae8c6e27Sflorian if(d->security == sec_status_secure) 190ae8c6e27Sflorian return 1; 191ae8c6e27Sflorian d->security = val_verify_rrset_entry(env, ve, nsec, kkey, reason, 192*7037e34cSflorian reason_bogus, LDNS_SECTION_AUTHORITY, qstate, &verified, 193*7037e34cSflorian reasonbuf, reasonlen); 194ae8c6e27Sflorian if(d->security == sec_status_secure) { 195ae8c6e27Sflorian rrset_update_sec_status(env->rrset_cache, nsec, *env->now); 196ae8c6e27Sflorian return 1; 197ae8c6e27Sflorian } 198ae8c6e27Sflorian return 0; 199ae8c6e27Sflorian } 200ae8c6e27Sflorian 201ae8c6e27Sflorian enum sec_status 202ae8c6e27Sflorian val_nsec_prove_nodata_dsreply(struct module_env* env, struct val_env* ve, 203ae8c6e27Sflorian struct query_info* qinfo, struct reply_info* rep, 204ae8c6e27Sflorian struct key_entry_key* kkey, time_t* proof_ttl, char** reason, 205*7037e34cSflorian sldns_ede_code* reason_bogus, struct module_qstate* qstate, 206*7037e34cSflorian char* reasonbuf, size_t reasonlen) 207ae8c6e27Sflorian { 208ae8c6e27Sflorian struct ub_packed_rrset_key* nsec = reply_find_rrset_section_ns( 209ae8c6e27Sflorian rep, qinfo->qname, qinfo->qname_len, LDNS_RR_TYPE_NSEC, 210ae8c6e27Sflorian qinfo->qclass); 211ae8c6e27Sflorian enum sec_status sec; 212ae8c6e27Sflorian size_t i; 213ae8c6e27Sflorian uint8_t* wc = NULL, *ce = NULL; 214ae8c6e27Sflorian int valid_nsec = 0; 215ae8c6e27Sflorian struct ub_packed_rrset_key* wc_nsec = NULL; 216ae8c6e27Sflorian 217ae8c6e27Sflorian /* If we have a NSEC at the same name, it must prove one 218ae8c6e27Sflorian * of two things 219ae8c6e27Sflorian * -- 220ae8c6e27Sflorian * 1) this is a delegation point and there is no DS 221ae8c6e27Sflorian * 2) this is not a delegation point */ 222ae8c6e27Sflorian if(nsec) { 223d500c338Sflorian if(!nsec_verify_rrset(env, ve, nsec, kkey, reason, 224*7037e34cSflorian reason_bogus, qstate, reasonbuf, reasonlen)) { 225ae8c6e27Sflorian verbose(VERB_ALGO, "NSEC RRset for the " 226ae8c6e27Sflorian "referral did not verify."); 227ae8c6e27Sflorian return sec_status_bogus; 228ae8c6e27Sflorian } 229ae8c6e27Sflorian sec = val_nsec_proves_no_ds(nsec, qinfo); 230ae8c6e27Sflorian if(sec == sec_status_bogus) { 231ae8c6e27Sflorian /* something was wrong. */ 232ae8c6e27Sflorian *reason = "NSEC does not prove absence of DS"; 233d500c338Sflorian *reason_bogus = LDNS_EDE_DNSSEC_BOGUS; 234ae8c6e27Sflorian return sec; 235ae8c6e27Sflorian } else if(sec == sec_status_insecure) { 236ae8c6e27Sflorian /* this wasn't a delegation point. */ 237ae8c6e27Sflorian return sec; 238ae8c6e27Sflorian } else if(sec == sec_status_secure) { 239ae8c6e27Sflorian /* this proved no DS. */ 240ae8c6e27Sflorian *proof_ttl = ub_packed_rrset_ttl(nsec); 241ae8c6e27Sflorian return sec; 242ae8c6e27Sflorian } 243ae8c6e27Sflorian /* if unchecked, fall through to next proof */ 244ae8c6e27Sflorian } 245ae8c6e27Sflorian 246ae8c6e27Sflorian /* Otherwise, there is no NSEC at qname. This could be an ENT. 247ae8c6e27Sflorian * (ENT=empty non terminal). If not, this is broken. */ 248ae8c6e27Sflorian 249ae8c6e27Sflorian /* verify NSEC rrsets in auth section */ 250ae8c6e27Sflorian for(i=rep->an_numrrsets; i < rep->an_numrrsets+rep->ns_numrrsets; 251ae8c6e27Sflorian i++) { 252ae8c6e27Sflorian if(rep->rrsets[i]->rk.type != htons(LDNS_RR_TYPE_NSEC)) 253ae8c6e27Sflorian continue; 254ae8c6e27Sflorian if(!nsec_verify_rrset(env, ve, rep->rrsets[i], kkey, reason, 255*7037e34cSflorian reason_bogus, qstate, reasonbuf, reasonlen)) { 256ae8c6e27Sflorian verbose(VERB_ALGO, "NSEC for empty non-terminal " 257ae8c6e27Sflorian "did not verify."); 258d500c338Sflorian *reason = "NSEC for empty non-terminal " 259d500c338Sflorian "did not verify."; 260ae8c6e27Sflorian return sec_status_bogus; 261ae8c6e27Sflorian } 262ae8c6e27Sflorian if(nsec_proves_nodata(rep->rrsets[i], qinfo, &wc)) { 263ae8c6e27Sflorian verbose(VERB_ALGO, "NSEC for empty non-terminal " 264ae8c6e27Sflorian "proved no DS."); 265ae8c6e27Sflorian *proof_ttl = rrset_get_ttl(rep->rrsets[i]); 266ae8c6e27Sflorian if(wc && dname_is_wild(rep->rrsets[i]->rk.dname)) 267ae8c6e27Sflorian wc_nsec = rep->rrsets[i]; 268ae8c6e27Sflorian valid_nsec = 1; 269ae8c6e27Sflorian } 270ae8c6e27Sflorian if(val_nsec_proves_name_error(rep->rrsets[i], qinfo->qname)) { 271ae8c6e27Sflorian ce = nsec_closest_encloser(qinfo->qname, 272ae8c6e27Sflorian rep->rrsets[i]); 273ae8c6e27Sflorian } 274ae8c6e27Sflorian } 275ae8c6e27Sflorian if(wc && !ce) 276ae8c6e27Sflorian valid_nsec = 0; 277ae8c6e27Sflorian else if(wc && ce) { 278ae8c6e27Sflorian /* ce and wc must match */ 279ae8c6e27Sflorian if(query_dname_compare(wc, ce) != 0) 280ae8c6e27Sflorian valid_nsec = 0; 281ae8c6e27Sflorian else if(!wc_nsec) 282ae8c6e27Sflorian valid_nsec = 0; 283ae8c6e27Sflorian } 284ae8c6e27Sflorian if(valid_nsec) { 285ae8c6e27Sflorian if(wc) { 286ae8c6e27Sflorian /* check if this is a delegation */ 287ae8c6e27Sflorian *reason = "NSEC for wildcard does not prove absence of DS"; 288ae8c6e27Sflorian return val_nsec_proves_no_ds(wc_nsec, qinfo); 289ae8c6e27Sflorian } 290ae8c6e27Sflorian /* valid nsec proves empty nonterminal */ 291ae8c6e27Sflorian return sec_status_insecure; 292ae8c6e27Sflorian } 293ae8c6e27Sflorian 294ae8c6e27Sflorian /* NSEC proof did not conclusively point to DS or no DS */ 295ae8c6e27Sflorian return sec_status_unchecked; 296ae8c6e27Sflorian } 297ae8c6e27Sflorian 298ae8c6e27Sflorian int nsec_proves_nodata(struct ub_packed_rrset_key* nsec, 299ae8c6e27Sflorian struct query_info* qinfo, uint8_t** wc) 300ae8c6e27Sflorian { 301ae8c6e27Sflorian log_assert(wc); 302ae8c6e27Sflorian if(query_dname_compare(nsec->rk.dname, qinfo->qname) != 0) { 303ae8c6e27Sflorian uint8_t* nm; 304ae8c6e27Sflorian size_t ln; 305ae8c6e27Sflorian 306ae8c6e27Sflorian /* empty-non-terminal checking. 307ae8c6e27Sflorian * Done before wildcard, because this is an exact match, 308ae8c6e27Sflorian * and would prevent a wildcard from matching. */ 309ae8c6e27Sflorian 310ae8c6e27Sflorian /* If the nsec is proving that qname is an ENT, the nsec owner 311ae8c6e27Sflorian * will be less than qname, and the next name will be a child 312ae8c6e27Sflorian * domain of the qname. */ 313ae8c6e27Sflorian if(!nsec_get_next(nsec, &nm, &ln)) 314ae8c6e27Sflorian return 0; /* bad nsec */ 315ae8c6e27Sflorian if(dname_strict_subdomain_c(nm, qinfo->qname) && 316ae8c6e27Sflorian dname_canonical_compare(nsec->rk.dname, 317ae8c6e27Sflorian qinfo->qname) < 0) { 318ae8c6e27Sflorian return 1; /* proves ENT */ 319ae8c6e27Sflorian } 320ae8c6e27Sflorian 321ae8c6e27Sflorian /* wildcard checking. */ 322ae8c6e27Sflorian 323ae8c6e27Sflorian /* If this is a wildcard NSEC, make sure that a) it was 324ae8c6e27Sflorian * possible to have generated qname from the wildcard and 325ae8c6e27Sflorian * b) the type map does not contain qtype. Note that this 326ae8c6e27Sflorian * does NOT prove that this wildcard was the applicable 327ae8c6e27Sflorian * wildcard. */ 328ae8c6e27Sflorian if(dname_is_wild(nsec->rk.dname)) { 329ae8c6e27Sflorian /* the purported closest encloser. */ 330ae8c6e27Sflorian uint8_t* ce = nsec->rk.dname; 331ae8c6e27Sflorian size_t ce_len = nsec->rk.dname_len; 332ae8c6e27Sflorian dname_remove_label(&ce, &ce_len); 333ae8c6e27Sflorian 334ae8c6e27Sflorian /* The qname must be a strict subdomain of the 335ae8c6e27Sflorian * closest encloser, for the wildcard to apply 336ae8c6e27Sflorian */ 337ae8c6e27Sflorian if(dname_strict_subdomain_c(qinfo->qname, ce)) { 338ae8c6e27Sflorian /* here we have a matching NSEC for the qname, 339ae8c6e27Sflorian * perform matching NSEC checks */ 340ae8c6e27Sflorian if(nsec_has_type(nsec, LDNS_RR_TYPE_CNAME)) { 341ae8c6e27Sflorian /* should have gotten the wildcard CNAME */ 342ae8c6e27Sflorian return 0; 343ae8c6e27Sflorian } 344ae8c6e27Sflorian if(nsec_has_type(nsec, LDNS_RR_TYPE_NS) && 345ae8c6e27Sflorian !nsec_has_type(nsec, LDNS_RR_TYPE_SOA)) { 346ae8c6e27Sflorian /* wrong parentside (wildcard) NSEC used */ 347ae8c6e27Sflorian return 0; 348ae8c6e27Sflorian } 349ae8c6e27Sflorian if(nsec_has_type(nsec, qinfo->qtype)) { 350ae8c6e27Sflorian return 0; 351ae8c6e27Sflorian } 352ae8c6e27Sflorian *wc = ce; 353ae8c6e27Sflorian return 1; 354ae8c6e27Sflorian } 355ae8c6e27Sflorian } else { 356ae8c6e27Sflorian /* See if the next owner name covers a wildcard 357ae8c6e27Sflorian * empty non-terminal. */ 358ae8c6e27Sflorian while (dname_canonical_compare(nsec->rk.dname, nm) < 0) { 359ae8c6e27Sflorian /* wildcard does not apply if qname below 360ae8c6e27Sflorian * the name that exists under the '*' */ 361ae8c6e27Sflorian if (dname_subdomain_c(qinfo->qname, nm)) 362ae8c6e27Sflorian break; 363ae8c6e27Sflorian /* but if it is a wildcard and qname is below 364ae8c6e27Sflorian * it, then the wildcard applies. The wildcard 365ae8c6e27Sflorian * is an empty nonterminal. nodata proven. */ 366ae8c6e27Sflorian if (dname_is_wild(nm)) { 367ae8c6e27Sflorian size_t ce_len = ln; 368ae8c6e27Sflorian uint8_t* ce = nm; 369ae8c6e27Sflorian dname_remove_label(&ce, &ce_len); 370ae8c6e27Sflorian if(dname_strict_subdomain_c(qinfo->qname, ce)) { 371ae8c6e27Sflorian *wc = ce; 372ae8c6e27Sflorian return 1; 373ae8c6e27Sflorian } 374ae8c6e27Sflorian } 375ae8c6e27Sflorian dname_remove_label(&nm, &ln); 376ae8c6e27Sflorian } 377ae8c6e27Sflorian } 378ae8c6e27Sflorian 379ae8c6e27Sflorian /* Otherwise, this NSEC does not prove ENT and is not a 380ae8c6e27Sflorian * wildcard, so it does not prove NODATA. */ 381ae8c6e27Sflorian return 0; 382ae8c6e27Sflorian } 383ae8c6e27Sflorian 384ae8c6e27Sflorian /* If the qtype exists, then we should have gotten it. */ 385ae8c6e27Sflorian if(nsec_has_type(nsec, qinfo->qtype)) { 386ae8c6e27Sflorian return 0; 387ae8c6e27Sflorian } 388ae8c6e27Sflorian 389ae8c6e27Sflorian /* if the name is a CNAME node, then we should have gotten the CNAME*/ 390ae8c6e27Sflorian if(nsec_has_type(nsec, LDNS_RR_TYPE_CNAME)) { 391ae8c6e27Sflorian return 0; 392ae8c6e27Sflorian } 393ae8c6e27Sflorian 394ae8c6e27Sflorian /* If an NS set exists at this name, and NOT a SOA (so this is a 395ae8c6e27Sflorian * zone cut, not a zone apex), then we should have gotten a 396ae8c6e27Sflorian * referral (or we just got the wrong NSEC). 397ae8c6e27Sflorian * The reverse of this check is used when qtype is DS, since that 398ae8c6e27Sflorian * must use the NSEC from above the zone cut. */ 399ae8c6e27Sflorian if(qinfo->qtype != LDNS_RR_TYPE_DS && 400ae8c6e27Sflorian nsec_has_type(nsec, LDNS_RR_TYPE_NS) && 401ae8c6e27Sflorian !nsec_has_type(nsec, LDNS_RR_TYPE_SOA)) { 402ae8c6e27Sflorian return 0; 403ae8c6e27Sflorian } else if(qinfo->qtype == LDNS_RR_TYPE_DS && 404ae8c6e27Sflorian nsec_has_type(nsec, LDNS_RR_TYPE_SOA) && 405ae8c6e27Sflorian !dname_is_root(qinfo->qname)) { 406ae8c6e27Sflorian return 0; 407ae8c6e27Sflorian } 408ae8c6e27Sflorian 409ae8c6e27Sflorian return 1; 410ae8c6e27Sflorian } 411ae8c6e27Sflorian 412ae8c6e27Sflorian int 413ae8c6e27Sflorian val_nsec_proves_name_error(struct ub_packed_rrset_key* nsec, uint8_t* qname) 414ae8c6e27Sflorian { 415ae8c6e27Sflorian uint8_t* owner = nsec->rk.dname; 416ae8c6e27Sflorian uint8_t* next; 417ae8c6e27Sflorian size_t nlen; 418ae8c6e27Sflorian if(!nsec_get_next(nsec, &next, &nlen)) 419ae8c6e27Sflorian return 0; 420ae8c6e27Sflorian 421ae8c6e27Sflorian /* If NSEC owner == qname, then this NSEC proves that qname exists. */ 422ae8c6e27Sflorian if(query_dname_compare(qname, owner) == 0) { 423ae8c6e27Sflorian return 0; 424ae8c6e27Sflorian } 425ae8c6e27Sflorian 426ae8c6e27Sflorian /* If NSEC is a parent of qname, we need to check the type map 427ae8c6e27Sflorian * If the parent name has a DNAME or is a delegation point, then 428ae8c6e27Sflorian * this NSEC is being misused. */ 429ae8c6e27Sflorian if(dname_subdomain_c(qname, owner) && 430ae8c6e27Sflorian (nsec_has_type(nsec, LDNS_RR_TYPE_DNAME) || 431ae8c6e27Sflorian (nsec_has_type(nsec, LDNS_RR_TYPE_NS) 432ae8c6e27Sflorian && !nsec_has_type(nsec, LDNS_RR_TYPE_SOA)) 433ae8c6e27Sflorian )) { 434ae8c6e27Sflorian return 0; 435ae8c6e27Sflorian } 436ae8c6e27Sflorian 437ae8c6e27Sflorian if(query_dname_compare(owner, next) == 0) { 438ae8c6e27Sflorian /* this nsec is the only nsec */ 439ae8c6e27Sflorian /* zone.name NSEC zone.name, disproves everything else */ 440ae8c6e27Sflorian /* but only for subdomains of that zone */ 441ae8c6e27Sflorian if(dname_strict_subdomain_c(qname, next)) 442ae8c6e27Sflorian return 1; 443ae8c6e27Sflorian } 444ae8c6e27Sflorian else if(dname_canonical_compare(owner, next) > 0) { 445ae8c6e27Sflorian /* this is the last nsec, ....(bigger) NSEC zonename(smaller) */ 446ae8c6e27Sflorian /* the names after the last (owner) name do not exist 447ae8c6e27Sflorian * there are no names before the zone name in the zone 448ae8c6e27Sflorian * but the qname must be a subdomain of the zone name(next). */ 449ae8c6e27Sflorian if(dname_canonical_compare(owner, qname) < 0 && 450ae8c6e27Sflorian dname_strict_subdomain_c(qname, next)) 451ae8c6e27Sflorian return 1; 452ae8c6e27Sflorian } else { 453ae8c6e27Sflorian /* regular NSEC, (smaller) NSEC (larger) */ 454ae8c6e27Sflorian if(dname_canonical_compare(owner, qname) < 0 && 455ae8c6e27Sflorian dname_canonical_compare(qname, next) < 0) { 456ae8c6e27Sflorian return 1; 457ae8c6e27Sflorian } 458ae8c6e27Sflorian } 459ae8c6e27Sflorian return 0; 460ae8c6e27Sflorian } 461ae8c6e27Sflorian 462ae8c6e27Sflorian int val_nsec_proves_insecuredelegation(struct ub_packed_rrset_key* nsec, 463ae8c6e27Sflorian struct query_info* qinfo) 464ae8c6e27Sflorian { 465ae8c6e27Sflorian if(nsec_has_type(nsec, LDNS_RR_TYPE_NS) && 466ae8c6e27Sflorian !nsec_has_type(nsec, LDNS_RR_TYPE_DS) && 467ae8c6e27Sflorian !nsec_has_type(nsec, LDNS_RR_TYPE_SOA)) { 468ae8c6e27Sflorian /* see if nsec signals an insecure delegation */ 469ae8c6e27Sflorian if(qinfo->qtype == LDNS_RR_TYPE_DS) { 470ae8c6e27Sflorian /* if type is DS and qname is equal to nsec, then it 471ae8c6e27Sflorian * is an exact match nsec, result not insecure */ 472ae8c6e27Sflorian if(dname_strict_subdomain_c(qinfo->qname, 473ae8c6e27Sflorian nsec->rk.dname)) 474ae8c6e27Sflorian return 1; 475ae8c6e27Sflorian } else { 476ae8c6e27Sflorian if(dname_subdomain_c(qinfo->qname, nsec->rk.dname)) 477ae8c6e27Sflorian return 1; 478ae8c6e27Sflorian } 479ae8c6e27Sflorian } 480ae8c6e27Sflorian return 0; 481ae8c6e27Sflorian } 482ae8c6e27Sflorian 483ae8c6e27Sflorian uint8_t* 484ae8c6e27Sflorian nsec_closest_encloser(uint8_t* qname, struct ub_packed_rrset_key* nsec) 485ae8c6e27Sflorian { 486ae8c6e27Sflorian uint8_t* next; 487ae8c6e27Sflorian size_t nlen; 488ae8c6e27Sflorian uint8_t* common1, *common2; 489ae8c6e27Sflorian if(!nsec_get_next(nsec, &next, &nlen)) 490ae8c6e27Sflorian return NULL; 491ae8c6e27Sflorian /* longest common with owner or next name */ 492ae8c6e27Sflorian common1 = dname_get_shared_topdomain(nsec->rk.dname, qname); 493ae8c6e27Sflorian common2 = dname_get_shared_topdomain(next, qname); 494ae8c6e27Sflorian if(dname_count_labels(common1) > dname_count_labels(common2)) 495ae8c6e27Sflorian return common1; 496ae8c6e27Sflorian return common2; 497ae8c6e27Sflorian } 498ae8c6e27Sflorian 499ae8c6e27Sflorian int val_nsec_proves_positive_wildcard(struct ub_packed_rrset_key* nsec, 500ae8c6e27Sflorian struct query_info* qinf, uint8_t* wc) 501ae8c6e27Sflorian { 502ae8c6e27Sflorian uint8_t* ce; 503ae8c6e27Sflorian /* 1) prove that qname doesn't exist and 504ae8c6e27Sflorian * 2) that the correct wildcard was used 505ae8c6e27Sflorian * nsec has been verified already. */ 506ae8c6e27Sflorian if(!val_nsec_proves_name_error(nsec, qinf->qname)) 507ae8c6e27Sflorian return 0; 508ae8c6e27Sflorian /* check wildcard name */ 509ae8c6e27Sflorian ce = nsec_closest_encloser(qinf->qname, nsec); 510ae8c6e27Sflorian if(!ce) 511ae8c6e27Sflorian return 0; 512ae8c6e27Sflorian if(query_dname_compare(wc, ce) != 0) { 513ae8c6e27Sflorian return 0; 514ae8c6e27Sflorian } 515ae8c6e27Sflorian return 1; 516ae8c6e27Sflorian } 517ae8c6e27Sflorian 518ae8c6e27Sflorian int 519ae8c6e27Sflorian val_nsec_proves_no_wc(struct ub_packed_rrset_key* nsec, uint8_t* qname, 520ae8c6e27Sflorian size_t qnamelen) 521ae8c6e27Sflorian { 522ae8c6e27Sflorian /* Determine if a NSEC record proves the non-existence of a 523ae8c6e27Sflorian * wildcard that could have produced qname. */ 524ae8c6e27Sflorian int labs; 525ae8c6e27Sflorian uint8_t* ce = nsec_closest_encloser(qname, nsec); 526ae8c6e27Sflorian uint8_t* strip; 527ae8c6e27Sflorian size_t striplen; 528ae8c6e27Sflorian uint8_t buf[LDNS_MAX_DOMAINLEN+3]; 529ae8c6e27Sflorian if(!ce) 530ae8c6e27Sflorian return 0; 531ae8c6e27Sflorian /* we can subtract the closest encloser count - since that is the 532ae8c6e27Sflorian * largest shared topdomain with owner and next NSEC name, 533ae8c6e27Sflorian * because the NSEC is no proof for names shorter than the owner 534ae8c6e27Sflorian * and next names. */ 535ae8c6e27Sflorian labs = dname_count_labels(qname) - dname_count_labels(ce); 536ae8c6e27Sflorian 537ae8c6e27Sflorian if(labs > 0) { 538ae8c6e27Sflorian /* i is number of labels to strip off qname, prepend * wild */ 539ae8c6e27Sflorian strip = qname; 540ae8c6e27Sflorian striplen = qnamelen; 541ae8c6e27Sflorian dname_remove_labels(&strip, &striplen, labs); 542ae8c6e27Sflorian if(striplen > LDNS_MAX_DOMAINLEN-2) 543ae8c6e27Sflorian return 0; /* too long to prepend wildcard */ 544ae8c6e27Sflorian buf[0] = 1; 545ae8c6e27Sflorian buf[1] = (uint8_t)'*'; 546ae8c6e27Sflorian memmove(buf+2, strip, striplen); 547ae8c6e27Sflorian if(val_nsec_proves_name_error(nsec, buf)) { 548ae8c6e27Sflorian return 1; 549ae8c6e27Sflorian } 550ae8c6e27Sflorian } 551ae8c6e27Sflorian return 0; 552ae8c6e27Sflorian } 553