1933707f3Ssthen /* 24bfc71b0Ssthen * validator/val_nsec3.c - validator NSEC3 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 NSEC3 checking, the different NSEC3 proofs 414bfc71b0Ssthen * for denial of existence, and proofs for presence of types. 42933707f3Ssthen */ 43933707f3Ssthen #include "config.h" 44933707f3Ssthen #include <ctype.h> 45933707f3Ssthen #include "validator/val_nsec3.h" 4624893edcSsthen #include "validator/val_secalgo.h" 47933707f3Ssthen #include "validator/validator.h" 48933707f3Ssthen #include "validator/val_kentry.h" 49933707f3Ssthen #include "services/cache/rrset.h" 50933707f3Ssthen #include "util/regional.h" 51933707f3Ssthen #include "util/rbtree.h" 52933707f3Ssthen #include "util/module.h" 53933707f3Ssthen #include "util/net_help.h" 54933707f3Ssthen #include "util/data/packed_rrset.h" 55933707f3Ssthen #include "util/data/dname.h" 56933707f3Ssthen #include "util/data/msgreply.h" 57933707f3Ssthen /* we include nsec.h for the bitmap_has_type function */ 58933707f3Ssthen #include "validator/val_nsec.h" 59fdfb4ba6Ssthen #include "sldns/sbuffer.h" 60817bdb8fSflorian #include "util/config_file.h" 61817bdb8fSflorian 62817bdb8fSflorian /** 63817bdb8fSflorian * Max number of NSEC3 calculations at once, suspend query for later. 64817bdb8fSflorian * 8 is low enough and allows for cases where multiple proofs are needed. 65817bdb8fSflorian */ 66817bdb8fSflorian #define MAX_NSEC3_CALCULATIONS 8 67817bdb8fSflorian /** 68817bdb8fSflorian * When all allowed NSEC3 calculations at once resulted in error treat as 69817bdb8fSflorian * bogus. NSEC3 hash errors are not cached and this helps breaks loops with 70817bdb8fSflorian * erroneous data. 71817bdb8fSflorian */ 72817bdb8fSflorian #define MAX_NSEC3_ERRORS -1 73933707f3Ssthen 74933707f3Ssthen /** 75933707f3Ssthen * This function we get from ldns-compat or from base system 76933707f3Ssthen * it returns the number of data bytes stored at the target, or <0 on error. 77933707f3Ssthen */ 785d76a658Ssthen int sldns_b32_ntop_extended_hex(uint8_t const *src, size_t srclength, 79933707f3Ssthen char *target, size_t targsize); 80933707f3Ssthen /** 81933707f3Ssthen * This function we get from ldns-compat or from base system 82933707f3Ssthen * it returns the number of data bytes stored at the target, or <0 on error. 83933707f3Ssthen */ 845d76a658Ssthen int sldns_b32_pton_extended_hex(char const *src, size_t hashed_owner_str_len, 85933707f3Ssthen uint8_t *target, size_t targsize); 86933707f3Ssthen 87933707f3Ssthen /** 88933707f3Ssthen * Closest encloser (ce) proof results 89933707f3Ssthen * Contains the ce and the next-closer (nc) proof. 90933707f3Ssthen */ 91933707f3Ssthen struct ce_response { 92933707f3Ssthen /** the closest encloser name */ 93933707f3Ssthen uint8_t* ce; 94933707f3Ssthen /** length of ce */ 95933707f3Ssthen size_t ce_len; 96933707f3Ssthen /** NSEC3 record that proved ce. rrset */ 97933707f3Ssthen struct ub_packed_rrset_key* ce_rrset; 98933707f3Ssthen /** NSEC3 record that proved ce. rr number */ 99933707f3Ssthen int ce_rr; 100933707f3Ssthen /** NSEC3 record that proved nc. rrset */ 101933707f3Ssthen struct ub_packed_rrset_key* nc_rrset; 102933707f3Ssthen /** NSEC3 record that proved nc. rr*/ 103933707f3Ssthen int nc_rr; 104933707f3Ssthen }; 105933707f3Ssthen 106933707f3Ssthen /** 107933707f3Ssthen * Filter conditions for NSEC3 proof 108933707f3Ssthen * Used to iterate over the applicable NSEC3 RRs. 109933707f3Ssthen */ 110933707f3Ssthen struct nsec3_filter { 111933707f3Ssthen /** Zone name, only NSEC3 records for this zone are considered */ 112933707f3Ssthen uint8_t* zone; 113933707f3Ssthen /** length of the zonename */ 114933707f3Ssthen size_t zone_len; 115933707f3Ssthen /** the list of NSEC3s to filter; array */ 116933707f3Ssthen struct ub_packed_rrset_key** list; 117933707f3Ssthen /** number of rrsets in list */ 118933707f3Ssthen size_t num; 119933707f3Ssthen /** class of records for the NSEC3, only this class applies */ 120933707f3Ssthen uint16_t fclass; 121933707f3Ssthen }; 122933707f3Ssthen 123933707f3Ssthen /** return number of rrs in an rrset */ 124933707f3Ssthen static size_t 125933707f3Ssthen rrset_get_count(struct ub_packed_rrset_key* rrset) 126933707f3Ssthen { 127933707f3Ssthen struct packed_rrset_data* d = (struct packed_rrset_data*) 128933707f3Ssthen rrset->entry.data; 129933707f3Ssthen if(!d) return 0; 130933707f3Ssthen return d->count; 131933707f3Ssthen } 132933707f3Ssthen 133933707f3Ssthen /** return if nsec3 RR has unknown flags */ 134933707f3Ssthen static int 135933707f3Ssthen nsec3_unknown_flags(struct ub_packed_rrset_key* rrset, int r) 136933707f3Ssthen { 137933707f3Ssthen struct packed_rrset_data* d = (struct packed_rrset_data*) 138933707f3Ssthen rrset->entry.data; 139933707f3Ssthen log_assert(d && r < (int)d->count); 140933707f3Ssthen if(d->rr_len[r] < 2+2) 141933707f3Ssthen return 0; /* malformed */ 142933707f3Ssthen return (int)(d->rr_data[r][2+1] & NSEC3_UNKNOWN_FLAGS); 143933707f3Ssthen } 144933707f3Ssthen 145933707f3Ssthen int 146933707f3Ssthen nsec3_has_optout(struct ub_packed_rrset_key* rrset, int r) 147933707f3Ssthen { 148933707f3Ssthen struct packed_rrset_data* d = (struct packed_rrset_data*) 149933707f3Ssthen rrset->entry.data; 150933707f3Ssthen log_assert(d && r < (int)d->count); 151933707f3Ssthen if(d->rr_len[r] < 2+2) 152933707f3Ssthen return 0; /* malformed */ 153933707f3Ssthen return (int)(d->rr_data[r][2+1] & NSEC3_OPTOUT); 154933707f3Ssthen } 155933707f3Ssthen 156933707f3Ssthen /** return nsec3 RR algorithm */ 157933707f3Ssthen static int 158933707f3Ssthen nsec3_get_algo(struct ub_packed_rrset_key* rrset, int r) 159933707f3Ssthen { 160933707f3Ssthen struct packed_rrset_data* d = (struct packed_rrset_data*) 161933707f3Ssthen rrset->entry.data; 162933707f3Ssthen log_assert(d && r < (int)d->count); 163933707f3Ssthen if(d->rr_len[r] < 2+1) 164933707f3Ssthen return 0; /* malformed */ 165933707f3Ssthen return (int)(d->rr_data[r][2+0]); 166933707f3Ssthen } 167933707f3Ssthen 168933707f3Ssthen /** return if nsec3 RR has known algorithm */ 169933707f3Ssthen static int 170933707f3Ssthen nsec3_known_algo(struct ub_packed_rrset_key* rrset, int r) 171933707f3Ssthen { 172933707f3Ssthen struct packed_rrset_data* d = (struct packed_rrset_data*) 173933707f3Ssthen rrset->entry.data; 174933707f3Ssthen log_assert(d && r < (int)d->count); 175933707f3Ssthen if(d->rr_len[r] < 2+1) 176933707f3Ssthen return 0; /* malformed */ 177933707f3Ssthen switch(d->rr_data[r][2+0]) { 178933707f3Ssthen case NSEC3_HASH_SHA1: 179933707f3Ssthen return 1; 180933707f3Ssthen } 181933707f3Ssthen return 0; 182933707f3Ssthen } 183933707f3Ssthen 184933707f3Ssthen /** return nsec3 RR iteration count */ 185933707f3Ssthen static size_t 186933707f3Ssthen nsec3_get_iter(struct ub_packed_rrset_key* rrset, int r) 187933707f3Ssthen { 188933707f3Ssthen uint16_t i; 189933707f3Ssthen struct packed_rrset_data* d = (struct packed_rrset_data*) 190933707f3Ssthen rrset->entry.data; 191933707f3Ssthen log_assert(d && r < (int)d->count); 192933707f3Ssthen if(d->rr_len[r] < 2+4) 193933707f3Ssthen return 0; /* malformed */ 194933707f3Ssthen memmove(&i, d->rr_data[r]+2+2, sizeof(i)); 195933707f3Ssthen i = ntohs(i); 196933707f3Ssthen return (size_t)i; 197933707f3Ssthen } 198933707f3Ssthen 199933707f3Ssthen /** return nsec3 RR salt */ 200933707f3Ssthen static int 201933707f3Ssthen nsec3_get_salt(struct ub_packed_rrset_key* rrset, int r, 202933707f3Ssthen uint8_t** salt, size_t* saltlen) 203933707f3Ssthen { 204933707f3Ssthen struct packed_rrset_data* d = (struct packed_rrset_data*) 205933707f3Ssthen rrset->entry.data; 206933707f3Ssthen log_assert(d && r < (int)d->count); 207933707f3Ssthen if(d->rr_len[r] < 2+5) { 208933707f3Ssthen *salt = 0; 209933707f3Ssthen *saltlen = 0; 210933707f3Ssthen return 0; /* malformed */ 211933707f3Ssthen } 212933707f3Ssthen *saltlen = (size_t)d->rr_data[r][2+4]; 213933707f3Ssthen if(d->rr_len[r] < 2+5+(size_t)*saltlen) { 214933707f3Ssthen *salt = 0; 215933707f3Ssthen *saltlen = 0; 216933707f3Ssthen return 0; /* malformed */ 217933707f3Ssthen } 218933707f3Ssthen *salt = d->rr_data[r]+2+5; 219933707f3Ssthen return 1; 220933707f3Ssthen } 221933707f3Ssthen 222933707f3Ssthen int nsec3_get_params(struct ub_packed_rrset_key* rrset, int r, 223933707f3Ssthen int* algo, size_t* iter, uint8_t** salt, size_t* saltlen) 224933707f3Ssthen { 225933707f3Ssthen if(!nsec3_known_algo(rrset, r) || nsec3_unknown_flags(rrset, r)) 226933707f3Ssthen return 0; 227933707f3Ssthen if(!nsec3_get_salt(rrset, r, salt, saltlen)) 228933707f3Ssthen return 0; 229933707f3Ssthen *algo = nsec3_get_algo(rrset, r); 230933707f3Ssthen *iter = nsec3_get_iter(rrset, r); 231933707f3Ssthen return 1; 232933707f3Ssthen } 233933707f3Ssthen 234933707f3Ssthen int 235933707f3Ssthen nsec3_get_nextowner(struct ub_packed_rrset_key* rrset, int r, 236933707f3Ssthen uint8_t** next, size_t* nextlen) 237933707f3Ssthen { 238933707f3Ssthen size_t saltlen; 239933707f3Ssthen struct packed_rrset_data* d = (struct packed_rrset_data*) 240933707f3Ssthen rrset->entry.data; 241933707f3Ssthen log_assert(d && r < (int)d->count); 242933707f3Ssthen if(d->rr_len[r] < 2+5) { 243933707f3Ssthen *next = 0; 244933707f3Ssthen *nextlen = 0; 245933707f3Ssthen return 0; /* malformed */ 246933707f3Ssthen } 247933707f3Ssthen saltlen = (size_t)d->rr_data[r][2+4]; 248933707f3Ssthen if(d->rr_len[r] < 2+5+saltlen+1) { 249933707f3Ssthen *next = 0; 250933707f3Ssthen *nextlen = 0; 251933707f3Ssthen return 0; /* malformed */ 252933707f3Ssthen } 253933707f3Ssthen *nextlen = (size_t)d->rr_data[r][2+5+saltlen]; 254933707f3Ssthen if(d->rr_len[r] < 2+5+saltlen+1+*nextlen) { 255933707f3Ssthen *next = 0; 256933707f3Ssthen *nextlen = 0; 257933707f3Ssthen return 0; /* malformed */ 258933707f3Ssthen } 259933707f3Ssthen *next = d->rr_data[r]+2+5+saltlen+1; 260933707f3Ssthen return 1; 261933707f3Ssthen } 262933707f3Ssthen 263933707f3Ssthen size_t nsec3_hash_to_b32(uint8_t* hash, size_t hashlen, uint8_t* zone, 264933707f3Ssthen size_t zonelen, uint8_t* buf, size_t max) 265933707f3Ssthen { 266933707f3Ssthen /* write b32 of name, leave one for length */ 267933707f3Ssthen int ret; 268933707f3Ssthen if(max < hashlen*2+1) /* quick approx of b32, as if hexb16 */ 269933707f3Ssthen return 0; 2705d76a658Ssthen ret = sldns_b32_ntop_extended_hex(hash, hashlen, (char*)buf+1, max-1); 271933707f3Ssthen if(ret < 1) 272933707f3Ssthen return 0; 273933707f3Ssthen buf[0] = (uint8_t)ret; /* length of b32 label */ 274933707f3Ssthen ret++; 275933707f3Ssthen if(max - ret < zonelen) 276933707f3Ssthen return 0; 277933707f3Ssthen memmove(buf+ret, zone, zonelen); 278933707f3Ssthen return zonelen+(size_t)ret; 279933707f3Ssthen } 280933707f3Ssthen 281933707f3Ssthen size_t nsec3_get_nextowner_b32(struct ub_packed_rrset_key* rrset, int r, 282933707f3Ssthen uint8_t* buf, size_t max) 283933707f3Ssthen { 284933707f3Ssthen uint8_t* nm, *zone; 285933707f3Ssthen size_t nmlen, zonelen; 286933707f3Ssthen if(!nsec3_get_nextowner(rrset, r, &nm, &nmlen)) 287933707f3Ssthen return 0; 288933707f3Ssthen /* append zone name; the owner name must be <b32>.zone */ 289933707f3Ssthen zone = rrset->rk.dname; 290933707f3Ssthen zonelen = rrset->rk.dname_len; 291933707f3Ssthen dname_remove_label(&zone, &zonelen); 292933707f3Ssthen return nsec3_hash_to_b32(nm, nmlen, zone, zonelen, buf, max); 293933707f3Ssthen } 294933707f3Ssthen 295933707f3Ssthen int 296933707f3Ssthen nsec3_has_type(struct ub_packed_rrset_key* rrset, int r, uint16_t type) 297933707f3Ssthen { 298933707f3Ssthen uint8_t* bitmap; 299933707f3Ssthen size_t bitlen, skiplen; 300933707f3Ssthen struct packed_rrset_data* d = (struct packed_rrset_data*) 301933707f3Ssthen rrset->entry.data; 302933707f3Ssthen log_assert(d && r < (int)d->count); 303933707f3Ssthen skiplen = 2+4; 304933707f3Ssthen /* skip salt */ 305933707f3Ssthen if(d->rr_len[r] < skiplen+1) 306933707f3Ssthen return 0; /* malformed, too short */ 307933707f3Ssthen skiplen += 1+(size_t)d->rr_data[r][skiplen]; 308933707f3Ssthen /* skip next hashed owner */ 309933707f3Ssthen if(d->rr_len[r] < skiplen+1) 310933707f3Ssthen return 0; /* malformed, too short */ 311933707f3Ssthen skiplen += 1+(size_t)d->rr_data[r][skiplen]; 312933707f3Ssthen if(d->rr_len[r] < skiplen) 313933707f3Ssthen return 0; /* malformed, too short */ 314933707f3Ssthen bitlen = d->rr_len[r] - skiplen; 315933707f3Ssthen bitmap = d->rr_data[r]+skiplen; 316933707f3Ssthen return nsecbitmap_has_type_rdata(bitmap, bitlen, type); 317933707f3Ssthen } 318933707f3Ssthen 319933707f3Ssthen /** 320933707f3Ssthen * Iterate through NSEC3 list, per RR 321933707f3Ssthen * This routine gives the next RR in the list (or sets rrset null). 322933707f3Ssthen * Usage: 323933707f3Ssthen * 324933707f3Ssthen * size_t rrsetnum; 325933707f3Ssthen * int rrnum; 326933707f3Ssthen * struct ub_packed_rrset_key* rrset; 327933707f3Ssthen * for(rrset=filter_first(filter, &rrsetnum, &rrnum); rrset; 328933707f3Ssthen * rrset=filter_next(filter, &rrsetnum, &rrnum)) 329933707f3Ssthen * do_stuff; 330933707f3Ssthen * 331933707f3Ssthen * Also filters out 332933707f3Ssthen * o unknown flag NSEC3s 333933707f3Ssthen * o unknown algorithm NSEC3s. 334933707f3Ssthen * @param filter: nsec3 filter structure. 335933707f3Ssthen * @param rrsetnum: in/out rrset number to look at. 336933707f3Ssthen * @param rrnum: in/out rr number in rrset to look at. 337933707f3Ssthen * @returns ptr to the next rrset (or NULL at end). 338933707f3Ssthen */ 339933707f3Ssthen static struct ub_packed_rrset_key* 340933707f3Ssthen filter_next(struct nsec3_filter* filter, size_t* rrsetnum, int* rrnum) 341933707f3Ssthen { 342933707f3Ssthen size_t i; 343933707f3Ssthen int r; 344933707f3Ssthen uint8_t* nm; 345933707f3Ssthen size_t nmlen; 346933707f3Ssthen if(!filter->zone) /* empty list */ 347933707f3Ssthen return NULL; 348933707f3Ssthen for(i=*rrsetnum; i<filter->num; i++) { 349933707f3Ssthen /* see if RRset qualifies */ 350933707f3Ssthen if(ntohs(filter->list[i]->rk.type) != LDNS_RR_TYPE_NSEC3 || 351933707f3Ssthen ntohs(filter->list[i]->rk.rrset_class) != 352933707f3Ssthen filter->fclass) 353933707f3Ssthen continue; 354933707f3Ssthen /* check RRset zone */ 355933707f3Ssthen nm = filter->list[i]->rk.dname; 356933707f3Ssthen nmlen = filter->list[i]->rk.dname_len; 357933707f3Ssthen dname_remove_label(&nm, &nmlen); 358933707f3Ssthen if(query_dname_compare(nm, filter->zone) != 0) 359933707f3Ssthen continue; 360933707f3Ssthen if(i == *rrsetnum) 361933707f3Ssthen r = (*rrnum) + 1; /* continue at next RR */ 362933707f3Ssthen else r = 0; /* new RRset start at first RR */ 363933707f3Ssthen for(; r < (int)rrset_get_count(filter->list[i]); r++) { 364933707f3Ssthen /* skip unknown flags, algo */ 365933707f3Ssthen if(nsec3_unknown_flags(filter->list[i], r) || 366933707f3Ssthen !nsec3_known_algo(filter->list[i], r)) 367933707f3Ssthen continue; 368933707f3Ssthen /* this one is a good target */ 369933707f3Ssthen *rrsetnum = i; 370933707f3Ssthen *rrnum = r; 371933707f3Ssthen return filter->list[i]; 372933707f3Ssthen } 373933707f3Ssthen } 374933707f3Ssthen return NULL; 375933707f3Ssthen } 376933707f3Ssthen 377933707f3Ssthen /** 378933707f3Ssthen * Start iterating over NSEC3 records. 379933707f3Ssthen * @param filter: the filter structure, must have been filter_init-ed. 38024893edcSsthen * @param rrsetnum: can be undefined on call, initialised. 38124893edcSsthen * @param rrnum: can be undefined on call, initialised. 382933707f3Ssthen * @return first rrset of an NSEC3, together with rrnum this points to 383933707f3Ssthen * the first RR to examine. Is NULL on empty list. 384933707f3Ssthen */ 385933707f3Ssthen static struct ub_packed_rrset_key* 386933707f3Ssthen filter_first(struct nsec3_filter* filter, size_t* rrsetnum, int* rrnum) 387933707f3Ssthen { 388933707f3Ssthen *rrsetnum = 0; 389933707f3Ssthen *rrnum = -1; 390933707f3Ssthen return filter_next(filter, rrsetnum, rrnum); 391933707f3Ssthen } 392933707f3Ssthen 393933707f3Ssthen /** see if at least one RR is known (flags, algo) */ 394933707f3Ssthen static int 395933707f3Ssthen nsec3_rrset_has_known(struct ub_packed_rrset_key* s) 396933707f3Ssthen { 397933707f3Ssthen int r; 398933707f3Ssthen for(r=0; r < (int)rrset_get_count(s); r++) { 399933707f3Ssthen if(!nsec3_unknown_flags(s, r) && nsec3_known_algo(s, r)) 400933707f3Ssthen return 1; 401933707f3Ssthen } 402933707f3Ssthen return 0; 403933707f3Ssthen } 404933707f3Ssthen 405933707f3Ssthen /** 406933707f3Ssthen * Initialize the filter structure. 407933707f3Ssthen * Finds the zone by looking at available NSEC3 records and best match. 408933707f3Ssthen * (skips the unknown flag and unknown algo NSEC3s). 409933707f3Ssthen * 410933707f3Ssthen * @param filter: nsec3 filter structure. 411933707f3Ssthen * @param list: list of rrsets, an array of them. 412933707f3Ssthen * @param num: number of rrsets in list. 413933707f3Ssthen * @param qinfo: 414933707f3Ssthen * query name to match a zone for. 415933707f3Ssthen * query type (if DS a higher zone must be chosen) 416933707f3Ssthen * qclass, to filter NSEC3s with. 417933707f3Ssthen */ 418933707f3Ssthen static void 419933707f3Ssthen filter_init(struct nsec3_filter* filter, struct ub_packed_rrset_key** list, 420933707f3Ssthen size_t num, struct query_info* qinfo) 421933707f3Ssthen { 422933707f3Ssthen size_t i; 423933707f3Ssthen uint8_t* nm; 424933707f3Ssthen size_t nmlen; 425933707f3Ssthen filter->zone = NULL; 426933707f3Ssthen filter->zone_len = 0; 427933707f3Ssthen filter->list = list; 428933707f3Ssthen filter->num = num; 429933707f3Ssthen filter->fclass = qinfo->qclass; 430933707f3Ssthen for(i=0; i<num; i++) { 431933707f3Ssthen /* ignore other stuff in the list */ 432933707f3Ssthen if(ntohs(list[i]->rk.type) != LDNS_RR_TYPE_NSEC3 || 433933707f3Ssthen ntohs(list[i]->rk.rrset_class) != qinfo->qclass) 434933707f3Ssthen continue; 435933707f3Ssthen /* skip unknown flags, algo */ 436933707f3Ssthen if(!nsec3_rrset_has_known(list[i])) 437933707f3Ssthen continue; 438933707f3Ssthen 439933707f3Ssthen /* since NSEC3s are base32.zonename, we can find the zone 440933707f3Ssthen * name by stripping off the first label of the record */ 441933707f3Ssthen nm = list[i]->rk.dname; 442933707f3Ssthen nmlen = list[i]->rk.dname_len; 443933707f3Ssthen dname_remove_label(&nm, &nmlen); 444933707f3Ssthen /* if we find a domain that can prove about the qname, 445933707f3Ssthen * and if this domain is closer to the qname */ 446933707f3Ssthen if(dname_subdomain_c(qinfo->qname, nm) && (!filter->zone || 447933707f3Ssthen dname_subdomain_c(nm, filter->zone))) { 448933707f3Ssthen /* for a type DS do not accept a zone equal to qname*/ 449933707f3Ssthen if(qinfo->qtype == LDNS_RR_TYPE_DS && 450933707f3Ssthen query_dname_compare(qinfo->qname, nm) == 0 && 451933707f3Ssthen !dname_is_root(qinfo->qname)) 452933707f3Ssthen continue; 453933707f3Ssthen filter->zone = nm; 454933707f3Ssthen filter->zone_len = nmlen; 455933707f3Ssthen } 456933707f3Ssthen } 457933707f3Ssthen } 458933707f3Ssthen 459933707f3Ssthen /** 460933707f3Ssthen * Find max iteration count using config settings and key size 461933707f3Ssthen * @param ve: validator environment with iteration count config settings. 462933707f3Ssthen * @param bits: key size 463933707f3Ssthen * @return max iteration count 464933707f3Ssthen */ 465933707f3Ssthen static size_t 466933707f3Ssthen get_max_iter(struct val_env* ve, size_t bits) 467933707f3Ssthen { 468933707f3Ssthen int i; 469933707f3Ssthen log_assert(ve->nsec3_keyiter_count > 0); 470933707f3Ssthen /* round up to nearest config keysize, linear search, keep it small */ 471933707f3Ssthen for(i=0; i<ve->nsec3_keyiter_count; i++) { 472933707f3Ssthen if(bits <= ve->nsec3_keysize[i]) 473933707f3Ssthen return ve->nsec3_maxiter[i]; 474933707f3Ssthen } 475933707f3Ssthen /* else, use value for biggest key */ 476933707f3Ssthen return ve->nsec3_maxiter[ve->nsec3_keyiter_count-1]; 477933707f3Ssthen } 478933707f3Ssthen 479933707f3Ssthen /** 480933707f3Ssthen * Determine if any of the NSEC3 rrs iteration count is too high, from key. 481933707f3Ssthen * @param ve: validator environment with iteration count config settings. 482933707f3Ssthen * @param filter: what NSEC3s to loop over. 483933707f3Ssthen * @param kkey: key entry used for verification; used for iteration counts. 484933707f3Ssthen * @return 1 if some nsec3s are above the max iteration count. 485933707f3Ssthen */ 486933707f3Ssthen static int 487933707f3Ssthen nsec3_iteration_count_high(struct val_env* ve, struct nsec3_filter* filter, 488933707f3Ssthen struct key_entry_key* kkey) 489933707f3Ssthen { 490933707f3Ssthen size_t rrsetnum; 491933707f3Ssthen int rrnum; 492933707f3Ssthen struct ub_packed_rrset_key* rrset; 493933707f3Ssthen /* first determine the max number of iterations */ 494933707f3Ssthen size_t bits = key_entry_keysize(kkey); 495933707f3Ssthen size_t max_iter = get_max_iter(ve, bits); 496933707f3Ssthen verbose(VERB_ALGO, "nsec3: keysize %d bits, max iterations %d", 497933707f3Ssthen (int)bits, (int)max_iter); 498933707f3Ssthen 499933707f3Ssthen for(rrset=filter_first(filter, &rrsetnum, &rrnum); rrset; 500933707f3Ssthen rrset=filter_next(filter, &rrsetnum, &rrnum)) { 501933707f3Ssthen if(nsec3_get_iter(rrset, rrnum) > max_iter) 502933707f3Ssthen return 1; 503933707f3Ssthen } 504933707f3Ssthen return 0; 505933707f3Ssthen } 506933707f3Ssthen 507933707f3Ssthen /* nsec3_cache_compare for rbtree */ 508933707f3Ssthen int 509933707f3Ssthen nsec3_hash_cmp(const void* c1, const void* c2) 510933707f3Ssthen { 511933707f3Ssthen struct nsec3_cached_hash* h1 = (struct nsec3_cached_hash*)c1; 512933707f3Ssthen struct nsec3_cached_hash* h2 = (struct nsec3_cached_hash*)c2; 513933707f3Ssthen uint8_t* s1, *s2; 514933707f3Ssthen size_t s1len, s2len; 515933707f3Ssthen int c = query_dname_compare(h1->dname, h2->dname); 516933707f3Ssthen if(c != 0) 517933707f3Ssthen return c; 518933707f3Ssthen /* compare parameters */ 519933707f3Ssthen /* if both malformed, its equal, robustness */ 520933707f3Ssthen if(nsec3_get_algo(h1->nsec3, h1->rr) != 521933707f3Ssthen nsec3_get_algo(h2->nsec3, h2->rr)) { 522933707f3Ssthen if(nsec3_get_algo(h1->nsec3, h1->rr) < 523933707f3Ssthen nsec3_get_algo(h2->nsec3, h2->rr)) 524933707f3Ssthen return -1; 525933707f3Ssthen return 1; 526933707f3Ssthen } 527933707f3Ssthen if(nsec3_get_iter(h1->nsec3, h1->rr) != 528933707f3Ssthen nsec3_get_iter(h2->nsec3, h2->rr)) { 529933707f3Ssthen if(nsec3_get_iter(h1->nsec3, h1->rr) < 530933707f3Ssthen nsec3_get_iter(h2->nsec3, h2->rr)) 531933707f3Ssthen return -1; 532933707f3Ssthen return 1; 533933707f3Ssthen } 534933707f3Ssthen (void)nsec3_get_salt(h1->nsec3, h1->rr, &s1, &s1len); 535933707f3Ssthen (void)nsec3_get_salt(h2->nsec3, h2->rr, &s2, &s2len); 536452a1548Ssthen if(s1len == 0 && s2len == 0) 537452a1548Ssthen return 0; 538452a1548Ssthen if(!s1) return -1; 539452a1548Ssthen if(!s2) return 1; 540933707f3Ssthen if(s1len != s2len) { 541933707f3Ssthen if(s1len < s2len) 542933707f3Ssthen return -1; 543933707f3Ssthen return 1; 544933707f3Ssthen } 545933707f3Ssthen return memcmp(s1, s2, s1len); 546933707f3Ssthen } 547933707f3Ssthen 548817bdb8fSflorian int 549817bdb8fSflorian nsec3_cache_table_init(struct nsec3_cache_table* ct, struct regional* region) 550817bdb8fSflorian { 551817bdb8fSflorian if(ct->ct) return 1; 552817bdb8fSflorian ct->ct = (rbtree_type*)regional_alloc(region, sizeof(*ct->ct)); 553817bdb8fSflorian if(!ct->ct) return 0; 554817bdb8fSflorian ct->region = region; 555817bdb8fSflorian rbtree_init(ct->ct, &nsec3_hash_cmp); 556817bdb8fSflorian return 1; 557817bdb8fSflorian } 558817bdb8fSflorian 559933707f3Ssthen size_t 5605d76a658Ssthen nsec3_get_hashed(sldns_buffer* buf, uint8_t* nm, size_t nmlen, int algo, 561933707f3Ssthen size_t iter, uint8_t* salt, size_t saltlen, uint8_t* res, size_t max) 562933707f3Ssthen { 563933707f3Ssthen size_t i, hash_len; 564933707f3Ssthen /* prepare buffer for first iteration */ 5655d76a658Ssthen sldns_buffer_clear(buf); 5665d76a658Ssthen sldns_buffer_write(buf, nm, nmlen); 5675d76a658Ssthen query_dname_tolower(sldns_buffer_begin(buf)); 5685d76a658Ssthen sldns_buffer_write(buf, salt, saltlen); 5695d76a658Ssthen sldns_buffer_flip(buf); 57024893edcSsthen hash_len = nsec3_hash_algo_size_supported(algo); 57124893edcSsthen if(hash_len == 0) { 57224893edcSsthen log_err("nsec3 hash of unknown algo %d", algo); 57324893edcSsthen return 0; 57424893edcSsthen } 575933707f3Ssthen if(hash_len > max) 576933707f3Ssthen return 0; 57724893edcSsthen if(!secalgo_nsec3_hash(algo, (unsigned char*)sldns_buffer_begin(buf), 57824893edcSsthen sldns_buffer_limit(buf), (unsigned char*)res)) 57924893edcSsthen return 0; 580933707f3Ssthen for(i=0; i<iter; i++) { 5815d76a658Ssthen sldns_buffer_clear(buf); 5825d76a658Ssthen sldns_buffer_write(buf, res, hash_len); 5835d76a658Ssthen sldns_buffer_write(buf, salt, saltlen); 5845d76a658Ssthen sldns_buffer_flip(buf); 58524893edcSsthen if(!secalgo_nsec3_hash(algo, 5865d76a658Ssthen (unsigned char*)sldns_buffer_begin(buf), 58724893edcSsthen sldns_buffer_limit(buf), (unsigned char*)res)) 588933707f3Ssthen return 0; 589933707f3Ssthen } 590933707f3Ssthen return hash_len; 591933707f3Ssthen } 592933707f3Ssthen 593933707f3Ssthen /** perform hash of name */ 594933707f3Ssthen static int 5955d76a658Ssthen nsec3_calc_hash(struct regional* region, sldns_buffer* buf, 596933707f3Ssthen struct nsec3_cached_hash* c) 597933707f3Ssthen { 598933707f3Ssthen int algo = nsec3_get_algo(c->nsec3, c->rr); 599933707f3Ssthen size_t iter = nsec3_get_iter(c->nsec3, c->rr); 600933707f3Ssthen uint8_t* salt; 601933707f3Ssthen size_t saltlen, i; 602933707f3Ssthen if(!nsec3_get_salt(c->nsec3, c->rr, &salt, &saltlen)) 603933707f3Ssthen return -1; 604933707f3Ssthen /* prepare buffer for first iteration */ 6055d76a658Ssthen sldns_buffer_clear(buf); 6065d76a658Ssthen sldns_buffer_write(buf, c->dname, c->dname_len); 6075d76a658Ssthen query_dname_tolower(sldns_buffer_begin(buf)); 6085d76a658Ssthen sldns_buffer_write(buf, salt, saltlen); 6095d76a658Ssthen sldns_buffer_flip(buf); 61024893edcSsthen c->hash_len = nsec3_hash_algo_size_supported(algo); 61124893edcSsthen if(c->hash_len == 0) { 61224893edcSsthen log_err("nsec3 hash of unknown algo %d", algo); 61324893edcSsthen return -1; 61424893edcSsthen } 61524893edcSsthen c->hash = (uint8_t*)regional_alloc(region, c->hash_len); 616933707f3Ssthen if(!c->hash) 617933707f3Ssthen return 0; 61824893edcSsthen (void)secalgo_nsec3_hash(algo, (unsigned char*)sldns_buffer_begin(buf), 61924893edcSsthen sldns_buffer_limit(buf), (unsigned char*)c->hash); 620933707f3Ssthen for(i=0; i<iter; i++) { 6215d76a658Ssthen sldns_buffer_clear(buf); 6225d76a658Ssthen sldns_buffer_write(buf, c->hash, c->hash_len); 6235d76a658Ssthen sldns_buffer_write(buf, salt, saltlen); 6245d76a658Ssthen sldns_buffer_flip(buf); 62524893edcSsthen (void)secalgo_nsec3_hash(algo, 6265d76a658Ssthen (unsigned char*)sldns_buffer_begin(buf), 62724893edcSsthen sldns_buffer_limit(buf), (unsigned char*)c->hash); 628933707f3Ssthen } 629933707f3Ssthen return 1; 630933707f3Ssthen } 631933707f3Ssthen 632933707f3Ssthen /** perform b32 encoding of hash */ 633933707f3Ssthen static int 6345d76a658Ssthen nsec3_calc_b32(struct regional* region, sldns_buffer* buf, 635933707f3Ssthen struct nsec3_cached_hash* c) 636933707f3Ssthen { 637933707f3Ssthen int r; 6385d76a658Ssthen sldns_buffer_clear(buf); 6395d76a658Ssthen r = sldns_b32_ntop_extended_hex(c->hash, c->hash_len, 6405d76a658Ssthen (char*)sldns_buffer_begin(buf), sldns_buffer_limit(buf)); 641933707f3Ssthen if(r < 1) { 642933707f3Ssthen log_err("b32_ntop_extended_hex: error in encoding: %d", r); 643933707f3Ssthen return 0; 644933707f3Ssthen } 645933707f3Ssthen c->b32_len = (size_t)r; 6465d76a658Ssthen c->b32 = regional_alloc_init(region, sldns_buffer_begin(buf), 647933707f3Ssthen c->b32_len); 648933707f3Ssthen if(!c->b32) 649933707f3Ssthen return 0; 650933707f3Ssthen return 1; 651933707f3Ssthen } 652933707f3Ssthen 653933707f3Ssthen int 65477079be7Ssthen nsec3_hash_name(rbtree_type* table, struct regional* region, sldns_buffer* buf, 655933707f3Ssthen struct ub_packed_rrset_key* nsec3, int rr, uint8_t* dname, 656933707f3Ssthen size_t dname_len, struct nsec3_cached_hash** hash) 657933707f3Ssthen { 658933707f3Ssthen struct nsec3_cached_hash* c; 659933707f3Ssthen struct nsec3_cached_hash looki; 660933707f3Ssthen #ifdef UNBOUND_DEBUG 66177079be7Ssthen rbnode_type* n; 662933707f3Ssthen #endif 663933707f3Ssthen int r; 664933707f3Ssthen looki.node.key = &looki; 665933707f3Ssthen looki.nsec3 = nsec3; 666933707f3Ssthen looki.rr = rr; 667933707f3Ssthen looki.dname = dname; 668933707f3Ssthen looki.dname_len = dname_len; 669933707f3Ssthen /* lookup first in cache */ 670933707f3Ssthen c = (struct nsec3_cached_hash*)rbtree_search(table, &looki); 671933707f3Ssthen if(c) { 672933707f3Ssthen *hash = c; 673817bdb8fSflorian return 2; 674933707f3Ssthen } 675933707f3Ssthen /* create a new entry */ 676933707f3Ssthen c = (struct nsec3_cached_hash*)regional_alloc(region, sizeof(*c)); 677933707f3Ssthen if(!c) return 0; 678933707f3Ssthen c->node.key = c; 679933707f3Ssthen c->nsec3 = nsec3; 680933707f3Ssthen c->rr = rr; 681933707f3Ssthen c->dname = dname; 682933707f3Ssthen c->dname_len = dname_len; 683933707f3Ssthen r = nsec3_calc_hash(region, buf, c); 684933707f3Ssthen if(r != 1) 685817bdb8fSflorian return r; /* returns -1 or 0 */ 686933707f3Ssthen r = nsec3_calc_b32(region, buf, c); 687933707f3Ssthen if(r != 1) 688817bdb8fSflorian return r; /* returns 0 */ 689933707f3Ssthen #ifdef UNBOUND_DEBUG 690933707f3Ssthen n = 691229e174cSsthen #else 692229e174cSsthen (void) 693933707f3Ssthen #endif 694933707f3Ssthen rbtree_insert(table, &c->node); 695933707f3Ssthen log_assert(n); /* cannot be duplicate, just did lookup */ 696933707f3Ssthen *hash = c; 697933707f3Ssthen return 1; 698933707f3Ssthen } 699933707f3Ssthen 700933707f3Ssthen /** 701933707f3Ssthen * compare a label lowercased 702933707f3Ssthen */ 703933707f3Ssthen static int 704933707f3Ssthen label_compare_lower(uint8_t* lab1, uint8_t* lab2, size_t lablen) 705933707f3Ssthen { 706933707f3Ssthen size_t i; 707933707f3Ssthen for(i=0; i<lablen; i++) { 70898f3ca02Sbrad if(tolower((unsigned char)*lab1) != tolower((unsigned char)*lab2)) { 70998f3ca02Sbrad if(tolower((unsigned char)*lab1) < tolower((unsigned char)*lab2)) 710933707f3Ssthen return -1; 711933707f3Ssthen return 1; 712933707f3Ssthen } 713933707f3Ssthen lab1++; 714933707f3Ssthen lab2++; 715933707f3Ssthen } 716933707f3Ssthen return 0; 717933707f3Ssthen } 718933707f3Ssthen 719933707f3Ssthen /** 720933707f3Ssthen * Compare a hashed name with the owner name of an NSEC3 RRset. 721933707f3Ssthen * @param flt: filter with zone name. 722933707f3Ssthen * @param hash: the hashed name. 723933707f3Ssthen * @param s: rrset with owner name. 724933707f3Ssthen * @return true if matches exactly, false if not. 725933707f3Ssthen */ 726933707f3Ssthen static int 727933707f3Ssthen nsec3_hash_matches_owner(struct nsec3_filter* flt, 728933707f3Ssthen struct nsec3_cached_hash* hash, struct ub_packed_rrset_key* s) 729933707f3Ssthen { 730933707f3Ssthen uint8_t* nm = s->rk.dname; 731817bdb8fSflorian if(!hash) return 0; /* please clang */ 732933707f3Ssthen /* compare, does hash of name based on params in this NSEC3 733933707f3Ssthen * match the owner name of this NSEC3? 734933707f3Ssthen * name must be: <hashlength>base32 . zone name 735933707f3Ssthen * so; first label must not be root label (not zero length), 736933707f3Ssthen * and match the b32 encoded hash length, 737933707f3Ssthen * and the label content match the b32 encoded hash 738933707f3Ssthen * and the rest must be the zone name. 739933707f3Ssthen */ 740933707f3Ssthen if(hash->b32_len != 0 && (size_t)nm[0] == hash->b32_len && 741933707f3Ssthen label_compare_lower(nm+1, hash->b32, hash->b32_len) == 0 && 742933707f3Ssthen query_dname_compare(nm+(size_t)nm[0]+1, flt->zone) == 0) { 743933707f3Ssthen return 1; 744933707f3Ssthen } 745933707f3Ssthen return 0; 746933707f3Ssthen } 747933707f3Ssthen 748933707f3Ssthen /** 749933707f3Ssthen * Find matching NSEC3 750933707f3Ssthen * Find the NSEC3Record that matches a hash of a name. 751933707f3Ssthen * @param env: module environment with temporary region and buffer. 752933707f3Ssthen * @param flt: the NSEC3 RR filter, contains zone name and RRs. 753933707f3Ssthen * @param ct: cached hashes table. 754933707f3Ssthen * @param nm: name to look for. 755933707f3Ssthen * @param nmlen: length of name. 756933707f3Ssthen * @param rrset: nsec3 that matches is returned here. 757933707f3Ssthen * @param rr: rr number in nsec3 rrset that matches. 758817bdb8fSflorian * @param calculations: current hash calculations. 759933707f3Ssthen * @return true if a matching NSEC3 is found, false if not. 760933707f3Ssthen */ 761933707f3Ssthen static int 762933707f3Ssthen find_matching_nsec3(struct module_env* env, struct nsec3_filter* flt, 763817bdb8fSflorian struct nsec3_cache_table* ct, uint8_t* nm, size_t nmlen, 764817bdb8fSflorian struct ub_packed_rrset_key** rrset, int* rr, 765817bdb8fSflorian int* calculations) 766933707f3Ssthen { 767933707f3Ssthen size_t i_rs; 768933707f3Ssthen int i_rr; 769933707f3Ssthen struct ub_packed_rrset_key* s; 770452a1548Ssthen struct nsec3_cached_hash* hash = NULL; 771933707f3Ssthen int r; 772817bdb8fSflorian int calc_errors = 0; 773933707f3Ssthen 774933707f3Ssthen /* this loop skips other-zone and unknown NSEC3s, also non-NSEC3 RRs */ 775933707f3Ssthen for(s=filter_first(flt, &i_rs, &i_rr); s; 776933707f3Ssthen s=filter_next(flt, &i_rs, &i_rr)) { 777817bdb8fSflorian /* check if we are allowed more calculations */ 778817bdb8fSflorian if(*calculations >= MAX_NSEC3_CALCULATIONS) { 779817bdb8fSflorian if(calc_errors == *calculations) { 780817bdb8fSflorian *calculations = MAX_NSEC3_ERRORS; 781817bdb8fSflorian } 782817bdb8fSflorian break; 783817bdb8fSflorian } 784933707f3Ssthen /* get name hashed for this NSEC3 RR */ 785817bdb8fSflorian r = nsec3_hash_name(ct->ct, ct->region, env->scratch_buffer, 786933707f3Ssthen s, i_rr, nm, nmlen, &hash); 787933707f3Ssthen if(r == 0) { 788933707f3Ssthen log_err("nsec3: malloc failure"); 789933707f3Ssthen break; /* alloc failure */ 790817bdb8fSflorian } else if(r < 0) { 791817bdb8fSflorian /* malformed NSEC3 */ 792817bdb8fSflorian calc_errors++; 793817bdb8fSflorian (*calculations)++; 794817bdb8fSflorian continue; 795817bdb8fSflorian } else { 796817bdb8fSflorian if(r == 1) (*calculations)++; 797817bdb8fSflorian if(nsec3_hash_matches_owner(flt, hash, s)) { 798933707f3Ssthen *rrset = s; /* rrset with this name */ 799933707f3Ssthen *rr = i_rr; /* matches hash with these parameters */ 800933707f3Ssthen return 1; 801933707f3Ssthen } 802933707f3Ssthen } 803817bdb8fSflorian } 804933707f3Ssthen *rrset = NULL; 805933707f3Ssthen *rr = 0; 806933707f3Ssthen return 0; 807933707f3Ssthen } 808933707f3Ssthen 809933707f3Ssthen int 810933707f3Ssthen nsec3_covers(uint8_t* zone, struct nsec3_cached_hash* hash, 8115d76a658Ssthen struct ub_packed_rrset_key* rrset, int rr, sldns_buffer* buf) 812933707f3Ssthen { 813933707f3Ssthen uint8_t* next, *owner; 814933707f3Ssthen size_t nextlen; 815933707f3Ssthen int len; 816933707f3Ssthen if(!nsec3_get_nextowner(rrset, rr, &next, &nextlen)) 817933707f3Ssthen return 0; /* malformed RR proves nothing */ 818933707f3Ssthen 819817bdb8fSflorian if(!hash) return 0; /* please clang */ 820933707f3Ssthen /* check the owner name is a hashed value . apex 821933707f3Ssthen * base32 encoded values must have equal length. 822933707f3Ssthen * hash_value and next hash value must have equal length. */ 823933707f3Ssthen if(nextlen != hash->hash_len || hash->hash_len==0||hash->b32_len==0|| 824933707f3Ssthen (size_t)*rrset->rk.dname != hash->b32_len || 825933707f3Ssthen query_dname_compare(rrset->rk.dname+1+ 826933707f3Ssthen (size_t)*rrset->rk.dname, zone) != 0) 827933707f3Ssthen return 0; /* bad lengths or owner name */ 828933707f3Ssthen 829933707f3Ssthen /* This is the "normal case: owner < next and owner < hash < next */ 830933707f3Ssthen if(label_compare_lower(rrset->rk.dname+1, hash->b32, 831933707f3Ssthen hash->b32_len) < 0 && 832933707f3Ssthen memcmp(hash->hash, next, nextlen) < 0) 833933707f3Ssthen return 1; 834933707f3Ssthen 835933707f3Ssthen /* convert owner name from text to binary */ 8365d76a658Ssthen sldns_buffer_clear(buf); 8375d76a658Ssthen owner = sldns_buffer_begin(buf); 8385d76a658Ssthen len = sldns_b32_pton_extended_hex((char*)rrset->rk.dname+1, 8395d76a658Ssthen hash->b32_len, owner, sldns_buffer_limit(buf)); 840933707f3Ssthen if(len<1) 841933707f3Ssthen return 0; /* bad owner name in some way */ 842933707f3Ssthen if((size_t)len != hash->hash_len || (size_t)len != nextlen) 843933707f3Ssthen return 0; /* wrong length */ 844933707f3Ssthen 845933707f3Ssthen /* this is the end of zone case: next <= owner && 846933707f3Ssthen * (hash > owner || hash < next) 847933707f3Ssthen * this also covers the only-apex case of next==owner. 848933707f3Ssthen */ 849933707f3Ssthen if(memcmp(next, owner, nextlen) <= 0 && 850933707f3Ssthen ( memcmp(hash->hash, owner, nextlen) > 0 || 851933707f3Ssthen memcmp(hash->hash, next, nextlen) < 0)) { 852933707f3Ssthen return 1; 853933707f3Ssthen } 854933707f3Ssthen return 0; 855933707f3Ssthen } 856933707f3Ssthen 857933707f3Ssthen /** 858933707f3Ssthen * findCoveringNSEC3 859933707f3Ssthen * Given a name, find a covering NSEC3 from among a list of NSEC3s. 860933707f3Ssthen * 861933707f3Ssthen * @param env: module environment with temporary region and buffer. 862933707f3Ssthen * @param flt: the NSEC3 RR filter, contains zone name and RRs. 863933707f3Ssthen * @param ct: cached hashes table. 864933707f3Ssthen * @param nm: name to check if covered. 865933707f3Ssthen * @param nmlen: length of name. 866933707f3Ssthen * @param rrset: covering NSEC3 rrset is returned here. 867933707f3Ssthen * @param rr: rr of cover is returned here. 868817bdb8fSflorian * @param calculations: current hash calculations. 869933707f3Ssthen * @return true if a covering NSEC3 is found, false if not. 870933707f3Ssthen */ 871933707f3Ssthen static int 872933707f3Ssthen find_covering_nsec3(struct module_env* env, struct nsec3_filter* flt, 873817bdb8fSflorian struct nsec3_cache_table* ct, uint8_t* nm, size_t nmlen, 874817bdb8fSflorian struct ub_packed_rrset_key** rrset, int* rr, 875817bdb8fSflorian int* calculations) 876933707f3Ssthen { 877933707f3Ssthen size_t i_rs; 878933707f3Ssthen int i_rr; 879933707f3Ssthen struct ub_packed_rrset_key* s; 880452a1548Ssthen struct nsec3_cached_hash* hash = NULL; 881933707f3Ssthen int r; 882817bdb8fSflorian int calc_errors = 0; 883933707f3Ssthen 884933707f3Ssthen /* this loop skips other-zone and unknown NSEC3s, also non-NSEC3 RRs */ 885933707f3Ssthen for(s=filter_first(flt, &i_rs, &i_rr); s; 886933707f3Ssthen s=filter_next(flt, &i_rs, &i_rr)) { 887817bdb8fSflorian /* check if we are allowed more calculations */ 888817bdb8fSflorian if(*calculations >= MAX_NSEC3_CALCULATIONS) { 889817bdb8fSflorian if(calc_errors == *calculations) { 890817bdb8fSflorian *calculations = MAX_NSEC3_ERRORS; 891817bdb8fSflorian } 892817bdb8fSflorian break; 893817bdb8fSflorian } 894933707f3Ssthen /* get name hashed for this NSEC3 RR */ 895817bdb8fSflorian r = nsec3_hash_name(ct->ct, ct->region, env->scratch_buffer, 896933707f3Ssthen s, i_rr, nm, nmlen, &hash); 897933707f3Ssthen if(r == 0) { 898933707f3Ssthen log_err("nsec3: malloc failure"); 899933707f3Ssthen break; /* alloc failure */ 900817bdb8fSflorian } else if(r < 0) { 901817bdb8fSflorian /* malformed NSEC3 */ 902817bdb8fSflorian calc_errors++; 903817bdb8fSflorian (*calculations)++; 904817bdb8fSflorian continue; 905817bdb8fSflorian } else { 906817bdb8fSflorian if(r == 1) (*calculations)++; 907817bdb8fSflorian if(nsec3_covers(flt->zone, hash, s, i_rr, 908933707f3Ssthen env->scratch_buffer)) { 909933707f3Ssthen *rrset = s; /* rrset with this name */ 910933707f3Ssthen *rr = i_rr; /* covers hash with these parameters */ 911933707f3Ssthen return 1; 912933707f3Ssthen } 913933707f3Ssthen } 914817bdb8fSflorian } 915933707f3Ssthen *rrset = NULL; 916933707f3Ssthen *rr = 0; 917933707f3Ssthen return 0; 918933707f3Ssthen } 919933707f3Ssthen 920933707f3Ssthen /** 921933707f3Ssthen * findClosestEncloser 922933707f3Ssthen * Given a name and a list of NSEC3s, find the candidate closest encloser. 923933707f3Ssthen * This will be the first ancestor of 'name' (including itself) to have a 924933707f3Ssthen * matching NSEC3 RR. 925933707f3Ssthen * @param env: module environment with temporary region and buffer. 926933707f3Ssthen * @param flt: the NSEC3 RR filter, contains zone name and RRs. 927933707f3Ssthen * @param ct: cached hashes table. 928933707f3Ssthen * @param qinfo: query that is verified for. 929933707f3Ssthen * @param ce: closest encloser information is returned in here. 930817bdb8fSflorian * @param calculations: current hash calculations. 931933707f3Ssthen * @return true if a closest encloser candidate is found, false if not. 932933707f3Ssthen */ 933933707f3Ssthen static int 934933707f3Ssthen nsec3_find_closest_encloser(struct module_env* env, struct nsec3_filter* flt, 935817bdb8fSflorian struct nsec3_cache_table* ct, struct query_info* qinfo, 936817bdb8fSflorian struct ce_response* ce, int* calculations) 937933707f3Ssthen { 938933707f3Ssthen uint8_t* nm = qinfo->qname; 939933707f3Ssthen size_t nmlen = qinfo->qname_len; 940933707f3Ssthen 941933707f3Ssthen /* This scans from longest name to shortest, so the first match 942933707f3Ssthen * we find is the only viable candidate. */ 943933707f3Ssthen 944933707f3Ssthen /* (David:) FIXME: modify so that the NSEC3 matching the zone apex need 945933707f3Ssthen * not be present. (Mark Andrews idea). 946933707f3Ssthen * (Wouter:) But make sure you check for DNAME bit in zone apex, 947933707f3Ssthen * if the NSEC3 you find is the only NSEC3 in the zone, then this 948933707f3Ssthen * may be the case. */ 949933707f3Ssthen 950933707f3Ssthen while(dname_subdomain_c(nm, flt->zone)) { 951817bdb8fSflorian if(*calculations >= MAX_NSEC3_CALCULATIONS || 952817bdb8fSflorian *calculations == MAX_NSEC3_ERRORS) { 953817bdb8fSflorian return 0; 954817bdb8fSflorian } 955933707f3Ssthen if(find_matching_nsec3(env, flt, ct, nm, nmlen, 956817bdb8fSflorian &ce->ce_rrset, &ce->ce_rr, calculations)) { 957933707f3Ssthen ce->ce = nm; 958933707f3Ssthen ce->ce_len = nmlen; 959933707f3Ssthen return 1; 960933707f3Ssthen } 961933707f3Ssthen dname_remove_label(&nm, &nmlen); 962933707f3Ssthen } 963933707f3Ssthen return 0; 964933707f3Ssthen } 965933707f3Ssthen 966933707f3Ssthen /** 967933707f3Ssthen * Given a qname and its proven closest encloser, calculate the "next 968933707f3Ssthen * closest" name. Basically, this is the name that is one label longer than 969933707f3Ssthen * the closest encloser that is still a subdomain of qname. 970933707f3Ssthen * 971933707f3Ssthen * @param qname: query name. 972933707f3Ssthen * @param qnamelen: length of qname. 973933707f3Ssthen * @param ce: closest encloser 974933707f3Ssthen * @param nm: result name. 975933707f3Ssthen * @param nmlen: length of nm. 976933707f3Ssthen */ 977933707f3Ssthen static void 978933707f3Ssthen next_closer(uint8_t* qname, size_t qnamelen, uint8_t* ce, 979933707f3Ssthen uint8_t** nm, size_t* nmlen) 980933707f3Ssthen { 981933707f3Ssthen int strip = dname_count_labels(qname) - dname_count_labels(ce) -1; 982933707f3Ssthen *nm = qname; 983933707f3Ssthen *nmlen = qnamelen; 984933707f3Ssthen if(strip>0) 985933707f3Ssthen dname_remove_labels(nm, nmlen, strip); 986933707f3Ssthen } 987933707f3Ssthen 988933707f3Ssthen /** 989933707f3Ssthen * proveClosestEncloser 990933707f3Ssthen * Given a List of nsec3 RRs, find and prove the closest encloser to qname. 991933707f3Ssthen * @param env: module environment with temporary region and buffer. 992933707f3Ssthen * @param flt: the NSEC3 RR filter, contains zone name and RRs. 993933707f3Ssthen * @param ct: cached hashes table. 994933707f3Ssthen * @param qinfo: query that is verified for. 995933707f3Ssthen * @param prove_does_not_exist: If true, then if the closest encloser 996933707f3Ssthen * turns out to be qname, then null is returned. 997933707f3Ssthen * If set true, and the return value is true, then you can be 998933707f3Ssthen * certain that the ce.nc_rrset and ce.nc_rr are set properly. 999933707f3Ssthen * @param ce: closest encloser information is returned in here. 1000817bdb8fSflorian * @param calculations: pointer to the current NSEC3 hash calculations. 1001933707f3Ssthen * @return bogus if no closest encloser could be proven. 1002933707f3Ssthen * secure if a closest encloser could be proven, ce is set. 1003933707f3Ssthen * insecure if the closest-encloser candidate turns out to prove 1004933707f3Ssthen * that an insecure delegation exists above the qname. 1005817bdb8fSflorian * unchecked if no more hash calculations are allowed at this point. 1006933707f3Ssthen */ 1007933707f3Ssthen static enum sec_status 1008933707f3Ssthen nsec3_prove_closest_encloser(struct module_env* env, struct nsec3_filter* flt, 1009817bdb8fSflorian struct nsec3_cache_table* ct, struct query_info* qinfo, 1010817bdb8fSflorian int prove_does_not_exist, struct ce_response* ce, int* calculations) 1011933707f3Ssthen { 1012933707f3Ssthen uint8_t* nc; 1013933707f3Ssthen size_t nc_len; 1014933707f3Ssthen /* robust: clean out ce, in case it gets abused later */ 1015933707f3Ssthen memset(ce, 0, sizeof(*ce)); 1016933707f3Ssthen 1017817bdb8fSflorian if(!nsec3_find_closest_encloser(env, flt, ct, qinfo, ce, calculations)) { 1018817bdb8fSflorian if(*calculations == MAX_NSEC3_ERRORS) { 1019817bdb8fSflorian verbose(VERB_ALGO, "nsec3 proveClosestEncloser: could " 1020817bdb8fSflorian "not find a candidate for the closest " 1021817bdb8fSflorian "encloser; all attempted hash calculations " 1022817bdb8fSflorian "were erroneous; bogus"); 1023817bdb8fSflorian return sec_status_bogus; 1024817bdb8fSflorian } else if(*calculations >= MAX_NSEC3_CALCULATIONS) { 1025817bdb8fSflorian verbose(VERB_ALGO, "nsec3 proveClosestEncloser: could " 1026817bdb8fSflorian "not find a candidate for the closest " 1027817bdb8fSflorian "encloser; reached MAX_NSEC3_CALCULATIONS " 1028817bdb8fSflorian "(%d); unchecked still", 1029817bdb8fSflorian MAX_NSEC3_CALCULATIONS); 1030817bdb8fSflorian return sec_status_unchecked; 1031817bdb8fSflorian } 1032933707f3Ssthen verbose(VERB_ALGO, "nsec3 proveClosestEncloser: could " 1033933707f3Ssthen "not find a candidate for the closest encloser."); 1034933707f3Ssthen return sec_status_bogus; 1035933707f3Ssthen } 1036933707f3Ssthen log_nametypeclass(VERB_ALGO, "ce candidate", ce->ce, 0, 0); 1037933707f3Ssthen 1038933707f3Ssthen if(query_dname_compare(ce->ce, qinfo->qname) == 0) { 1039933707f3Ssthen if(prove_does_not_exist) { 1040933707f3Ssthen verbose(VERB_ALGO, "nsec3 proveClosestEncloser: " 1041933707f3Ssthen "proved that qname existed, bad"); 1042933707f3Ssthen return sec_status_bogus; 1043933707f3Ssthen } 1044933707f3Ssthen /* otherwise, we need to nothing else to prove that qname 1045933707f3Ssthen * is its own closest encloser. */ 1046933707f3Ssthen return sec_status_secure; 1047933707f3Ssthen } 1048933707f3Ssthen 1049933707f3Ssthen /* If the closest encloser is actually a delegation, then the 1050933707f3Ssthen * response should have been a referral. If it is a DNAME, then 1051933707f3Ssthen * it should have been a DNAME response. */ 1052933707f3Ssthen if(nsec3_has_type(ce->ce_rrset, ce->ce_rr, LDNS_RR_TYPE_NS) && 1053933707f3Ssthen !nsec3_has_type(ce->ce_rrset, ce->ce_rr, LDNS_RR_TYPE_SOA)) { 1054933707f3Ssthen if(!nsec3_has_type(ce->ce_rrset, ce->ce_rr, LDNS_RR_TYPE_DS)) { 1055933707f3Ssthen verbose(VERB_ALGO, "nsec3 proveClosestEncloser: " 1056933707f3Ssthen "closest encloser is insecure delegation"); 1057933707f3Ssthen return sec_status_insecure; 1058933707f3Ssthen } 1059933707f3Ssthen verbose(VERB_ALGO, "nsec3 proveClosestEncloser: closest " 1060933707f3Ssthen "encloser was a delegation, bad"); 1061933707f3Ssthen return sec_status_bogus; 1062933707f3Ssthen } 1063933707f3Ssthen if(nsec3_has_type(ce->ce_rrset, ce->ce_rr, LDNS_RR_TYPE_DNAME)) { 1064933707f3Ssthen verbose(VERB_ALGO, "nsec3 proveClosestEncloser: closest " 1065933707f3Ssthen "encloser was a DNAME, bad"); 1066933707f3Ssthen return sec_status_bogus; 1067933707f3Ssthen } 1068933707f3Ssthen 1069933707f3Ssthen /* Otherwise, we need to show that the next closer name is covered. */ 1070933707f3Ssthen next_closer(qinfo->qname, qinfo->qname_len, ce->ce, &nc, &nc_len); 1071933707f3Ssthen if(!find_covering_nsec3(env, flt, ct, nc, nc_len, 1072817bdb8fSflorian &ce->nc_rrset, &ce->nc_rr, calculations)) { 1073817bdb8fSflorian if(*calculations == MAX_NSEC3_ERRORS) { 1074817bdb8fSflorian verbose(VERB_ALGO, "nsec3: Could not find proof that the " 1075817bdb8fSflorian "candidate encloser was the closest encloser; " 1076817bdb8fSflorian "all attempted hash calculations were " 1077817bdb8fSflorian "erroneous; bogus"); 1078817bdb8fSflorian return sec_status_bogus; 1079817bdb8fSflorian } else if(*calculations >= MAX_NSEC3_CALCULATIONS) { 1080817bdb8fSflorian verbose(VERB_ALGO, "nsec3: Could not find proof that the " 1081817bdb8fSflorian "candidate encloser was the closest encloser; " 1082817bdb8fSflorian "reached MAX_NSEC3_CALCULATIONS (%d); " 1083817bdb8fSflorian "unchecked still", 1084817bdb8fSflorian MAX_NSEC3_CALCULATIONS); 1085817bdb8fSflorian return sec_status_unchecked; 1086817bdb8fSflorian } 1087933707f3Ssthen verbose(VERB_ALGO, "nsec3: Could not find proof that the " 1088933707f3Ssthen "candidate encloser was the closest encloser"); 1089933707f3Ssthen return sec_status_bogus; 1090933707f3Ssthen } 1091933707f3Ssthen return sec_status_secure; 1092933707f3Ssthen } 1093933707f3Ssthen 1094933707f3Ssthen /** allocate a wildcard for the closest encloser */ 1095933707f3Ssthen static uint8_t* 1096933707f3Ssthen nsec3_ce_wildcard(struct regional* region, uint8_t* ce, size_t celen, 1097933707f3Ssthen size_t* len) 1098933707f3Ssthen { 1099933707f3Ssthen uint8_t* nm; 1100933707f3Ssthen if(celen > LDNS_MAX_DOMAINLEN - 2) 1101933707f3Ssthen return 0; /* too long */ 1102933707f3Ssthen nm = (uint8_t*)regional_alloc(region, celen+2); 1103933707f3Ssthen if(!nm) { 1104933707f3Ssthen log_err("nsec3 wildcard: out of memory"); 1105933707f3Ssthen return 0; /* alloc failure */ 1106933707f3Ssthen } 1107933707f3Ssthen nm[0] = 1; 1108933707f3Ssthen nm[1] = (uint8_t)'*'; /* wildcard label */ 1109933707f3Ssthen memmove(nm+2, ce, celen); 1110933707f3Ssthen *len = celen+2; 1111933707f3Ssthen return nm; 1112933707f3Ssthen } 1113933707f3Ssthen 1114933707f3Ssthen /** Do the name error proof */ 1115933707f3Ssthen static enum sec_status 1116933707f3Ssthen nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt, 1117817bdb8fSflorian struct nsec3_cache_table* ct, struct query_info* qinfo, int* calc) 1118933707f3Ssthen { 1119933707f3Ssthen struct ce_response ce; 1120933707f3Ssthen uint8_t* wc; 1121933707f3Ssthen size_t wclen; 1122933707f3Ssthen struct ub_packed_rrset_key* wc_rrset; 1123933707f3Ssthen int wc_rr; 1124933707f3Ssthen enum sec_status sec; 1125933707f3Ssthen 1126933707f3Ssthen /* First locate and prove the closest encloser to qname. We will 1127933707f3Ssthen * use the variant that fails if the closest encloser turns out 1128933707f3Ssthen * to be qname. */ 1129817bdb8fSflorian sec = nsec3_prove_closest_encloser(env, flt, ct, qinfo, 1, &ce, calc); 1130933707f3Ssthen if(sec != sec_status_secure) { 1131933707f3Ssthen if(sec == sec_status_bogus) 1132933707f3Ssthen verbose(VERB_ALGO, "nsec3 nameerror proof: failed " 1133933707f3Ssthen "to prove a closest encloser"); 1134817bdb8fSflorian else if(sec == sec_status_unchecked) 1135817bdb8fSflorian verbose(VERB_ALGO, "nsec3 nameerror proof: will " 1136817bdb8fSflorian "continue proving closest encloser after " 1137817bdb8fSflorian "suspend"); 1138933707f3Ssthen else verbose(VERB_ALGO, "nsec3 nameerror proof: closest " 1139933707f3Ssthen "nsec3 is an insecure delegation"); 1140933707f3Ssthen return sec; 1141933707f3Ssthen } 1142bdfc4d55Sflorian log_nametypeclass(VERB_ALGO, "nsec3 nameerror: proven ce=", ce.ce,0,0); 1143933707f3Ssthen 1144933707f3Ssthen /* At this point, we know that qname does not exist. Now we need 1145933707f3Ssthen * to prove that the wildcard does not exist. */ 1146933707f3Ssthen log_assert(ce.ce); 1147817bdb8fSflorian wc = nsec3_ce_wildcard(ct->region, ce.ce, ce.ce_len, &wclen); 1148817bdb8fSflorian if(!wc) { 1149817bdb8fSflorian verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove " 1150817bdb8fSflorian "that the applicable wildcard did not exist."); 1151817bdb8fSflorian return sec_status_bogus; 1152817bdb8fSflorian } 1153817bdb8fSflorian if(!find_covering_nsec3(env, flt, ct, wc, wclen, &wc_rrset, &wc_rr, calc)) { 1154817bdb8fSflorian if(*calc == MAX_NSEC3_ERRORS) { 1155817bdb8fSflorian verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove " 1156817bdb8fSflorian "that the applicable wildcard did not exist; " 1157817bdb8fSflorian "all attempted hash calculations were " 1158817bdb8fSflorian "erroneous; bogus"); 1159817bdb8fSflorian return sec_status_bogus; 1160817bdb8fSflorian } else if(*calc >= MAX_NSEC3_CALCULATIONS) { 1161817bdb8fSflorian verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove " 1162817bdb8fSflorian "that the applicable wildcard did not exist; " 1163817bdb8fSflorian "reached MAX_NSEC3_CALCULATIONS (%d); " 1164817bdb8fSflorian "unchecked still", 1165817bdb8fSflorian MAX_NSEC3_CALCULATIONS); 1166817bdb8fSflorian return sec_status_unchecked; 1167817bdb8fSflorian } 1168933707f3Ssthen verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove " 1169933707f3Ssthen "that the applicable wildcard did not exist."); 1170933707f3Ssthen return sec_status_bogus; 1171933707f3Ssthen } 1172933707f3Ssthen 1173933707f3Ssthen if(ce.nc_rrset && nsec3_has_optout(ce.nc_rrset, ce.nc_rr)) { 1174933707f3Ssthen verbose(VERB_ALGO, "nsec3 nameerror proof: nc has optout"); 1175933707f3Ssthen return sec_status_insecure; 1176933707f3Ssthen } 1177933707f3Ssthen return sec_status_secure; 1178933707f3Ssthen } 1179933707f3Ssthen 1180933707f3Ssthen enum sec_status 1181933707f3Ssthen nsec3_prove_nameerror(struct module_env* env, struct val_env* ve, 1182933707f3Ssthen struct ub_packed_rrset_key** list, size_t num, 1183817bdb8fSflorian struct query_info* qinfo, struct key_entry_key* kkey, 1184817bdb8fSflorian struct nsec3_cache_table* ct, int* calc) 1185933707f3Ssthen { 1186933707f3Ssthen struct nsec3_filter flt; 1187933707f3Ssthen 1188933707f3Ssthen if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) 1189933707f3Ssthen return sec_status_bogus; /* no valid NSEC3s, bogus */ 1190933707f3Ssthen filter_init(&flt, list, num, qinfo); /* init RR iterator */ 1191933707f3Ssthen if(!flt.zone) 1192933707f3Ssthen return sec_status_bogus; /* no RRs */ 1193933707f3Ssthen if(nsec3_iteration_count_high(ve, &flt, kkey)) 1194933707f3Ssthen return sec_status_insecure; /* iteration count too high */ 1195933707f3Ssthen log_nametypeclass(VERB_ALGO, "start nsec3 nameerror proof, zone", 1196933707f3Ssthen flt.zone, 0, 0); 1197817bdb8fSflorian return nsec3_do_prove_nameerror(env, &flt, ct, qinfo, calc); 1198933707f3Ssthen } 1199933707f3Ssthen 1200933707f3Ssthen /* 1201933707f3Ssthen * No code to handle qtype=NSEC3 specially. 1202933707f3Ssthen * This existed in early drafts, but was later (-05) removed. 1203933707f3Ssthen */ 1204933707f3Ssthen 1205933707f3Ssthen /** Do the nodata proof */ 1206933707f3Ssthen static enum sec_status 1207933707f3Ssthen nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, 1208817bdb8fSflorian struct nsec3_cache_table* ct, struct query_info* qinfo, 1209817bdb8fSflorian int* calc) 1210933707f3Ssthen { 1211933707f3Ssthen struct ce_response ce; 1212933707f3Ssthen uint8_t* wc; 1213933707f3Ssthen size_t wclen; 1214933707f3Ssthen struct ub_packed_rrset_key* rrset; 1215933707f3Ssthen int rr; 1216933707f3Ssthen enum sec_status sec; 1217933707f3Ssthen 1218933707f3Ssthen if(find_matching_nsec3(env, flt, ct, qinfo->qname, qinfo->qname_len, 1219817bdb8fSflorian &rrset, &rr, calc)) { 1220933707f3Ssthen /* cases 1 and 2 */ 1221933707f3Ssthen if(nsec3_has_type(rrset, rr, qinfo->qtype)) { 1222933707f3Ssthen verbose(VERB_ALGO, "proveNodata: Matching NSEC3 " 1223933707f3Ssthen "proved that type existed, bogus"); 1224933707f3Ssthen return sec_status_bogus; 1225933707f3Ssthen } else if(nsec3_has_type(rrset, rr, LDNS_RR_TYPE_CNAME)) { 1226933707f3Ssthen verbose(VERB_ALGO, "proveNodata: Matching NSEC3 " 1227933707f3Ssthen "proved that a CNAME existed, bogus"); 1228933707f3Ssthen return sec_status_bogus; 1229933707f3Ssthen } 1230933707f3Ssthen 1231933707f3Ssthen /* 1232933707f3Ssthen * If type DS: filter_init zone find already found a parent 1233933707f3Ssthen * zone, so this nsec3 is from a parent zone. 1234933707f3Ssthen * o can be not a delegation (unusual query for normal name, 1235933707f3Ssthen * no DS anyway, but we can verify that). 1236933707f3Ssthen * o can be a delegation (which is the usual DS check). 1237933707f3Ssthen * o may not have the SOA bit set (only the top of the 1238933707f3Ssthen * zone, which must have been above the name, has that). 1239933707f3Ssthen * Except for the root; which is checked by itself. 1240933707f3Ssthen * 1241933707f3Ssthen * If not type DS: matching nsec3 must not be a delegation. 1242933707f3Ssthen */ 1243933707f3Ssthen if(qinfo->qtype == LDNS_RR_TYPE_DS && qinfo->qname_len != 1 12443dcb24b8Ssthen && nsec3_has_type(rrset, rr, LDNS_RR_TYPE_SOA) && 12453dcb24b8Ssthen !dname_is_root(qinfo->qname)) { 1246933707f3Ssthen verbose(VERB_ALGO, "proveNodata: apex NSEC3 " 1247933707f3Ssthen "abused for no DS proof, bogus"); 1248933707f3Ssthen return sec_status_bogus; 1249933707f3Ssthen } else if(qinfo->qtype != LDNS_RR_TYPE_DS && 1250933707f3Ssthen nsec3_has_type(rrset, rr, LDNS_RR_TYPE_NS) && 1251933707f3Ssthen !nsec3_has_type(rrset, rr, LDNS_RR_TYPE_SOA)) { 1252933707f3Ssthen if(!nsec3_has_type(rrset, rr, LDNS_RR_TYPE_DS)) { 1253933707f3Ssthen verbose(VERB_ALGO, "proveNodata: matching " 1254933707f3Ssthen "NSEC3 is insecure delegation"); 1255933707f3Ssthen return sec_status_insecure; 1256933707f3Ssthen } 1257933707f3Ssthen verbose(VERB_ALGO, "proveNodata: matching " 1258933707f3Ssthen "NSEC3 is a delegation, bogus"); 1259933707f3Ssthen return sec_status_bogus; 1260933707f3Ssthen } 1261933707f3Ssthen return sec_status_secure; 1262933707f3Ssthen } 1263817bdb8fSflorian if(*calc == MAX_NSEC3_ERRORS) { 1264817bdb8fSflorian verbose(VERB_ALGO, "proveNodata: all attempted hash " 1265817bdb8fSflorian "calculations were erroneous while finding a matching " 1266817bdb8fSflorian "NSEC3, bogus"); 1267817bdb8fSflorian return sec_status_bogus; 1268817bdb8fSflorian } else if(*calc >= MAX_NSEC3_CALCULATIONS) { 1269817bdb8fSflorian verbose(VERB_ALGO, "proveNodata: reached " 1270817bdb8fSflorian "MAX_NSEC3_CALCULATIONS (%d) while finding a " 1271817bdb8fSflorian "matching NSEC3; unchecked still", 1272817bdb8fSflorian MAX_NSEC3_CALCULATIONS); 1273817bdb8fSflorian return sec_status_unchecked; 1274817bdb8fSflorian } 1275933707f3Ssthen 1276933707f3Ssthen /* For cases 3 - 5, we need the proven closest encloser, and it 1277933707f3Ssthen * can't match qname. Although, at this point, we know that it 1278933707f3Ssthen * won't since we just checked that. */ 1279817bdb8fSflorian sec = nsec3_prove_closest_encloser(env, flt, ct, qinfo, 1, &ce, calc); 1280933707f3Ssthen if(sec == sec_status_bogus) { 1281933707f3Ssthen verbose(VERB_ALGO, "proveNodata: did not match qname, " 1282933707f3Ssthen "nor found a proven closest encloser."); 1283933707f3Ssthen return sec_status_bogus; 1284933707f3Ssthen } else if(sec==sec_status_insecure && qinfo->qtype!=LDNS_RR_TYPE_DS){ 1285933707f3Ssthen verbose(VERB_ALGO, "proveNodata: closest nsec3 is insecure " 1286933707f3Ssthen "delegation."); 1287933707f3Ssthen return sec_status_insecure; 1288817bdb8fSflorian } else if(sec==sec_status_unchecked) { 1289817bdb8fSflorian return sec_status_unchecked; 1290933707f3Ssthen } 1291933707f3Ssthen 1292933707f3Ssthen /* Case 3: removed */ 1293933707f3Ssthen 1294933707f3Ssthen /* Case 4: */ 1295933707f3Ssthen log_assert(ce.ce); 1296817bdb8fSflorian wc = nsec3_ce_wildcard(ct->region, ce.ce, ce.ce_len, &wclen); 1297817bdb8fSflorian if(wc && find_matching_nsec3(env, flt, ct, wc, wclen, &rrset, &rr, 1298817bdb8fSflorian calc)) { 1299933707f3Ssthen /* found wildcard */ 1300933707f3Ssthen if(nsec3_has_type(rrset, rr, qinfo->qtype)) { 1301933707f3Ssthen verbose(VERB_ALGO, "nsec3 nodata proof: matching " 1302933707f3Ssthen "wildcard had qtype, bogus"); 1303933707f3Ssthen return sec_status_bogus; 1304933707f3Ssthen } else if(nsec3_has_type(rrset, rr, LDNS_RR_TYPE_CNAME)) { 1305933707f3Ssthen verbose(VERB_ALGO, "nsec3 nodata proof: matching " 1306933707f3Ssthen "wildcard had a CNAME, bogus"); 1307933707f3Ssthen return sec_status_bogus; 1308933707f3Ssthen } 1309933707f3Ssthen if(qinfo->qtype == LDNS_RR_TYPE_DS && qinfo->qname_len != 1 1310933707f3Ssthen && nsec3_has_type(rrset, rr, LDNS_RR_TYPE_SOA)) { 1311933707f3Ssthen verbose(VERB_ALGO, "nsec3 nodata proof: matching " 1312933707f3Ssthen "wildcard for no DS proof has a SOA, bogus"); 1313933707f3Ssthen return sec_status_bogus; 1314933707f3Ssthen } else if(qinfo->qtype != LDNS_RR_TYPE_DS && 1315933707f3Ssthen nsec3_has_type(rrset, rr, LDNS_RR_TYPE_NS) && 1316933707f3Ssthen !nsec3_has_type(rrset, rr, LDNS_RR_TYPE_SOA)) { 1317933707f3Ssthen verbose(VERB_ALGO, "nsec3 nodata proof: matching " 131877079be7Ssthen "wildcard is a delegation, bogus"); 1319933707f3Ssthen return sec_status_bogus; 1320933707f3Ssthen } 1321933707f3Ssthen /* everything is peachy keen, except for optout spans */ 1322933707f3Ssthen if(ce.nc_rrset && nsec3_has_optout(ce.nc_rrset, ce.nc_rr)) { 1323933707f3Ssthen verbose(VERB_ALGO, "nsec3 nodata proof: matching " 1324933707f3Ssthen "wildcard is in optout range, insecure"); 1325933707f3Ssthen return sec_status_insecure; 1326933707f3Ssthen } 1327933707f3Ssthen return sec_status_secure; 1328933707f3Ssthen } 1329817bdb8fSflorian if(*calc == MAX_NSEC3_ERRORS) { 1330817bdb8fSflorian verbose(VERB_ALGO, "nsec3 nodata proof: all attempted hash " 1331817bdb8fSflorian "calculations were erroneous while matching " 1332817bdb8fSflorian "wildcard, bogus"); 1333817bdb8fSflorian return sec_status_bogus; 1334817bdb8fSflorian } else if(*calc >= MAX_NSEC3_CALCULATIONS) { 1335817bdb8fSflorian verbose(VERB_ALGO, "nsec3 nodata proof: reached " 1336817bdb8fSflorian "MAX_NSEC3_CALCULATIONS (%d) while matching " 1337817bdb8fSflorian "wildcard, unchecked still", 1338817bdb8fSflorian MAX_NSEC3_CALCULATIONS); 1339817bdb8fSflorian return sec_status_unchecked; 1340817bdb8fSflorian } 1341933707f3Ssthen 1342933707f3Ssthen /* Case 5: */ 1343933707f3Ssthen /* Due to forwarders, cnames, and other collating effects, we 1344933707f3Ssthen * can see the ordinary unsigned data from a zone beneath an 1345933707f3Ssthen * insecure delegation under an optout here */ 1346933707f3Ssthen if(!ce.nc_rrset) { 1347933707f3Ssthen verbose(VERB_ALGO, "nsec3 nodata proof: no next closer nsec3"); 1348933707f3Ssthen return sec_status_bogus; 1349933707f3Ssthen } 1350933707f3Ssthen 1351933707f3Ssthen /* We need to make sure that the covering NSEC3 is opt-out. */ 1352933707f3Ssthen log_assert(ce.nc_rrset); 1353933707f3Ssthen if(!nsec3_has_optout(ce.nc_rrset, ce.nc_rr)) { 1354933707f3Ssthen if(qinfo->qtype == LDNS_RR_TYPE_DS) 1355933707f3Ssthen verbose(VERB_ALGO, "proveNodata: covering NSEC3 was not " 1356933707f3Ssthen "opt-out in an opt-out DS NOERROR/NODATA case."); 1357933707f3Ssthen else verbose(VERB_ALGO, "proveNodata: could not find matching " 1358933707f3Ssthen "NSEC3, nor matching wildcard, nor optout NSEC3 " 1359933707f3Ssthen "-- no more options, bogus."); 1360933707f3Ssthen return sec_status_bogus; 1361933707f3Ssthen } 1362933707f3Ssthen /* RFC5155 section 9.2: if nc has optout then no AD flag set */ 1363933707f3Ssthen return sec_status_insecure; 1364933707f3Ssthen } 1365933707f3Ssthen 1366933707f3Ssthen enum sec_status 1367933707f3Ssthen nsec3_prove_nodata(struct module_env* env, struct val_env* ve, 1368933707f3Ssthen struct ub_packed_rrset_key** list, size_t num, 1369817bdb8fSflorian struct query_info* qinfo, struct key_entry_key* kkey, 1370817bdb8fSflorian struct nsec3_cache_table* ct, int* calc) 1371933707f3Ssthen { 1372933707f3Ssthen struct nsec3_filter flt; 1373933707f3Ssthen 1374933707f3Ssthen if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) 1375933707f3Ssthen return sec_status_bogus; /* no valid NSEC3s, bogus */ 1376933707f3Ssthen filter_init(&flt, list, num, qinfo); /* init RR iterator */ 1377933707f3Ssthen if(!flt.zone) 1378933707f3Ssthen return sec_status_bogus; /* no RRs */ 1379933707f3Ssthen if(nsec3_iteration_count_high(ve, &flt, kkey)) 1380933707f3Ssthen return sec_status_insecure; /* iteration count too high */ 1381817bdb8fSflorian return nsec3_do_prove_nodata(env, &flt, ct, qinfo, calc); 1382933707f3Ssthen } 1383933707f3Ssthen 1384933707f3Ssthen enum sec_status 1385933707f3Ssthen nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, 1386933707f3Ssthen struct ub_packed_rrset_key** list, size_t num, 1387817bdb8fSflorian struct query_info* qinfo, struct key_entry_key* kkey, uint8_t* wc, 1388817bdb8fSflorian struct nsec3_cache_table* ct, int* calc) 1389933707f3Ssthen { 1390933707f3Ssthen struct nsec3_filter flt; 1391933707f3Ssthen struct ce_response ce; 1392933707f3Ssthen uint8_t* nc; 1393933707f3Ssthen size_t nc_len; 1394933707f3Ssthen size_t wclen; 1395933707f3Ssthen (void)dname_count_size_labels(wc, &wclen); 1396933707f3Ssthen 1397933707f3Ssthen if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) 1398933707f3Ssthen return sec_status_bogus; /* no valid NSEC3s, bogus */ 1399933707f3Ssthen filter_init(&flt, list, num, qinfo); /* init RR iterator */ 1400933707f3Ssthen if(!flt.zone) 1401933707f3Ssthen return sec_status_bogus; /* no RRs */ 1402933707f3Ssthen if(nsec3_iteration_count_high(ve, &flt, kkey)) 1403933707f3Ssthen return sec_status_insecure; /* iteration count too high */ 1404933707f3Ssthen 1405933707f3Ssthen /* We know what the (purported) closest encloser is by just 1406933707f3Ssthen * looking at the supposed generating wildcard. 1407933707f3Ssthen * The *. has already been removed from the wc name. 1408933707f3Ssthen */ 1409933707f3Ssthen memset(&ce, 0, sizeof(ce)); 1410933707f3Ssthen ce.ce = wc; 1411933707f3Ssthen ce.ce_len = wclen; 1412933707f3Ssthen 1413933707f3Ssthen /* Now we still need to prove that the original data did not exist. 1414933707f3Ssthen * Otherwise, we need to show that the next closer name is covered. */ 1415933707f3Ssthen next_closer(qinfo->qname, qinfo->qname_len, ce.ce, &nc, &nc_len); 1416817bdb8fSflorian if(!find_covering_nsec3(env, &flt, ct, nc, nc_len, 1417817bdb8fSflorian &ce.nc_rrset, &ce.nc_rr, calc)) { 1418817bdb8fSflorian if(*calc == MAX_NSEC3_ERRORS) { 1419817bdb8fSflorian verbose(VERB_ALGO, "proveWildcard: did not find a " 1420817bdb8fSflorian "covering NSEC3 that covered the next closer " 1421817bdb8fSflorian "name; all attempted hash calculations were " 1422817bdb8fSflorian "erroneous; bogus"); 1423817bdb8fSflorian return sec_status_bogus; 1424817bdb8fSflorian } else if(*calc >= MAX_NSEC3_CALCULATIONS) { 1425817bdb8fSflorian verbose(VERB_ALGO, "proveWildcard: did not find a " 1426817bdb8fSflorian "covering NSEC3 that covered the next closer " 1427817bdb8fSflorian "name; reached MAX_NSEC3_CALCULATIONS " 1428817bdb8fSflorian "(%d); unchecked still", 1429817bdb8fSflorian MAX_NSEC3_CALCULATIONS); 1430817bdb8fSflorian return sec_status_unchecked; 1431817bdb8fSflorian } 1432933707f3Ssthen verbose(VERB_ALGO, "proveWildcard: did not find a covering " 1433933707f3Ssthen "NSEC3 that covered the next closer name."); 1434933707f3Ssthen return sec_status_bogus; 1435933707f3Ssthen } 1436933707f3Ssthen if(ce.nc_rrset && nsec3_has_optout(ce.nc_rrset, ce.nc_rr)) { 1437933707f3Ssthen verbose(VERB_ALGO, "proveWildcard: NSEC3 optout"); 1438933707f3Ssthen return sec_status_insecure; 1439933707f3Ssthen } 1440933707f3Ssthen return sec_status_secure; 1441933707f3Ssthen } 1442933707f3Ssthen 1443933707f3Ssthen /** test if list is all secure */ 1444933707f3Ssthen static int 1445933707f3Ssthen list_is_secure(struct module_env* env, struct val_env* ve, 1446933707f3Ssthen struct ub_packed_rrset_key** list, size_t num, 14470bdb4f62Ssthen struct key_entry_key* kkey, char** reason, sldns_ede_code *reason_bogus, 1448*98bc733bSsthen struct module_qstate* qstate, char* reasonbuf, size_t reasonlen) 1449933707f3Ssthen { 1450933707f3Ssthen struct packed_rrset_data* d; 1451933707f3Ssthen size_t i; 1452817bdb8fSflorian int verified = 0; 1453933707f3Ssthen for(i=0; i<num; i++) { 1454933707f3Ssthen d = (struct packed_rrset_data*)list[i]->entry.data; 1455933707f3Ssthen if(list[i]->rk.type != htons(LDNS_RR_TYPE_NSEC3)) 1456933707f3Ssthen continue; 1457933707f3Ssthen if(d->security == sec_status_secure) 1458933707f3Ssthen continue; 1459933707f3Ssthen rrset_check_sec_status(env->rrset_cache, list[i], *env->now); 1460933707f3Ssthen if(d->security == sec_status_secure) 1461933707f3Ssthen continue; 1462933707f3Ssthen d->security = val_verify_rrset_entry(env, ve, list[i], kkey, 1463817bdb8fSflorian reason, reason_bogus, LDNS_SECTION_AUTHORITY, qstate, 1464*98bc733bSsthen &verified, reasonbuf, reasonlen); 1465933707f3Ssthen if(d->security != sec_status_secure) { 1466933707f3Ssthen verbose(VERB_ALGO, "NSEC3 did not verify"); 1467933707f3Ssthen return 0; 1468933707f3Ssthen } 1469933707f3Ssthen rrset_update_sec_status(env->rrset_cache, list[i], *env->now); 1470933707f3Ssthen } 1471933707f3Ssthen return 1; 1472933707f3Ssthen } 1473933707f3Ssthen 1474933707f3Ssthen enum sec_status 1475933707f3Ssthen nsec3_prove_nods(struct module_env* env, struct val_env* ve, 1476933707f3Ssthen struct ub_packed_rrset_key** list, size_t num, 1477bdfc4d55Sflorian struct query_info* qinfo, struct key_entry_key* kkey, char** reason, 1478817bdb8fSflorian sldns_ede_code* reason_bogus, struct module_qstate* qstate, 1479*98bc733bSsthen struct nsec3_cache_table* ct, char* reasonbuf, size_t reasonlen) 1480933707f3Ssthen { 1481933707f3Ssthen struct nsec3_filter flt; 1482933707f3Ssthen struct ce_response ce; 1483933707f3Ssthen struct ub_packed_rrset_key* rrset; 1484933707f3Ssthen int rr; 1485817bdb8fSflorian int calc = 0; 1486817bdb8fSflorian enum sec_status sec; 1487817bdb8fSflorian 1488933707f3Ssthen log_assert(qinfo->qtype == LDNS_RR_TYPE_DS); 1489933707f3Ssthen 1490933707f3Ssthen if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) { 1491933707f3Ssthen *reason = "no valid NSEC3s"; 1492933707f3Ssthen return sec_status_bogus; /* no valid NSEC3s, bogus */ 1493933707f3Ssthen } 1494*98bc733bSsthen if(!list_is_secure(env, ve, list, num, kkey, reason, reason_bogus, 1495*98bc733bSsthen qstate, reasonbuf, reasonlen)) { 14960bdb4f62Ssthen *reason = "not all NSEC3 records secure"; 1497933707f3Ssthen return sec_status_bogus; /* not all NSEC3 records secure */ 14980bdb4f62Ssthen } 1499933707f3Ssthen filter_init(&flt, list, num, qinfo); /* init RR iterator */ 1500933707f3Ssthen if(!flt.zone) { 1501933707f3Ssthen *reason = "no NSEC3 records"; 1502933707f3Ssthen return sec_status_bogus; /* no RRs */ 1503933707f3Ssthen } 1504933707f3Ssthen if(nsec3_iteration_count_high(ve, &flt, kkey)) 1505933707f3Ssthen return sec_status_insecure; /* iteration count too high */ 1506933707f3Ssthen 1507933707f3Ssthen /* Look for a matching NSEC3 to qname -- this is the normal 1508933707f3Ssthen * NODATA case. */ 1509817bdb8fSflorian if(find_matching_nsec3(env, &flt, ct, qinfo->qname, qinfo->qname_len, 1510817bdb8fSflorian &rrset, &rr, &calc)) { 1511933707f3Ssthen /* If the matching NSEC3 has the SOA bit set, it is from 1512933707f3Ssthen * the wrong zone (the child instead of the parent). If 1513933707f3Ssthen * it has the DS bit set, then we were lied to. */ 1514933707f3Ssthen if(nsec3_has_type(rrset, rr, LDNS_RR_TYPE_SOA) && 1515933707f3Ssthen qinfo->qname_len != 1) { 1516933707f3Ssthen verbose(VERB_ALGO, "nsec3 provenods: NSEC3 is from" 1517933707f3Ssthen " child zone, bogus"); 1518933707f3Ssthen *reason = "NSEC3 from child zone"; 1519933707f3Ssthen return sec_status_bogus; 1520933707f3Ssthen } else if(nsec3_has_type(rrset, rr, LDNS_RR_TYPE_DS)) { 1521933707f3Ssthen verbose(VERB_ALGO, "nsec3 provenods: NSEC3 has qtype" 1522933707f3Ssthen " DS, bogus"); 1523933707f3Ssthen *reason = "NSEC3 has DS in bitmap"; 1524933707f3Ssthen return sec_status_bogus; 1525933707f3Ssthen } 1526933707f3Ssthen /* If the NSEC3 RR doesn't have the NS bit set, then 1527933707f3Ssthen * this wasn't a delegation point. */ 1528933707f3Ssthen if(!nsec3_has_type(rrset, rr, LDNS_RR_TYPE_NS)) 1529933707f3Ssthen return sec_status_indeterminate; 1530933707f3Ssthen /* Otherwise, this proves no DS. */ 1531933707f3Ssthen return sec_status_secure; 1532933707f3Ssthen } 1533817bdb8fSflorian if(calc == MAX_NSEC3_ERRORS) { 1534817bdb8fSflorian verbose(VERB_ALGO, "nsec3 provenods: all attempted hash " 1535817bdb8fSflorian "calculations were erroneous while finding a matching " 1536817bdb8fSflorian "NSEC3, bogus"); 1537817bdb8fSflorian return sec_status_bogus; 1538817bdb8fSflorian } else if(calc >= MAX_NSEC3_CALCULATIONS) { 1539817bdb8fSflorian verbose(VERB_ALGO, "nsec3 provenods: reached " 1540817bdb8fSflorian "MAX_NSEC3_CALCULATIONS (%d) while finding a " 1541817bdb8fSflorian "matching NSEC3, unchecked still", 1542817bdb8fSflorian MAX_NSEC3_CALCULATIONS); 1543817bdb8fSflorian return sec_status_unchecked; 1544817bdb8fSflorian } 1545933707f3Ssthen 1546933707f3Ssthen /* Otherwise, we are probably in the opt-out case. */ 1547817bdb8fSflorian sec = nsec3_prove_closest_encloser(env, &flt, ct, qinfo, 1, &ce, &calc); 1548817bdb8fSflorian if(sec == sec_status_unchecked) { 1549817bdb8fSflorian return sec_status_unchecked; 1550817bdb8fSflorian } else if(sec != sec_status_secure) { 1551933707f3Ssthen /* an insecure delegation *above* the qname does not prove 1552933707f3Ssthen * anything about this qname exactly, and bogus is bogus */ 1553933707f3Ssthen verbose(VERB_ALGO, "nsec3 provenods: did not match qname, " 1554933707f3Ssthen "nor found a proven closest encloser."); 1555933707f3Ssthen *reason = "no NSEC3 closest encloser"; 1556933707f3Ssthen return sec_status_bogus; 1557933707f3Ssthen } 1558933707f3Ssthen 1559933707f3Ssthen /* robust extra check */ 1560933707f3Ssthen if(!ce.nc_rrset) { 1561933707f3Ssthen verbose(VERB_ALGO, "nsec3 nods proof: no next closer nsec3"); 1562933707f3Ssthen *reason = "no NSEC3 next closer"; 1563933707f3Ssthen return sec_status_bogus; 1564933707f3Ssthen } 1565933707f3Ssthen 1566933707f3Ssthen /* we had the closest encloser proof, then we need to check that the 1567933707f3Ssthen * covering NSEC3 was opt-out -- the proveClosestEncloser step already 1568933707f3Ssthen * checked to see if the closest encloser was a delegation or DNAME. 1569933707f3Ssthen */ 1570933707f3Ssthen log_assert(ce.nc_rrset); 1571933707f3Ssthen if(!nsec3_has_optout(ce.nc_rrset, ce.nc_rr)) { 1572933707f3Ssthen verbose(VERB_ALGO, "nsec3 provenods: covering NSEC3 was not " 1573933707f3Ssthen "opt-out in an opt-out DS NOERROR/NODATA case."); 1574933707f3Ssthen *reason = "covering NSEC3 was not opt-out in an opt-out " 1575933707f3Ssthen "DS NOERROR/NODATA case"; 1576933707f3Ssthen return sec_status_bogus; 1577933707f3Ssthen } 1578933707f3Ssthen /* RFC5155 section 9.2: if nc has optout then no AD flag set */ 1579933707f3Ssthen return sec_status_insecure; 1580933707f3Ssthen } 1581933707f3Ssthen 1582933707f3Ssthen enum sec_status 1583933707f3Ssthen nsec3_prove_nxornodata(struct module_env* env, struct val_env* ve, 1584933707f3Ssthen struct ub_packed_rrset_key** list, size_t num, 1585817bdb8fSflorian struct query_info* qinfo, struct key_entry_key* kkey, int* nodata, 1586817bdb8fSflorian struct nsec3_cache_table* ct, int* calc) 1587933707f3Ssthen { 1588933707f3Ssthen enum sec_status sec, secnx; 1589933707f3Ssthen struct nsec3_filter flt; 1590933707f3Ssthen *nodata = 0; 1591933707f3Ssthen 1592933707f3Ssthen if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) 1593933707f3Ssthen return sec_status_bogus; /* no valid NSEC3s, bogus */ 1594933707f3Ssthen filter_init(&flt, list, num, qinfo); /* init RR iterator */ 1595933707f3Ssthen if(!flt.zone) 1596933707f3Ssthen return sec_status_bogus; /* no RRs */ 1597933707f3Ssthen if(nsec3_iteration_count_high(ve, &flt, kkey)) 1598933707f3Ssthen return sec_status_insecure; /* iteration count too high */ 1599933707f3Ssthen 1600933707f3Ssthen /* try nxdomain and nodata after another, while keeping the 1601933707f3Ssthen * hash cache intact */ 1602933707f3Ssthen 1603817bdb8fSflorian secnx = nsec3_do_prove_nameerror(env, &flt, ct, qinfo, calc); 1604933707f3Ssthen if(secnx==sec_status_secure) 1605933707f3Ssthen return sec_status_secure; 1606817bdb8fSflorian else if(secnx == sec_status_unchecked) 1607817bdb8fSflorian return sec_status_unchecked; 1608817bdb8fSflorian sec = nsec3_do_prove_nodata(env, &flt, ct, qinfo, calc); 1609933707f3Ssthen if(sec==sec_status_secure) { 1610933707f3Ssthen *nodata = 1; 1611933707f3Ssthen } else if(sec == sec_status_insecure) { 1612933707f3Ssthen *nodata = 1; 1613933707f3Ssthen } else if(secnx == sec_status_insecure) { 1614933707f3Ssthen sec = sec_status_insecure; 1615817bdb8fSflorian } else if(sec == sec_status_unchecked) { 1616817bdb8fSflorian return sec_status_unchecked; 1617933707f3Ssthen } 1618933707f3Ssthen return sec; 1619933707f3Ssthen } 1620