1933707f3Ssthen /* 2933707f3Ssthen * validator/validator.c - secure validator DNS query response module 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 a module that performs validation of DNS queries. 40933707f3Ssthen * According to RFC 4034. 41933707f3Ssthen */ 42933707f3Ssthen #include "config.h" 4320237c55Ssthen #include <ctype.h> 44933707f3Ssthen #include "validator/validator.h" 45933707f3Ssthen #include "validator/val_anchor.h" 46933707f3Ssthen #include "validator/val_kcache.h" 47933707f3Ssthen #include "validator/val_kentry.h" 48933707f3Ssthen #include "validator/val_utils.h" 49933707f3Ssthen #include "validator/val_nsec.h" 50933707f3Ssthen #include "validator/val_nsec3.h" 51933707f3Ssthen #include "validator/val_neg.h" 52933707f3Ssthen #include "validator/val_sigcrypt.h" 53933707f3Ssthen #include "validator/autotrust.h" 54933707f3Ssthen #include "services/cache/dns.h" 55938a3a5eSflorian #include "services/cache/rrset.h" 56933707f3Ssthen #include "util/data/dname.h" 57933707f3Ssthen #include "util/module.h" 58933707f3Ssthen #include "util/log.h" 59933707f3Ssthen #include "util/net_help.h" 60933707f3Ssthen #include "util/regional.h" 61933707f3Ssthen #include "util/config_file.h" 62933707f3Ssthen #include "util/fptr_wlist.h" 63a58bff56Ssthen #include "sldns/rrdef.h" 64a58bff56Ssthen #include "sldns/wire2str.h" 652be9e038Ssthen #include "sldns/str2wire.h" 66933707f3Ssthen 67817bdb8fSflorian /** Max number of RRSIGs to validate at once, suspend query for later. */ 68817bdb8fSflorian #define MAX_VALIDATE_AT_ONCE 8 69817bdb8fSflorian /** Max number of validation suspends allowed, error out otherwise. */ 70817bdb8fSflorian #define MAX_VALIDATION_SUSPENDS 16 71817bdb8fSflorian 72933707f3Ssthen /* forward decl for cache response and normal super inform calls of a DS */ 73933707f3Ssthen static void process_ds_response(struct module_qstate* qstate, 74933707f3Ssthen struct val_qstate* vq, int id, int rcode, struct dns_msg* msg, 75*98bc733bSsthen struct query_info* qinfo, struct sock_list* origin, int* suspend, 76*98bc733bSsthen struct module_qstate* sub_qstate); 77933707f3Ssthen 780bdb4f62Ssthen 798b7325afSsthen /* Updates the suplied EDE (RFC8914) code selectively so we don't lose 808b7325afSsthen * a more specific code */ 810bdb4f62Ssthen static void 820bdb4f62Ssthen update_reason_bogus(struct reply_info* rep, sldns_ede_code reason_bogus) 830bdb4f62Ssthen { 848b7325afSsthen if(reason_bogus == LDNS_EDE_NONE) return; 858b7325afSsthen if(reason_bogus == LDNS_EDE_DNSSEC_BOGUS 868b7325afSsthen && rep->reason_bogus != LDNS_EDE_NONE 878b7325afSsthen && rep->reason_bogus != LDNS_EDE_DNSSEC_BOGUS) return; 880bdb4f62Ssthen rep->reason_bogus = reason_bogus; 890bdb4f62Ssthen } 900bdb4f62Ssthen 910bdb4f62Ssthen 92933707f3Ssthen /** fill up nsec3 key iterations config entry */ 93933707f3Ssthen static int 94933707f3Ssthen fill_nsec3_iter(struct val_env* ve, char* s, int c) 95933707f3Ssthen { 96933707f3Ssthen char* e; 97933707f3Ssthen int i; 98933707f3Ssthen free(ve->nsec3_keysize); 99933707f3Ssthen free(ve->nsec3_maxiter); 100933707f3Ssthen ve->nsec3_keysize = (size_t*)calloc(sizeof(size_t), (size_t)c); 101933707f3Ssthen ve->nsec3_maxiter = (size_t*)calloc(sizeof(size_t), (size_t)c); 102933707f3Ssthen if(!ve->nsec3_keysize || !ve->nsec3_maxiter) { 103933707f3Ssthen log_err("out of memory"); 104933707f3Ssthen return 0; 105933707f3Ssthen } 106933707f3Ssthen for(i=0; i<c; i++) { 107933707f3Ssthen ve->nsec3_keysize[i] = (size_t)strtol(s, &e, 10); 108933707f3Ssthen if(s == e) { 109933707f3Ssthen log_err("cannot parse: %s", s); 110933707f3Ssthen return 0; 111933707f3Ssthen } 112933707f3Ssthen s = e; 113933707f3Ssthen ve->nsec3_maxiter[i] = (size_t)strtol(s, &e, 10); 114933707f3Ssthen if(s == e) { 115933707f3Ssthen log_err("cannot parse: %s", s); 116933707f3Ssthen return 0; 117933707f3Ssthen } 118933707f3Ssthen s = e; 119933707f3Ssthen if(i>0 && ve->nsec3_keysize[i-1] >= ve->nsec3_keysize[i]) { 120933707f3Ssthen log_err("nsec3 key iterations not ascending: %d %d", 121933707f3Ssthen (int)ve->nsec3_keysize[i-1], 122933707f3Ssthen (int)ve->nsec3_keysize[i]); 123933707f3Ssthen return 0; 124933707f3Ssthen } 125933707f3Ssthen verbose(VERB_ALGO, "validator nsec3cfg keysz %d mxiter %d", 126933707f3Ssthen (int)ve->nsec3_keysize[i], (int)ve->nsec3_maxiter[i]); 127933707f3Ssthen } 128933707f3Ssthen return 1; 129933707f3Ssthen } 130933707f3Ssthen 131933707f3Ssthen /** apply config settings to validator */ 132933707f3Ssthen static int 133933707f3Ssthen val_apply_cfg(struct module_env* env, struct val_env* val_env, 134933707f3Ssthen struct config_file* cfg) 135933707f3Ssthen { 136933707f3Ssthen int c; 137933707f3Ssthen val_env->bogus_ttl = (uint32_t)cfg->bogus_ttl; 138933707f3Ssthen if(!env->anchors) 139933707f3Ssthen env->anchors = anchors_create(); 140933707f3Ssthen if(!env->anchors) { 141933707f3Ssthen log_err("out of memory"); 142933707f3Ssthen return 0; 143933707f3Ssthen } 144eaf2578eSsthen if (env->key_cache) 145eaf2578eSsthen val_env->kcache = env->key_cache; 146933707f3Ssthen if(!val_env->kcache) 147933707f3Ssthen val_env->kcache = key_cache_create(cfg); 148933707f3Ssthen if(!val_env->kcache) { 149933707f3Ssthen log_err("out of memory"); 150933707f3Ssthen return 0; 151933707f3Ssthen } 152933707f3Ssthen env->key_cache = val_env->kcache; 153933707f3Ssthen if(!anchors_apply_cfg(env->anchors, cfg)) { 154933707f3Ssthen log_err("validator: error in trustanchors config"); 155933707f3Ssthen return 0; 156933707f3Ssthen } 157933707f3Ssthen val_env->date_override = cfg->val_date_override; 158933707f3Ssthen val_env->skew_min = cfg->val_sig_skew_min; 159933707f3Ssthen val_env->skew_max = cfg->val_sig_skew_max; 160191f22c6Ssthen val_env->max_restart = cfg->val_max_restart; 161933707f3Ssthen c = cfg_count_numbers(cfg->val_nsec3_key_iterations); 162933707f3Ssthen if(c < 1 || (c&1)) { 163e21c60efSsthen log_err("validator: unparsable or odd nsec3 key " 164933707f3Ssthen "iterations: %s", cfg->val_nsec3_key_iterations); 165933707f3Ssthen return 0; 166933707f3Ssthen } 167933707f3Ssthen val_env->nsec3_keyiter_count = c/2; 168933707f3Ssthen if(!fill_nsec3_iter(val_env, cfg->val_nsec3_key_iterations, c/2)) { 169933707f3Ssthen log_err("validator: cannot apply nsec3 key iterations"); 170933707f3Ssthen return 0; 171933707f3Ssthen } 172eaf2578eSsthen if (env->neg_cache) 173eaf2578eSsthen val_env->neg_cache = env->neg_cache; 174933707f3Ssthen if(!val_env->neg_cache) 175933707f3Ssthen val_env->neg_cache = val_neg_create(cfg, 176933707f3Ssthen val_env->nsec3_maxiter[val_env->nsec3_keyiter_count-1]); 177933707f3Ssthen if(!val_env->neg_cache) { 178933707f3Ssthen log_err("out of memory"); 179933707f3Ssthen return 0; 180933707f3Ssthen } 181933707f3Ssthen env->neg_cache = val_env->neg_cache; 182933707f3Ssthen return 1; 183933707f3Ssthen } 184933707f3Ssthen 18577079be7Ssthen #ifdef USE_ECDSA_EVP_WORKAROUND 18677079be7Ssthen void ecdsa_evp_workaround_init(void); 18777079be7Ssthen #endif 188933707f3Ssthen int 189933707f3Ssthen val_init(struct module_env* env, int id) 190933707f3Ssthen { 191933707f3Ssthen struct val_env* val_env = (struct val_env*)calloc(1, 192933707f3Ssthen sizeof(struct val_env)); 193933707f3Ssthen if(!val_env) { 194933707f3Ssthen log_err("malloc failure"); 195933707f3Ssthen return 0; 196933707f3Ssthen } 197933707f3Ssthen env->modinfo[id] = (void*)val_env; 198933707f3Ssthen env->need_to_validate = 1; 199933707f3Ssthen lock_basic_init(&val_env->bogus_lock); 200933707f3Ssthen lock_protect(&val_env->bogus_lock, &val_env->num_rrset_bogus, 201933707f3Ssthen sizeof(val_env->num_rrset_bogus)); 20277079be7Ssthen #ifdef USE_ECDSA_EVP_WORKAROUND 20377079be7Ssthen ecdsa_evp_workaround_init(); 20477079be7Ssthen #endif 205933707f3Ssthen if(!val_apply_cfg(env, val_env, env->cfg)) { 206933707f3Ssthen log_err("validator: could not apply configuration settings."); 207933707f3Ssthen return 0; 208933707f3Ssthen } 209d896b962Ssthen if(env->cfg->disable_edns_do) { 210d896b962Ssthen struct trust_anchor* anchor = anchors_find_any_noninsecure( 211d896b962Ssthen env->anchors); 212d896b962Ssthen if(anchor) { 213d896b962Ssthen char b[LDNS_MAX_DOMAINLEN+2]; 214d896b962Ssthen dname_str(anchor->name, b); 215d896b962Ssthen log_warn("validator: disable-edns-do is enabled, but there is a trust anchor for '%s'. Since DNSSEC could not work, the disable-edns-do setting is turned off. Continuing without it.", b); 216d896b962Ssthen lock_basic_unlock(&anchor->lock); 217d896b962Ssthen env->cfg->disable_edns_do = 0; 218d896b962Ssthen } 219d896b962Ssthen } 22077079be7Ssthen 221933707f3Ssthen return 1; 222933707f3Ssthen } 223933707f3Ssthen 224933707f3Ssthen void 225933707f3Ssthen val_deinit(struct module_env* env, int id) 226933707f3Ssthen { 227933707f3Ssthen struct val_env* val_env; 228933707f3Ssthen if(!env || !env->modinfo[id]) 229933707f3Ssthen return; 230933707f3Ssthen val_env = (struct val_env*)env->modinfo[id]; 231933707f3Ssthen lock_basic_destroy(&val_env->bogus_lock); 232933707f3Ssthen anchors_delete(env->anchors); 233933707f3Ssthen env->anchors = NULL; 234933707f3Ssthen key_cache_delete(val_env->kcache); 235eaf2578eSsthen env->key_cache = NULL; 236933707f3Ssthen neg_cache_delete(val_env->neg_cache); 237eaf2578eSsthen env->neg_cache = NULL; 238933707f3Ssthen free(val_env->nsec3_keysize); 239933707f3Ssthen free(val_env->nsec3_maxiter); 240933707f3Ssthen free(val_env); 241933707f3Ssthen env->modinfo[id] = NULL; 242933707f3Ssthen } 243933707f3Ssthen 244933707f3Ssthen /** fill in message structure */ 245933707f3Ssthen static struct val_qstate* 246933707f3Ssthen val_new_getmsg(struct module_qstate* qstate, struct val_qstate* vq) 247933707f3Ssthen { 248933707f3Ssthen if(!qstate->return_msg || qstate->return_rcode != LDNS_RCODE_NOERROR) { 249933707f3Ssthen /* create a message to verify */ 250933707f3Ssthen verbose(VERB_ALGO, "constructing reply for validation"); 251933707f3Ssthen vq->orig_msg = (struct dns_msg*)regional_alloc(qstate->region, 252933707f3Ssthen sizeof(struct dns_msg)); 253933707f3Ssthen if(!vq->orig_msg) 254933707f3Ssthen return NULL; 255933707f3Ssthen vq->orig_msg->qinfo = qstate->qinfo; 256933707f3Ssthen vq->orig_msg->rep = (struct reply_info*)regional_alloc( 257933707f3Ssthen qstate->region, sizeof(struct reply_info)); 258933707f3Ssthen if(!vq->orig_msg->rep) 259933707f3Ssthen return NULL; 260933707f3Ssthen memset(vq->orig_msg->rep, 0, sizeof(struct reply_info)); 261933707f3Ssthen vq->orig_msg->rep->flags = (uint16_t)(qstate->return_rcode&0xf) 262933707f3Ssthen |BIT_QR|BIT_RA|(qstate->query_flags|(BIT_CD|BIT_RD)); 263933707f3Ssthen vq->orig_msg->rep->qdcount = 1; 2640bdb4f62Ssthen vq->orig_msg->rep->reason_bogus = LDNS_EDE_NONE; 265933707f3Ssthen } else { 266933707f3Ssthen vq->orig_msg = qstate->return_msg; 267933707f3Ssthen } 268933707f3Ssthen vq->qchase = qstate->qinfo; 269933707f3Ssthen /* chase reply will be an edited (sub)set of the orig msg rrset ptrs */ 270933707f3Ssthen vq->chase_reply = regional_alloc_init(qstate->region, 271933707f3Ssthen vq->orig_msg->rep, 272933707f3Ssthen sizeof(struct reply_info) - sizeof(struct rrset_ref)); 273933707f3Ssthen if(!vq->chase_reply) 274933707f3Ssthen return NULL; 275a58bff56Ssthen if(vq->orig_msg->rep->rrset_count > RR_COUNT_MAX) 276a58bff56Ssthen return NULL; /* protect against integer overflow */ 277*98bc733bSsthen /* Over allocate (+an_numrrsets) in case we need to put extra DNAME 278*98bc733bSsthen * records for unsigned CNAME repetitions */ 279*98bc733bSsthen vq->chase_reply->rrsets = regional_alloc(qstate->region, 280*98bc733bSsthen sizeof(struct ub_packed_rrset_key*) * 281*98bc733bSsthen (vq->orig_msg->rep->rrset_count 282*98bc733bSsthen + vq->orig_msg->rep->an_numrrsets)); 283933707f3Ssthen if(!vq->chase_reply->rrsets) 284933707f3Ssthen return NULL; 285*98bc733bSsthen memmove(vq->chase_reply->rrsets, vq->orig_msg->rep->rrsets, 286*98bc733bSsthen sizeof(struct ub_packed_rrset_key*) * 287*98bc733bSsthen vq->orig_msg->rep->rrset_count); 288933707f3Ssthen vq->rrset_skip = 0; 289933707f3Ssthen return vq; 290933707f3Ssthen } 291933707f3Ssthen 292933707f3Ssthen /** allocate new validator query state */ 293933707f3Ssthen static struct val_qstate* 294933707f3Ssthen val_new(struct module_qstate* qstate, int id) 295933707f3Ssthen { 296933707f3Ssthen struct val_qstate* vq = (struct val_qstate*)regional_alloc( 297933707f3Ssthen qstate->region, sizeof(*vq)); 298933707f3Ssthen log_assert(!qstate->minfo[id]); 299933707f3Ssthen if(!vq) 300933707f3Ssthen return NULL; 301933707f3Ssthen memset(vq, 0, sizeof(*vq)); 302933707f3Ssthen qstate->minfo[id] = vq; 303933707f3Ssthen vq->state = VAL_INIT_STATE; 304933707f3Ssthen return val_new_getmsg(qstate, vq); 305933707f3Ssthen } 306933707f3Ssthen 307817bdb8fSflorian /** reset validator query state for query restart */ 308817bdb8fSflorian static void 309817bdb8fSflorian val_restart(struct val_qstate* vq) 310817bdb8fSflorian { 311817bdb8fSflorian struct comm_timer* temp_timer; 312817bdb8fSflorian int restart_count; 313817bdb8fSflorian if(!vq) return; 314817bdb8fSflorian temp_timer = vq->suspend_timer; 315817bdb8fSflorian restart_count = vq->restart_count+1; 316817bdb8fSflorian memset(vq, 0, sizeof(*vq)); 317817bdb8fSflorian vq->suspend_timer = temp_timer; 318817bdb8fSflorian vq->restart_count = restart_count; 319817bdb8fSflorian vq->state = VAL_INIT_STATE; 320817bdb8fSflorian } 321817bdb8fSflorian 322933707f3Ssthen /** 323933707f3Ssthen * Exit validation with an error status 324933707f3Ssthen * 325933707f3Ssthen * @param qstate: query state 326933707f3Ssthen * @param id: validator id. 327933707f3Ssthen * @return false, for use by caller to return to stop processing. 328933707f3Ssthen */ 329933707f3Ssthen static int 330933707f3Ssthen val_error(struct module_qstate* qstate, int id) 331933707f3Ssthen { 332933707f3Ssthen qstate->ext_state[id] = module_error; 333933707f3Ssthen qstate->return_rcode = LDNS_RCODE_SERVFAIL; 334933707f3Ssthen return 0; 335933707f3Ssthen } 336933707f3Ssthen 337933707f3Ssthen /** 338933707f3Ssthen * Check to see if a given response needs to go through the validation 339933707f3Ssthen * process. Typical reasons for this routine to return false are: CD bit was 340933707f3Ssthen * on in the original request, or the response is a kind of message that 341933707f3Ssthen * is unvalidatable (i.e., SERVFAIL, REFUSED, etc.) 342933707f3Ssthen * 343933707f3Ssthen * @param qstate: query state. 344933707f3Ssthen * @param ret_rc: rcode for this message (if noerror - examine ret_msg). 345933707f3Ssthen * @param ret_msg: return msg, can be NULL; look at rcode instead. 346933707f3Ssthen * @return true if the response could use validation (although this does not 347933707f3Ssthen * mean we can actually validate this response). 348933707f3Ssthen */ 349933707f3Ssthen static int 350933707f3Ssthen needs_validation(struct module_qstate* qstate, int ret_rc, 351933707f3Ssthen struct dns_msg* ret_msg) 352933707f3Ssthen { 353933707f3Ssthen int rcode; 354933707f3Ssthen 35557dceb2aSbrad /* If the CD bit is on in the original request, then you could think 35657dceb2aSbrad * that we don't bother to validate anything. 35757dceb2aSbrad * But this is signalled internally with the valrec flag. 35857dceb2aSbrad * User queries are validated with BIT_CD to make our cache clean 35957dceb2aSbrad * so that bogus messages get retried by the upstream also for 36057dceb2aSbrad * downstream validators that set BIT_CD. 36157dceb2aSbrad * For DNS64 bit_cd signals no dns64 processing, but we want to 36257dceb2aSbrad * provide validation there too */ 36357dceb2aSbrad /* 364933707f3Ssthen if(qstate->query_flags & BIT_CD) { 365933707f3Ssthen verbose(VERB_ALGO, "not validating response due to CD bit"); 366933707f3Ssthen return 0; 367933707f3Ssthen } 36857dceb2aSbrad */ 36957dceb2aSbrad if(qstate->is_valrec) { 37057dceb2aSbrad verbose(VERB_ALGO, "not validating response, is valrec" 37157dceb2aSbrad "(validation recursion lookup)"); 37257dceb2aSbrad return 0; 37357dceb2aSbrad } 374933707f3Ssthen 375933707f3Ssthen if(ret_rc != LDNS_RCODE_NOERROR || !ret_msg) 376933707f3Ssthen rcode = ret_rc; 377933707f3Ssthen else rcode = (int)FLAGS_GET_RCODE(ret_msg->rep->flags); 378933707f3Ssthen 379933707f3Ssthen if(rcode != LDNS_RCODE_NOERROR && rcode != LDNS_RCODE_NXDOMAIN) { 3805d76a658Ssthen if(verbosity >= VERB_ALGO) { 3815d76a658Ssthen char rc[16]; 3825d76a658Ssthen rc[0]=0; 3835d76a658Ssthen (void)sldns_wire2str_rcode_buf(rcode, rc, sizeof(rc)); 3845d76a658Ssthen verbose(VERB_ALGO, "cannot validate non-answer, rcode %s", rc); 3855d76a658Ssthen } 386933707f3Ssthen return 0; 387933707f3Ssthen } 388933707f3Ssthen 389933707f3Ssthen /* cannot validate positive RRSIG response. (negatives can) */ 390933707f3Ssthen if(qstate->qinfo.qtype == LDNS_RR_TYPE_RRSIG && 391933707f3Ssthen rcode == LDNS_RCODE_NOERROR && ret_msg && 392933707f3Ssthen ret_msg->rep->an_numrrsets > 0) { 393933707f3Ssthen verbose(VERB_ALGO, "cannot validate RRSIG, no sigs on sigs."); 394933707f3Ssthen return 0; 395933707f3Ssthen } 396933707f3Ssthen return 1; 397933707f3Ssthen } 398933707f3Ssthen 399933707f3Ssthen /** 400933707f3Ssthen * Check to see if the response has already been validated. 401933707f3Ssthen * @param ret_msg: return msg, can be NULL 402933707f3Ssthen * @return true if the response has already been validated 403933707f3Ssthen */ 404933707f3Ssthen static int 405933707f3Ssthen already_validated(struct dns_msg* ret_msg) 406933707f3Ssthen { 407933707f3Ssthen /* validate unchecked, and re-validate bogus messages */ 408933707f3Ssthen if (ret_msg && ret_msg->rep->security > sec_status_bogus) 409933707f3Ssthen { 410933707f3Ssthen verbose(VERB_ALGO, "response has already been validated: %s", 411933707f3Ssthen sec_status_to_string(ret_msg->rep->security)); 412933707f3Ssthen return 1; 413933707f3Ssthen } 414933707f3Ssthen return 0; 415933707f3Ssthen } 416933707f3Ssthen 417933707f3Ssthen /** 418933707f3Ssthen * Generate a request for DNS data. 419933707f3Ssthen * 420933707f3Ssthen * @param qstate: query state that is the parent. 421933707f3Ssthen * @param id: module id. 422933707f3Ssthen * @param name: what name to query for. 423933707f3Ssthen * @param namelen: length of name. 424933707f3Ssthen * @param qtype: query type. 425933707f3Ssthen * @param qclass: query class. 426933707f3Ssthen * @param flags: additional flags, such as the CD bit (BIT_CD), or 0. 4272be9e038Ssthen * @param newq: If the subquery is newly created, it is returned, 4282be9e038Ssthen * otherwise NULL is returned 4292be9e038Ssthen * @param detached: true if this qstate should not attach to the subquery 430933707f3Ssthen * @return false on alloc failure. 431933707f3Ssthen */ 432933707f3Ssthen static int 433933707f3Ssthen generate_request(struct module_qstate* qstate, int id, uint8_t* name, 4342be9e038Ssthen size_t namelen, uint16_t qtype, uint16_t qclass, uint16_t flags, 4352be9e038Ssthen struct module_qstate** newq, int detached) 436933707f3Ssthen { 437933707f3Ssthen struct val_qstate* vq = (struct val_qstate*)qstate->minfo[id]; 438933707f3Ssthen struct query_info ask; 43957dceb2aSbrad int valrec; 440933707f3Ssthen ask.qname = name; 441933707f3Ssthen ask.qname_len = namelen; 442933707f3Ssthen ask.qtype = qtype; 443933707f3Ssthen ask.qclass = qclass; 44477079be7Ssthen ask.local_alias = NULL; 445933707f3Ssthen log_query_info(VERB_ALGO, "generate request", &ask); 44657dceb2aSbrad /* enable valrec flag to avoid recursion to the same validation 4472c144df0Ssthen * routine, this lookup is simply a lookup. */ 4482c144df0Ssthen valrec = 1; 4492308e98cSsthen 4502308e98cSsthen fptr_ok(fptr_whitelist_modenv_detect_cycle(qstate->env->detect_cycle)); 4512308e98cSsthen if((*qstate->env->detect_cycle)(qstate, &ask, 4522308e98cSsthen (uint16_t)(BIT_RD|flags), 0, valrec)) { 4532308e98cSsthen verbose(VERB_ALGO, "Could not generate request: cycle detected"); 4542308e98cSsthen return 0; 4552308e98cSsthen } 4562308e98cSsthen 4572be9e038Ssthen if(detached) { 4582be9e038Ssthen struct mesh_state* sub = NULL; 4592be9e038Ssthen fptr_ok(fptr_whitelist_modenv_add_sub( 4602be9e038Ssthen qstate->env->add_sub)); 4612be9e038Ssthen if(!(*qstate->env->add_sub)(qstate, &ask, 4622be9e038Ssthen (uint16_t)(BIT_RD|flags), 0, valrec, newq, &sub)){ 463933707f3Ssthen log_err("Could not generate request: out of memory"); 464933707f3Ssthen return 0; 465933707f3Ssthen } 4662be9e038Ssthen } 4672be9e038Ssthen else { 4682be9e038Ssthen fptr_ok(fptr_whitelist_modenv_attach_sub( 4692be9e038Ssthen qstate->env->attach_sub)); 4702be9e038Ssthen if(!(*qstate->env->attach_sub)(qstate, &ask, 4712be9e038Ssthen (uint16_t)(BIT_RD|flags), 0, valrec, newq)){ 4722be9e038Ssthen log_err("Could not generate request: out of memory"); 4732be9e038Ssthen return 0; 4742be9e038Ssthen } 4752be9e038Ssthen } 476933707f3Ssthen /* newq; validator does not need state created for that 477933707f3Ssthen * query, and its a 'normal' for iterator as well */ 4782be9e038Ssthen if(*newq) { 479933707f3Ssthen /* add our blacklist to the query blacklist */ 4802be9e038Ssthen sock_list_merge(&(*newq)->blacklist, (*newq)->region, 481933707f3Ssthen vq->chain_blacklist); 482933707f3Ssthen } 483933707f3Ssthen qstate->ext_state[id] = module_wait_subquery; 484933707f3Ssthen return 1; 485933707f3Ssthen } 486933707f3Ssthen 487933707f3Ssthen /** 4882be9e038Ssthen * Generate, send and detach key tag signaling query. 4892be9e038Ssthen * 4902be9e038Ssthen * @param qstate: query state. 4912be9e038Ssthen * @param id: module id. 4922be9e038Ssthen * @param ta: trust anchor, locked. 4932be9e038Ssthen * @return false on a processing error. 4942be9e038Ssthen */ 4952be9e038Ssthen static int 4962be9e038Ssthen generate_keytag_query(struct module_qstate* qstate, int id, 4972be9e038Ssthen struct trust_anchor* ta) 4982be9e038Ssthen { 4992be9e038Ssthen /* 3 bytes for "_ta", 5 bytes per tag (4 bytes + "-") */ 5002be9e038Ssthen #define MAX_LABEL_TAGS (LDNS_MAX_LABELLEN-3)/5 5012be9e038Ssthen size_t i, numtag; 5022be9e038Ssthen uint16_t tags[MAX_LABEL_TAGS]; 5032be9e038Ssthen char tagstr[LDNS_MAX_LABELLEN+1] = "_ta"; /* +1 for NULL byte */ 5042be9e038Ssthen size_t tagstr_left = sizeof(tagstr) - strlen(tagstr); 5052be9e038Ssthen char* tagstr_pos = tagstr + strlen(tagstr); 5062be9e038Ssthen uint8_t dnamebuf[LDNS_MAX_DOMAINLEN+1]; /* +1 for label length byte */ 5072be9e038Ssthen size_t dnamebuf_len = sizeof(dnamebuf); 5082be9e038Ssthen uint8_t* keytagdname; 5092be9e038Ssthen struct module_qstate* newq = NULL; 5102be9e038Ssthen enum module_ext_state ext_state = qstate->ext_state[id]; 5112be9e038Ssthen 5122be9e038Ssthen numtag = anchor_list_keytags(ta, tags, MAX_LABEL_TAGS); 5132be9e038Ssthen if(numtag == 0) 5142be9e038Ssthen return 0; 5152be9e038Ssthen 5162be9e038Ssthen for(i=0; i<numtag; i++) { 5172be9e038Ssthen /* Buffer can't overflow; numtag is limited to tags that fit in 5182be9e038Ssthen * the buffer. */ 5192be9e038Ssthen snprintf(tagstr_pos, tagstr_left, "-%04x", (unsigned)tags[i]); 5202be9e038Ssthen tagstr_left -= strlen(tagstr_pos); 5212be9e038Ssthen tagstr_pos += strlen(tagstr_pos); 5222be9e038Ssthen } 5232be9e038Ssthen 5242be9e038Ssthen sldns_str2wire_dname_buf_origin(tagstr, dnamebuf, &dnamebuf_len, 5252be9e038Ssthen ta->name, ta->namelen); 5262be9e038Ssthen if(!(keytagdname = (uint8_t*)regional_alloc_init(qstate->region, 5272be9e038Ssthen dnamebuf, dnamebuf_len))) { 5282be9e038Ssthen log_err("could not generate key tag query: out of memory"); 5292be9e038Ssthen return 0; 5302be9e038Ssthen } 5312be9e038Ssthen 532938a3a5eSflorian log_nametypeclass(VERB_OPS, "generate keytag query", keytagdname, 5332be9e038Ssthen LDNS_RR_TYPE_NULL, ta->dclass); 5342be9e038Ssthen if(!generate_request(qstate, id, keytagdname, dnamebuf_len, 5352be9e038Ssthen LDNS_RR_TYPE_NULL, ta->dclass, 0, &newq, 1)) { 5362308e98cSsthen verbose(VERB_ALGO, "failed to generate key tag signaling request"); 5372be9e038Ssthen return 0; 5382be9e038Ssthen } 5392be9e038Ssthen 540e21c60efSsthen /* Not interested in subquery response. Restore the ext_state, 5412be9e038Ssthen * that might be changed by generate_request() */ 5422be9e038Ssthen qstate->ext_state[id] = ext_state; 5432be9e038Ssthen 5442be9e038Ssthen return 1; 5452be9e038Ssthen } 5462be9e038Ssthen 5472be9e038Ssthen /** 54820237c55Ssthen * Get keytag as uint16_t from string 54920237c55Ssthen * 55020237c55Ssthen * @param start: start of string containing keytag 55120237c55Ssthen * @param keytag: pointer where to store the extracted keytag 55220237c55Ssthen * @return: 1 if keytag was extracted, else 0. 55320237c55Ssthen */ 55420237c55Ssthen static int 55520237c55Ssthen sentinel_get_keytag(char* start, uint16_t* keytag) { 55620237c55Ssthen char* keytag_str; 55720237c55Ssthen char* e = NULL; 55820237c55Ssthen keytag_str = calloc(1, SENTINEL_KEYTAG_LEN + 1 /* null byte */); 55920237c55Ssthen if(!keytag_str) 56020237c55Ssthen return 0; 56120237c55Ssthen memmove(keytag_str, start, SENTINEL_KEYTAG_LEN); 56220237c55Ssthen keytag_str[SENTINEL_KEYTAG_LEN] = '\0'; 56320237c55Ssthen *keytag = (uint16_t)strtol(keytag_str, &e, 10); 56420237c55Ssthen if(!e || *e != '\0') { 56520237c55Ssthen free(keytag_str); 56620237c55Ssthen return 0; 56720237c55Ssthen } 56820237c55Ssthen free(keytag_str); 56920237c55Ssthen return 1; 57020237c55Ssthen } 57120237c55Ssthen 57220237c55Ssthen /** 573933707f3Ssthen * Prime trust anchor for use. 574933707f3Ssthen * Generate and dispatch a priming query for the given trust anchor. 575933707f3Ssthen * The trust anchor can be DNSKEY or DS and does not have to be signed. 576933707f3Ssthen * 577933707f3Ssthen * @param qstate: query state. 578933707f3Ssthen * @param vq: validator query state. 579933707f3Ssthen * @param id: module id. 580933707f3Ssthen * @param toprime: what to prime. 581933707f3Ssthen * @return false on a processing error. 582933707f3Ssthen */ 583933707f3Ssthen static int 584933707f3Ssthen prime_trust_anchor(struct module_qstate* qstate, struct val_qstate* vq, 585933707f3Ssthen int id, struct trust_anchor* toprime) 586933707f3Ssthen { 5872be9e038Ssthen struct module_qstate* newq = NULL; 588933707f3Ssthen int ret = generate_request(qstate, id, toprime->name, toprime->namelen, 5892be9e038Ssthen LDNS_RR_TYPE_DNSKEY, toprime->dclass, BIT_CD, &newq, 0); 5902be9e038Ssthen 5912be9e038Ssthen if(newq && qstate->env->cfg->trust_anchor_signaling && 5922be9e038Ssthen !generate_keytag_query(qstate, id, toprime)) { 5932308e98cSsthen verbose(VERB_ALGO, "keytag signaling query failed"); 5942be9e038Ssthen return 0; 5952be9e038Ssthen } 5962be9e038Ssthen 597933707f3Ssthen if(!ret) { 5982308e98cSsthen verbose(VERB_ALGO, "Could not prime trust anchor"); 599933707f3Ssthen return 0; 600933707f3Ssthen } 601933707f3Ssthen /* ignore newq; validator does not need state created for that 602933707f3Ssthen * query, and its a 'normal' for iterator as well */ 603933707f3Ssthen vq->wait_prime_ta = 1; /* to elicit PRIME_RESP_STATE processing 604933707f3Ssthen from the validator inform_super() routine */ 605933707f3Ssthen /* store trust anchor name for later lookup when prime returns */ 606933707f3Ssthen vq->trust_anchor_name = regional_alloc_init(qstate->region, 607933707f3Ssthen toprime->name, toprime->namelen); 608933707f3Ssthen vq->trust_anchor_len = toprime->namelen; 609933707f3Ssthen vq->trust_anchor_labs = toprime->namelabs; 610933707f3Ssthen if(!vq->trust_anchor_name) { 611933707f3Ssthen log_err("Could not prime trust anchor: out of memory"); 612933707f3Ssthen return 0; 613933707f3Ssthen } 614933707f3Ssthen return 1; 615933707f3Ssthen } 616933707f3Ssthen 617933707f3Ssthen /** 618933707f3Ssthen * Validate if the ANSWER and AUTHORITY sections contain valid rrsets. 619933707f3Ssthen * They must be validly signed with the given key. 620933707f3Ssthen * Tries to validate ADDITIONAL rrsets as well, but only to check them. 621933707f3Ssthen * Allows unsigned CNAME after a DNAME that expands the DNAME. 622933707f3Ssthen * 623933707f3Ssthen * Note that by the time this method is called, the process of finding the 624933707f3Ssthen * trusted DNSKEY rrset that signs this response must already have been 625933707f3Ssthen * completed. 626933707f3Ssthen * 627933707f3Ssthen * @param qstate: query state. 628817bdb8fSflorian * @param vq: validator query state. 629933707f3Ssthen * @param env: module env for verify. 630933707f3Ssthen * @param ve: validator env for verify. 631933707f3Ssthen * @param chase_reply: answer to validate. 632933707f3Ssthen * @param key_entry: the key entry, which is trusted, and which matches 633933707f3Ssthen * the signer of the answer. The key entry isgood(). 634817bdb8fSflorian * @param suspend: returned true if the task takes too long and needs to 635817bdb8fSflorian * suspend to continue the effort later. 636933707f3Ssthen * @return false if any of the rrsets in the an or ns sections of the message 637933707f3Ssthen * fail to verify. The message is then set to bogus. 638933707f3Ssthen */ 639933707f3Ssthen static int 640817bdb8fSflorian validate_msg_signatures(struct module_qstate* qstate, struct val_qstate* vq, 6412bdc0ed1Ssthen struct module_env* env, struct val_env* ve, 642817bdb8fSflorian struct reply_info* chase_reply, struct key_entry_key* key_entry, 643817bdb8fSflorian int* suspend) 644933707f3Ssthen { 645933707f3Ssthen uint8_t* sname; 646933707f3Ssthen size_t i, slen; 647933707f3Ssthen struct ub_packed_rrset_key* s; 648933707f3Ssthen enum sec_status sec; 6492bdc0ed1Ssthen int num_verifies = 0, verified, have_state = 0; 650*98bc733bSsthen char reasonbuf[256]; 651933707f3Ssthen char* reason = NULL; 6520bdb4f62Ssthen sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS; 653817bdb8fSflorian *suspend = 0; 654817bdb8fSflorian if(vq->msg_signatures_state) { 655817bdb8fSflorian /* Pick up the state, and reset it, may not be needed now. */ 656817bdb8fSflorian vq->msg_signatures_state = 0; 657817bdb8fSflorian have_state = 1; 658817bdb8fSflorian } 659933707f3Ssthen 660933707f3Ssthen /* validate the ANSWER section */ 661933707f3Ssthen for(i=0; i<chase_reply->an_numrrsets; i++) { 662817bdb8fSflorian if(have_state && i <= vq->msg_signatures_index) 663817bdb8fSflorian continue; 664933707f3Ssthen s = chase_reply->rrsets[i]; 665933707f3Ssthen /* Skip the CNAME following a (validated) DNAME. 666933707f3Ssthen * Because of the normalization routines in the iterator, 667933707f3Ssthen * there will always be an unsigned CNAME following a DNAME 6682bdc0ed1Ssthen * (unless qtype=DNAME in the answer part). */ 6692bdc0ed1Ssthen if(i>0 && ntohs(chase_reply->rrsets[i-1]->rk.type) == 6702bdc0ed1Ssthen LDNS_RR_TYPE_DNAME && 6712bdc0ed1Ssthen ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME && 6722bdc0ed1Ssthen ((struct packed_rrset_data*)chase_reply->rrsets[i-1]->entry.data)->security == sec_status_secure && 6732bdc0ed1Ssthen dname_strict_subdomain_c(s->rk.dname, chase_reply->rrsets[i-1]->rk.dname) 6742bdc0ed1Ssthen ) { 675933707f3Ssthen /* CNAME was synthesized by our own iterator */ 676933707f3Ssthen /* since the DNAME verified, mark the CNAME as secure */ 677933707f3Ssthen ((struct packed_rrset_data*)s->entry.data)->security = 678933707f3Ssthen sec_status_secure; 679933707f3Ssthen ((struct packed_rrset_data*)s->entry.data)->trust = 680933707f3Ssthen rrset_trust_validated; 681933707f3Ssthen continue; 682933707f3Ssthen } 683933707f3Ssthen 684933707f3Ssthen /* Verify the answer rrset */ 685bdfc4d55Sflorian sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason, 686*98bc733bSsthen &reason_bogus, LDNS_SECTION_ANSWER, qstate, &verified, 687*98bc733bSsthen reasonbuf, sizeof(reasonbuf)); 688933707f3Ssthen /* If the (answer) rrset failed to validate, then this 689933707f3Ssthen * message is BAD. */ 690933707f3Ssthen if(sec != sec_status_secure) { 691933707f3Ssthen log_nametypeclass(VERB_QUERY, "validator: response " 692933707f3Ssthen "has failed ANSWER rrset:", s->rk.dname, 693933707f3Ssthen ntohs(s->rk.type), ntohs(s->rk.rrset_class)); 6940bdb4f62Ssthen errinf_ede(qstate, reason, reason_bogus); 695933707f3Ssthen if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME) 696933707f3Ssthen errinf(qstate, "for CNAME"); 697933707f3Ssthen else if(ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME) 698933707f3Ssthen errinf(qstate, "for DNAME"); 699933707f3Ssthen errinf_origin(qstate, qstate->reply_origin); 700933707f3Ssthen chase_reply->security = sec_status_bogus; 7010bdb4f62Ssthen update_reason_bogus(chase_reply, reason_bogus); 7020bdb4f62Ssthen 703933707f3Ssthen return 0; 704933707f3Ssthen } 705933707f3Ssthen 706817bdb8fSflorian num_verifies += verified; 707817bdb8fSflorian if(num_verifies > MAX_VALIDATE_AT_ONCE && 708817bdb8fSflorian i+1 < (env->cfg->val_clean_additional? 709817bdb8fSflorian chase_reply->an_numrrsets+chase_reply->ns_numrrsets: 710817bdb8fSflorian chase_reply->rrset_count)) { 711817bdb8fSflorian /* If the number of RRSIGs exceeds the maximum in 712817bdb8fSflorian * one go, suspend. Only suspend if there is a next 713817bdb8fSflorian * rrset to verify, i+1<loopmax. Store where to 714817bdb8fSflorian * continue later. */ 715817bdb8fSflorian *suspend = 1; 716817bdb8fSflorian vq->msg_signatures_state = 1; 717817bdb8fSflorian vq->msg_signatures_index = i; 718817bdb8fSflorian verbose(VERB_ALGO, "msg signature validation " 719817bdb8fSflorian "suspended"); 720817bdb8fSflorian return 0; 721817bdb8fSflorian } 722933707f3Ssthen } 723933707f3Ssthen 724933707f3Ssthen /* validate the AUTHORITY section */ 725933707f3Ssthen for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+ 726933707f3Ssthen chase_reply->ns_numrrsets; i++) { 727817bdb8fSflorian if(have_state && i <= vq->msg_signatures_index) 728817bdb8fSflorian continue; 729933707f3Ssthen s = chase_reply->rrsets[i]; 730bdfc4d55Sflorian sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason, 731817bdb8fSflorian &reason_bogus, LDNS_SECTION_AUTHORITY, qstate, 732*98bc733bSsthen &verified, reasonbuf, sizeof(reasonbuf)); 733933707f3Ssthen /* If anything in the authority section fails to be secure, 734933707f3Ssthen * we have a bad message. */ 735933707f3Ssthen if(sec != sec_status_secure) { 736933707f3Ssthen log_nametypeclass(VERB_QUERY, "validator: response " 737933707f3Ssthen "has failed AUTHORITY rrset:", s->rk.dname, 738933707f3Ssthen ntohs(s->rk.type), ntohs(s->rk.rrset_class)); 7390bdb4f62Ssthen errinf_ede(qstate, reason, reason_bogus); 740933707f3Ssthen errinf_origin(qstate, qstate->reply_origin); 741a58bff56Ssthen errinf_rrset(qstate, s); 742933707f3Ssthen chase_reply->security = sec_status_bogus; 7430bdb4f62Ssthen update_reason_bogus(chase_reply, reason_bogus); 744933707f3Ssthen return 0; 745933707f3Ssthen } 746817bdb8fSflorian num_verifies += verified; 747817bdb8fSflorian if(num_verifies > MAX_VALIDATE_AT_ONCE && 748817bdb8fSflorian i+1 < (env->cfg->val_clean_additional? 749817bdb8fSflorian chase_reply->an_numrrsets+chase_reply->ns_numrrsets: 750817bdb8fSflorian chase_reply->rrset_count)) { 751817bdb8fSflorian *suspend = 1; 752817bdb8fSflorian vq->msg_signatures_state = 1; 753817bdb8fSflorian vq->msg_signatures_index = i; 754817bdb8fSflorian verbose(VERB_ALGO, "msg signature validation " 755817bdb8fSflorian "suspended"); 756817bdb8fSflorian return 0; 757817bdb8fSflorian } 758933707f3Ssthen } 759933707f3Ssthen 7602be9e038Ssthen /* If set, the validator should clean the additional section of 7612be9e038Ssthen * secure messages. */ 7622be9e038Ssthen if(!env->cfg->val_clean_additional) 763933707f3Ssthen return 1; 7642be9e038Ssthen /* attempt to validate the ADDITIONAL section rrsets */ 765933707f3Ssthen for(i=chase_reply->an_numrrsets+chase_reply->ns_numrrsets; 766933707f3Ssthen i<chase_reply->rrset_count; i++) { 767817bdb8fSflorian if(have_state && i <= vq->msg_signatures_index) 768817bdb8fSflorian continue; 769933707f3Ssthen s = chase_reply->rrsets[i]; 770933707f3Ssthen /* only validate rrs that have signatures with the key */ 771933707f3Ssthen /* leave others unchecked, those get removed later on too */ 772933707f3Ssthen val_find_rrset_signer(s, &sname, &slen); 7730bdb4f62Ssthen 774817bdb8fSflorian verified = 0; 775933707f3Ssthen if(sname && query_dname_compare(sname, key_entry->name)==0) 776933707f3Ssthen (void)val_verify_rrset_entry(env, ve, s, key_entry, 777817bdb8fSflorian &reason, NULL, LDNS_SECTION_ADDITIONAL, qstate, 778*98bc733bSsthen &verified, reasonbuf, sizeof(reasonbuf)); 779933707f3Ssthen /* the additional section can fail to be secure, 780933707f3Ssthen * it is optional, check signature in case we need 781933707f3Ssthen * to clean the additional section later. */ 782817bdb8fSflorian num_verifies += verified; 783817bdb8fSflorian if(num_verifies > MAX_VALIDATE_AT_ONCE && 784817bdb8fSflorian i+1 < chase_reply->rrset_count) { 785817bdb8fSflorian *suspend = 1; 786817bdb8fSflorian vq->msg_signatures_state = 1; 787817bdb8fSflorian vq->msg_signatures_index = i; 788817bdb8fSflorian verbose(VERB_ALGO, "msg signature validation " 789817bdb8fSflorian "suspended"); 790817bdb8fSflorian return 0; 791817bdb8fSflorian } 792933707f3Ssthen } 793933707f3Ssthen 794933707f3Ssthen return 1; 795933707f3Ssthen } 796933707f3Ssthen 797817bdb8fSflorian void 798817bdb8fSflorian validate_suspend_timer_cb(void* arg) 799817bdb8fSflorian { 800817bdb8fSflorian struct module_qstate* qstate = (struct module_qstate*)arg; 801817bdb8fSflorian verbose(VERB_ALGO, "validate_suspend timer, continue"); 802817bdb8fSflorian mesh_run(qstate->env->mesh, qstate->mesh_info, module_event_pass, 803817bdb8fSflorian NULL); 804817bdb8fSflorian } 805817bdb8fSflorian 806817bdb8fSflorian /** Setup timer to continue validation of msg signatures later */ 807817bdb8fSflorian static int 808817bdb8fSflorian validate_suspend_setup_timer(struct module_qstate* qstate, 809817bdb8fSflorian struct val_qstate* vq, int id, enum val_state resume_state) 810817bdb8fSflorian { 811817bdb8fSflorian struct timeval tv; 812817bdb8fSflorian int usec, slack, base; 813817bdb8fSflorian if(vq->suspend_count >= MAX_VALIDATION_SUSPENDS) { 814817bdb8fSflorian verbose(VERB_ALGO, "validate_suspend timer: " 815817bdb8fSflorian "reached MAX_VALIDATION_SUSPENDS (%d); error out", 816817bdb8fSflorian MAX_VALIDATION_SUSPENDS); 817817bdb8fSflorian errinf(qstate, "max validation suspends reached, " 818817bdb8fSflorian "too many RRSIG validations"); 819817bdb8fSflorian return 0; 820817bdb8fSflorian } 821817bdb8fSflorian verbose(VERB_ALGO, "validate_suspend timer, set for suspend"); 822817bdb8fSflorian vq->state = resume_state; 823817bdb8fSflorian qstate->ext_state[id] = module_wait_reply; 824817bdb8fSflorian if(!vq->suspend_timer) { 825817bdb8fSflorian vq->suspend_timer = comm_timer_create( 826817bdb8fSflorian qstate->env->worker_base, 827817bdb8fSflorian validate_suspend_timer_cb, qstate); 828817bdb8fSflorian if(!vq->suspend_timer) { 829817bdb8fSflorian log_err("validate_suspend_setup_timer: " 830817bdb8fSflorian "out of memory for comm_timer_create"); 831817bdb8fSflorian return 0; 832817bdb8fSflorian } 833817bdb8fSflorian } 834817bdb8fSflorian /* The timer is activated later, after other events in the event 835817bdb8fSflorian * loop have been processed. The query state can also be deleted, 836817bdb8fSflorian * when the list is full and query states are dropped. */ 837817bdb8fSflorian /* Extend wait time if there are a lot of queries or if this one 838817bdb8fSflorian * is taking long, to keep around cpu time for ordinary queries. */ 839817bdb8fSflorian usec = 50000; /* 50 msec */ 840817bdb8fSflorian slack = 0; 841817bdb8fSflorian if(qstate->env->mesh->all.count >= qstate->env->mesh->max_reply_states) 842817bdb8fSflorian slack += 3; 843817bdb8fSflorian else if(qstate->env->mesh->all.count >= qstate->env->mesh->max_reply_states/2) 844817bdb8fSflorian slack += 2; 845817bdb8fSflorian else if(qstate->env->mesh->all.count >= qstate->env->mesh->max_reply_states/4) 846817bdb8fSflorian slack += 1; 847817bdb8fSflorian if(vq->suspend_count > 3) 848817bdb8fSflorian slack += 3; 849817bdb8fSflorian else if(vq->suspend_count > 0) 850817bdb8fSflorian slack += vq->suspend_count; 851817bdb8fSflorian if(slack != 0 && slack <= 12 /* No numeric overflow. */) { 852817bdb8fSflorian usec = usec << slack; 853817bdb8fSflorian } 854817bdb8fSflorian /* Spread such timeouts within 90%-100% of the original timer. */ 855817bdb8fSflorian base = usec * 9/10; 856817bdb8fSflorian usec = base + ub_random_max(qstate->env->rnd, usec-base); 857817bdb8fSflorian tv.tv_usec = (usec % 1000000); 858817bdb8fSflorian tv.tv_sec = (usec / 1000000); 859817bdb8fSflorian vq->suspend_count ++; 860817bdb8fSflorian comm_timer_set(vq->suspend_timer, &tv); 861817bdb8fSflorian return 1; 862817bdb8fSflorian } 863817bdb8fSflorian 864933707f3Ssthen /** 865933707f3Ssthen * Detect wrong truncated response (say from BIND 9.6.1 that is forwarding 866933707f3Ssthen * and saw the NS record without signatures from a referral). 867933707f3Ssthen * The positive response has a mangled authority section. 868933707f3Ssthen * Remove that authority section and the additional section. 869933707f3Ssthen * @param rep: reply 870933707f3Ssthen * @return true if a wrongly truncated response. 871933707f3Ssthen */ 872933707f3Ssthen static int 873933707f3Ssthen detect_wrongly_truncated(struct reply_info* rep) 874933707f3Ssthen { 875933707f3Ssthen size_t i; 876933707f3Ssthen /* only NS in authority, and it is bogus */ 877933707f3Ssthen if(rep->ns_numrrsets != 1 || rep->an_numrrsets == 0) 878933707f3Ssthen return 0; 879933707f3Ssthen if(ntohs(rep->rrsets[ rep->an_numrrsets ]->rk.type) != LDNS_RR_TYPE_NS) 880933707f3Ssthen return 0; 881933707f3Ssthen if(((struct packed_rrset_data*)rep->rrsets[ rep->an_numrrsets ] 882933707f3Ssthen ->entry.data)->security == sec_status_secure) 883933707f3Ssthen return 0; 884933707f3Ssthen /* answer section is present and secure */ 885933707f3Ssthen for(i=0; i<rep->an_numrrsets; i++) { 886933707f3Ssthen if(((struct packed_rrset_data*)rep->rrsets[ i ] 887933707f3Ssthen ->entry.data)->security != sec_status_secure) 888933707f3Ssthen return 0; 889933707f3Ssthen } 890933707f3Ssthen verbose(VERB_ALGO, "truncating to minimal response"); 891933707f3Ssthen return 1; 892933707f3Ssthen } 893933707f3Ssthen 8943b25f654Sbrad /** 8953b25f654Sbrad * For messages that are not referrals, if the chase reply contains an 8963b25f654Sbrad * unsigned NS record in the authority section it could have been 8973b25f654Sbrad * inserted by a (BIND) forwarder that thinks the zone is insecure, and 8983b25f654Sbrad * that has an NS record without signatures in cache. Remove the NS 8993b25f654Sbrad * record since the reply does not hinge on that record (in the authority 9003b25f654Sbrad * section), but do not remove it if it removes the last record from the 9013b25f654Sbrad * answer+authority sections. 9023b25f654Sbrad * @param chase_reply: the chased reply, we have a key for this contents, 9033b25f654Sbrad * so we should have signatures for these rrsets and not having 9043b25f654Sbrad * signatures means it will be bogus. 9053b25f654Sbrad * @param orig_reply: original reply, remove NS from there as well because 9063b25f654Sbrad * we cannot mark the NS record as DNSSEC valid because it is not 9073b25f654Sbrad * validated by signatures. 9083b25f654Sbrad */ 9093b25f654Sbrad static void 9103b25f654Sbrad remove_spurious_authority(struct reply_info* chase_reply, 9113b25f654Sbrad struct reply_info* orig_reply) 9123b25f654Sbrad { 9133b25f654Sbrad size_t i, found = 0; 9143b25f654Sbrad int remove = 0; 9153b25f654Sbrad /* if no answer and only 1 auth RRset, do not remove that one */ 9163b25f654Sbrad if(chase_reply->an_numrrsets == 0 && chase_reply->ns_numrrsets == 1) 9173b25f654Sbrad return; 9183b25f654Sbrad /* search authority section for unsigned NS records */ 9193b25f654Sbrad for(i = chase_reply->an_numrrsets; 9203b25f654Sbrad i < chase_reply->an_numrrsets+chase_reply->ns_numrrsets; i++) { 9213b25f654Sbrad struct packed_rrset_data* d = (struct packed_rrset_data*) 9223b25f654Sbrad chase_reply->rrsets[i]->entry.data; 9233b25f654Sbrad if(ntohs(chase_reply->rrsets[i]->rk.type) == LDNS_RR_TYPE_NS 9243b25f654Sbrad && d->rrsig_count == 0) { 9253b25f654Sbrad found = i; 9263b25f654Sbrad remove = 1; 9273b25f654Sbrad break; 9283b25f654Sbrad } 9293b25f654Sbrad } 9303b25f654Sbrad /* see if we found the entry */ 9313b25f654Sbrad if(!remove) return; 9323b25f654Sbrad log_rrset_key(VERB_ALGO, "Removing spurious unsigned NS record " 9333b25f654Sbrad "(likely inserted by forwarder)", chase_reply->rrsets[found]); 9343b25f654Sbrad 9353b25f654Sbrad /* find rrset in orig_reply */ 9363b25f654Sbrad for(i = orig_reply->an_numrrsets; 9373b25f654Sbrad i < orig_reply->an_numrrsets+orig_reply->ns_numrrsets; i++) { 9383b25f654Sbrad if(ntohs(orig_reply->rrsets[i]->rk.type) == LDNS_RR_TYPE_NS 9393b25f654Sbrad && query_dname_compare(orig_reply->rrsets[i]->rk.dname, 9403b25f654Sbrad chase_reply->rrsets[found]->rk.dname) == 0) { 9413b25f654Sbrad /* remove from orig_msg */ 9423b25f654Sbrad val_reply_remove_auth(orig_reply, i); 9433b25f654Sbrad break; 9443b25f654Sbrad } 9453b25f654Sbrad } 9463b25f654Sbrad /* remove rrset from chase_reply */ 9473b25f654Sbrad val_reply_remove_auth(chase_reply, found); 9483b25f654Sbrad } 949933707f3Ssthen 950933707f3Ssthen /** 951933707f3Ssthen * Given a "positive" response -- a response that contains an answer to the 952933707f3Ssthen * question, and no CNAME chain, validate this response. 953933707f3Ssthen * 954933707f3Ssthen * The answer and authority RRsets must already be verified as secure. 955933707f3Ssthen * 956933707f3Ssthen * @param env: module env for verify. 957933707f3Ssthen * @param ve: validator env for verify. 958933707f3Ssthen * @param qchase: query that was made. 959933707f3Ssthen * @param chase_reply: answer to that query to validate. 960933707f3Ssthen * @param kkey: the key entry, which is trusted, and which matches 961933707f3Ssthen * the signer of the answer. The key entry isgood(). 962817bdb8fSflorian * @param qstate: query state for the region. 963817bdb8fSflorian * @param vq: validator state for the nsec3 cache table. 964817bdb8fSflorian * @param nsec3_calculations: current nsec3 hash calculations. 965817bdb8fSflorian * @param suspend: returned true if the task takes too long and needs to 966817bdb8fSflorian * suspend to continue the effort later. 967933707f3Ssthen */ 968933707f3Ssthen static void 969933707f3Ssthen validate_positive_response(struct module_env* env, struct val_env* ve, 970933707f3Ssthen struct query_info* qchase, struct reply_info* chase_reply, 971817bdb8fSflorian struct key_entry_key* kkey, struct module_qstate* qstate, 972817bdb8fSflorian struct val_qstate* vq, int* nsec3_calculations, int* suspend) 973933707f3Ssthen { 974933707f3Ssthen uint8_t* wc = NULL; 975938a3a5eSflorian size_t wl; 976938a3a5eSflorian int wc_cached = 0; 977933707f3Ssthen int wc_NSEC_ok = 0; 978933707f3Ssthen int nsec3s_seen = 0; 979933707f3Ssthen size_t i; 980933707f3Ssthen struct ub_packed_rrset_key* s; 981817bdb8fSflorian *suspend = 0; 982933707f3Ssthen 983933707f3Ssthen /* validate the ANSWER section - this will be the answer itself */ 984933707f3Ssthen for(i=0; i<chase_reply->an_numrrsets; i++) { 985933707f3Ssthen s = chase_reply->rrsets[i]; 986933707f3Ssthen 987933707f3Ssthen /* Check to see if the rrset is the result of a wildcard 988933707f3Ssthen * expansion. If so, an additional check will need to be 989933707f3Ssthen * made in the authority section. */ 990938a3a5eSflorian if(!val_rrset_wildcard(s, &wc, &wl)) { 991933707f3Ssthen log_nametypeclass(VERB_QUERY, "Positive response has " 992933707f3Ssthen "inconsistent wildcard sigs:", s->rk.dname, 993933707f3Ssthen ntohs(s->rk.type), ntohs(s->rk.rrset_class)); 994933707f3Ssthen chase_reply->security = sec_status_bogus; 9950bdb4f62Ssthen update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); 996933707f3Ssthen return; 997933707f3Ssthen } 998938a3a5eSflorian if(wc && !wc_cached && env->cfg->aggressive_nsec) { 999938a3a5eSflorian rrset_cache_update_wildcard(env->rrset_cache, s, wc, wl, 1000938a3a5eSflorian env->alloc, *env->now); 1001938a3a5eSflorian wc_cached = 1; 1002938a3a5eSflorian } 1003938a3a5eSflorian 1004933707f3Ssthen } 1005933707f3Ssthen 1006933707f3Ssthen /* validate the AUTHORITY section as well - this will generally be 1007933707f3Ssthen * the NS rrset (which could be missing, no problem) */ 1008933707f3Ssthen for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+ 1009933707f3Ssthen chase_reply->ns_numrrsets; i++) { 1010933707f3Ssthen s = chase_reply->rrsets[i]; 1011933707f3Ssthen 1012933707f3Ssthen /* If this is a positive wildcard response, and we have a 1013933707f3Ssthen * (just verified) NSEC record, try to use it to 1) prove 1014933707f3Ssthen * that qname doesn't exist and 2) that the correct wildcard 1015933707f3Ssthen * was used. */ 1016933707f3Ssthen if(wc != NULL && ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) { 1017933707f3Ssthen if(val_nsec_proves_positive_wildcard(s, qchase, wc)) { 1018933707f3Ssthen wc_NSEC_ok = 1; 1019933707f3Ssthen } 1020933707f3Ssthen /* if not, continue looking for proof */ 1021933707f3Ssthen } 1022933707f3Ssthen 1023933707f3Ssthen /* Otherwise, if this is a positive wildcard response and 1024933707f3Ssthen * we have NSEC3 records */ 1025933707f3Ssthen if(wc != NULL && ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) { 1026933707f3Ssthen nsec3s_seen = 1; 1027933707f3Ssthen } 1028933707f3Ssthen } 1029933707f3Ssthen 1030933707f3Ssthen /* If this was a positive wildcard response that we haven't already 1031933707f3Ssthen * proven, and we have NSEC3 records, try to prove it using the NSEC3 1032933707f3Ssthen * records. */ 1033817bdb8fSflorian if(wc != NULL && !wc_NSEC_ok && nsec3s_seen && 1034817bdb8fSflorian nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { 1035933707f3Ssthen enum sec_status sec = nsec3_prove_wildcard(env, ve, 1036933707f3Ssthen chase_reply->rrsets+chase_reply->an_numrrsets, 1037817bdb8fSflorian chase_reply->ns_numrrsets, qchase, kkey, wc, 1038817bdb8fSflorian &vq->nsec3_cache_table, nsec3_calculations); 1039933707f3Ssthen if(sec == sec_status_insecure) { 1040933707f3Ssthen verbose(VERB_ALGO, "Positive wildcard response is " 1041933707f3Ssthen "insecure"); 1042933707f3Ssthen chase_reply->security = sec_status_insecure; 1043933707f3Ssthen return; 1044817bdb8fSflorian } else if(sec == sec_status_secure) { 1045933707f3Ssthen wc_NSEC_ok = 1; 1046817bdb8fSflorian } else if(sec == sec_status_unchecked) { 1047817bdb8fSflorian *suspend = 1; 1048817bdb8fSflorian return; 1049817bdb8fSflorian } 1050933707f3Ssthen } 1051933707f3Ssthen 1052933707f3Ssthen /* If after all this, we still haven't proven the positive wildcard 1053933707f3Ssthen * response, fail. */ 1054933707f3Ssthen if(wc != NULL && !wc_NSEC_ok) { 1055933707f3Ssthen verbose(VERB_QUERY, "positive response was wildcard " 1056933707f3Ssthen "expansion and did not prove original data " 1057933707f3Ssthen "did not exist"); 1058933707f3Ssthen chase_reply->security = sec_status_bogus; 10590bdb4f62Ssthen update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); 1060933707f3Ssthen return; 1061933707f3Ssthen } 1062933707f3Ssthen 1063933707f3Ssthen verbose(VERB_ALGO, "Successfully validated positive response"); 1064933707f3Ssthen chase_reply->security = sec_status_secure; 1065933707f3Ssthen } 1066933707f3Ssthen 1067933707f3Ssthen /** 1068933707f3Ssthen * Validate a NOERROR/NODATA signed response -- a response that has a 1069933707f3Ssthen * NOERROR Rcode but no ANSWER section RRsets. This consists of making 1070933707f3Ssthen * certain that the authority section NSEC/NSEC3s proves that the qname 1071933707f3Ssthen * does exist and the qtype doesn't. 1072933707f3Ssthen * 1073933707f3Ssthen * The answer and authority RRsets must already be verified as secure. 1074933707f3Ssthen * 1075933707f3Ssthen * @param env: module env for verify. 1076933707f3Ssthen * @param ve: validator env for verify. 1077933707f3Ssthen * @param qchase: query that was made. 1078933707f3Ssthen * @param chase_reply: answer to that query to validate. 1079933707f3Ssthen * @param kkey: the key entry, which is trusted, and which matches 1080933707f3Ssthen * the signer of the answer. The key entry isgood(). 1081817bdb8fSflorian * @param qstate: query state for the region. 1082817bdb8fSflorian * @param vq: validator state for the nsec3 cache table. 1083817bdb8fSflorian * @param nsec3_calculations: current nsec3 hash calculations. 1084817bdb8fSflorian * @param suspend: returned true if the task takes too long and needs to 1085817bdb8fSflorian * suspend to continue the effort later. 1086933707f3Ssthen */ 1087933707f3Ssthen static void 1088933707f3Ssthen validate_nodata_response(struct module_env* env, struct val_env* ve, 1089933707f3Ssthen struct query_info* qchase, struct reply_info* chase_reply, 1090817bdb8fSflorian struct key_entry_key* kkey, struct module_qstate* qstate, 1091817bdb8fSflorian struct val_qstate* vq, int* nsec3_calculations, int* suspend) 1092933707f3Ssthen { 1093933707f3Ssthen /* Since we are here, there must be nothing in the ANSWER section to 1094933707f3Ssthen * validate. */ 1095933707f3Ssthen /* (Note: CNAME/DNAME responses will not directly get here -- 10964bfc71b0Ssthen * instead, they are chased down into individual CNAME validations, 1097933707f3Ssthen * and at the end of the cname chain a POSITIVE, or CNAME_NOANSWER 1098933707f3Ssthen * validation.) */ 1099933707f3Ssthen 1100933707f3Ssthen /* validate the AUTHORITY section */ 1101933707f3Ssthen int has_valid_nsec = 0; /* If true, then the NODATA has been proven.*/ 1102933707f3Ssthen uint8_t* ce = NULL; /* for wildcard nodata responses. This is the 1103933707f3Ssthen proven closest encloser. */ 1104933707f3Ssthen uint8_t* wc = NULL; /* for wildcard nodata responses. wildcard nsec */ 1105933707f3Ssthen int nsec3s_seen = 0; /* nsec3s seen */ 1106933707f3Ssthen struct ub_packed_rrset_key* s; 1107933707f3Ssthen size_t i; 1108817bdb8fSflorian *suspend = 0; 1109933707f3Ssthen 1110933707f3Ssthen for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+ 1111933707f3Ssthen chase_reply->ns_numrrsets; i++) { 1112933707f3Ssthen s = chase_reply->rrsets[i]; 1113933707f3Ssthen /* If we encounter an NSEC record, try to use it to prove 1114933707f3Ssthen * NODATA. 1115933707f3Ssthen * This needs to handle the ENT NODATA case. */ 1116933707f3Ssthen if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) { 1117933707f3Ssthen if(nsec_proves_nodata(s, qchase, &wc)) { 1118933707f3Ssthen has_valid_nsec = 1; 1119933707f3Ssthen /* sets wc-encloser if wildcard applicable */ 1120933707f3Ssthen } 1121933707f3Ssthen if(val_nsec_proves_name_error(s, qchase->qname)) { 1122933707f3Ssthen ce = nsec_closest_encloser(qchase->qname, s); 1123933707f3Ssthen } 1124933707f3Ssthen if(val_nsec_proves_insecuredelegation(s, qchase)) { 1125933707f3Ssthen verbose(VERB_ALGO, "delegation is insecure"); 1126933707f3Ssthen chase_reply->security = sec_status_insecure; 1127933707f3Ssthen return; 1128933707f3Ssthen } 1129933707f3Ssthen } else if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) { 1130933707f3Ssthen nsec3s_seen = 1; 1131933707f3Ssthen } 1132933707f3Ssthen } 1133933707f3Ssthen 1134933707f3Ssthen /* check to see if we have a wildcard NODATA proof. */ 1135933707f3Ssthen 1136933707f3Ssthen /* The wildcard NODATA is 1 NSEC proving that qname does not exist 1137933707f3Ssthen * (and also proving what the closest encloser is), and 1 NSEC 1138933707f3Ssthen * showing the matching wildcard, which must be *.closest_encloser. */ 1139933707f3Ssthen if(wc && !ce) 1140933707f3Ssthen has_valid_nsec = 0; 1141933707f3Ssthen else if(wc && ce) { 1142933707f3Ssthen if(query_dname_compare(wc, ce) != 0) { 1143933707f3Ssthen has_valid_nsec = 0; 1144933707f3Ssthen } 1145933707f3Ssthen } 1146933707f3Ssthen 1147817bdb8fSflorian if(!has_valid_nsec && nsec3s_seen && 1148817bdb8fSflorian nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { 1149933707f3Ssthen enum sec_status sec = nsec3_prove_nodata(env, ve, 1150933707f3Ssthen chase_reply->rrsets+chase_reply->an_numrrsets, 1151817bdb8fSflorian chase_reply->ns_numrrsets, qchase, kkey, 1152817bdb8fSflorian &vq->nsec3_cache_table, nsec3_calculations); 1153933707f3Ssthen if(sec == sec_status_insecure) { 1154933707f3Ssthen verbose(VERB_ALGO, "NODATA response is insecure"); 1155933707f3Ssthen chase_reply->security = sec_status_insecure; 1156933707f3Ssthen return; 1157817bdb8fSflorian } else if(sec == sec_status_secure) { 1158933707f3Ssthen has_valid_nsec = 1; 1159817bdb8fSflorian } else if(sec == sec_status_unchecked) { 1160817bdb8fSflorian /* check is incomplete; suspend */ 1161817bdb8fSflorian *suspend = 1; 1162817bdb8fSflorian return; 1163817bdb8fSflorian } 1164933707f3Ssthen } 1165933707f3Ssthen 1166933707f3Ssthen if(!has_valid_nsec) { 1167933707f3Ssthen verbose(VERB_QUERY, "NODATA response failed to prove NODATA " 1168933707f3Ssthen "status with NSEC/NSEC3"); 1169933707f3Ssthen if(verbosity >= VERB_ALGO) 1170933707f3Ssthen log_dns_msg("Failed NODATA", qchase, chase_reply); 1171933707f3Ssthen chase_reply->security = sec_status_bogus; 11720bdb4f62Ssthen update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); 1173933707f3Ssthen return; 1174933707f3Ssthen } 1175933707f3Ssthen 1176933707f3Ssthen verbose(VERB_ALGO, "successfully validated NODATA response."); 1177933707f3Ssthen chase_reply->security = sec_status_secure; 1178933707f3Ssthen } 1179933707f3Ssthen 1180933707f3Ssthen /** 1181933707f3Ssthen * Validate a NAMEERROR signed response -- a response that has a NXDOMAIN 1182933707f3Ssthen * Rcode. 1183933707f3Ssthen * This consists of making certain that the authority section NSEC proves 1184933707f3Ssthen * that the qname doesn't exist and the covering wildcard also doesn't exist.. 1185933707f3Ssthen * 1186933707f3Ssthen * The answer and authority RRsets must have already been verified as secure. 1187933707f3Ssthen * 1188933707f3Ssthen * @param env: module env for verify. 1189933707f3Ssthen * @param ve: validator env for verify. 1190933707f3Ssthen * @param qchase: query that was made. 1191933707f3Ssthen * @param chase_reply: answer to that query to validate. 1192933707f3Ssthen * @param kkey: the key entry, which is trusted, and which matches 1193933707f3Ssthen * the signer of the answer. The key entry isgood(). 11945d76a658Ssthen * @param rcode: adjusted RCODE, in case of RCODE/proof mismatch leniency. 1195817bdb8fSflorian * @param qstate: query state for the region. 1196817bdb8fSflorian * @param vq: validator state for the nsec3 cache table. 1197817bdb8fSflorian * @param nsec3_calculations: current nsec3 hash calculations. 1198817bdb8fSflorian * @param suspend: returned true if the task takes too long and needs to 1199817bdb8fSflorian * suspend to continue the effort later. 1200933707f3Ssthen */ 1201933707f3Ssthen static void 1202933707f3Ssthen validate_nameerror_response(struct module_env* env, struct val_env* ve, 1203933707f3Ssthen struct query_info* qchase, struct reply_info* chase_reply, 1204817bdb8fSflorian struct key_entry_key* kkey, int* rcode, 1205817bdb8fSflorian struct module_qstate* qstate, struct val_qstate* vq, 1206817bdb8fSflorian int* nsec3_calculations, int* suspend) 1207933707f3Ssthen { 1208933707f3Ssthen int has_valid_nsec = 0; 1209933707f3Ssthen int has_valid_wnsec = 0; 1210933707f3Ssthen int nsec3s_seen = 0; 1211933707f3Ssthen struct ub_packed_rrset_key* s; 1212933707f3Ssthen size_t i; 1213938a3a5eSflorian uint8_t* ce; 1214938a3a5eSflorian int ce_labs = 0; 1215938a3a5eSflorian int prev_ce_labs = 0; 1216817bdb8fSflorian *suspend = 0; 1217933707f3Ssthen 1218933707f3Ssthen for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+ 1219933707f3Ssthen chase_reply->ns_numrrsets; i++) { 1220933707f3Ssthen s = chase_reply->rrsets[i]; 1221933707f3Ssthen if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) { 1222933707f3Ssthen if(val_nsec_proves_name_error(s, qchase->qname)) 1223933707f3Ssthen has_valid_nsec = 1; 1224938a3a5eSflorian ce = nsec_closest_encloser(qchase->qname, s); 1225938a3a5eSflorian ce_labs = dname_count_labels(ce); 1226938a3a5eSflorian /* Use longest closest encloser to prove wildcard. */ 1227938a3a5eSflorian if(ce_labs > prev_ce_labs || 1228938a3a5eSflorian (ce_labs == prev_ce_labs && 1229938a3a5eSflorian has_valid_wnsec == 0)) { 1230933707f3Ssthen if(val_nsec_proves_no_wc(s, qchase->qname, 1231933707f3Ssthen qchase->qname_len)) 1232933707f3Ssthen has_valid_wnsec = 1; 1233938a3a5eSflorian else 1234938a3a5eSflorian has_valid_wnsec = 0; 1235938a3a5eSflorian } 1236938a3a5eSflorian prev_ce_labs = ce_labs; 1237933707f3Ssthen if(val_nsec_proves_insecuredelegation(s, qchase)) { 1238933707f3Ssthen verbose(VERB_ALGO, "delegation is insecure"); 1239933707f3Ssthen chase_reply->security = sec_status_insecure; 1240933707f3Ssthen return; 1241933707f3Ssthen } 1242933707f3Ssthen } else if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) 1243933707f3Ssthen nsec3s_seen = 1; 1244933707f3Ssthen } 1245933707f3Ssthen 1246817bdb8fSflorian if((!has_valid_nsec || !has_valid_wnsec) && nsec3s_seen && 1247817bdb8fSflorian nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { 1248933707f3Ssthen /* use NSEC3 proof, both answer and auth rrsets, in case 1249933707f3Ssthen * NSEC3s end up in the answer (due to qtype=NSEC3 or so) */ 1250933707f3Ssthen chase_reply->security = nsec3_prove_nameerror(env, ve, 1251933707f3Ssthen chase_reply->rrsets, chase_reply->an_numrrsets+ 1252817bdb8fSflorian chase_reply->ns_numrrsets, qchase, kkey, 1253817bdb8fSflorian &vq->nsec3_cache_table, nsec3_calculations); 1254817bdb8fSflorian if(chase_reply->security == sec_status_unchecked) { 1255817bdb8fSflorian *suspend = 1; 1256817bdb8fSflorian return; 1257817bdb8fSflorian } else if(chase_reply->security != sec_status_secure) { 1258933707f3Ssthen verbose(VERB_QUERY, "NameError response failed nsec, " 1259933707f3Ssthen "nsec3 proof was %s", sec_status_to_string( 1260933707f3Ssthen chase_reply->security)); 1261933707f3Ssthen return; 1262933707f3Ssthen } 1263933707f3Ssthen has_valid_nsec = 1; 1264933707f3Ssthen has_valid_wnsec = 1; 1265933707f3Ssthen } 1266933707f3Ssthen 1267933707f3Ssthen /* If the message fails to prove either condition, it is bogus. */ 1268933707f3Ssthen if(!has_valid_nsec) { 1269817bdb8fSflorian validate_nodata_response(env, ve, qchase, chase_reply, kkey, 1270817bdb8fSflorian qstate, vq, nsec3_calculations, suspend); 1271817bdb8fSflorian if(*suspend) return; 1272933707f3Ssthen verbose(VERB_QUERY, "NameError response has failed to prove: " 1273933707f3Ssthen "qname does not exist"); 1274817bdb8fSflorian /* Be lenient with RCODE in NSEC NameError responses */ 1275817bdb8fSflorian if(chase_reply->security == sec_status_secure) { 1276817bdb8fSflorian *rcode = LDNS_RCODE_NOERROR; 1277817bdb8fSflorian } else { 1278933707f3Ssthen chase_reply->security = sec_status_bogus; 12790bdb4f62Ssthen update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); 1280817bdb8fSflorian } 1281933707f3Ssthen return; 1282933707f3Ssthen } 1283933707f3Ssthen 1284933707f3Ssthen if(!has_valid_wnsec) { 1285817bdb8fSflorian validate_nodata_response(env, ve, qchase, chase_reply, kkey, 1286817bdb8fSflorian qstate, vq, nsec3_calculations, suspend); 1287817bdb8fSflorian if(*suspend) return; 1288933707f3Ssthen verbose(VERB_QUERY, "NameError response has failed to prove: " 1289933707f3Ssthen "covering wildcard does not exist"); 1290817bdb8fSflorian /* Be lenient with RCODE in NSEC NameError responses */ 1291817bdb8fSflorian if (chase_reply->security == sec_status_secure) { 1292817bdb8fSflorian *rcode = LDNS_RCODE_NOERROR; 1293817bdb8fSflorian } else { 1294933707f3Ssthen chase_reply->security = sec_status_bogus; 12950bdb4f62Ssthen update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); 1296817bdb8fSflorian } 1297933707f3Ssthen return; 1298933707f3Ssthen } 1299933707f3Ssthen 1300933707f3Ssthen /* Otherwise, we consider the message secure. */ 1301933707f3Ssthen verbose(VERB_ALGO, "successfully validated NAME ERROR response."); 1302933707f3Ssthen chase_reply->security = sec_status_secure; 1303933707f3Ssthen } 1304933707f3Ssthen 1305933707f3Ssthen /** 1306933707f3Ssthen * Given a referral response, validate rrsets and take least trusted rrset 1307933707f3Ssthen * as the current validation status. 1308933707f3Ssthen * 1309933707f3Ssthen * Note that by the time this method is called, the process of finding the 1310933707f3Ssthen * trusted DNSKEY rrset that signs this response must already have been 1311933707f3Ssthen * completed. 1312933707f3Ssthen * 1313933707f3Ssthen * @param chase_reply: answer to validate. 1314933707f3Ssthen */ 1315933707f3Ssthen static void 1316933707f3Ssthen validate_referral_response(struct reply_info* chase_reply) 1317933707f3Ssthen { 1318933707f3Ssthen size_t i; 1319933707f3Ssthen enum sec_status s; 1320933707f3Ssthen /* message security equals lowest rrset security */ 1321933707f3Ssthen chase_reply->security = sec_status_secure; 1322933707f3Ssthen for(i=0; i<chase_reply->rrset_count; i++) { 1323933707f3Ssthen s = ((struct packed_rrset_data*)chase_reply->rrsets[i] 1324933707f3Ssthen ->entry.data)->security; 1325933707f3Ssthen if(s < chase_reply->security) 1326933707f3Ssthen chase_reply->security = s; 1327933707f3Ssthen } 1328933707f3Ssthen verbose(VERB_ALGO, "validated part of referral response as %s", 1329933707f3Ssthen sec_status_to_string(chase_reply->security)); 1330933707f3Ssthen } 1331933707f3Ssthen 1332933707f3Ssthen /** 1333933707f3Ssthen * Given an "ANY" response -- a response that contains an answer to a 1334933707f3Ssthen * qtype==ANY question, with answers. This does no checking that all 1335933707f3Ssthen * types are present. 1336933707f3Ssthen * 1337933707f3Ssthen * NOTE: it may be possible to get parent-side delegation point records 1338933707f3Ssthen * here, which won't all be signed. Right now, this routine relies on the 1339933707f3Ssthen * upstream iterative resolver to not return these responses -- instead 1340933707f3Ssthen * treating them as referrals. 1341933707f3Ssthen * 1342933707f3Ssthen * NOTE: RFC 4035 is silent on this issue, so this may change upon 1343933707f3Ssthen * clarification. Clarification draft -05 says to not check all types are 1344933707f3Ssthen * present. 1345933707f3Ssthen * 1346933707f3Ssthen * Note that by the time this method is called, the process of finding the 1347933707f3Ssthen * trusted DNSKEY rrset that signs this response must already have been 1348933707f3Ssthen * completed. 1349933707f3Ssthen * 1350933707f3Ssthen * @param env: module env for verify. 1351933707f3Ssthen * @param ve: validator env for verify. 1352933707f3Ssthen * @param qchase: query that was made. 1353933707f3Ssthen * @param chase_reply: answer to that query to validate. 1354933707f3Ssthen * @param kkey: the key entry, which is trusted, and which matches 1355933707f3Ssthen * the signer of the answer. The key entry isgood(). 1356817bdb8fSflorian * @param qstate: query state for the region. 1357817bdb8fSflorian * @param vq: validator state for the nsec3 cache table. 1358817bdb8fSflorian * @param nsec3_calculations: current nsec3 hash calculations. 1359817bdb8fSflorian * @param suspend: returned true if the task takes too long and needs to 1360817bdb8fSflorian * suspend to continue the effort later. 1361933707f3Ssthen */ 1362933707f3Ssthen static void 1363933707f3Ssthen validate_any_response(struct module_env* env, struct val_env* ve, 1364933707f3Ssthen struct query_info* qchase, struct reply_info* chase_reply, 1365817bdb8fSflorian struct key_entry_key* kkey, struct module_qstate* qstate, 1366817bdb8fSflorian struct val_qstate* vq, int* nsec3_calculations, int* suspend) 1367933707f3Ssthen { 1368933707f3Ssthen /* all answer and auth rrsets already verified */ 1369933707f3Ssthen /* but check if a wildcard response is given, then check NSEC/NSEC3 1370933707f3Ssthen * for qname denial to see if wildcard is applicable */ 1371933707f3Ssthen uint8_t* wc = NULL; 1372938a3a5eSflorian size_t wl; 1373933707f3Ssthen int wc_NSEC_ok = 0; 1374933707f3Ssthen int nsec3s_seen = 0; 1375933707f3Ssthen size_t i; 1376933707f3Ssthen struct ub_packed_rrset_key* s; 1377817bdb8fSflorian *suspend = 0; 1378933707f3Ssthen 1379933707f3Ssthen if(qchase->qtype != LDNS_RR_TYPE_ANY) { 1380933707f3Ssthen log_err("internal error: ANY validation called for non-ANY"); 1381933707f3Ssthen chase_reply->security = sec_status_bogus; 13820bdb4f62Ssthen update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); 1383933707f3Ssthen return; 1384933707f3Ssthen } 1385933707f3Ssthen 1386933707f3Ssthen /* validate the ANSWER section - this will be the answer itself */ 1387933707f3Ssthen for(i=0; i<chase_reply->an_numrrsets; i++) { 1388933707f3Ssthen s = chase_reply->rrsets[i]; 1389933707f3Ssthen 1390933707f3Ssthen /* Check to see if the rrset is the result of a wildcard 1391933707f3Ssthen * expansion. If so, an additional check will need to be 1392933707f3Ssthen * made in the authority section. */ 1393938a3a5eSflorian if(!val_rrset_wildcard(s, &wc, &wl)) { 1394933707f3Ssthen log_nametypeclass(VERB_QUERY, "Positive ANY response" 1395933707f3Ssthen " has inconsistent wildcard sigs:", 1396933707f3Ssthen s->rk.dname, ntohs(s->rk.type), 1397933707f3Ssthen ntohs(s->rk.rrset_class)); 1398933707f3Ssthen chase_reply->security = sec_status_bogus; 13990bdb4f62Ssthen update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); 1400933707f3Ssthen return; 1401933707f3Ssthen } 1402933707f3Ssthen } 1403933707f3Ssthen 1404933707f3Ssthen /* if it was a wildcard, check for NSEC/NSEC3s in both answer 1405933707f3Ssthen * and authority sections (NSEC may be moved to the ANSWER section) */ 1406933707f3Ssthen if(wc != NULL) 1407933707f3Ssthen for(i=0; i<chase_reply->an_numrrsets+chase_reply->ns_numrrsets; 1408933707f3Ssthen i++) { 1409933707f3Ssthen s = chase_reply->rrsets[i]; 1410933707f3Ssthen 1411933707f3Ssthen /* If this is a positive wildcard response, and we have a 1412933707f3Ssthen * (just verified) NSEC record, try to use it to 1) prove 1413933707f3Ssthen * that qname doesn't exist and 2) that the correct wildcard 1414933707f3Ssthen * was used. */ 1415933707f3Ssthen if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) { 1416933707f3Ssthen if(val_nsec_proves_positive_wildcard(s, qchase, wc)) { 1417933707f3Ssthen wc_NSEC_ok = 1; 1418933707f3Ssthen } 1419933707f3Ssthen /* if not, continue looking for proof */ 1420933707f3Ssthen } 1421933707f3Ssthen 1422933707f3Ssthen /* Otherwise, if this is a positive wildcard response and 1423933707f3Ssthen * we have NSEC3 records */ 1424933707f3Ssthen if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) { 1425933707f3Ssthen nsec3s_seen = 1; 1426933707f3Ssthen } 1427933707f3Ssthen } 1428933707f3Ssthen 1429933707f3Ssthen /* If this was a positive wildcard response that we haven't already 1430933707f3Ssthen * proven, and we have NSEC3 records, try to prove it using the NSEC3 1431933707f3Ssthen * records. */ 1432817bdb8fSflorian if(wc != NULL && !wc_NSEC_ok && nsec3s_seen && 1433817bdb8fSflorian nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { 1434933707f3Ssthen /* look both in answer and auth section for NSEC3s */ 1435933707f3Ssthen enum sec_status sec = nsec3_prove_wildcard(env, ve, 1436933707f3Ssthen chase_reply->rrsets, 1437933707f3Ssthen chase_reply->an_numrrsets+chase_reply->ns_numrrsets, 1438817bdb8fSflorian qchase, kkey, wc, &vq->nsec3_cache_table, 1439817bdb8fSflorian nsec3_calculations); 1440933707f3Ssthen if(sec == sec_status_insecure) { 1441933707f3Ssthen verbose(VERB_ALGO, "Positive ANY wildcard response is " 1442933707f3Ssthen "insecure"); 1443933707f3Ssthen chase_reply->security = sec_status_insecure; 1444933707f3Ssthen return; 1445817bdb8fSflorian } else if(sec == sec_status_secure) { 1446933707f3Ssthen wc_NSEC_ok = 1; 1447817bdb8fSflorian } else if(sec == sec_status_unchecked) { 1448817bdb8fSflorian *suspend = 1; 1449817bdb8fSflorian return; 1450817bdb8fSflorian } 1451933707f3Ssthen } 1452933707f3Ssthen 1453933707f3Ssthen /* If after all this, we still haven't proven the positive wildcard 1454933707f3Ssthen * response, fail. */ 1455933707f3Ssthen if(wc != NULL && !wc_NSEC_ok) { 1456933707f3Ssthen verbose(VERB_QUERY, "positive ANY response was wildcard " 1457933707f3Ssthen "expansion and did not prove original data " 1458933707f3Ssthen "did not exist"); 1459933707f3Ssthen chase_reply->security = sec_status_bogus; 14600bdb4f62Ssthen update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); 1461933707f3Ssthen return; 1462933707f3Ssthen } 1463933707f3Ssthen 1464933707f3Ssthen verbose(VERB_ALGO, "Successfully validated positive ANY response"); 1465933707f3Ssthen chase_reply->security = sec_status_secure; 1466933707f3Ssthen } 1467933707f3Ssthen 1468933707f3Ssthen /** 1469933707f3Ssthen * Validate CNAME response, or DNAME+CNAME. 1470933707f3Ssthen * This is just like a positive proof, except that this is about a 1471933707f3Ssthen * DNAME+CNAME. Possible wildcard proof. 1472933707f3Ssthen * Difference with positive proof is that this routine refuses 1473933707f3Ssthen * wildcarded DNAMEs. 1474933707f3Ssthen * 1475933707f3Ssthen * The answer and authority rrsets must already be verified as secure. 1476933707f3Ssthen * 1477933707f3Ssthen * @param env: module env for verify. 1478933707f3Ssthen * @param ve: validator env for verify. 1479933707f3Ssthen * @param qchase: query that was made. 1480933707f3Ssthen * @param chase_reply: answer to that query to validate. 1481933707f3Ssthen * @param kkey: the key entry, which is trusted, and which matches 1482933707f3Ssthen * the signer of the answer. The key entry isgood(). 1483817bdb8fSflorian * @param qstate: query state for the region. 1484817bdb8fSflorian * @param vq: validator state for the nsec3 cache table. 1485817bdb8fSflorian * @param nsec3_calculations: current nsec3 hash calculations. 1486817bdb8fSflorian * @param suspend: returned true if the task takes too long and needs to 1487817bdb8fSflorian * suspend to continue the effort later. 1488933707f3Ssthen */ 1489933707f3Ssthen static void 1490933707f3Ssthen validate_cname_response(struct module_env* env, struct val_env* ve, 1491933707f3Ssthen struct query_info* qchase, struct reply_info* chase_reply, 1492817bdb8fSflorian struct key_entry_key* kkey, struct module_qstate* qstate, 1493817bdb8fSflorian struct val_qstate* vq, int* nsec3_calculations, int* suspend) 1494933707f3Ssthen { 1495933707f3Ssthen uint8_t* wc = NULL; 1496938a3a5eSflorian size_t wl; 1497933707f3Ssthen int wc_NSEC_ok = 0; 1498933707f3Ssthen int nsec3s_seen = 0; 1499933707f3Ssthen size_t i; 1500933707f3Ssthen struct ub_packed_rrset_key* s; 1501817bdb8fSflorian *suspend = 0; 1502933707f3Ssthen 1503933707f3Ssthen /* validate the ANSWER section - this will be the CNAME (+DNAME) */ 1504933707f3Ssthen for(i=0; i<chase_reply->an_numrrsets; i++) { 1505933707f3Ssthen s = chase_reply->rrsets[i]; 1506933707f3Ssthen 1507933707f3Ssthen /* Check to see if the rrset is the result of a wildcard 1508933707f3Ssthen * expansion. If so, an additional check will need to be 1509933707f3Ssthen * made in the authority section. */ 1510938a3a5eSflorian if(!val_rrset_wildcard(s, &wc, &wl)) { 1511933707f3Ssthen log_nametypeclass(VERB_QUERY, "Cname response has " 1512933707f3Ssthen "inconsistent wildcard sigs:", s->rk.dname, 1513933707f3Ssthen ntohs(s->rk.type), ntohs(s->rk.rrset_class)); 1514933707f3Ssthen chase_reply->security = sec_status_bogus; 15150bdb4f62Ssthen update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); 1516933707f3Ssthen return; 1517933707f3Ssthen } 1518933707f3Ssthen 1519933707f3Ssthen /* Refuse wildcarded DNAMEs rfc 4597. 1520933707f3Ssthen * Do not follow a wildcarded DNAME because 1521933707f3Ssthen * its synthesized CNAME expansion is underdefined */ 1522933707f3Ssthen if(qchase->qtype != LDNS_RR_TYPE_DNAME && 1523933707f3Ssthen ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME && wc) { 1524933707f3Ssthen log_nametypeclass(VERB_QUERY, "cannot validate a " 1525933707f3Ssthen "wildcarded DNAME:", s->rk.dname, 1526933707f3Ssthen ntohs(s->rk.type), ntohs(s->rk.rrset_class)); 1527933707f3Ssthen chase_reply->security = sec_status_bogus; 15280bdb4f62Ssthen update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); 1529933707f3Ssthen return; 1530933707f3Ssthen } 1531229e174cSsthen 1532229e174cSsthen /* If we have found a CNAME, stop looking for one. 1533229e174cSsthen * The iterator has placed the CNAME chain in correct 1534229e174cSsthen * order. */ 1535229e174cSsthen if (ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME) { 1536229e174cSsthen break; 1537229e174cSsthen } 1538933707f3Ssthen } 1539933707f3Ssthen 1540933707f3Ssthen /* AUTHORITY section */ 1541933707f3Ssthen for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+ 1542933707f3Ssthen chase_reply->ns_numrrsets; i++) { 1543933707f3Ssthen s = chase_reply->rrsets[i]; 1544933707f3Ssthen 1545933707f3Ssthen /* If this is a positive wildcard response, and we have a 1546933707f3Ssthen * (just verified) NSEC record, try to use it to 1) prove 1547933707f3Ssthen * that qname doesn't exist and 2) that the correct wildcard 1548933707f3Ssthen * was used. */ 1549933707f3Ssthen if(wc != NULL && ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) { 1550933707f3Ssthen if(val_nsec_proves_positive_wildcard(s, qchase, wc)) { 1551933707f3Ssthen wc_NSEC_ok = 1; 1552933707f3Ssthen } 1553933707f3Ssthen /* if not, continue looking for proof */ 1554933707f3Ssthen } 1555933707f3Ssthen 1556933707f3Ssthen /* Otherwise, if this is a positive wildcard response and 1557933707f3Ssthen * we have NSEC3 records */ 1558933707f3Ssthen if(wc != NULL && ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) { 1559933707f3Ssthen nsec3s_seen = 1; 1560933707f3Ssthen } 1561933707f3Ssthen } 1562933707f3Ssthen 1563933707f3Ssthen /* If this was a positive wildcard response that we haven't already 1564933707f3Ssthen * proven, and we have NSEC3 records, try to prove it using the NSEC3 1565933707f3Ssthen * records. */ 1566817bdb8fSflorian if(wc != NULL && !wc_NSEC_ok && nsec3s_seen && 1567817bdb8fSflorian nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { 1568933707f3Ssthen enum sec_status sec = nsec3_prove_wildcard(env, ve, 1569933707f3Ssthen chase_reply->rrsets+chase_reply->an_numrrsets, 1570817bdb8fSflorian chase_reply->ns_numrrsets, qchase, kkey, wc, 1571817bdb8fSflorian &vq->nsec3_cache_table, nsec3_calculations); 1572933707f3Ssthen if(sec == sec_status_insecure) { 1573933707f3Ssthen verbose(VERB_ALGO, "wildcard CNAME response is " 1574933707f3Ssthen "insecure"); 1575933707f3Ssthen chase_reply->security = sec_status_insecure; 1576933707f3Ssthen return; 1577817bdb8fSflorian } else if(sec == sec_status_secure) { 1578933707f3Ssthen wc_NSEC_ok = 1; 1579817bdb8fSflorian } else if(sec == sec_status_unchecked) { 1580817bdb8fSflorian *suspend = 1; 1581817bdb8fSflorian return; 1582817bdb8fSflorian } 1583933707f3Ssthen } 1584933707f3Ssthen 1585933707f3Ssthen /* If after all this, we still haven't proven the positive wildcard 1586933707f3Ssthen * response, fail. */ 1587933707f3Ssthen if(wc != NULL && !wc_NSEC_ok) { 1588933707f3Ssthen verbose(VERB_QUERY, "CNAME response was wildcard " 1589933707f3Ssthen "expansion and did not prove original data " 1590933707f3Ssthen "did not exist"); 1591933707f3Ssthen chase_reply->security = sec_status_bogus; 15920bdb4f62Ssthen update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); 1593933707f3Ssthen return; 1594933707f3Ssthen } 1595933707f3Ssthen 1596933707f3Ssthen verbose(VERB_ALGO, "Successfully validated CNAME response"); 1597933707f3Ssthen chase_reply->security = sec_status_secure; 1598933707f3Ssthen } 1599933707f3Ssthen 1600933707f3Ssthen /** 1601933707f3Ssthen * Validate CNAME NOANSWER response, no more data after a CNAME chain. 1602933707f3Ssthen * This can be a NODATA or a NAME ERROR case, but not both at the same time. 1603933707f3Ssthen * We don't know because the rcode has been set to NOERROR by the CNAME. 1604933707f3Ssthen * 1605933707f3Ssthen * The answer and authority rrsets must already be verified as secure. 1606933707f3Ssthen * 1607933707f3Ssthen * @param env: module env for verify. 1608933707f3Ssthen * @param ve: validator env for verify. 1609933707f3Ssthen * @param qchase: query that was made. 1610933707f3Ssthen * @param chase_reply: answer to that query to validate. 1611933707f3Ssthen * @param kkey: the key entry, which is trusted, and which matches 1612933707f3Ssthen * the signer of the answer. The key entry isgood(). 1613817bdb8fSflorian * @param qstate: query state for the region. 1614817bdb8fSflorian * @param vq: validator state for the nsec3 cache table. 1615817bdb8fSflorian * @param nsec3_calculations: current nsec3 hash calculations. 1616817bdb8fSflorian * @param suspend: returned true if the task takes too long and needs to 1617817bdb8fSflorian * suspend to continue the effort later. 1618933707f3Ssthen */ 1619933707f3Ssthen static void 1620933707f3Ssthen validate_cname_noanswer_response(struct module_env* env, struct val_env* ve, 1621933707f3Ssthen struct query_info* qchase, struct reply_info* chase_reply, 1622817bdb8fSflorian struct key_entry_key* kkey, struct module_qstate* qstate, 1623817bdb8fSflorian struct val_qstate* vq, int* nsec3_calculations, int* suspend) 1624933707f3Ssthen { 1625933707f3Ssthen int nodata_valid_nsec = 0; /* If true, then NODATA has been proven.*/ 1626933707f3Ssthen uint8_t* ce = NULL; /* for wildcard nodata responses. This is the 1627933707f3Ssthen proven closest encloser. */ 1628933707f3Ssthen uint8_t* wc = NULL; /* for wildcard nodata responses. wildcard nsec */ 1629bdfc4d55Sflorian int nxdomain_valid_nsec = 0; /* if true, nameerror has been proven */ 1630933707f3Ssthen int nxdomain_valid_wnsec = 0; 1631933707f3Ssthen int nsec3s_seen = 0; /* nsec3s seen */ 1632933707f3Ssthen struct ub_packed_rrset_key* s; 1633933707f3Ssthen size_t i; 1634938a3a5eSflorian uint8_t* nsec_ce; /* Used to find the NSEC with the longest ce */ 1635938a3a5eSflorian int ce_labs = 0; 1636938a3a5eSflorian int prev_ce_labs = 0; 1637817bdb8fSflorian *suspend = 0; 1638933707f3Ssthen 1639933707f3Ssthen /* the AUTHORITY section */ 1640933707f3Ssthen for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+ 1641933707f3Ssthen chase_reply->ns_numrrsets; i++) { 1642933707f3Ssthen s = chase_reply->rrsets[i]; 1643933707f3Ssthen 1644933707f3Ssthen /* If we encounter an NSEC record, try to use it to prove 1645933707f3Ssthen * NODATA. This needs to handle the ENT NODATA case. 1646933707f3Ssthen * Also try to prove NAMEERROR, and absence of a wildcard */ 1647933707f3Ssthen if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) { 1648933707f3Ssthen if(nsec_proves_nodata(s, qchase, &wc)) { 1649933707f3Ssthen nodata_valid_nsec = 1; 1650933707f3Ssthen /* set wc encloser if wildcard applicable */ 1651933707f3Ssthen } 1652933707f3Ssthen if(val_nsec_proves_name_error(s, qchase->qname)) { 1653933707f3Ssthen ce = nsec_closest_encloser(qchase->qname, s); 1654933707f3Ssthen nxdomain_valid_nsec = 1; 1655933707f3Ssthen } 1656938a3a5eSflorian nsec_ce = nsec_closest_encloser(qchase->qname, s); 1657938a3a5eSflorian ce_labs = dname_count_labels(nsec_ce); 1658938a3a5eSflorian /* Use longest closest encloser to prove wildcard. */ 1659938a3a5eSflorian if(ce_labs > prev_ce_labs || 1660938a3a5eSflorian (ce_labs == prev_ce_labs && 1661938a3a5eSflorian nxdomain_valid_wnsec == 0)) { 1662933707f3Ssthen if(val_nsec_proves_no_wc(s, qchase->qname, 1663933707f3Ssthen qchase->qname_len)) 1664933707f3Ssthen nxdomain_valid_wnsec = 1; 1665938a3a5eSflorian else 1666938a3a5eSflorian nxdomain_valid_wnsec = 0; 1667938a3a5eSflorian } 1668938a3a5eSflorian prev_ce_labs = ce_labs; 1669933707f3Ssthen if(val_nsec_proves_insecuredelegation(s, qchase)) { 1670933707f3Ssthen verbose(VERB_ALGO, "delegation is insecure"); 1671933707f3Ssthen chase_reply->security = sec_status_insecure; 1672933707f3Ssthen return; 1673933707f3Ssthen } 1674933707f3Ssthen } else if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) { 1675933707f3Ssthen nsec3s_seen = 1; 1676933707f3Ssthen } 1677933707f3Ssthen } 1678933707f3Ssthen 1679933707f3Ssthen /* check to see if we have a wildcard NODATA proof. */ 1680933707f3Ssthen 1681933707f3Ssthen /* The wildcard NODATA is 1 NSEC proving that qname does not exists 1682933707f3Ssthen * (and also proving what the closest encloser is), and 1 NSEC 1683933707f3Ssthen * showing the matching wildcard, which must be *.closest_encloser. */ 1684933707f3Ssthen if(wc && !ce) 1685933707f3Ssthen nodata_valid_nsec = 0; 1686933707f3Ssthen else if(wc && ce) { 1687933707f3Ssthen if(query_dname_compare(wc, ce) != 0) { 1688933707f3Ssthen nodata_valid_nsec = 0; 1689933707f3Ssthen } 1690933707f3Ssthen } 1691933707f3Ssthen if(nxdomain_valid_nsec && !nxdomain_valid_wnsec) { 1692933707f3Ssthen /* name error is missing wildcard denial proof */ 1693933707f3Ssthen nxdomain_valid_nsec = 0; 1694933707f3Ssthen } 1695933707f3Ssthen 1696933707f3Ssthen if(nodata_valid_nsec && nxdomain_valid_nsec) { 1697933707f3Ssthen verbose(VERB_QUERY, "CNAMEchain to noanswer proves that name " 1698933707f3Ssthen "exists and not exists, bogus"); 1699933707f3Ssthen chase_reply->security = sec_status_bogus; 17000bdb4f62Ssthen update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); 1701933707f3Ssthen return; 1702933707f3Ssthen } 1703817bdb8fSflorian if(!nodata_valid_nsec && !nxdomain_valid_nsec && nsec3s_seen && 1704817bdb8fSflorian nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { 1705933707f3Ssthen int nodata; 1706933707f3Ssthen enum sec_status sec = nsec3_prove_nxornodata(env, ve, 1707933707f3Ssthen chase_reply->rrsets+chase_reply->an_numrrsets, 1708817bdb8fSflorian chase_reply->ns_numrrsets, qchase, kkey, &nodata, 1709817bdb8fSflorian &vq->nsec3_cache_table, nsec3_calculations); 1710933707f3Ssthen if(sec == sec_status_insecure) { 1711933707f3Ssthen verbose(VERB_ALGO, "CNAMEchain to noanswer response " 1712933707f3Ssthen "is insecure"); 1713933707f3Ssthen chase_reply->security = sec_status_insecure; 1714933707f3Ssthen return; 1715933707f3Ssthen } else if(sec == sec_status_secure) { 1716933707f3Ssthen if(nodata) 1717933707f3Ssthen nodata_valid_nsec = 1; 1718933707f3Ssthen else nxdomain_valid_nsec = 1; 1719817bdb8fSflorian } else if(sec == sec_status_unchecked) { 1720817bdb8fSflorian *suspend = 1; 1721817bdb8fSflorian return; 1722933707f3Ssthen } 1723933707f3Ssthen } 1724933707f3Ssthen 1725933707f3Ssthen if(!nodata_valid_nsec && !nxdomain_valid_nsec) { 1726933707f3Ssthen verbose(VERB_QUERY, "CNAMEchain to noanswer response failed " 1727933707f3Ssthen "to prove status with NSEC/NSEC3"); 1728933707f3Ssthen if(verbosity >= VERB_ALGO) 1729933707f3Ssthen log_dns_msg("Failed CNAMEnoanswer", qchase, chase_reply); 1730933707f3Ssthen chase_reply->security = sec_status_bogus; 17310bdb4f62Ssthen update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); 1732933707f3Ssthen return; 1733933707f3Ssthen } 1734933707f3Ssthen 1735933707f3Ssthen if(nodata_valid_nsec) 1736933707f3Ssthen verbose(VERB_ALGO, "successfully validated CNAME chain to a " 1737933707f3Ssthen "NODATA response."); 1738933707f3Ssthen else verbose(VERB_ALGO, "successfully validated CNAME chain to a " 1739933707f3Ssthen "NAMEERROR response."); 1740933707f3Ssthen chase_reply->security = sec_status_secure; 1741933707f3Ssthen } 1742933707f3Ssthen 1743933707f3Ssthen /** 1744933707f3Ssthen * Process init state for validator. 1745933707f3Ssthen * Process the INIT state. First tier responses start in the INIT state. 1746933707f3Ssthen * This is where they are vetted for validation suitability, and the initial 1747933707f3Ssthen * key search is done. 1748933707f3Ssthen * 1749933707f3Ssthen * Currently, events the come through this routine will be either promoted 1750933707f3Ssthen * to FINISHED/CNAME_RESP (no validation needed), FINDKEY (next step to 1751933707f3Ssthen * validation), or will be (temporarily) retired and a new priming request 1752933707f3Ssthen * event will be generated. 1753933707f3Ssthen * 1754933707f3Ssthen * @param qstate: query state. 1755933707f3Ssthen * @param vq: validator query state. 1756933707f3Ssthen * @param ve: validator shared global environment. 1757933707f3Ssthen * @param id: module id. 1758933707f3Ssthen * @return true if the event should be processed further on return, false if 1759933707f3Ssthen * not. 1760933707f3Ssthen */ 1761933707f3Ssthen static int 1762933707f3Ssthen processInit(struct module_qstate* qstate, struct val_qstate* vq, 1763933707f3Ssthen struct val_env* ve, int id) 1764933707f3Ssthen { 1765933707f3Ssthen uint8_t* lookup_name; 1766933707f3Ssthen size_t lookup_len; 1767933707f3Ssthen struct trust_anchor* anchor; 1768933707f3Ssthen enum val_classification subtype = val_classify_response( 1769933707f3Ssthen qstate->query_flags, &qstate->qinfo, &vq->qchase, 1770933707f3Ssthen vq->orig_msg->rep, vq->rrset_skip); 1771191f22c6Ssthen if(vq->restart_count > ve->max_restart) { 1772933707f3Ssthen verbose(VERB_ALGO, "restart count exceeded"); 1773933707f3Ssthen return val_error(qstate, id); 1774933707f3Ssthen } 17750bdb4f62Ssthen 17760bdb4f62Ssthen /* correctly initialize reason_bogus */ 17770bdb4f62Ssthen update_reason_bogus(vq->chase_reply, LDNS_EDE_DNSSEC_BOGUS); 17780bdb4f62Ssthen 1779933707f3Ssthen verbose(VERB_ALGO, "validator classification %s", 1780933707f3Ssthen val_classification_to_string(subtype)); 1781933707f3Ssthen if(subtype == VAL_CLASS_REFERRAL && 1782933707f3Ssthen vq->rrset_skip < vq->orig_msg->rep->rrset_count) { 1783933707f3Ssthen /* referral uses the rrset name as qchase, to find keys for 1784933707f3Ssthen * that rrset */ 1785933707f3Ssthen vq->qchase.qname = vq->orig_msg->rep-> 1786933707f3Ssthen rrsets[vq->rrset_skip]->rk.dname; 1787933707f3Ssthen vq->qchase.qname_len = vq->orig_msg->rep-> 1788933707f3Ssthen rrsets[vq->rrset_skip]->rk.dname_len; 1789933707f3Ssthen vq->qchase.qtype = ntohs(vq->orig_msg->rep-> 1790933707f3Ssthen rrsets[vq->rrset_skip]->rk.type); 1791933707f3Ssthen vq->qchase.qclass = ntohs(vq->orig_msg->rep-> 1792933707f3Ssthen rrsets[vq->rrset_skip]->rk.rrset_class); 1793933707f3Ssthen } 1794933707f3Ssthen lookup_name = vq->qchase.qname; 1795933707f3Ssthen lookup_len = vq->qchase.qname_len; 1796933707f3Ssthen /* for type DS look at the parent side for keys/trustanchor */ 1797933707f3Ssthen /* also for NSEC not at apex */ 1798933707f3Ssthen if(vq->qchase.qtype == LDNS_RR_TYPE_DS || 1799933707f3Ssthen (vq->qchase.qtype == LDNS_RR_TYPE_NSEC && 1800933707f3Ssthen vq->orig_msg->rep->rrset_count > vq->rrset_skip && 1801933707f3Ssthen ntohs(vq->orig_msg->rep->rrsets[vq->rrset_skip]->rk.type) == 1802933707f3Ssthen LDNS_RR_TYPE_NSEC && 1803933707f3Ssthen !(vq->orig_msg->rep->rrsets[vq->rrset_skip]-> 1804933707f3Ssthen rk.flags&PACKED_RRSET_NSEC_AT_APEX))) { 1805933707f3Ssthen dname_remove_label(&lookup_name, &lookup_len); 1806933707f3Ssthen } 1807933707f3Ssthen 1808933707f3Ssthen val_mark_indeterminate(vq->chase_reply, qstate->env->anchors, 1809933707f3Ssthen qstate->env->rrset_cache, qstate->env); 1810933707f3Ssthen vq->key_entry = NULL; 1811933707f3Ssthen vq->empty_DS_name = NULL; 1812933707f3Ssthen vq->ds_rrset = 0; 1813933707f3Ssthen anchor = anchors_lookup(qstate->env->anchors, 1814933707f3Ssthen lookup_name, lookup_len, vq->qchase.qclass); 1815933707f3Ssthen 1816933707f3Ssthen /* Determine the signer/lookup name */ 1817933707f3Ssthen val_find_signer(subtype, &vq->qchase, vq->orig_msg->rep, 1818933707f3Ssthen vq->rrset_skip, &vq->signer_name, &vq->signer_len); 1819933707f3Ssthen if(vq->signer_name != NULL && 1820933707f3Ssthen !dname_subdomain_c(lookup_name, vq->signer_name)) { 1821933707f3Ssthen log_nametypeclass(VERB_ALGO, "this signer name is not a parent " 1822933707f3Ssthen "of lookupname, omitted", vq->signer_name, 0, 0); 1823933707f3Ssthen vq->signer_name = NULL; 1824933707f3Ssthen } 1825933707f3Ssthen if(vq->signer_name == NULL) { 1826933707f3Ssthen log_nametypeclass(VERB_ALGO, "no signer, using", lookup_name, 1827933707f3Ssthen 0, 0); 1828933707f3Ssthen } else { 1829933707f3Ssthen lookup_name = vq->signer_name; 1830933707f3Ssthen lookup_len = vq->signer_len; 1831933707f3Ssthen log_nametypeclass(VERB_ALGO, "signer is", lookup_name, 0, 0); 1832933707f3Ssthen } 1833933707f3Ssthen 1834933707f3Ssthen /* for NXDOMAIN it could be signed by a parent of the trust anchor */ 1835933707f3Ssthen if(subtype == VAL_CLASS_NAMEERROR && vq->signer_name && 1836933707f3Ssthen anchor && dname_strict_subdomain_c(anchor->name, lookup_name)){ 1837933707f3Ssthen lock_basic_unlock(&anchor->lock); 1838933707f3Ssthen anchor = anchors_lookup(qstate->env->anchors, 1839933707f3Ssthen lookup_name, lookup_len, vq->qchase.qclass); 1840933707f3Ssthen if(!anchor) { /* unsigned parent denies anchor*/ 1841933707f3Ssthen verbose(VERB_QUERY, "unsigned parent zone denies" 1842933707f3Ssthen " trust anchor, indeterminate"); 1843933707f3Ssthen vq->chase_reply->security = sec_status_indeterminate; 18440bdb4f62Ssthen update_reason_bogus(vq->chase_reply, LDNS_EDE_DNSSEC_INDETERMINATE); 1845933707f3Ssthen vq->state = VAL_FINISHED_STATE; 1846933707f3Ssthen return 1; 1847933707f3Ssthen } 1848933707f3Ssthen verbose(VERB_ALGO, "trust anchor NXDOMAIN by signed parent"); 1849933707f3Ssthen } else if(subtype == VAL_CLASS_POSITIVE && 1850933707f3Ssthen qstate->qinfo.qtype == LDNS_RR_TYPE_DNSKEY && 1851933707f3Ssthen query_dname_compare(lookup_name, qstate->qinfo.qname) == 0) { 1852933707f3Ssthen /* is a DNSKEY so lookup a bit higher since we want to 1853933707f3Ssthen * get it from a parent or from trustanchor */ 1854933707f3Ssthen dname_remove_label(&lookup_name, &lookup_len); 1855933707f3Ssthen } 1856933707f3Ssthen 1857933707f3Ssthen if(vq->rrset_skip > 0 || subtype == VAL_CLASS_CNAME || 1858933707f3Ssthen subtype == VAL_CLASS_REFERRAL) { 1859933707f3Ssthen /* extract this part of orig_msg into chase_reply for 1860933707f3Ssthen * the eventual VALIDATE stage */ 1861933707f3Ssthen val_fill_reply(vq->chase_reply, vq->orig_msg->rep, 1862933707f3Ssthen vq->rrset_skip, lookup_name, lookup_len, 1863933707f3Ssthen vq->signer_name); 1864933707f3Ssthen if(verbosity >= VERB_ALGO) 1865933707f3Ssthen log_dns_msg("chased extract", &vq->qchase, 1866933707f3Ssthen vq->chase_reply); 1867933707f3Ssthen } 1868933707f3Ssthen 1869933707f3Ssthen vq->key_entry = key_cache_obtain(ve->kcache, lookup_name, lookup_len, 1870933707f3Ssthen vq->qchase.qclass, qstate->region, *qstate->env->now); 1871933707f3Ssthen 18722c144df0Ssthen /* there is no key and no trust anchor */ 1873933707f3Ssthen if(vq->key_entry == NULL && anchor == NULL) { 1874933707f3Ssthen /*response isn't under a trust anchor, so we cannot validate.*/ 1875933707f3Ssthen vq->chase_reply->security = sec_status_indeterminate; 18760bdb4f62Ssthen update_reason_bogus(vq->chase_reply, LDNS_EDE_DNSSEC_INDETERMINATE); 1877933707f3Ssthen /* go to finished state to cache this result */ 1878933707f3Ssthen vq->state = VAL_FINISHED_STATE; 1879933707f3Ssthen return 1; 1880933707f3Ssthen } 1881933707f3Ssthen /* if not key, or if keyentry is *above* the trustanchor, i.e. 1882933707f3Ssthen * the keyentry is based on another (higher) trustanchor */ 1883933707f3Ssthen else if(vq->key_entry == NULL || (anchor && 1884933707f3Ssthen dname_strict_subdomain_c(anchor->name, vq->key_entry->name))) { 1885933707f3Ssthen /* trust anchor is an 'unsigned' trust anchor */ 1886933707f3Ssthen if(anchor && anchor->numDS == 0 && anchor->numDNSKEY == 0) { 1887933707f3Ssthen vq->chase_reply->security = sec_status_insecure; 1888933707f3Ssthen val_mark_insecure(vq->chase_reply, anchor->name, 1889933707f3Ssthen qstate->env->rrset_cache, qstate->env); 1890933707f3Ssthen lock_basic_unlock(&anchor->lock); 1891933707f3Ssthen /* go to finished state to cache this result */ 1892933707f3Ssthen vq->state = VAL_FINISHED_STATE; 1893933707f3Ssthen return 1; 1894933707f3Ssthen } 1895933707f3Ssthen /* fire off a trust anchor priming query. */ 1896933707f3Ssthen verbose(VERB_DETAIL, "prime trust anchor"); 1897933707f3Ssthen if(!prime_trust_anchor(qstate, vq, id, anchor)) { 1898933707f3Ssthen lock_basic_unlock(&anchor->lock); 1899933707f3Ssthen return val_error(qstate, id); 1900933707f3Ssthen } 1901933707f3Ssthen lock_basic_unlock(&anchor->lock); 1902933707f3Ssthen /* and otherwise, don't continue processing this event. 1903933707f3Ssthen * (it will be reactivated when the priming query returns). */ 1904933707f3Ssthen vq->state = VAL_FINDKEY_STATE; 1905933707f3Ssthen return 0; 1906933707f3Ssthen } 1907933707f3Ssthen if(anchor) { 1908933707f3Ssthen lock_basic_unlock(&anchor->lock); 1909933707f3Ssthen } 1910933707f3Ssthen 1911933707f3Ssthen if(key_entry_isnull(vq->key_entry)) { 1912933707f3Ssthen /* response is under a null key, so we cannot validate 1913933707f3Ssthen * However, we do set the status to INSECURE, since it is 1914933707f3Ssthen * essentially proven insecure. */ 1915933707f3Ssthen vq->chase_reply->security = sec_status_insecure; 1916933707f3Ssthen val_mark_insecure(vq->chase_reply, vq->key_entry->name, 1917933707f3Ssthen qstate->env->rrset_cache, qstate->env); 1918933707f3Ssthen /* go to finished state to cache this result */ 1919933707f3Ssthen vq->state = VAL_FINISHED_STATE; 1920933707f3Ssthen return 1; 1921933707f3Ssthen } else if(key_entry_isbad(vq->key_entry)) { 19228b7325afSsthen /* Bad keys should have the relevant EDE code and text */ 19238b7325afSsthen sldns_ede_code ede = key_entry_get_reason_bogus(vq->key_entry); 1924933707f3Ssthen /* key is bad, chain is bad, reply is bogus */ 1925933707f3Ssthen errinf_dname(qstate, "key for validation", vq->key_entry->name); 19260bdb4f62Ssthen errinf_ede(qstate, "is marked as invalid", ede); 1927933707f3Ssthen errinf(qstate, "because of a previous"); 1928933707f3Ssthen errinf(qstate, key_entry_get_reason(vq->key_entry)); 19290bdb4f62Ssthen 1930933707f3Ssthen /* no retries, stop bothering the authority until timeout */ 1931191f22c6Ssthen vq->restart_count = ve->max_restart; 1932933707f3Ssthen vq->chase_reply->security = sec_status_bogus; 19330bdb4f62Ssthen update_reason_bogus(vq->chase_reply, ede); 1934933707f3Ssthen vq->state = VAL_FINISHED_STATE; 1935933707f3Ssthen return 1; 1936933707f3Ssthen } 1937933707f3Ssthen 1938933707f3Ssthen /* otherwise, we have our "closest" cached key -- continue 1939933707f3Ssthen * processing in the next state. */ 1940933707f3Ssthen vq->state = VAL_FINDKEY_STATE; 1941933707f3Ssthen return 1; 1942933707f3Ssthen } 1943933707f3Ssthen 1944933707f3Ssthen /** 1945933707f3Ssthen * Process the FINDKEY state. Generally this just calculates the next name 1946933707f3Ssthen * to query and either issues a DS or a DNSKEY query. It will check to see 1947933707f3Ssthen * if the correct key has already been reached, in which case it will 1948933707f3Ssthen * advance the event to the next state. 1949933707f3Ssthen * 1950933707f3Ssthen * @param qstate: query state. 1951933707f3Ssthen * @param vq: validator query state. 1952933707f3Ssthen * @param id: module id. 1953933707f3Ssthen * @return true if the event should be processed further on return, false if 1954933707f3Ssthen * not. 1955933707f3Ssthen */ 1956933707f3Ssthen static int 1957933707f3Ssthen processFindKey(struct module_qstate* qstate, struct val_qstate* vq, int id) 1958933707f3Ssthen { 1959933707f3Ssthen uint8_t* target_key_name, *current_key_name; 1960933707f3Ssthen size_t target_key_len; 1961933707f3Ssthen int strip_lab; 19622be9e038Ssthen struct module_qstate* newq = NULL; 1963933707f3Ssthen 1964933707f3Ssthen log_query_info(VERB_ALGO, "validator: FindKey", &vq->qchase); 1965933707f3Ssthen /* We know that state.key_entry is not 0 or bad key -- if it were, 1966933707f3Ssthen * then previous processing should have directed this event to 1967933707f3Ssthen * a different state. 19682c144df0Ssthen * It could be an isnull key, which signals the DNSKEY failed 19692c144df0Ssthen * with retry and has to be looked up again. */ 1970933707f3Ssthen log_assert(vq->key_entry && !key_entry_isbad(vq->key_entry)); 1971933707f3Ssthen if(key_entry_isnull(vq->key_entry)) { 1972933707f3Ssthen if(!generate_request(qstate, id, vq->ds_rrset->rk.dname, 1973933707f3Ssthen vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY, 19742be9e038Ssthen vq->qchase.qclass, BIT_CD, &newq, 0)) { 19752308e98cSsthen verbose(VERB_ALGO, "error generating DNSKEY request"); 1976933707f3Ssthen return val_error(qstate, id); 1977933707f3Ssthen } 1978933707f3Ssthen return 0; 1979933707f3Ssthen } 1980933707f3Ssthen 1981933707f3Ssthen target_key_name = vq->signer_name; 1982933707f3Ssthen target_key_len = vq->signer_len; 1983933707f3Ssthen if(!target_key_name) { 1984933707f3Ssthen target_key_name = vq->qchase.qname; 1985933707f3Ssthen target_key_len = vq->qchase.qname_len; 1986933707f3Ssthen } 1987933707f3Ssthen 1988933707f3Ssthen current_key_name = vq->key_entry->name; 1989933707f3Ssthen 1990933707f3Ssthen /* If our current key entry matches our target, then we are done. */ 1991933707f3Ssthen if(query_dname_compare(target_key_name, current_key_name) == 0) { 1992933707f3Ssthen vq->state = VAL_VALIDATE_STATE; 1993933707f3Ssthen return 1; 1994933707f3Ssthen } 1995933707f3Ssthen 1996933707f3Ssthen if(vq->empty_DS_name) { 1997933707f3Ssthen /* if the last empty nonterminal/emptyDS name we detected is 1998933707f3Ssthen * below the current key, use that name to make progress 1999933707f3Ssthen * along the chain of trust */ 2000933707f3Ssthen if(query_dname_compare(target_key_name, 2001933707f3Ssthen vq->empty_DS_name) == 0) { 2002933707f3Ssthen /* do not query for empty_DS_name again */ 2003933707f3Ssthen verbose(VERB_ALGO, "Cannot retrieve DS for signature"); 20040bdb4f62Ssthen errinf_ede(qstate, "no signatures", LDNS_EDE_RRSIGS_MISSING); 2005933707f3Ssthen errinf_origin(qstate, qstate->reply_origin); 2006933707f3Ssthen vq->chase_reply->security = sec_status_bogus; 20070bdb4f62Ssthen update_reason_bogus(vq->chase_reply, LDNS_EDE_RRSIGS_MISSING); 2008933707f3Ssthen vq->state = VAL_FINISHED_STATE; 2009933707f3Ssthen return 1; 2010933707f3Ssthen } 2011933707f3Ssthen current_key_name = vq->empty_DS_name; 2012933707f3Ssthen } 2013933707f3Ssthen 2014933707f3Ssthen log_nametypeclass(VERB_ALGO, "current keyname", current_key_name, 2015933707f3Ssthen LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN); 2016933707f3Ssthen log_nametypeclass(VERB_ALGO, "target keyname", target_key_name, 2017933707f3Ssthen LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN); 2018933707f3Ssthen /* assert we are walking down the DNS tree */ 2019933707f3Ssthen if(!dname_subdomain_c(target_key_name, current_key_name)) { 2020933707f3Ssthen verbose(VERB_ALGO, "bad signer name"); 2021933707f3Ssthen vq->chase_reply->security = sec_status_bogus; 2022933707f3Ssthen vq->state = VAL_FINISHED_STATE; 2023933707f3Ssthen return 1; 2024933707f3Ssthen } 2025933707f3Ssthen /* so this value is >= -1 */ 2026933707f3Ssthen strip_lab = dname_count_labels(target_key_name) - 2027933707f3Ssthen dname_count_labels(current_key_name) - 1; 2028933707f3Ssthen log_assert(strip_lab >= -1); 2029933707f3Ssthen verbose(VERB_ALGO, "striplab %d", strip_lab); 2030933707f3Ssthen if(strip_lab > 0) { 2031933707f3Ssthen dname_remove_labels(&target_key_name, &target_key_len, 2032933707f3Ssthen strip_lab); 2033933707f3Ssthen } 2034933707f3Ssthen log_nametypeclass(VERB_ALGO, "next keyname", target_key_name, 2035933707f3Ssthen LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN); 2036933707f3Ssthen 2037933707f3Ssthen /* The next step is either to query for the next DS, or to query 2038933707f3Ssthen * for the next DNSKEY. */ 2039933707f3Ssthen if(vq->ds_rrset) 2040933707f3Ssthen log_nametypeclass(VERB_ALGO, "DS RRset", vq->ds_rrset->rk.dname, LDNS_RR_TYPE_DS, LDNS_RR_CLASS_IN); 2041933707f3Ssthen else verbose(VERB_ALGO, "No DS RRset"); 2042933707f3Ssthen 2043933707f3Ssthen if(vq->ds_rrset && query_dname_compare(vq->ds_rrset->rk.dname, 2044933707f3Ssthen vq->key_entry->name) != 0) { 2045933707f3Ssthen if(!generate_request(qstate, id, vq->ds_rrset->rk.dname, 2046933707f3Ssthen vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY, 20472be9e038Ssthen vq->qchase.qclass, BIT_CD, &newq, 0)) { 20482308e98cSsthen verbose(VERB_ALGO, "error generating DNSKEY request"); 2049933707f3Ssthen return val_error(qstate, id); 2050933707f3Ssthen } 2051933707f3Ssthen return 0; 2052933707f3Ssthen } 2053933707f3Ssthen 2054933707f3Ssthen if(!vq->ds_rrset || query_dname_compare(vq->ds_rrset->rk.dname, 2055933707f3Ssthen target_key_name) != 0) { 2056933707f3Ssthen /* check if there is a cache entry : pick up an NSEC if 2057933707f3Ssthen * there is no DS, check if that NSEC has DS-bit unset, and 20584bfc71b0Ssthen * thus can disprove the secure delegation we seek. 2059933707f3Ssthen * We can then use that NSEC even in the absence of a SOA 2060933707f3Ssthen * record that would be required by the iterator to supply 2061933707f3Ssthen * a completely protocol-correct response. 2062933707f3Ssthen * Uses negative cache for NSEC3 lookup of DS responses. */ 2063933707f3Ssthen /* only if cache not blacklisted, of course */ 2064933707f3Ssthen struct dns_msg* msg; 2065817bdb8fSflorian int suspend; 2066817bdb8fSflorian if(vq->sub_ds_msg) { 2067817bdb8fSflorian /* We have a suspended DS reply from a sub-query; 2068817bdb8fSflorian * process it. */ 2069817bdb8fSflorian verbose(VERB_ALGO, "Process suspended sub DS response"); 2070817bdb8fSflorian msg = vq->sub_ds_msg; 2071817bdb8fSflorian process_ds_response(qstate, vq, id, LDNS_RCODE_NOERROR, 2072*98bc733bSsthen msg, &msg->qinfo, NULL, &suspend, NULL); 2073817bdb8fSflorian if(suspend) { 2074817bdb8fSflorian /* we'll come back here later to continue */ 2075817bdb8fSflorian if(!validate_suspend_setup_timer(qstate, vq, 2076817bdb8fSflorian id, VAL_FINDKEY_STATE)) 2077817bdb8fSflorian return val_error(qstate, id); 2078817bdb8fSflorian return 0; 2079817bdb8fSflorian } 2080817bdb8fSflorian vq->sub_ds_msg = NULL; 2081817bdb8fSflorian return 1; /* continue processing ds-response results */ 2082817bdb8fSflorian } else if(!qstate->blacklist && !vq->chain_blacklist && 2083933707f3Ssthen (msg=val_find_DS(qstate->env, target_key_name, 2084933707f3Ssthen target_key_len, vq->qchase.qclass, qstate->region, 2085933707f3Ssthen vq->key_entry->name)) ) { 2086933707f3Ssthen verbose(VERB_ALGO, "Process cached DS response"); 2087933707f3Ssthen process_ds_response(qstate, vq, id, LDNS_RCODE_NOERROR, 2088*98bc733bSsthen msg, &msg->qinfo, NULL, &suspend, NULL); 2089817bdb8fSflorian if(suspend) { 2090817bdb8fSflorian /* we'll come back here later to continue */ 2091817bdb8fSflorian if(!validate_suspend_setup_timer(qstate, vq, 2092817bdb8fSflorian id, VAL_FINDKEY_STATE)) 2093817bdb8fSflorian return val_error(qstate, id); 2094817bdb8fSflorian return 0; 2095817bdb8fSflorian } 2096933707f3Ssthen return 1; /* continue processing ds-response results */ 2097933707f3Ssthen } 2098933707f3Ssthen if(!generate_request(qstate, id, target_key_name, 2099933707f3Ssthen target_key_len, LDNS_RR_TYPE_DS, vq->qchase.qclass, 21002be9e038Ssthen BIT_CD, &newq, 0)) { 21012308e98cSsthen verbose(VERB_ALGO, "error generating DS request"); 2102933707f3Ssthen return val_error(qstate, id); 2103933707f3Ssthen } 2104933707f3Ssthen return 0; 2105933707f3Ssthen } 2106933707f3Ssthen 2107933707f3Ssthen /* Otherwise, it is time to query for the DNSKEY */ 2108933707f3Ssthen if(!generate_request(qstate, id, vq->ds_rrset->rk.dname, 2109933707f3Ssthen vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY, 21102be9e038Ssthen vq->qchase.qclass, BIT_CD, &newq, 0)) { 21112308e98cSsthen verbose(VERB_ALGO, "error generating DNSKEY request"); 2112933707f3Ssthen return val_error(qstate, id); 2113933707f3Ssthen } 2114933707f3Ssthen 2115933707f3Ssthen return 0; 2116933707f3Ssthen } 2117933707f3Ssthen 2118933707f3Ssthen /** 2119933707f3Ssthen * Process the VALIDATE stage, the init and findkey stages are finished, 2120933707f3Ssthen * and the right keys are available to validate the response. 2121933707f3Ssthen * Or, there are no keys available, in order to invalidate the response. 2122933707f3Ssthen * 2123933707f3Ssthen * After validation, the status is recorded in the message and rrsets, 2124933707f3Ssthen * and finished state is started. 2125933707f3Ssthen * 2126933707f3Ssthen * @param qstate: query state. 2127933707f3Ssthen * @param vq: validator query state. 2128933707f3Ssthen * @param ve: validator shared global environment. 2129933707f3Ssthen * @param id: module id. 2130933707f3Ssthen * @return true if the event should be processed further on return, false if 2131933707f3Ssthen * not. 2132933707f3Ssthen */ 2133933707f3Ssthen static int 2134933707f3Ssthen processValidate(struct module_qstate* qstate, struct val_qstate* vq, 2135933707f3Ssthen struct val_env* ve, int id) 2136933707f3Ssthen { 2137933707f3Ssthen enum val_classification subtype; 2138817bdb8fSflorian int rcode, suspend, nsec3_calculations = 0; 2139933707f3Ssthen 2140933707f3Ssthen if(!vq->key_entry) { 2141933707f3Ssthen verbose(VERB_ALGO, "validate: no key entry, failed"); 2142933707f3Ssthen return val_error(qstate, id); 2143933707f3Ssthen } 2144933707f3Ssthen 2145933707f3Ssthen /* This is the default next state. */ 2146933707f3Ssthen vq->state = VAL_FINISHED_STATE; 2147933707f3Ssthen 2148933707f3Ssthen /* Unsigned responses must be underneath a "null" key entry.*/ 2149933707f3Ssthen if(key_entry_isnull(vq->key_entry)) { 2150933707f3Ssthen verbose(VERB_DETAIL, "Verified that %sresponse is INSECURE", 2151933707f3Ssthen vq->signer_name?"":"unsigned "); 2152933707f3Ssthen vq->chase_reply->security = sec_status_insecure; 2153933707f3Ssthen val_mark_insecure(vq->chase_reply, vq->key_entry->name, 2154933707f3Ssthen qstate->env->rrset_cache, qstate->env); 21558b7325afSsthen key_cache_insert(ve->kcache, vq->key_entry, 21568b7325afSsthen qstate->env->cfg->val_log_level >= 2); 2157933707f3Ssthen return 1; 2158933707f3Ssthen } 2159933707f3Ssthen 2160933707f3Ssthen if(key_entry_isbad(vq->key_entry)) { 2161933707f3Ssthen log_nametypeclass(VERB_DETAIL, "Could not establish a chain " 2162933707f3Ssthen "of trust to keys for", vq->key_entry->name, 2163933707f3Ssthen LDNS_RR_TYPE_DNSKEY, vq->key_entry->key_class); 2164933707f3Ssthen vq->chase_reply->security = sec_status_bogus; 21658b7325afSsthen update_reason_bogus(vq->chase_reply, 21668b7325afSsthen key_entry_get_reason_bogus(vq->key_entry)); 21670bdb4f62Ssthen errinf_ede(qstate, "while building chain of trust", 21688b7325afSsthen key_entry_get_reason_bogus(vq->key_entry)); 2169191f22c6Ssthen if(vq->restart_count >= ve->max_restart) 21708b7325afSsthen key_cache_insert(ve->kcache, vq->key_entry, 21718b7325afSsthen qstate->env->cfg->val_log_level >= 2); 2172933707f3Ssthen return 1; 2173933707f3Ssthen } 2174933707f3Ssthen 2175933707f3Ssthen /* signerName being null is the indicator that this response was 2176933707f3Ssthen * unsigned */ 2177933707f3Ssthen if(vq->signer_name == NULL) { 2178933707f3Ssthen log_query_info(VERB_ALGO, "processValidate: state has no " 2179933707f3Ssthen "signer name", &vq->qchase); 2180933707f3Ssthen verbose(VERB_DETAIL, "Could not establish validation of " 2181933707f3Ssthen "INSECURE status of unsigned response."); 21820bdb4f62Ssthen errinf_ede(qstate, "no signatures", LDNS_EDE_RRSIGS_MISSING); 2183933707f3Ssthen errinf_origin(qstate, qstate->reply_origin); 2184933707f3Ssthen vq->chase_reply->security = sec_status_bogus; 21850bdb4f62Ssthen update_reason_bogus(vq->chase_reply, LDNS_EDE_RRSIGS_MISSING); 2186933707f3Ssthen return 1; 2187933707f3Ssthen } 2188933707f3Ssthen subtype = val_classify_response(qstate->query_flags, &qstate->qinfo, 2189933707f3Ssthen &vq->qchase, vq->orig_msg->rep, vq->rrset_skip); 21903b25f654Sbrad if(subtype != VAL_CLASS_REFERRAL) 21913b25f654Sbrad remove_spurious_authority(vq->chase_reply, vq->orig_msg->rep); 2192933707f3Ssthen 2193933707f3Ssthen /* check signatures in the message; 2194933707f3Ssthen * answer and authority must be valid, additional is only checked. */ 21952bdc0ed1Ssthen if(!validate_msg_signatures(qstate, vq, qstate->env, ve, 2196817bdb8fSflorian vq->chase_reply, vq->key_entry, &suspend)) { 2197817bdb8fSflorian if(suspend) { 2198817bdb8fSflorian if(!validate_suspend_setup_timer(qstate, vq, 2199817bdb8fSflorian id, VAL_VALIDATE_STATE)) 2200817bdb8fSflorian return val_error(qstate, id); 2201817bdb8fSflorian return 0; 2202817bdb8fSflorian } 2203933707f3Ssthen /* workaround bad recursor out there that truncates (even 2204933707f3Ssthen * with EDNS4k) to 512 by removing RRSIG from auth section 2205933707f3Ssthen * for positive replies*/ 2206933707f3Ssthen if((subtype == VAL_CLASS_POSITIVE || subtype == VAL_CLASS_ANY 2207933707f3Ssthen || subtype == VAL_CLASS_CNAME) && 2208933707f3Ssthen detect_wrongly_truncated(vq->orig_msg->rep)) { 2209933707f3Ssthen /* truncate the message some more */ 2210933707f3Ssthen vq->orig_msg->rep->ns_numrrsets = 0; 2211933707f3Ssthen vq->orig_msg->rep->ar_numrrsets = 0; 2212933707f3Ssthen vq->orig_msg->rep->rrset_count = 2213933707f3Ssthen vq->orig_msg->rep->an_numrrsets; 2214933707f3Ssthen vq->chase_reply->ns_numrrsets = 0; 2215933707f3Ssthen vq->chase_reply->ar_numrrsets = 0; 2216933707f3Ssthen vq->chase_reply->rrset_count = 2217933707f3Ssthen vq->chase_reply->an_numrrsets; 2218933707f3Ssthen qstate->errinf = NULL; 2219933707f3Ssthen } 2220933707f3Ssthen else { 2221933707f3Ssthen verbose(VERB_DETAIL, "Validate: message contains " 2222933707f3Ssthen "bad rrsets"); 2223933707f3Ssthen return 1; 2224933707f3Ssthen } 2225933707f3Ssthen } 2226933707f3Ssthen 2227933707f3Ssthen switch(subtype) { 2228933707f3Ssthen case VAL_CLASS_POSITIVE: 2229933707f3Ssthen verbose(VERB_ALGO, "Validating a positive response"); 2230933707f3Ssthen validate_positive_response(qstate->env, ve, 2231817bdb8fSflorian &vq->qchase, vq->chase_reply, vq->key_entry, 2232817bdb8fSflorian qstate, vq, &nsec3_calculations, &suspend); 2233817bdb8fSflorian if(suspend) { 2234817bdb8fSflorian if(!validate_suspend_setup_timer(qstate, 2235817bdb8fSflorian vq, id, VAL_VALIDATE_STATE)) 2236817bdb8fSflorian return val_error(qstate, id); 2237817bdb8fSflorian return 0; 2238817bdb8fSflorian } 2239933707f3Ssthen verbose(VERB_DETAIL, "validate(positive): %s", 2240933707f3Ssthen sec_status_to_string( 2241933707f3Ssthen vq->chase_reply->security)); 2242933707f3Ssthen break; 2243933707f3Ssthen 2244933707f3Ssthen case VAL_CLASS_NODATA: 2245933707f3Ssthen verbose(VERB_ALGO, "Validating a nodata response"); 2246933707f3Ssthen validate_nodata_response(qstate->env, ve, 2247817bdb8fSflorian &vq->qchase, vq->chase_reply, vq->key_entry, 2248817bdb8fSflorian qstate, vq, &nsec3_calculations, &suspend); 2249817bdb8fSflorian if(suspend) { 2250817bdb8fSflorian if(!validate_suspend_setup_timer(qstate, 2251817bdb8fSflorian vq, id, VAL_VALIDATE_STATE)) 2252817bdb8fSflorian return val_error(qstate, id); 2253817bdb8fSflorian return 0; 2254817bdb8fSflorian } 2255933707f3Ssthen verbose(VERB_DETAIL, "validate(nodata): %s", 2256933707f3Ssthen sec_status_to_string( 2257933707f3Ssthen vq->chase_reply->security)); 2258933707f3Ssthen break; 2259933707f3Ssthen 2260933707f3Ssthen case VAL_CLASS_NAMEERROR: 22615d76a658Ssthen rcode = (int)FLAGS_GET_RCODE(vq->orig_msg->rep->flags); 2262933707f3Ssthen verbose(VERB_ALGO, "Validating a nxdomain response"); 2263933707f3Ssthen validate_nameerror_response(qstate->env, ve, 2264817bdb8fSflorian &vq->qchase, vq->chase_reply, vq->key_entry, &rcode, 2265817bdb8fSflorian qstate, vq, &nsec3_calculations, &suspend); 2266817bdb8fSflorian if(suspend) { 2267817bdb8fSflorian if(!validate_suspend_setup_timer(qstate, 2268817bdb8fSflorian vq, id, VAL_VALIDATE_STATE)) 2269817bdb8fSflorian return val_error(qstate, id); 2270817bdb8fSflorian return 0; 2271817bdb8fSflorian } 2272933707f3Ssthen verbose(VERB_DETAIL, "validate(nxdomain): %s", 2273933707f3Ssthen sec_status_to_string( 2274933707f3Ssthen vq->chase_reply->security)); 22755d76a658Ssthen FLAGS_SET_RCODE(vq->orig_msg->rep->flags, rcode); 22765d76a658Ssthen FLAGS_SET_RCODE(vq->chase_reply->flags, rcode); 2277933707f3Ssthen break; 2278933707f3Ssthen 2279933707f3Ssthen case VAL_CLASS_CNAME: 2280933707f3Ssthen verbose(VERB_ALGO, "Validating a cname response"); 2281933707f3Ssthen validate_cname_response(qstate->env, ve, 2282817bdb8fSflorian &vq->qchase, vq->chase_reply, vq->key_entry, 2283817bdb8fSflorian qstate, vq, &nsec3_calculations, &suspend); 2284817bdb8fSflorian if(suspend) { 2285817bdb8fSflorian if(!validate_suspend_setup_timer(qstate, 2286817bdb8fSflorian vq, id, VAL_VALIDATE_STATE)) 2287817bdb8fSflorian return val_error(qstate, id); 2288817bdb8fSflorian return 0; 2289817bdb8fSflorian } 2290933707f3Ssthen verbose(VERB_DETAIL, "validate(cname): %s", 2291933707f3Ssthen sec_status_to_string( 2292933707f3Ssthen vq->chase_reply->security)); 2293933707f3Ssthen break; 2294933707f3Ssthen 2295933707f3Ssthen case VAL_CLASS_CNAMENOANSWER: 2296933707f3Ssthen verbose(VERB_ALGO, "Validating a cname noanswer " 2297933707f3Ssthen "response"); 2298933707f3Ssthen validate_cname_noanswer_response(qstate->env, ve, 2299817bdb8fSflorian &vq->qchase, vq->chase_reply, vq->key_entry, 2300817bdb8fSflorian qstate, vq, &nsec3_calculations, &suspend); 2301817bdb8fSflorian if(suspend) { 2302817bdb8fSflorian if(!validate_suspend_setup_timer(qstate, 2303817bdb8fSflorian vq, id, VAL_VALIDATE_STATE)) 2304817bdb8fSflorian return val_error(qstate, id); 2305817bdb8fSflorian return 0; 2306817bdb8fSflorian } 2307933707f3Ssthen verbose(VERB_DETAIL, "validate(cname_noanswer): %s", 2308933707f3Ssthen sec_status_to_string( 2309933707f3Ssthen vq->chase_reply->security)); 2310933707f3Ssthen break; 2311933707f3Ssthen 2312933707f3Ssthen case VAL_CLASS_REFERRAL: 2313933707f3Ssthen verbose(VERB_ALGO, "Validating a referral response"); 2314933707f3Ssthen validate_referral_response(vq->chase_reply); 2315933707f3Ssthen verbose(VERB_DETAIL, "validate(referral): %s", 2316933707f3Ssthen sec_status_to_string( 2317933707f3Ssthen vq->chase_reply->security)); 2318933707f3Ssthen break; 2319933707f3Ssthen 2320933707f3Ssthen case VAL_CLASS_ANY: 2321933707f3Ssthen verbose(VERB_ALGO, "Validating a positive ANY " 2322933707f3Ssthen "response"); 2323933707f3Ssthen validate_any_response(qstate->env, ve, &vq->qchase, 2324817bdb8fSflorian vq->chase_reply, vq->key_entry, qstate, vq, 2325817bdb8fSflorian &nsec3_calculations, &suspend); 2326817bdb8fSflorian if(suspend) { 2327817bdb8fSflorian if(!validate_suspend_setup_timer(qstate, 2328817bdb8fSflorian vq, id, VAL_VALIDATE_STATE)) 2329817bdb8fSflorian return val_error(qstate, id); 2330817bdb8fSflorian return 0; 2331817bdb8fSflorian } 2332933707f3Ssthen verbose(VERB_DETAIL, "validate(positive_any): %s", 2333933707f3Ssthen sec_status_to_string( 2334933707f3Ssthen vq->chase_reply->security)); 2335933707f3Ssthen break; 2336933707f3Ssthen 2337933707f3Ssthen default: 2338933707f3Ssthen log_err("validate: unhandled response subtype: %d", 2339933707f3Ssthen subtype); 2340933707f3Ssthen } 2341933707f3Ssthen if(vq->chase_reply->security == sec_status_bogus) { 2342933707f3Ssthen if(subtype == VAL_CLASS_POSITIVE) 2343933707f3Ssthen errinf(qstate, "wildcard"); 2344933707f3Ssthen else errinf(qstate, val_classification_to_string(subtype)); 2345933707f3Ssthen errinf(qstate, "proof failed"); 2346933707f3Ssthen errinf_origin(qstate, qstate->reply_origin); 2347933707f3Ssthen } 2348933707f3Ssthen 2349933707f3Ssthen return 1; 2350933707f3Ssthen } 2351933707f3Ssthen 2352933707f3Ssthen /** 2353933707f3Ssthen * The Finished state. The validation status (good or bad) has been determined. 2354933707f3Ssthen * 2355933707f3Ssthen * @param qstate: query state. 2356933707f3Ssthen * @param vq: validator query state. 2357933707f3Ssthen * @param ve: validator shared global environment. 2358933707f3Ssthen * @param id: module id. 2359933707f3Ssthen * @return true if the event should be processed further on return, false if 2360933707f3Ssthen * not. 2361933707f3Ssthen */ 2362933707f3Ssthen static int 2363933707f3Ssthen processFinished(struct module_qstate* qstate, struct val_qstate* vq, 2364933707f3Ssthen struct val_env* ve, int id) 2365933707f3Ssthen { 2366933707f3Ssthen enum val_classification subtype = val_classify_response( 2367933707f3Ssthen qstate->query_flags, &qstate->qinfo, &vq->qchase, 2368933707f3Ssthen vq->orig_msg->rep, vq->rrset_skip); 2369933707f3Ssthen 2370933707f3Ssthen /* store overall validation result in orig_msg */ 23710bdb4f62Ssthen if(vq->rrset_skip == 0) { 2372933707f3Ssthen vq->orig_msg->rep->security = vq->chase_reply->security; 23730bdb4f62Ssthen update_reason_bogus(vq->orig_msg->rep, vq->chase_reply->reason_bogus); 23740bdb4f62Ssthen } else if(subtype != VAL_CLASS_REFERRAL || 23753dcb24b8Ssthen vq->rrset_skip < vq->orig_msg->rep->an_numrrsets + 2376933707f3Ssthen vq->orig_msg->rep->ns_numrrsets) { 2377933707f3Ssthen /* ignore sec status of additional section if a referral 2378933707f3Ssthen * type message skips there and 2379933707f3Ssthen * use the lowest security status as end result. */ 23800bdb4f62Ssthen if(vq->chase_reply->security < vq->orig_msg->rep->security) { 2381933707f3Ssthen vq->orig_msg->rep->security = 2382933707f3Ssthen vq->chase_reply->security; 23830bdb4f62Ssthen update_reason_bogus(vq->orig_msg->rep, vq->chase_reply->reason_bogus); 23840bdb4f62Ssthen } 2385933707f3Ssthen } 2386933707f3Ssthen 2387933707f3Ssthen if(subtype == VAL_CLASS_REFERRAL) { 2388933707f3Ssthen /* for a referral, move to next unchecked rrset and check it*/ 2389933707f3Ssthen vq->rrset_skip = val_next_unchecked(vq->orig_msg->rep, 2390933707f3Ssthen vq->rrset_skip); 2391933707f3Ssthen if(vq->rrset_skip < vq->orig_msg->rep->rrset_count) { 2392933707f3Ssthen /* and restart for this rrset */ 2393933707f3Ssthen verbose(VERB_ALGO, "validator: go to next rrset"); 2394933707f3Ssthen vq->chase_reply->security = sec_status_unchecked; 2395933707f3Ssthen vq->state = VAL_INIT_STATE; 2396933707f3Ssthen return 1; 2397933707f3Ssthen } 2398933707f3Ssthen /* referral chase is done */ 2399933707f3Ssthen } 2400933707f3Ssthen if(vq->chase_reply->security != sec_status_bogus && 2401933707f3Ssthen subtype == VAL_CLASS_CNAME) { 2402933707f3Ssthen /* chase the CNAME; process next part of the message */ 2403933707f3Ssthen if(!val_chase_cname(&vq->qchase, vq->orig_msg->rep, 2404933707f3Ssthen &vq->rrset_skip)) { 2405933707f3Ssthen verbose(VERB_ALGO, "validator: failed to chase CNAME"); 2406933707f3Ssthen vq->orig_msg->rep->security = sec_status_bogus; 24070bdb4f62Ssthen update_reason_bogus(vq->orig_msg->rep, LDNS_EDE_DNSSEC_BOGUS); 2408933707f3Ssthen } else { 2409933707f3Ssthen /* restart process for new qchase at rrset_skip */ 2410933707f3Ssthen log_query_info(VERB_ALGO, "validator: chased to", 2411933707f3Ssthen &vq->qchase); 2412933707f3Ssthen vq->chase_reply->security = sec_status_unchecked; 2413933707f3Ssthen vq->state = VAL_INIT_STATE; 2414933707f3Ssthen return 1; 2415933707f3Ssthen } 2416933707f3Ssthen } 2417933707f3Ssthen 2418933707f3Ssthen if(vq->orig_msg->rep->security == sec_status_secure) { 2419933707f3Ssthen /* If the message is secure, check that all rrsets are 2420933707f3Ssthen * secure (i.e. some inserted RRset for CNAME chain with 2421933707f3Ssthen * a different signer name). And drop additional rrsets 2422933707f3Ssthen * that are not secure (if clean-additional option is set) */ 2423933707f3Ssthen /* this may cause the msg to be marked bogus */ 24242be9e038Ssthen val_check_nonsecure(qstate->env, vq->orig_msg->rep); 2425933707f3Ssthen if(vq->orig_msg->rep->security == sec_status_secure) { 2426933707f3Ssthen log_query_info(VERB_DETAIL, "validation success", 2427933707f3Ssthen &qstate->qinfo); 2428938a3a5eSflorian if(!qstate->no_cache_store) { 2429938a3a5eSflorian val_neg_addreply(qstate->env->neg_cache, 2430938a3a5eSflorian vq->orig_msg->rep); 2431938a3a5eSflorian } 2432933707f3Ssthen } 2433933707f3Ssthen } 2434933707f3Ssthen 2435933707f3Ssthen /* if the result is bogus - set message ttl to bogus ttl to avoid 2436933707f3Ssthen * endless bogus revalidation */ 2437933707f3Ssthen if(vq->orig_msg->rep->security == sec_status_bogus) { 2438933707f3Ssthen /* see if we can try again to fetch data */ 2439191f22c6Ssthen if(vq->restart_count < ve->max_restart) { 2440933707f3Ssthen verbose(VERB_ALGO, "validation failed, " 2441933707f3Ssthen "blacklist and retry to fetch data"); 2442933707f3Ssthen val_blacklist(&qstate->blacklist, qstate->region, 2443933707f3Ssthen qstate->reply_origin, 0); 2444933707f3Ssthen qstate->reply_origin = NULL; 2445933707f3Ssthen qstate->errinf = NULL; 2446817bdb8fSflorian val_restart(vq); 2447933707f3Ssthen verbose(VERB_ALGO, "pass back to next module"); 2448933707f3Ssthen qstate->ext_state[id] = module_restart_next; 2449933707f3Ssthen return 0; 2450933707f3Ssthen } 2451933707f3Ssthen 2452933707f3Ssthen vq->orig_msg->rep->ttl = ve->bogus_ttl; 2453933707f3Ssthen vq->orig_msg->rep->prefetch_ttl = 2454933707f3Ssthen PREFETCH_TTL_CALC(vq->orig_msg->rep->ttl); 24552308e98cSsthen vq->orig_msg->rep->serve_expired_ttl = 24562308e98cSsthen vq->orig_msg->rep->ttl + qstate->env->cfg->serve_expired_ttl; 24572308e98cSsthen if((qstate->env->cfg->val_log_level >= 1 || 24582308e98cSsthen qstate->env->cfg->log_servfail) && 2459933707f3Ssthen !qstate->env->cfg->val_log_squelch) { 24602308e98cSsthen if(qstate->env->cfg->val_log_level < 2 && 24612308e98cSsthen !qstate->env->cfg->log_servfail) 2462ebf5bb73Ssthen log_query_info(NO_VERBOSE, "validation failure", 2463933707f3Ssthen &qstate->qinfo); 2464933707f3Ssthen else { 24652bdc0ed1Ssthen char* err_str = errinf_to_str_bogus(qstate, 24662bdc0ed1Ssthen qstate->region); 24678b7325afSsthen if(err_str) { 24688b7325afSsthen log_info("%s", err_str); 24692bdc0ed1Ssthen vq->orig_msg->rep->reason_bogus_str = err_str; 24708b7325afSsthen } 2471933707f3Ssthen } 2472933707f3Ssthen } 24732be9e038Ssthen /* 24742be9e038Ssthen * If set, the validator will not make messages bogus, instead 24752be9e038Ssthen * indeterminate is issued, so that no clients receive SERVFAIL. 24762be9e038Ssthen * This allows an operator to run validation 'shadow' without 24772be9e038Ssthen * hurting responses to clients. 24782be9e038Ssthen */ 2479933707f3Ssthen /* If we are in permissive mode, bogus gets indeterminate */ 24802be9e038Ssthen if(qstate->env->cfg->val_permissive_mode) 2481933707f3Ssthen vq->orig_msg->rep->security = sec_status_indeterminate; 2482933707f3Ssthen } 2483933707f3Ssthen 248420237c55Ssthen if(vq->orig_msg->rep->security == sec_status_secure && 248520237c55Ssthen qstate->env->cfg->root_key_sentinel && 248620237c55Ssthen (qstate->qinfo.qtype == LDNS_RR_TYPE_A || 248720237c55Ssthen qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA)) { 248820237c55Ssthen char* keytag_start; 248920237c55Ssthen uint16_t keytag; 249020237c55Ssthen if(*qstate->qinfo.qname == strlen(SENTINEL_IS) + 249120237c55Ssthen SENTINEL_KEYTAG_LEN && 249220237c55Ssthen dname_lab_startswith(qstate->qinfo.qname, SENTINEL_IS, 249320237c55Ssthen &keytag_start)) { 249420237c55Ssthen if(sentinel_get_keytag(keytag_start, &keytag) && 249520237c55Ssthen !anchor_has_keytag(qstate->env->anchors, 249620237c55Ssthen (uint8_t*)"", 1, 0, vq->qchase.qclass, keytag)) { 249720237c55Ssthen vq->orig_msg->rep->security = 249820237c55Ssthen sec_status_secure_sentinel_fail; 249920237c55Ssthen } 250020237c55Ssthen } else if(*qstate->qinfo.qname == strlen(SENTINEL_NOT) + 250120237c55Ssthen SENTINEL_KEYTAG_LEN && 250220237c55Ssthen dname_lab_startswith(qstate->qinfo.qname, SENTINEL_NOT, 250320237c55Ssthen &keytag_start)) { 250420237c55Ssthen if(sentinel_get_keytag(keytag_start, &keytag) && 250520237c55Ssthen anchor_has_keytag(qstate->env->anchors, 250620237c55Ssthen (uint8_t*)"", 1, 0, vq->qchase.qclass, keytag)) { 250720237c55Ssthen vq->orig_msg->rep->security = 250820237c55Ssthen sec_status_secure_sentinel_fail; 250920237c55Ssthen } 251020237c55Ssthen } 251120237c55Ssthen } 25128b7325afSsthen 25138b7325afSsthen /* Update rep->reason_bogus as it is the one being cached */ 25148b7325afSsthen update_reason_bogus(vq->orig_msg->rep, errinf_to_reason_bogus(qstate)); 2515933707f3Ssthen /* store results in cache */ 25162be9e038Ssthen if(qstate->query_flags&BIT_RD) { 2517d8d14d0cSsthen /* if secure, this will override cache anyway, no need 2518d8d14d0cSsthen * to check if from parentNS */ 25192be9e038Ssthen if(!qstate->no_cache_store) { 2520933707f3Ssthen if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo, 252157dceb2aSbrad vq->orig_msg->rep, 0, qstate->prefetch_leeway, 0, NULL, 2522d1e2768aSsthen qstate->query_flags, qstate->qstarttime)) { 2523933707f3Ssthen log_err("out of memory caching validator results"); 2524933707f3Ssthen } 25252be9e038Ssthen } 2526933707f3Ssthen } else { 2527933707f3Ssthen /* for a referral, store the verified RRsets */ 2528933707f3Ssthen /* and this does not get prefetched, so no leeway */ 2529933707f3Ssthen if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo, 253057dceb2aSbrad vq->orig_msg->rep, 1, 0, 0, NULL, 2531d1e2768aSsthen qstate->query_flags, qstate->qstarttime)) { 2532933707f3Ssthen log_err("out of memory caching validator results"); 2533933707f3Ssthen } 2534933707f3Ssthen } 2535933707f3Ssthen qstate->return_rcode = LDNS_RCODE_NOERROR; 2536933707f3Ssthen qstate->return_msg = vq->orig_msg; 2537933707f3Ssthen qstate->ext_state[id] = module_finished; 2538933707f3Ssthen return 0; 2539933707f3Ssthen } 2540933707f3Ssthen 2541933707f3Ssthen /** 2542933707f3Ssthen * Handle validator state. 2543933707f3Ssthen * If a method returns true, the next state is started. If false, then 2544933707f3Ssthen * processing will stop. 2545933707f3Ssthen * @param qstate: query state. 2546933707f3Ssthen * @param vq: validator query state. 2547933707f3Ssthen * @param ve: validator shared global environment. 2548933707f3Ssthen * @param id: module id. 2549933707f3Ssthen */ 2550933707f3Ssthen static void 2551933707f3Ssthen val_handle(struct module_qstate* qstate, struct val_qstate* vq, 2552933707f3Ssthen struct val_env* ve, int id) 2553933707f3Ssthen { 2554933707f3Ssthen int cont = 1; 2555933707f3Ssthen while(cont) { 2556933707f3Ssthen verbose(VERB_ALGO, "val handle processing q with state %s", 2557933707f3Ssthen val_state_to_string(vq->state)); 2558933707f3Ssthen switch(vq->state) { 2559933707f3Ssthen case VAL_INIT_STATE: 2560933707f3Ssthen cont = processInit(qstate, vq, ve, id); 2561933707f3Ssthen break; 2562933707f3Ssthen case VAL_FINDKEY_STATE: 2563933707f3Ssthen cont = processFindKey(qstate, vq, id); 2564933707f3Ssthen break; 2565933707f3Ssthen case VAL_VALIDATE_STATE: 2566933707f3Ssthen cont = processValidate(qstate, vq, ve, id); 2567933707f3Ssthen break; 2568933707f3Ssthen case VAL_FINISHED_STATE: 2569933707f3Ssthen cont = processFinished(qstate, vq, ve, id); 2570933707f3Ssthen break; 2571933707f3Ssthen default: 2572933707f3Ssthen log_warn("validator: invalid state %d", 2573933707f3Ssthen vq->state); 2574933707f3Ssthen cont = 0; 2575933707f3Ssthen break; 2576933707f3Ssthen } 2577933707f3Ssthen } 2578933707f3Ssthen } 2579933707f3Ssthen 2580933707f3Ssthen void 2581933707f3Ssthen val_operate(struct module_qstate* qstate, enum module_ev event, int id, 2582933707f3Ssthen struct outbound_entry* outbound) 2583933707f3Ssthen { 2584933707f3Ssthen struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; 2585933707f3Ssthen struct val_qstate* vq = (struct val_qstate*)qstate->minfo[id]; 2586933707f3Ssthen verbose(VERB_QUERY, "validator[module %d] operate: extstate:%s " 2587933707f3Ssthen "event:%s", id, strextstate(qstate->ext_state[id]), 2588933707f3Ssthen strmodulevent(event)); 2589933707f3Ssthen log_query_info(VERB_QUERY, "validator operate: query", 2590933707f3Ssthen &qstate->qinfo); 2591933707f3Ssthen if(vq && qstate->qinfo.qname != vq->qchase.qname) 2592933707f3Ssthen log_query_info(VERB_QUERY, "validator operate: chased to", 2593933707f3Ssthen &vq->qchase); 2594933707f3Ssthen (void)outbound; 2595933707f3Ssthen if(event == module_event_new || 2596933707f3Ssthen (event == module_event_pass && vq == NULL)) { 259777079be7Ssthen 2598933707f3Ssthen /* pass request to next module, to get it */ 2599933707f3Ssthen verbose(VERB_ALGO, "validator: pass to next module"); 2600933707f3Ssthen qstate->ext_state[id] = module_wait_module; 2601933707f3Ssthen return; 2602933707f3Ssthen } 2603933707f3Ssthen if(event == module_event_moddone) { 2604933707f3Ssthen /* check if validation is needed */ 2605933707f3Ssthen verbose(VERB_ALGO, "validator: nextmodule returned"); 260677079be7Ssthen 2607933707f3Ssthen if(!needs_validation(qstate, qstate->return_rcode, 2608933707f3Ssthen qstate->return_msg)) { 2609933707f3Ssthen /* no need to validate this */ 2610933707f3Ssthen if(qstate->return_msg) 2611933707f3Ssthen qstate->return_msg->rep->security = 2612933707f3Ssthen sec_status_indeterminate; 2613933707f3Ssthen qstate->ext_state[id] = module_finished; 2614933707f3Ssthen return; 2615933707f3Ssthen } 2616933707f3Ssthen if(already_validated(qstate->return_msg)) { 2617933707f3Ssthen qstate->ext_state[id] = module_finished; 2618933707f3Ssthen return; 2619933707f3Ssthen } 2620933707f3Ssthen /* qclass ANY should have validation result from spawned 2621933707f3Ssthen * queries. If we get here, it is bogus or an internal error */ 2622933707f3Ssthen if(qstate->qinfo.qclass == LDNS_RR_CLASS_ANY) { 2623933707f3Ssthen verbose(VERB_ALGO, "cannot validate classANY: bogus"); 26240bdb4f62Ssthen if(qstate->return_msg) { 2625933707f3Ssthen qstate->return_msg->rep->security = 2626933707f3Ssthen sec_status_bogus; 26270bdb4f62Ssthen update_reason_bogus(qstate->return_msg->rep, LDNS_EDE_DNSSEC_BOGUS); 26280bdb4f62Ssthen } 2629933707f3Ssthen qstate->ext_state[id] = module_finished; 2630933707f3Ssthen return; 2631933707f3Ssthen } 2632933707f3Ssthen /* create state to start validation */ 2633933707f3Ssthen qstate->ext_state[id] = module_error; /* override this */ 2634933707f3Ssthen if(!vq) { 2635933707f3Ssthen vq = val_new(qstate, id); 2636933707f3Ssthen if(!vq) { 2637933707f3Ssthen log_err("validator: malloc failure"); 2638933707f3Ssthen qstate->ext_state[id] = module_error; 2639933707f3Ssthen return; 2640933707f3Ssthen } 2641933707f3Ssthen } else if(!vq->orig_msg) { 2642933707f3Ssthen if(!val_new_getmsg(qstate, vq)) { 2643933707f3Ssthen log_err("validator: malloc failure"); 2644933707f3Ssthen qstate->ext_state[id] = module_error; 2645933707f3Ssthen return; 2646933707f3Ssthen } 2647933707f3Ssthen } 2648933707f3Ssthen val_handle(qstate, vq, ve, id); 2649933707f3Ssthen return; 2650933707f3Ssthen } 2651933707f3Ssthen if(event == module_event_pass) { 2652933707f3Ssthen qstate->ext_state[id] = module_error; /* override this */ 2653933707f3Ssthen /* continue processing, since val_env exists */ 2654933707f3Ssthen val_handle(qstate, vq, ve, id); 2655933707f3Ssthen return; 2656933707f3Ssthen } 2657933707f3Ssthen log_err("validator: bad event %s", strmodulevent(event)); 2658933707f3Ssthen qstate->ext_state[id] = module_error; 2659933707f3Ssthen return; 2660933707f3Ssthen } 2661933707f3Ssthen 2662933707f3Ssthen /** 2663933707f3Ssthen * Evaluate the response to a priming request. 2664933707f3Ssthen * 2665933707f3Ssthen * @param dnskey_rrset: DNSKEY rrset (can be NULL if none) in prime reply. 2666933707f3Ssthen * (this rrset is allocated in the wrong region, not the qstate). 2667933707f3Ssthen * @param ta: trust anchor. 2668933707f3Ssthen * @param qstate: qstate that needs key. 2669933707f3Ssthen * @param id: module id. 2670*98bc733bSsthen * @param sub_qstate: the sub query state, that is the lookup that fetched 2671*98bc733bSsthen * the trust anchor data, it contains error information for the answer. 2672933707f3Ssthen * @return new key entry or NULL on allocation failure. 2673933707f3Ssthen * The key entry will either contain a validated DNSKEY rrset, or 2674933707f3Ssthen * represent a Null key (query failed, but validation did not), or a 2675933707f3Ssthen * Bad key (validation failed). 2676933707f3Ssthen */ 2677933707f3Ssthen static struct key_entry_key* 2678933707f3Ssthen primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset, 2679*98bc733bSsthen struct trust_anchor* ta, struct module_qstate* qstate, int id, 2680*98bc733bSsthen struct module_qstate* sub_qstate) 2681933707f3Ssthen { 2682933707f3Ssthen struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; 2683933707f3Ssthen struct key_entry_key* kkey = NULL; 2684933707f3Ssthen enum sec_status sec = sec_status_unchecked; 2685*98bc733bSsthen char reasonbuf[256]; 2686933707f3Ssthen char* reason = NULL; 26870bdb4f62Ssthen sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS; 2688a58bff56Ssthen int downprot = qstate->env->cfg->harden_algo_downgrade; 2689933707f3Ssthen 2690933707f3Ssthen if(!dnskey_rrset) { 2691*98bc733bSsthen char* err = errinf_to_str_misc(sub_qstate); 2692*98bc733bSsthen char rstr[1024]; 2693933707f3Ssthen log_nametypeclass(VERB_OPS, "failed to prime trust anchor -- " 2694933707f3Ssthen "could not fetch DNSKEY rrset", 2695933707f3Ssthen ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass); 26968b7325afSsthen reason_bogus = LDNS_EDE_DNSKEY_MISSING; 2697*98bc733bSsthen if(!err) { 2698*98bc733bSsthen snprintf(rstr, sizeof(rstr), "no DNSKEY rrset"); 2699*98bc733bSsthen } else { 2700*98bc733bSsthen snprintf(rstr, sizeof(rstr), "no DNSKEY rrset " 2701*98bc733bSsthen "[%s]", err); 2702*98bc733bSsthen } 2703933707f3Ssthen if(qstate->env->cfg->harden_dnssec_stripped) { 2704*98bc733bSsthen errinf_ede(qstate, rstr, reason_bogus); 2705933707f3Ssthen kkey = key_entry_create_bad(qstate->region, ta->name, 2706933707f3Ssthen ta->namelen, ta->dclass, BOGUS_KEY_TTL, 2707*98bc733bSsthen reason_bogus, rstr, *qstate->env->now); 2708933707f3Ssthen } else kkey = key_entry_create_null(qstate->region, ta->name, 2709933707f3Ssthen ta->namelen, ta->dclass, NULL_KEY_TTL, 2710*98bc733bSsthen reason_bogus, rstr, *qstate->env->now); 2711933707f3Ssthen if(!kkey) { 2712933707f3Ssthen log_err("out of memory: allocate fail prime key"); 2713933707f3Ssthen return NULL; 2714933707f3Ssthen } 2715933707f3Ssthen return kkey; 2716933707f3Ssthen } 2717933707f3Ssthen /* attempt to verify with trust anchor DS and DNSKEY */ 2718933707f3Ssthen kkey = val_verify_new_DNSKEYs_with_ta(qstate->region, qstate->env, ve, 2719933707f3Ssthen dnskey_rrset, ta->ds_rrset, ta->dnskey_rrset, downprot, 2720*98bc733bSsthen &reason, &reason_bogus, qstate, reasonbuf, sizeof(reasonbuf)); 2721933707f3Ssthen if(!kkey) { 2722933707f3Ssthen log_err("out of memory: verifying prime TA"); 2723933707f3Ssthen return NULL; 2724933707f3Ssthen } 2725933707f3Ssthen if(key_entry_isgood(kkey)) 2726933707f3Ssthen sec = sec_status_secure; 2727933707f3Ssthen else 2728933707f3Ssthen sec = sec_status_bogus; 2729933707f3Ssthen verbose(VERB_DETAIL, "validate keys with anchor(DS): %s", 2730933707f3Ssthen sec_status_to_string(sec)); 2731933707f3Ssthen 2732933707f3Ssthen if(sec != sec_status_secure) { 2733933707f3Ssthen log_nametypeclass(VERB_OPS, "failed to prime trust anchor -- " 2734933707f3Ssthen "DNSKEY rrset is not secure", 2735933707f3Ssthen ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass); 2736933707f3Ssthen /* NOTE: in this case, we should probably reject the trust 2737933707f3Ssthen * anchor for longer, perhaps forever. */ 2738933707f3Ssthen if(qstate->env->cfg->harden_dnssec_stripped) { 27390bdb4f62Ssthen errinf_ede(qstate, reason, reason_bogus); 2740933707f3Ssthen kkey = key_entry_create_bad(qstate->region, ta->name, 2741933707f3Ssthen ta->namelen, ta->dclass, BOGUS_KEY_TTL, 27428b7325afSsthen reason_bogus, reason, 2743933707f3Ssthen *qstate->env->now); 2744933707f3Ssthen } else kkey = key_entry_create_null(qstate->region, ta->name, 2745933707f3Ssthen ta->namelen, ta->dclass, NULL_KEY_TTL, 27468b7325afSsthen reason_bogus, reason, 2747933707f3Ssthen *qstate->env->now); 2748933707f3Ssthen if(!kkey) { 2749933707f3Ssthen log_err("out of memory: allocate null prime key"); 2750933707f3Ssthen return NULL; 2751933707f3Ssthen } 2752933707f3Ssthen return kkey; 2753933707f3Ssthen } 2754933707f3Ssthen 2755933707f3Ssthen log_nametypeclass(VERB_DETAIL, "Successfully primed trust anchor", 2756933707f3Ssthen ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass); 2757933707f3Ssthen return kkey; 2758933707f3Ssthen } 2759933707f3Ssthen 2760933707f3Ssthen /** 2761933707f3Ssthen * In inform supers, with the resulting message and rcode and the current 2762933707f3Ssthen * keyset in the super state, validate the DS response, returning a KeyEntry. 2763933707f3Ssthen * 2764933707f3Ssthen * @param qstate: query state that is validating and asked for a DS. 2765933707f3Ssthen * @param vq: validator query state 2766933707f3Ssthen * @param id: module id. 2767933707f3Ssthen * @param rcode: rcode result value. 2768933707f3Ssthen * @param msg: result message (if rcode is OK). 2769933707f3Ssthen * @param qinfo: from the sub query state, query info. 2770933707f3Ssthen * @param ke: the key entry to return. It returns 2771933707f3Ssthen * is_bad if the DS response fails to validate, is_null if the 2772933707f3Ssthen * DS response indicated an end to secure space, is_good if the DS 2773933707f3Ssthen * validated. It returns ke=NULL if the DS response indicated that the 2774933707f3Ssthen * request wasn't a delegation point. 2775*98bc733bSsthen * @param sub_qstate: the sub query state, that is the lookup that fetched 2776*98bc733bSsthen * the trust anchor data, it contains error information for the answer. 2777*98bc733bSsthen * Can be NULL. 2778817bdb8fSflorian * @return 2779817bdb8fSflorian * 0 on success, 2780817bdb8fSflorian * 1 on servfail error (malloc failure), 2781817bdb8fSflorian * 2 on NSEC3 suspend. 2782933707f3Ssthen */ 2783933707f3Ssthen static int 2784933707f3Ssthen ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, 2785933707f3Ssthen int id, int rcode, struct dns_msg* msg, struct query_info* qinfo, 2786*98bc733bSsthen struct key_entry_key** ke, struct module_qstate* sub_qstate) 2787933707f3Ssthen { 2788933707f3Ssthen struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; 2789*98bc733bSsthen char reasonbuf[256]; 2790933707f3Ssthen char* reason = NULL; 27910bdb4f62Ssthen sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS; 2792933707f3Ssthen enum val_classification subtype; 2793817bdb8fSflorian int verified; 2794933707f3Ssthen if(rcode != LDNS_RCODE_NOERROR) { 27955d76a658Ssthen char rc[16]; 27965d76a658Ssthen rc[0]=0; 27975d76a658Ssthen (void)sldns_wire2str_rcode_buf(rcode, rc, sizeof(rc)); 2798933707f3Ssthen /* errors here pretty much break validation */ 2799933707f3Ssthen verbose(VERB_DETAIL, "DS response was error, thus bogus"); 2800933707f3Ssthen errinf(qstate, rc); 28018b7325afSsthen reason = "no DS"; 2802*98bc733bSsthen if(sub_qstate) { 2803*98bc733bSsthen char* err = errinf_to_str_misc(sub_qstate); 2804*98bc733bSsthen if(err) { 2805*98bc733bSsthen char buf[1024]; 2806*98bc733bSsthen snprintf(buf, sizeof(buf), "[%s]", err); 2807*98bc733bSsthen errinf(qstate, buf); 2808*98bc733bSsthen } 2809*98bc733bSsthen } 28108b7325afSsthen reason_bogus = LDNS_EDE_NETWORK_ERROR; 28118b7325afSsthen errinf_ede(qstate, reason, reason_bogus); 2812933707f3Ssthen goto return_bogus; 2813933707f3Ssthen } 2814933707f3Ssthen 2815933707f3Ssthen subtype = val_classify_response(BIT_RD, qinfo, qinfo, msg->rep, 0); 2816933707f3Ssthen if(subtype == VAL_CLASS_POSITIVE) { 2817933707f3Ssthen struct ub_packed_rrset_key* ds; 2818933707f3Ssthen enum sec_status sec; 2819933707f3Ssthen ds = reply_find_answer_rrset(qinfo, msg->rep); 2820933707f3Ssthen /* If there was no DS rrset, then we have mis-classified 2821933707f3Ssthen * this message. */ 2822933707f3Ssthen if(!ds) { 2823933707f3Ssthen log_warn("internal error: POSITIVE DS response was " 2824933707f3Ssthen "missing DS."); 28258b7325afSsthen reason = "no DS record"; 28268b7325afSsthen errinf_ede(qstate, reason, reason_bogus); 2827933707f3Ssthen goto return_bogus; 2828933707f3Ssthen } 2829933707f3Ssthen /* Verify only returns BOGUS or SECURE. If the rrset is 2830933707f3Ssthen * bogus, then we are done. */ 2831933707f3Ssthen sec = val_verify_rrset_entry(qstate->env, ve, ds, 2832*98bc733bSsthen vq->key_entry, &reason, &reason_bogus, 2833*98bc733bSsthen LDNS_SECTION_ANSWER, qstate, &verified, reasonbuf, 2834*98bc733bSsthen sizeof(reasonbuf)); 2835933707f3Ssthen if(sec != sec_status_secure) { 2836933707f3Ssthen verbose(VERB_DETAIL, "DS rrset in DS response did " 2837933707f3Ssthen "not verify"); 28380bdb4f62Ssthen errinf_ede(qstate, reason, reason_bogus); 2839933707f3Ssthen goto return_bogus; 2840933707f3Ssthen } 2841933707f3Ssthen 2842933707f3Ssthen /* If the DS rrset validates, we still have to make sure 2843933707f3Ssthen * that they are usable. */ 2844933707f3Ssthen if(!val_dsset_isusable(ds)) { 2845933707f3Ssthen /* If they aren't usable, then we treat it like 2846933707f3Ssthen * there was no DS. */ 2847933707f3Ssthen *ke = key_entry_create_null(qstate->region, 2848933707f3Ssthen qinfo->qname, qinfo->qname_len, qinfo->qclass, 28498b7325afSsthen ub_packed_rrset_ttl(ds), 28508b7325afSsthen LDNS_EDE_UNSUPPORTED_DS_DIGEST, NULL, 28518b7325afSsthen *qstate->env->now); 2852817bdb8fSflorian return (*ke) == NULL; 2853933707f3Ssthen } 2854933707f3Ssthen 2855933707f3Ssthen /* Otherwise, we return the positive response. */ 2856933707f3Ssthen log_query_info(VERB_DETAIL, "validated DS", qinfo); 2857933707f3Ssthen *ke = key_entry_create_rrset(qstate->region, 2858933707f3Ssthen qinfo->qname, qinfo->qname_len, qinfo->qclass, ds, 28598b7325afSsthen NULL, LDNS_EDE_NONE, NULL, *qstate->env->now); 2860817bdb8fSflorian return (*ke) == NULL; 2861933707f3Ssthen } else if(subtype == VAL_CLASS_NODATA || 2862933707f3Ssthen subtype == VAL_CLASS_NAMEERROR) { 2863933707f3Ssthen /* NODATA means that the qname exists, but that there was 2864933707f3Ssthen * no DS. This is a pretty normal case. */ 2865229e174cSsthen time_t proof_ttl = 0; 2866933707f3Ssthen enum sec_status sec; 2867933707f3Ssthen 2868933707f3Ssthen /* make sure there are NSECs or NSEC3s with signatures */ 2869933707f3Ssthen if(!val_has_signed_nsecs(msg->rep, &reason)) { 2870933707f3Ssthen verbose(VERB_ALGO, "no NSECs: %s", reason); 28718b7325afSsthen reason_bogus = LDNS_EDE_NSEC_MISSING; 28728b7325afSsthen errinf_ede(qstate, reason, reason_bogus); 2873933707f3Ssthen goto return_bogus; 2874933707f3Ssthen } 2875933707f3Ssthen 2876933707f3Ssthen /* For subtype Name Error. 2877933707f3Ssthen * attempt ANS 2.8.1.0 compatibility where it sets rcode 2878933707f3Ssthen * to nxdomain, but really this is an Nodata/Noerror response. 2879933707f3Ssthen * Find and prove the empty nonterminal in that case */ 2880933707f3Ssthen 2881933707f3Ssthen /* Try to prove absence of the DS with NSEC */ 2882933707f3Ssthen sec = val_nsec_prove_nodata_dsreply( 2883933707f3Ssthen qstate->env, ve, qinfo, msg->rep, vq->key_entry, 2884*98bc733bSsthen &proof_ttl, &reason, &reason_bogus, qstate, 2885*98bc733bSsthen reasonbuf, sizeof(reasonbuf)); 2886933707f3Ssthen switch(sec) { 2887933707f3Ssthen case sec_status_secure: 2888933707f3Ssthen verbose(VERB_DETAIL, "NSEC RRset for the " 2889933707f3Ssthen "referral proved no DS."); 2890933707f3Ssthen *ke = key_entry_create_null(qstate->region, 2891933707f3Ssthen qinfo->qname, qinfo->qname_len, 2892933707f3Ssthen qinfo->qclass, proof_ttl, 28938b7325afSsthen LDNS_EDE_NONE, NULL, 2894933707f3Ssthen *qstate->env->now); 2895817bdb8fSflorian return (*ke) == NULL; 2896933707f3Ssthen case sec_status_insecure: 2897933707f3Ssthen verbose(VERB_DETAIL, "NSEC RRset for the " 2898933707f3Ssthen "referral proved not a delegation point"); 2899933707f3Ssthen *ke = NULL; 2900817bdb8fSflorian return 0; 2901933707f3Ssthen case sec_status_bogus: 2902933707f3Ssthen verbose(VERB_DETAIL, "NSEC RRset for the " 2903933707f3Ssthen "referral did not prove no DS."); 2904933707f3Ssthen errinf(qstate, reason); 2905933707f3Ssthen goto return_bogus; 2906933707f3Ssthen case sec_status_unchecked: 2907933707f3Ssthen default: 2908933707f3Ssthen /* NSEC proof did not work, try next */ 2909933707f3Ssthen break; 2910933707f3Ssthen } 2911933707f3Ssthen 2912817bdb8fSflorian if(!nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { 2913817bdb8fSflorian log_err("malloc failure in ds_response_to_ke for " 2914817bdb8fSflorian "NSEC3 cache"); 2915817bdb8fSflorian reason = "malloc failure"; 2916817bdb8fSflorian errinf_ede(qstate, reason, 0); 2917817bdb8fSflorian goto return_bogus; 2918817bdb8fSflorian } 2919933707f3Ssthen sec = nsec3_prove_nods(qstate->env, ve, 2920933707f3Ssthen msg->rep->rrsets + msg->rep->an_numrrsets, 2921bdfc4d55Sflorian msg->rep->ns_numrrsets, qinfo, vq->key_entry, &reason, 2922*98bc733bSsthen &reason_bogus, qstate, &vq->nsec3_cache_table, 2923*98bc733bSsthen reasonbuf, sizeof(reasonbuf)); 2924933707f3Ssthen switch(sec) { 2925933707f3Ssthen case sec_status_insecure: 2926933707f3Ssthen /* case insecure also continues to unsigned 2927933707f3Ssthen * space. If nsec3-iter-count too high or 2928933707f3Ssthen * optout, then treat below as unsigned */ 2929933707f3Ssthen case sec_status_secure: 2930933707f3Ssthen verbose(VERB_DETAIL, "NSEC3s for the " 2931933707f3Ssthen "referral proved no DS."); 2932933707f3Ssthen *ke = key_entry_create_null(qstate->region, 2933933707f3Ssthen qinfo->qname, qinfo->qname_len, 2934933707f3Ssthen qinfo->qclass, proof_ttl, 29358b7325afSsthen LDNS_EDE_NONE, NULL, 2936933707f3Ssthen *qstate->env->now); 2937817bdb8fSflorian return (*ke) == NULL; 2938933707f3Ssthen case sec_status_indeterminate: 2939933707f3Ssthen verbose(VERB_DETAIL, "NSEC3s for the " 2940933707f3Ssthen "referral proved no delegation"); 2941933707f3Ssthen *ke = NULL; 2942817bdb8fSflorian return 0; 2943933707f3Ssthen case sec_status_bogus: 2944933707f3Ssthen verbose(VERB_DETAIL, "NSEC3s for the " 2945933707f3Ssthen "referral did not prove no DS."); 29460bdb4f62Ssthen errinf_ede(qstate, reason, reason_bogus); 2947933707f3Ssthen goto return_bogus; 2948933707f3Ssthen case sec_status_unchecked: 2949817bdb8fSflorian return 2; 2950933707f3Ssthen default: 2951933707f3Ssthen /* NSEC3 proof did not work */ 2952933707f3Ssthen break; 2953933707f3Ssthen } 2954933707f3Ssthen 2955933707f3Ssthen /* Apparently, no available NSEC/NSEC3 proved NODATA, so 2956933707f3Ssthen * this is BOGUS. */ 2957933707f3Ssthen verbose(VERB_DETAIL, "DS %s ran out of options, so return " 2958933707f3Ssthen "bogus", val_classification_to_string(subtype)); 29598b7325afSsthen reason = "no DS but also no proof of that"; 29608b7325afSsthen errinf_ede(qstate, reason, reason_bogus); 2961933707f3Ssthen goto return_bogus; 2962933707f3Ssthen } else if(subtype == VAL_CLASS_CNAME || 2963933707f3Ssthen subtype == VAL_CLASS_CNAMENOANSWER) { 2964933707f3Ssthen /* if the CNAME matches the exact name we want and is signed 2965933707f3Ssthen * properly, then also, we are sure that no DS exists there, 2966933707f3Ssthen * much like a NODATA proof */ 2967933707f3Ssthen enum sec_status sec; 2968933707f3Ssthen struct ub_packed_rrset_key* cname; 2969933707f3Ssthen cname = reply_find_rrset_section_an(msg->rep, qinfo->qname, 2970933707f3Ssthen qinfo->qname_len, LDNS_RR_TYPE_CNAME, qinfo->qclass); 2971933707f3Ssthen if(!cname) { 29728b7325afSsthen reason = "validator classified CNAME but no " 29738b7325afSsthen "CNAME of the queried name for DS"; 29748b7325afSsthen errinf_ede(qstate, reason, reason_bogus); 2975933707f3Ssthen goto return_bogus; 2976933707f3Ssthen } 2977933707f3Ssthen if(((struct packed_rrset_data*)cname->entry.data)->rrsig_count 2978933707f3Ssthen == 0) { 2979933707f3Ssthen if(msg->rep->an_numrrsets != 0 && ntohs(msg->rep-> 2980933707f3Ssthen rrsets[0]->rk.type)==LDNS_RR_TYPE_DNAME) { 29818b7325afSsthen reason = "DS got DNAME answer"; 2982933707f3Ssthen } else { 29838b7325afSsthen reason = "DS got unsigned CNAME answer"; 2984933707f3Ssthen } 29858b7325afSsthen errinf_ede(qstate, reason, reason_bogus); 2986933707f3Ssthen goto return_bogus; 2987933707f3Ssthen } 2988933707f3Ssthen sec = val_verify_rrset_entry(qstate->env, ve, cname, 29898b7325afSsthen vq->key_entry, &reason, &reason_bogus, 2990*98bc733bSsthen LDNS_SECTION_ANSWER, qstate, &verified, reasonbuf, 2991*98bc733bSsthen sizeof(reasonbuf)); 2992933707f3Ssthen if(sec == sec_status_secure) { 2993933707f3Ssthen verbose(VERB_ALGO, "CNAME validated, " 2994933707f3Ssthen "proof that DS does not exist"); 2995933707f3Ssthen /* and that it is not a referral point */ 2996933707f3Ssthen *ke = NULL; 2997817bdb8fSflorian return 0; 2998933707f3Ssthen } 2999933707f3Ssthen errinf(qstate, "CNAME in DS response was not secure."); 30008b7325afSsthen errinf_ede(qstate, reason, reason_bogus); 3001933707f3Ssthen goto return_bogus; 3002933707f3Ssthen } else { 3003933707f3Ssthen verbose(VERB_QUERY, "Encountered an unhandled type of " 3004933707f3Ssthen "DS response, thus bogus."); 3005933707f3Ssthen errinf(qstate, "no DS and"); 30068b7325afSsthen reason = "no DS"; 3007933707f3Ssthen if(FLAGS_GET_RCODE(msg->rep->flags) != LDNS_RCODE_NOERROR) { 30085d76a658Ssthen char rc[16]; 30095d76a658Ssthen rc[0]=0; 30105d76a658Ssthen (void)sldns_wire2str_rcode_buf((int)FLAGS_GET_RCODE( 30115d76a658Ssthen msg->rep->flags), rc, sizeof(rc)); 3012933707f3Ssthen errinf(qstate, rc); 3013933707f3Ssthen } else errinf(qstate, val_classification_to_string(subtype)); 3014933707f3Ssthen errinf(qstate, "message fails to prove that"); 3015933707f3Ssthen goto return_bogus; 3016933707f3Ssthen } 3017933707f3Ssthen return_bogus: 3018933707f3Ssthen *ke = key_entry_create_bad(qstate->region, qinfo->qname, 30198b7325afSsthen qinfo->qname_len, qinfo->qclass, BOGUS_KEY_TTL, 30208b7325afSsthen reason_bogus, reason, *qstate->env->now); 3021817bdb8fSflorian return (*ke) == NULL; 3022933707f3Ssthen } 3023933707f3Ssthen 3024933707f3Ssthen /** 3025933707f3Ssthen * Process DS response. Called from inform_supers. 3026933707f3Ssthen * Because it is in inform_supers, the mesh itself is busy doing callbacks 3027933707f3Ssthen * for a state that is to be deleted soon; don't touch the mesh; instead 3028933707f3Ssthen * set a state in the super, as the super will be reactivated soon. 3029933707f3Ssthen * Perform processing to determine what state to set in the super. 3030933707f3Ssthen * 3031933707f3Ssthen * @param qstate: query state that is validating and asked for a DS. 3032933707f3Ssthen * @param vq: validator query state 3033933707f3Ssthen * @param id: module id. 3034933707f3Ssthen * @param rcode: rcode result value. 3035933707f3Ssthen * @param msg: result message (if rcode is OK). 3036933707f3Ssthen * @param qinfo: from the sub query state, query info. 3037933707f3Ssthen * @param origin: the origin of msg. 3038f46c52bfSsthen * @param suspend: returned true if the task takes too long and needs to 3039f46c52bfSsthen * suspend to continue the effort later. 3040*98bc733bSsthen * @param sub_qstate: the sub query state, that is the lookup that fetched 3041*98bc733bSsthen * the trust anchor data, it contains error information for the answer. 3042*98bc733bSsthen * Can be NULL. 3043933707f3Ssthen */ 3044933707f3Ssthen static void 3045933707f3Ssthen process_ds_response(struct module_qstate* qstate, struct val_qstate* vq, 3046933707f3Ssthen int id, int rcode, struct dns_msg* msg, struct query_info* qinfo, 3047*98bc733bSsthen struct sock_list* origin, int* suspend, 3048*98bc733bSsthen struct module_qstate* sub_qstate) 3049933707f3Ssthen { 3050191f22c6Ssthen struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; 3051933707f3Ssthen struct key_entry_key* dske = NULL; 3052933707f3Ssthen uint8_t* olds = vq->empty_DS_name; 3053817bdb8fSflorian int ret; 3054817bdb8fSflorian *suspend = 0; 3055933707f3Ssthen vq->empty_DS_name = NULL; 3056*98bc733bSsthen ret = ds_response_to_ke(qstate, vq, id, rcode, msg, qinfo, &dske, 3057*98bc733bSsthen sub_qstate); 3058817bdb8fSflorian if(ret != 0) { 3059817bdb8fSflorian switch(ret) { 3060817bdb8fSflorian case 1: 3061933707f3Ssthen log_err("malloc failure in process_ds_response"); 3062933707f3Ssthen vq->key_entry = NULL; /* make it error */ 3063933707f3Ssthen vq->state = VAL_VALIDATE_STATE; 3064933707f3Ssthen return; 3065817bdb8fSflorian case 2: 3066817bdb8fSflorian *suspend = 1; 3067817bdb8fSflorian return; 3068817bdb8fSflorian default: 3069817bdb8fSflorian log_err("unhandled error value for ds_response_to_ke"); 3070817bdb8fSflorian vq->key_entry = NULL; /* make it error */ 3071817bdb8fSflorian vq->state = VAL_VALIDATE_STATE; 3072817bdb8fSflorian return; 3073817bdb8fSflorian } 3074933707f3Ssthen } 3075933707f3Ssthen if(dske == NULL) { 3076933707f3Ssthen vq->empty_DS_name = regional_alloc_init(qstate->region, 3077933707f3Ssthen qinfo->qname, qinfo->qname_len); 3078933707f3Ssthen if(!vq->empty_DS_name) { 3079933707f3Ssthen log_err("malloc failure in empty_DS_name"); 3080933707f3Ssthen vq->key_entry = NULL; /* make it error */ 3081933707f3Ssthen vq->state = VAL_VALIDATE_STATE; 3082933707f3Ssthen return; 3083933707f3Ssthen } 3084933707f3Ssthen vq->empty_DS_len = qinfo->qname_len; 3085933707f3Ssthen vq->chain_blacklist = NULL; 3086933707f3Ssthen /* ds response indicated that we aren't on a delegation point. 3087933707f3Ssthen * Keep the forState.state on FINDKEY. */ 3088933707f3Ssthen } else if(key_entry_isgood(dske)) { 3089933707f3Ssthen vq->ds_rrset = key_entry_get_rrset(dske, qstate->region); 3090933707f3Ssthen if(!vq->ds_rrset) { 3091933707f3Ssthen log_err("malloc failure in process DS"); 3092933707f3Ssthen vq->key_entry = NULL; /* make it error */ 3093933707f3Ssthen vq->state = VAL_VALIDATE_STATE; 3094933707f3Ssthen return; 3095933707f3Ssthen } 3096933707f3Ssthen vq->chain_blacklist = NULL; /* fresh blacklist for next part*/ 3097933707f3Ssthen /* Keep the forState.state on FINDKEY. */ 3098933707f3Ssthen } else if(key_entry_isbad(dske) 3099191f22c6Ssthen && vq->restart_count < ve->max_restart) { 3100933707f3Ssthen vq->empty_DS_name = olds; 3101933707f3Ssthen val_blacklist(&vq->chain_blacklist, qstate->region, origin, 1); 3102933707f3Ssthen qstate->errinf = NULL; 3103933707f3Ssthen vq->restart_count++; 3104933707f3Ssthen } else { 3105933707f3Ssthen if(key_entry_isbad(dske)) { 3106933707f3Ssthen errinf_origin(qstate, origin); 3107933707f3Ssthen errinf_dname(qstate, "for DS", qinfo->qname); 3108933707f3Ssthen } 3109933707f3Ssthen /* NOTE: the reason for the DS to be not good (that is, 3110933707f3Ssthen * either bad or null) should have been logged by 3111933707f3Ssthen * dsResponseToKE. */ 3112933707f3Ssthen vq->key_entry = dske; 3113933707f3Ssthen /* The FINDKEY phase has ended, so move on. */ 3114933707f3Ssthen vq->state = VAL_VALIDATE_STATE; 3115933707f3Ssthen } 3116933707f3Ssthen } 3117933707f3Ssthen 3118933707f3Ssthen /** 3119933707f3Ssthen * Process DNSKEY response. Called from inform_supers. 3120933707f3Ssthen * Sets the key entry in the state. 3121933707f3Ssthen * Because it is in inform_supers, the mesh itself is busy doing callbacks 3122933707f3Ssthen * for a state that is to be deleted soon; don't touch the mesh; instead 3123933707f3Ssthen * set a state in the super, as the super will be reactivated soon. 3124933707f3Ssthen * Perform processing to determine what state to set in the super. 3125933707f3Ssthen * 3126933707f3Ssthen * @param qstate: query state that is validating and asked for a DNSKEY. 3127933707f3Ssthen * @param vq: validator query state 3128933707f3Ssthen * @param id: module id. 3129933707f3Ssthen * @param rcode: rcode result value. 3130933707f3Ssthen * @param msg: result message (if rcode is OK). 3131933707f3Ssthen * @param qinfo: from the sub query state, query info. 3132933707f3Ssthen * @param origin: the origin of msg. 3133*98bc733bSsthen * @param sub_qstate: the sub query state, that is the lookup that fetched 3134*98bc733bSsthen * the trust anchor data, it contains error information for the answer. 3135933707f3Ssthen */ 3136933707f3Ssthen static void 3137933707f3Ssthen process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq, 3138933707f3Ssthen int id, int rcode, struct dns_msg* msg, struct query_info* qinfo, 3139*98bc733bSsthen struct sock_list* origin, struct module_qstate* sub_qstate) 3140933707f3Ssthen { 3141933707f3Ssthen struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; 3142933707f3Ssthen struct key_entry_key* old = vq->key_entry; 3143933707f3Ssthen struct ub_packed_rrset_key* dnskey = NULL; 3144933707f3Ssthen int downprot; 3145*98bc733bSsthen char reasonbuf[256]; 3146933707f3Ssthen char* reason = NULL; 31470bdb4f62Ssthen sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS; 3148933707f3Ssthen 3149933707f3Ssthen if(rcode == LDNS_RCODE_NOERROR) 3150933707f3Ssthen dnskey = reply_find_answer_rrset(qinfo, msg->rep); 3151933707f3Ssthen 3152933707f3Ssthen if(dnskey == NULL) { 3153*98bc733bSsthen char* err; 3154*98bc733bSsthen char rstr[1024]; 3155933707f3Ssthen /* bad response */ 3156933707f3Ssthen verbose(VERB_DETAIL, "Missing DNSKEY RRset in response to " 3157933707f3Ssthen "DNSKEY query."); 31580bdb4f62Ssthen 3159191f22c6Ssthen if(vq->restart_count < ve->max_restart) { 3160933707f3Ssthen val_blacklist(&vq->chain_blacklist, qstate->region, 3161933707f3Ssthen origin, 1); 3162933707f3Ssthen qstate->errinf = NULL; 3163933707f3Ssthen vq->restart_count++; 3164933707f3Ssthen return; 3165933707f3Ssthen } 3166*98bc733bSsthen err = errinf_to_str_misc(sub_qstate); 3167*98bc733bSsthen if(!err) { 3168*98bc733bSsthen snprintf(rstr, sizeof(rstr), "No DNSKEY record"); 3169*98bc733bSsthen } else { 3170*98bc733bSsthen snprintf(rstr, sizeof(rstr), "No DNSKEY record " 3171*98bc733bSsthen "[%s]", err); 3172*98bc733bSsthen } 31738b7325afSsthen reason_bogus = LDNS_EDE_DNSKEY_MISSING; 3174933707f3Ssthen vq->key_entry = key_entry_create_bad(qstate->region, 3175933707f3Ssthen qinfo->qname, qinfo->qname_len, qinfo->qclass, 3176*98bc733bSsthen BOGUS_KEY_TTL, reason_bogus, rstr, *qstate->env->now); 3177933707f3Ssthen if(!vq->key_entry) { 3178933707f3Ssthen log_err("alloc failure in missing dnskey response"); 3179933707f3Ssthen /* key_entry is NULL for failure in Validate */ 3180933707f3Ssthen } 3181*98bc733bSsthen errinf_ede(qstate, rstr, reason_bogus); 3182933707f3Ssthen errinf_origin(qstate, origin); 3183933707f3Ssthen errinf_dname(qstate, "for key", qinfo->qname); 3184933707f3Ssthen vq->state = VAL_VALIDATE_STATE; 3185933707f3Ssthen return; 3186933707f3Ssthen } 3187933707f3Ssthen if(!vq->ds_rrset) { 3188933707f3Ssthen log_err("internal error: no DS rrset for new DNSKEY response"); 3189933707f3Ssthen vq->key_entry = NULL; 3190933707f3Ssthen vq->state = VAL_VALIDATE_STATE; 3191933707f3Ssthen return; 3192933707f3Ssthen } 3193a961b961Ssthen downprot = qstate->env->cfg->harden_algo_downgrade; 3194933707f3Ssthen vq->key_entry = val_verify_new_DNSKEYs(qstate->region, qstate->env, 3195*98bc733bSsthen ve, dnskey, vq->ds_rrset, downprot, &reason, &reason_bogus, 3196*98bc733bSsthen qstate, reasonbuf, sizeof(reasonbuf)); 3197933707f3Ssthen 3198933707f3Ssthen if(!vq->key_entry) { 3199933707f3Ssthen log_err("out of memory in verify new DNSKEYs"); 3200933707f3Ssthen vq->state = VAL_VALIDATE_STATE; 3201933707f3Ssthen return; 3202933707f3Ssthen } 3203933707f3Ssthen /* If the key entry isBad or isNull, then we can move on to the next 3204933707f3Ssthen * state. */ 3205933707f3Ssthen if(!key_entry_isgood(vq->key_entry)) { 3206933707f3Ssthen if(key_entry_isbad(vq->key_entry)) { 3207191f22c6Ssthen if(vq->restart_count < ve->max_restart) { 3208933707f3Ssthen val_blacklist(&vq->chain_blacklist, 3209933707f3Ssthen qstate->region, origin, 1); 3210933707f3Ssthen qstate->errinf = NULL; 3211933707f3Ssthen vq->restart_count++; 3212933707f3Ssthen vq->key_entry = old; 3213933707f3Ssthen return; 3214933707f3Ssthen } 3215933707f3Ssthen verbose(VERB_DETAIL, "Did not match a DS to a DNSKEY, " 3216933707f3Ssthen "thus bogus."); 32170bdb4f62Ssthen errinf_ede(qstate, reason, reason_bogus); 3218933707f3Ssthen errinf_origin(qstate, origin); 3219933707f3Ssthen errinf_dname(qstate, "for key", qinfo->qname); 3220933707f3Ssthen } 3221933707f3Ssthen vq->chain_blacklist = NULL; 3222933707f3Ssthen vq->state = VAL_VALIDATE_STATE; 3223933707f3Ssthen return; 3224933707f3Ssthen } 3225933707f3Ssthen vq->chain_blacklist = NULL; 3226933707f3Ssthen qstate->errinf = NULL; 3227933707f3Ssthen 3228933707f3Ssthen /* The DNSKEY validated, so cache it as a trusted key rrset. */ 32298b7325afSsthen key_cache_insert(ve->kcache, vq->key_entry, 32308b7325afSsthen qstate->env->cfg->val_log_level >= 2); 3231933707f3Ssthen 3232933707f3Ssthen /* If good, we stay in the FINDKEY state. */ 3233933707f3Ssthen log_query_info(VERB_DETAIL, "validated DNSKEY", qinfo); 3234933707f3Ssthen } 3235933707f3Ssthen 3236933707f3Ssthen /** 3237933707f3Ssthen * Process prime response 3238933707f3Ssthen * Sets the key entry in the state. 3239933707f3Ssthen * 3240933707f3Ssthen * @param qstate: query state that is validating and primed a trust anchor. 3241933707f3Ssthen * @param vq: validator query state 3242933707f3Ssthen * @param id: module id. 3243933707f3Ssthen * @param rcode: rcode result value. 3244933707f3Ssthen * @param msg: result message (if rcode is OK). 3245933707f3Ssthen * @param origin: the origin of msg. 3246*98bc733bSsthen * @param sub_qstate: the sub query state, that is the lookup that fetched 3247*98bc733bSsthen * the trust anchor data, it contains error information for the answer. 3248933707f3Ssthen */ 3249933707f3Ssthen static void 3250933707f3Ssthen process_prime_response(struct module_qstate* qstate, struct val_qstate* vq, 3251*98bc733bSsthen int id, int rcode, struct dns_msg* msg, struct sock_list* origin, 3252*98bc733bSsthen struct module_qstate* sub_qstate) 3253933707f3Ssthen { 3254933707f3Ssthen struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; 3255933707f3Ssthen struct ub_packed_rrset_key* dnskey_rrset = NULL; 3256933707f3Ssthen struct trust_anchor* ta = anchor_find(qstate->env->anchors, 3257933707f3Ssthen vq->trust_anchor_name, vq->trust_anchor_labs, 3258933707f3Ssthen vq->trust_anchor_len, vq->qchase.qclass); 3259933707f3Ssthen if(!ta) { 3260933707f3Ssthen /* trust anchor revoked, restart with less anchors */ 3261933707f3Ssthen vq->state = VAL_INIT_STATE; 3262933707f3Ssthen if(!vq->trust_anchor_name) 3263933707f3Ssthen vq->state = VAL_VALIDATE_STATE; /* break a loop */ 3264933707f3Ssthen vq->trust_anchor_name = NULL; 3265933707f3Ssthen return; 3266933707f3Ssthen } 3267933707f3Ssthen /* Fetch and validate the keyEntry that corresponds to the 3268933707f3Ssthen * current trust anchor. */ 3269933707f3Ssthen if(rcode == LDNS_RCODE_NOERROR) { 3270933707f3Ssthen dnskey_rrset = reply_find_rrset_section_an(msg->rep, 3271933707f3Ssthen ta->name, ta->namelen, LDNS_RR_TYPE_DNSKEY, 3272933707f3Ssthen ta->dclass); 3273933707f3Ssthen } 32742be9e038Ssthen 3275933707f3Ssthen if(ta->autr) { 3276bdfc4d55Sflorian if(!autr_process_prime(qstate->env, ve, ta, dnskey_rrset, 3277bdfc4d55Sflorian qstate)) { 3278933707f3Ssthen /* trust anchor revoked, restart with less anchors */ 3279933707f3Ssthen vq->state = VAL_INIT_STATE; 3280933707f3Ssthen vq->trust_anchor_name = NULL; 3281933707f3Ssthen return; 3282933707f3Ssthen } 3283933707f3Ssthen } 3284*98bc733bSsthen vq->key_entry = primeResponseToKE(dnskey_rrset, ta, qstate, id, 3285*98bc733bSsthen sub_qstate); 3286933707f3Ssthen lock_basic_unlock(&ta->lock); 3287933707f3Ssthen if(vq->key_entry) { 3288933707f3Ssthen if(key_entry_isbad(vq->key_entry) 3289191f22c6Ssthen && vq->restart_count < ve->max_restart) { 3290933707f3Ssthen val_blacklist(&vq->chain_blacklist, qstate->region, 3291933707f3Ssthen origin, 1); 3292933707f3Ssthen qstate->errinf = NULL; 3293933707f3Ssthen vq->restart_count++; 3294933707f3Ssthen vq->key_entry = NULL; 3295933707f3Ssthen vq->state = VAL_INIT_STATE; 3296933707f3Ssthen return; 3297933707f3Ssthen } 3298933707f3Ssthen vq->chain_blacklist = NULL; 3299933707f3Ssthen errinf_origin(qstate, origin); 3300933707f3Ssthen errinf_dname(qstate, "for trust anchor", ta->name); 3301933707f3Ssthen /* store the freshly primed entry in the cache */ 33028b7325afSsthen key_cache_insert(ve->kcache, vq->key_entry, 33038b7325afSsthen qstate->env->cfg->val_log_level >= 2); 3304933707f3Ssthen } 3305933707f3Ssthen 3306933707f3Ssthen /* If the result of the prime is a null key, skip the FINDKEY state.*/ 3307933707f3Ssthen if(!vq->key_entry || key_entry_isnull(vq->key_entry) || 3308933707f3Ssthen key_entry_isbad(vq->key_entry)) { 3309933707f3Ssthen vq->state = VAL_VALIDATE_STATE; 3310933707f3Ssthen } 3311933707f3Ssthen /* the qstate will be reactivated after inform_super is done */ 3312933707f3Ssthen } 3313933707f3Ssthen 3314933707f3Ssthen /* 3315933707f3Ssthen * inform validator super. 3316933707f3Ssthen * 3317933707f3Ssthen * @param qstate: query state that finished. 3318933707f3Ssthen * @param id: module id. 3319933707f3Ssthen * @param super: the qstate to inform. 3320933707f3Ssthen */ 3321933707f3Ssthen void 3322933707f3Ssthen val_inform_super(struct module_qstate* qstate, int id, 3323933707f3Ssthen struct module_qstate* super) 3324933707f3Ssthen { 3325933707f3Ssthen struct val_qstate* vq = (struct val_qstate*)super->minfo[id]; 3326933707f3Ssthen log_query_info(VERB_ALGO, "validator: inform_super, sub is", 3327933707f3Ssthen &qstate->qinfo); 3328933707f3Ssthen log_query_info(VERB_ALGO, "super is", &super->qinfo); 3329933707f3Ssthen if(!vq) { 3330933707f3Ssthen verbose(VERB_ALGO, "super: has no validator state"); 3331933707f3Ssthen return; 3332933707f3Ssthen } 3333933707f3Ssthen if(vq->wait_prime_ta) { 3334933707f3Ssthen vq->wait_prime_ta = 0; 3335933707f3Ssthen process_prime_response(super, vq, id, qstate->return_rcode, 3336*98bc733bSsthen qstate->return_msg, qstate->reply_origin, qstate); 3337933707f3Ssthen return; 3338933707f3Ssthen } 3339933707f3Ssthen if(qstate->qinfo.qtype == LDNS_RR_TYPE_DS) { 3340817bdb8fSflorian int suspend; 3341933707f3Ssthen process_ds_response(super, vq, id, qstate->return_rcode, 3342933707f3Ssthen qstate->return_msg, &qstate->qinfo, 3343*98bc733bSsthen qstate->reply_origin, &suspend, qstate); 3344817bdb8fSflorian /* If NSEC3 was needed during validation, NULL the NSEC3 cache; 3345817bdb8fSflorian * it will be re-initiated if needed later on. 3346817bdb8fSflorian * Validation (and the cache table) are happening/allocated in 3347817bdb8fSflorian * the super qstate whilst the RRs are allocated (and pointed 3348817bdb8fSflorian * to) in this sub qstate. */ 3349817bdb8fSflorian if(vq->nsec3_cache_table.ct) { 3350817bdb8fSflorian vq->nsec3_cache_table.ct = NULL; 3351817bdb8fSflorian } 3352817bdb8fSflorian if(suspend) { 3353817bdb8fSflorian /* deep copy the return_msg to vq->sub_ds_msg; it will 3354817bdb8fSflorian * be resumed later in the super state with the caveat 3355817bdb8fSflorian * that the initial calculations will be re-caclulated 3356817bdb8fSflorian * and re-suspended there before continuing. */ 3357817bdb8fSflorian vq->sub_ds_msg = dns_msg_deepcopy_region( 3358817bdb8fSflorian qstate->return_msg, super->region); 3359817bdb8fSflorian } 3360933707f3Ssthen return; 3361933707f3Ssthen } else if(qstate->qinfo.qtype == LDNS_RR_TYPE_DNSKEY) { 3362933707f3Ssthen process_dnskey_response(super, vq, id, qstate->return_rcode, 3363933707f3Ssthen qstate->return_msg, &qstate->qinfo, 3364*98bc733bSsthen qstate->reply_origin, qstate); 3365933707f3Ssthen return; 3366933707f3Ssthen } 3367933707f3Ssthen log_err("internal error in validator: no inform_supers possible"); 3368933707f3Ssthen } 3369933707f3Ssthen 3370933707f3Ssthen void 3371933707f3Ssthen val_clear(struct module_qstate* qstate, int id) 3372933707f3Ssthen { 3373817bdb8fSflorian struct val_qstate* vq; 3374933707f3Ssthen if(!qstate) 3375933707f3Ssthen return; 3376817bdb8fSflorian vq = (struct val_qstate*)qstate->minfo[id]; 3377817bdb8fSflorian if(vq) { 3378817bdb8fSflorian if(vq->suspend_timer) { 3379817bdb8fSflorian comm_timer_delete(vq->suspend_timer); 3380817bdb8fSflorian } 3381817bdb8fSflorian } 3382933707f3Ssthen /* everything is allocated in the region, so assign NULL */ 3383933707f3Ssthen qstate->minfo[id] = NULL; 3384933707f3Ssthen } 3385933707f3Ssthen 3386933707f3Ssthen size_t 3387933707f3Ssthen val_get_mem(struct module_env* env, int id) 3388933707f3Ssthen { 3389933707f3Ssthen struct val_env* ve = (struct val_env*)env->modinfo[id]; 3390933707f3Ssthen if(!ve) 3391933707f3Ssthen return 0; 3392933707f3Ssthen return sizeof(*ve) + key_cache_get_mem(ve->kcache) + 3393933707f3Ssthen val_neg_get_mem(ve->neg_cache) + 3394933707f3Ssthen sizeof(size_t)*2*ve->nsec3_keyiter_count; 3395933707f3Ssthen } 3396933707f3Ssthen 3397933707f3Ssthen /** 3398933707f3Ssthen * The validator function block 3399933707f3Ssthen */ 3400933707f3Ssthen static struct module_func_block val_block = { 3401933707f3Ssthen "validator", 3402*98bc733bSsthen NULL, NULL, &val_init, &val_deinit, &val_operate, &val_inform_super, 3403*98bc733bSsthen &val_clear, &val_get_mem 3404933707f3Ssthen }; 3405933707f3Ssthen 3406933707f3Ssthen struct module_func_block* 3407933707f3Ssthen val_get_funcblock(void) 3408933707f3Ssthen { 3409933707f3Ssthen return &val_block; 3410933707f3Ssthen } 3411933707f3Ssthen 3412933707f3Ssthen const char* 3413933707f3Ssthen val_state_to_string(enum val_state state) 3414933707f3Ssthen { 3415933707f3Ssthen switch(state) { 3416933707f3Ssthen case VAL_INIT_STATE: return "VAL_INIT_STATE"; 3417933707f3Ssthen case VAL_FINDKEY_STATE: return "VAL_FINDKEY_STATE"; 3418933707f3Ssthen case VAL_VALIDATE_STATE: return "VAL_VALIDATE_STATE"; 3419933707f3Ssthen case VAL_FINISHED_STATE: return "VAL_FINISHED_STATE"; 3420933707f3Ssthen } 3421933707f3Ssthen return "UNKNOWN VALIDATOR STATE"; 3422933707f3Ssthen } 3423933707f3Ssthen 3424