1933707f3Ssthen /* 2933707f3Ssthen * iterator/iter_scrub.c - scrubbing, normalization, sanitization of DNS msgs. 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 has routine(s) for cleaning up incoming DNS messages from 40933707f3Ssthen * possible useless or malicious junk in it. 41933707f3Ssthen */ 42933707f3Ssthen #include "config.h" 43933707f3Ssthen #include "iterator/iter_scrub.h" 44933707f3Ssthen #include "iterator/iterator.h" 45933707f3Ssthen #include "iterator/iter_priv.h" 46933707f3Ssthen #include "services/cache/rrset.h" 47933707f3Ssthen #include "util/log.h" 48933707f3Ssthen #include "util/net_help.h" 49933707f3Ssthen #include "util/regional.h" 50933707f3Ssthen #include "util/config_file.h" 51933707f3Ssthen #include "util/module.h" 52933707f3Ssthen #include "util/data/msgparse.h" 53933707f3Ssthen #include "util/data/dname.h" 54933707f3Ssthen #include "util/data/msgreply.h" 55933707f3Ssthen #include "util/alloc.h" 56a58bff56Ssthen #include "sldns/sbuffer.h" 57933707f3Ssthen 58933707f3Ssthen /** RRset flag used during scrubbing. The RRset is OK. */ 59933707f3Ssthen #define RRSET_SCRUB_OK 0x80 60933707f3Ssthen 61933707f3Ssthen /** remove rrset, update loop variables */ 62933707f3Ssthen static void 635d76a658Ssthen remove_rrset(const char* str, sldns_buffer* pkt, struct msg_parse* msg, 64933707f3Ssthen struct rrset_parse* prev, struct rrset_parse** rrset) 65933707f3Ssthen { 66229e174cSsthen if(verbosity >= VERB_QUERY && str 67933707f3Ssthen && (*rrset)->dname_len <= LDNS_MAX_DOMAINLEN) { 68933707f3Ssthen uint8_t buf[LDNS_MAX_DOMAINLEN+1]; 69933707f3Ssthen dname_pkt_copy(pkt, buf, (*rrset)->dname); 70933707f3Ssthen log_nametypeclass(VERB_QUERY, str, buf, 71933707f3Ssthen (*rrset)->type, ntohs((*rrset)->rrset_class)); 72933707f3Ssthen } 73933707f3Ssthen if(prev) 74933707f3Ssthen prev->rrset_all_next = (*rrset)->rrset_all_next; 75933707f3Ssthen else msg->rrset_first = (*rrset)->rrset_all_next; 76933707f3Ssthen if(msg->rrset_last == *rrset) 77933707f3Ssthen msg->rrset_last = prev; 78933707f3Ssthen msg->rrset_count --; 79933707f3Ssthen switch((*rrset)->section) { 80933707f3Ssthen case LDNS_SECTION_ANSWER: msg->an_rrsets--; break; 81933707f3Ssthen case LDNS_SECTION_AUTHORITY: msg->ns_rrsets--; break; 82933707f3Ssthen case LDNS_SECTION_ADDITIONAL: msg->ar_rrsets--; break; 83933707f3Ssthen default: log_assert(0); 84933707f3Ssthen } 85933707f3Ssthen msgparse_bucket_remove(msg, *rrset); 86933707f3Ssthen *rrset = (*rrset)->rrset_all_next; 87933707f3Ssthen } 88933707f3Ssthen 89933707f3Ssthen /** return true if rr type has additional names in it */ 90933707f3Ssthen static int 91933707f3Ssthen has_additional(uint16_t t) 92933707f3Ssthen { 93933707f3Ssthen switch(t) { 94933707f3Ssthen case LDNS_RR_TYPE_MB: 95933707f3Ssthen case LDNS_RR_TYPE_MD: 96933707f3Ssthen case LDNS_RR_TYPE_MF: 97933707f3Ssthen case LDNS_RR_TYPE_NS: 98933707f3Ssthen case LDNS_RR_TYPE_MX: 99933707f3Ssthen case LDNS_RR_TYPE_KX: 100933707f3Ssthen case LDNS_RR_TYPE_SRV: 101933707f3Ssthen return 1; 102933707f3Ssthen case LDNS_RR_TYPE_NAPTR: 103933707f3Ssthen /* TODO: NAPTR not supported, glue stripped off */ 104933707f3Ssthen return 0; 105933707f3Ssthen } 106933707f3Ssthen return 0; 107933707f3Ssthen } 108933707f3Ssthen 109933707f3Ssthen /** get additional name from rrset RR, return false if no name present */ 110933707f3Ssthen static int 111933707f3Ssthen get_additional_name(struct rrset_parse* rrset, struct rr_parse* rr, 1125d76a658Ssthen uint8_t** nm, size_t* nmlen, sldns_buffer* pkt) 113933707f3Ssthen { 114933707f3Ssthen size_t offset = 0; 115933707f3Ssthen size_t len, oldpos; 116933707f3Ssthen switch(rrset->type) { 117933707f3Ssthen case LDNS_RR_TYPE_MB: 118933707f3Ssthen case LDNS_RR_TYPE_MD: 119933707f3Ssthen case LDNS_RR_TYPE_MF: 120933707f3Ssthen case LDNS_RR_TYPE_NS: 121933707f3Ssthen offset = 0; 122933707f3Ssthen break; 123933707f3Ssthen case LDNS_RR_TYPE_MX: 124933707f3Ssthen case LDNS_RR_TYPE_KX: 125933707f3Ssthen offset = 2; 126933707f3Ssthen break; 127933707f3Ssthen case LDNS_RR_TYPE_SRV: 128933707f3Ssthen offset = 6; 129933707f3Ssthen break; 130933707f3Ssthen case LDNS_RR_TYPE_NAPTR: 131933707f3Ssthen /* TODO: NAPTR not supported, glue stripped off */ 132933707f3Ssthen return 0; 133933707f3Ssthen default: 134933707f3Ssthen return 0; 135933707f3Ssthen } 1365d76a658Ssthen len = sldns_read_uint16(rr->ttl_data+sizeof(uint32_t)); 137933707f3Ssthen if(len < offset+1) 138933707f3Ssthen return 0; /* rdata field too small */ 139933707f3Ssthen *nm = rr->ttl_data+sizeof(uint32_t)+sizeof(uint16_t)+offset; 1405d76a658Ssthen oldpos = sldns_buffer_position(pkt); 1415d76a658Ssthen sldns_buffer_set_position(pkt, (size_t)(*nm - sldns_buffer_begin(pkt))); 142933707f3Ssthen *nmlen = pkt_dname_len(pkt); 1435d76a658Ssthen sldns_buffer_set_position(pkt, oldpos); 144933707f3Ssthen if(*nmlen == 0) 145933707f3Ssthen return 0; 146933707f3Ssthen return 1; 147933707f3Ssthen } 148933707f3Ssthen 149933707f3Ssthen /** Place mark on rrsets in additional section they are OK */ 150933707f3Ssthen static void 1515d76a658Ssthen mark_additional_rrset(sldns_buffer* pkt, struct msg_parse* msg, 152933707f3Ssthen struct rrset_parse* rrset) 153933707f3Ssthen { 154933707f3Ssthen /* Mark A and AAAA for NS as appropriate additional section info. */ 155933707f3Ssthen uint8_t* nm = NULL; 156933707f3Ssthen size_t nmlen = 0; 157933707f3Ssthen struct rr_parse* rr; 158933707f3Ssthen 159933707f3Ssthen if(!has_additional(rrset->type)) 160933707f3Ssthen return; 161933707f3Ssthen for(rr = rrset->rr_first; rr; rr = rr->next) { 162933707f3Ssthen if(get_additional_name(rrset, rr, &nm, &nmlen, pkt)) { 163933707f3Ssthen /* mark A */ 16477079be7Ssthen hashvalue_type h = pkt_hash_rrset(pkt, nm, 16577079be7Ssthen LDNS_RR_TYPE_A, rrset->rrset_class, 0); 166933707f3Ssthen struct rrset_parse* r = msgparse_hashtable_lookup( 167933707f3Ssthen msg, pkt, h, 0, nm, nmlen, 168933707f3Ssthen LDNS_RR_TYPE_A, rrset->rrset_class); 169933707f3Ssthen if(r && r->section == LDNS_SECTION_ADDITIONAL) { 170933707f3Ssthen r->flags |= RRSET_SCRUB_OK; 171933707f3Ssthen } 172933707f3Ssthen 173933707f3Ssthen /* mark AAAA */ 174933707f3Ssthen h = pkt_hash_rrset(pkt, nm, LDNS_RR_TYPE_AAAA, 175933707f3Ssthen rrset->rrset_class, 0); 176933707f3Ssthen r = msgparse_hashtable_lookup(msg, pkt, h, 0, nm, 177933707f3Ssthen nmlen, LDNS_RR_TYPE_AAAA, rrset->rrset_class); 178933707f3Ssthen if(r && r->section == LDNS_SECTION_ADDITIONAL) { 179933707f3Ssthen r->flags |= RRSET_SCRUB_OK; 180933707f3Ssthen } 181933707f3Ssthen } 182933707f3Ssthen } 183933707f3Ssthen } 184933707f3Ssthen 185933707f3Ssthen /** Get target name of a CNAME */ 186933707f3Ssthen static int 187933707f3Ssthen parse_get_cname_target(struct rrset_parse* rrset, uint8_t** sname, 18806a13c09Ssthen size_t* snamelen, sldns_buffer* pkt) 189933707f3Ssthen { 19006a13c09Ssthen size_t oldpos, dlen; 191933707f3Ssthen if(rrset->rr_count != 1) { 192933707f3Ssthen struct rr_parse* sig; 193933707f3Ssthen verbose(VERB_ALGO, "Found CNAME rrset with " 194933707f3Ssthen "size > 1: %u", (unsigned)rrset->rr_count); 195933707f3Ssthen /* use the first CNAME! */ 196933707f3Ssthen rrset->rr_count = 1; 197933707f3Ssthen rrset->size = rrset->rr_first->size; 198933707f3Ssthen for(sig=rrset->rrsig_first; sig; sig=sig->next) 199933707f3Ssthen rrset->size += sig->size; 200933707f3Ssthen rrset->rr_last = rrset->rr_first; 201933707f3Ssthen rrset->rr_first->next = NULL; 202933707f3Ssthen } 203933707f3Ssthen if(rrset->rr_first->size < sizeof(uint16_t)+1) 204933707f3Ssthen return 0; /* CNAME rdata too small */ 205933707f3Ssthen *sname = rrset->rr_first->ttl_data + sizeof(uint32_t) 206933707f3Ssthen + sizeof(uint16_t); /* skip ttl, rdatalen */ 207933707f3Ssthen *snamelen = rrset->rr_first->size - sizeof(uint16_t); 20806a13c09Ssthen 20906a13c09Ssthen if(rrset->rr_first->outside_packet) { 21006a13c09Ssthen if(!dname_valid(*sname, *snamelen)) 21106a13c09Ssthen return 0; 21206a13c09Ssthen return 1; 21306a13c09Ssthen } 21406a13c09Ssthen oldpos = sldns_buffer_position(pkt); 21506a13c09Ssthen sldns_buffer_set_position(pkt, (size_t)(*sname - sldns_buffer_begin(pkt))); 21606a13c09Ssthen dlen = pkt_dname_len(pkt); 21706a13c09Ssthen sldns_buffer_set_position(pkt, oldpos); 21806a13c09Ssthen if(dlen == 0) 21906a13c09Ssthen return 0; /* parse fail on the rdata name */ 22006a13c09Ssthen *snamelen = dlen; 221933707f3Ssthen return 1; 222933707f3Ssthen } 223933707f3Ssthen 224933707f3Ssthen /** Synthesize CNAME from DNAME, false if too long */ 225933707f3Ssthen static int 226933707f3Ssthen synth_cname(uint8_t* qname, size_t qnamelen, struct rrset_parse* dname_rrset, 2275d76a658Ssthen uint8_t* alias, size_t* aliaslen, sldns_buffer* pkt) 228933707f3Ssthen { 229933707f3Ssthen /* we already know that sname is a strict subdomain of DNAME owner */ 230933707f3Ssthen uint8_t* dtarg = NULL; 231933707f3Ssthen size_t dtarglen; 23206a13c09Ssthen if(!parse_get_cname_target(dname_rrset, &dtarg, &dtarglen, pkt)) 233933707f3Ssthen return 0; 234ebf5bb73Ssthen if(qnamelen <= dname_rrset->dname_len) 235ebf5bb73Ssthen return 0; 236ebf5bb73Ssthen if(qnamelen == 0) 237ebf5bb73Ssthen return 0; 238933707f3Ssthen log_assert(qnamelen > dname_rrset->dname_len); 239933707f3Ssthen /* DNAME from com. to net. with qname example.com. -> example.net. */ 240933707f3Ssthen /* so: \3com\0 to \3net\0 and qname \7example\3com\0 */ 241933707f3Ssthen *aliaslen = qnamelen + dtarglen - dname_rrset->dname_len; 242933707f3Ssthen if(*aliaslen > LDNS_MAX_DOMAINLEN) 243933707f3Ssthen return 0; /* should have been RCODE YXDOMAIN */ 244933707f3Ssthen /* decompress dnames into buffer, we know it fits */ 245933707f3Ssthen dname_pkt_copy(pkt, alias, qname); 246933707f3Ssthen dname_pkt_copy(pkt, alias+(qnamelen-dname_rrset->dname_len), dtarg); 247933707f3Ssthen return 1; 248933707f3Ssthen } 249933707f3Ssthen 250933707f3Ssthen /** synthesize a CNAME rrset */ 251933707f3Ssthen static struct rrset_parse* 252933707f3Ssthen synth_cname_rrset(uint8_t** sname, size_t* snamelen, uint8_t* alias, 253933707f3Ssthen size_t aliaslen, struct regional* region, struct msg_parse* msg, 254933707f3Ssthen struct rrset_parse* rrset, struct rrset_parse* prev, 2555d76a658Ssthen struct rrset_parse* nx, sldns_buffer* pkt) 256933707f3Ssthen { 257933707f3Ssthen struct rrset_parse* cn = (struct rrset_parse*)regional_alloc(region, 258933707f3Ssthen sizeof(struct rrset_parse)); 259933707f3Ssthen if(!cn) 260933707f3Ssthen return NULL; 261933707f3Ssthen memset(cn, 0, sizeof(*cn)); 262933707f3Ssthen cn->rr_first = (struct rr_parse*)regional_alloc(region, 263933707f3Ssthen sizeof(struct rr_parse)); 264933707f3Ssthen if(!cn->rr_first) 265933707f3Ssthen return NULL; 266933707f3Ssthen cn->rr_last = cn->rr_first; 267933707f3Ssthen /* CNAME from sname to alias */ 268933707f3Ssthen cn->dname = (uint8_t*)regional_alloc(region, *snamelen); 269933707f3Ssthen if(!cn->dname) 270933707f3Ssthen return NULL; 271933707f3Ssthen dname_pkt_copy(pkt, cn->dname, *sname); 272933707f3Ssthen cn->dname_len = *snamelen; 273933707f3Ssthen cn->type = LDNS_RR_TYPE_CNAME; 274933707f3Ssthen cn->section = rrset->section; 275933707f3Ssthen cn->rrset_class = rrset->rrset_class; 276933707f3Ssthen cn->rr_count = 1; 277933707f3Ssthen cn->size = sizeof(uint16_t) + aliaslen; 278933707f3Ssthen cn->hash=pkt_hash_rrset(pkt, cn->dname, cn->type, cn->rrset_class, 0); 279933707f3Ssthen /* allocate TTL + rdatalen + uncompressed dname */ 280933707f3Ssthen memset(cn->rr_first, 0, sizeof(struct rr_parse)); 281933707f3Ssthen cn->rr_first->outside_packet = 1; 282933707f3Ssthen cn->rr_first->ttl_data = (uint8_t*)regional_alloc(region, 283933707f3Ssthen sizeof(uint32_t)+sizeof(uint16_t)+aliaslen); 284933707f3Ssthen if(!cn->rr_first->ttl_data) 285933707f3Ssthen return NULL; 286f46c52bfSsthen memmove(cn->rr_first->ttl_data, rrset->rr_first->ttl_data, 287f46c52bfSsthen sizeof(uint32_t)); /* RFC6672: synth CNAME TTL == DNAME TTL */ 2885d76a658Ssthen sldns_write_uint16(cn->rr_first->ttl_data+4, aliaslen); 289933707f3Ssthen memmove(cn->rr_first->ttl_data+6, alias, aliaslen); 290933707f3Ssthen cn->rr_first->size = sizeof(uint16_t)+aliaslen; 291933707f3Ssthen 292933707f3Ssthen /* link it in */ 293933707f3Ssthen cn->rrset_all_next = nx; 294933707f3Ssthen if(prev) 295933707f3Ssthen prev->rrset_all_next = cn; 296933707f3Ssthen else msg->rrset_first = cn; 297933707f3Ssthen if(nx == NULL) 298933707f3Ssthen msg->rrset_last = cn; 299933707f3Ssthen msg->rrset_count ++; 300933707f3Ssthen msg->an_rrsets++; 301933707f3Ssthen /* it is not inserted in the msg hashtable. */ 302933707f3Ssthen 303933707f3Ssthen *sname = cn->rr_first->ttl_data + sizeof(uint32_t)+sizeof(uint16_t); 304933707f3Ssthen *snamelen = aliaslen; 305933707f3Ssthen return cn; 306933707f3Ssthen } 307933707f3Ssthen 308933707f3Ssthen /** check if DNAME applies to a name */ 309933707f3Ssthen static int 3105d76a658Ssthen pkt_strict_sub(sldns_buffer* pkt, uint8_t* sname, uint8_t* dr) 311933707f3Ssthen { 312933707f3Ssthen uint8_t buf1[LDNS_MAX_DOMAINLEN+1]; 313933707f3Ssthen uint8_t buf2[LDNS_MAX_DOMAINLEN+1]; 314933707f3Ssthen /* decompress names */ 315933707f3Ssthen dname_pkt_copy(pkt, buf1, sname); 316933707f3Ssthen dname_pkt_copy(pkt, buf2, dr); 317933707f3Ssthen return dname_strict_subdomain_c(buf1, buf2); 318933707f3Ssthen } 319933707f3Ssthen 320933707f3Ssthen /** check subdomain with decompression */ 321933707f3Ssthen static int 3225d76a658Ssthen pkt_sub(sldns_buffer* pkt, uint8_t* comprname, uint8_t* zone) 323933707f3Ssthen { 324933707f3Ssthen uint8_t buf[LDNS_MAX_DOMAINLEN+1]; 325933707f3Ssthen dname_pkt_copy(pkt, buf, comprname); 326933707f3Ssthen return dname_subdomain_c(buf, zone); 327933707f3Ssthen } 328933707f3Ssthen 329933707f3Ssthen /** check subdomain with decompression, compressed is parent */ 330933707f3Ssthen static int 3315d76a658Ssthen sub_of_pkt(sldns_buffer* pkt, uint8_t* zone, uint8_t* comprname) 332933707f3Ssthen { 333933707f3Ssthen uint8_t buf[LDNS_MAX_DOMAINLEN+1]; 334933707f3Ssthen dname_pkt_copy(pkt, buf, comprname); 335933707f3Ssthen return dname_subdomain_c(zone, buf); 336933707f3Ssthen } 337933707f3Ssthen 3383150e5f6Ssthen /** Check if there are SOA records in the authority section (negative) */ 3393150e5f6Ssthen static int 3403150e5f6Ssthen soa_in_auth(struct msg_parse* msg) 3413150e5f6Ssthen { 3423150e5f6Ssthen struct rrset_parse* rrset; 3433150e5f6Ssthen for(rrset = msg->rrset_first; rrset; rrset = rrset->rrset_all_next) 3443150e5f6Ssthen if(rrset->type == LDNS_RR_TYPE_SOA && 3453150e5f6Ssthen rrset->section == LDNS_SECTION_AUTHORITY) 3463150e5f6Ssthen return 1; 3473150e5f6Ssthen return 0; 3483150e5f6Ssthen } 3493150e5f6Ssthen 3508b7325afSsthen /** Check if type is allowed in the authority section */ 3518b7325afSsthen static int 3528b7325afSsthen type_allowed_in_authority_section(uint16_t tp) 3538b7325afSsthen { 3548b7325afSsthen if(tp == LDNS_RR_TYPE_SOA || tp == LDNS_RR_TYPE_NS || 3558b7325afSsthen tp == LDNS_RR_TYPE_DS || tp == LDNS_RR_TYPE_NSEC || 3568b7325afSsthen tp == LDNS_RR_TYPE_NSEC3) 3578b7325afSsthen return 1; 3588b7325afSsthen return 0; 3598b7325afSsthen } 3608b7325afSsthen 3618b7325afSsthen /** Check if type is allowed in the additional section */ 3628b7325afSsthen static int 3638b7325afSsthen type_allowed_in_additional_section(uint16_t tp) 3648b7325afSsthen { 3658b7325afSsthen if(tp == LDNS_RR_TYPE_A || tp == LDNS_RR_TYPE_AAAA) 3668b7325afSsthen return 1; 3678b7325afSsthen return 0; 3688b7325afSsthen } 3698b7325afSsthen 370*98bc733bSsthen /** Shorten RRset */ 371*98bc733bSsthen static void 372*98bc733bSsthen shorten_rrset(sldns_buffer* pkt, struct rrset_parse* rrset, int count) 373*98bc733bSsthen { 374*98bc733bSsthen /* The too large NS RRset is shortened. This is so that too large 375*98bc733bSsthen * content does not overwhelm the cache. It may make the rrset 376*98bc733bSsthen * bogus if it was signed, and then the domain is not resolved any 377*98bc733bSsthen * more, that is okay, the NS RRset was too large. During a referral 378*98bc733bSsthen * it can be shortened and then the first part of the list could 379*98bc733bSsthen * be used to resolve. The scrub continues to disallow glue for the 380*98bc733bSsthen * removed nameserver RRs and removes that too. Because the glue 381*98bc733bSsthen * is not marked as okay, since the RRs have been removed here. */ 382*98bc733bSsthen int i; 383*98bc733bSsthen struct rr_parse* rr = rrset->rr_first, *prev = NULL; 384*98bc733bSsthen if(!rr) 385*98bc733bSsthen return; 386*98bc733bSsthen for(i=0; i<count; i++) { 387*98bc733bSsthen prev = rr; 388*98bc733bSsthen rr = rr->next; 389*98bc733bSsthen if(!rr) 390*98bc733bSsthen return; /* The RRset is already short. */ 391*98bc733bSsthen } 392*98bc733bSsthen if(verbosity >= VERB_QUERY 393*98bc733bSsthen && rrset->dname_len <= LDNS_MAX_DOMAINLEN) { 394*98bc733bSsthen uint8_t buf[LDNS_MAX_DOMAINLEN+1]; 395*98bc733bSsthen dname_pkt_copy(pkt, buf, rrset->dname); 396*98bc733bSsthen log_nametypeclass(VERB_QUERY, "normalize: shorten RRset:", buf, 397*98bc733bSsthen rrset->type, ntohs(rrset->rrset_class)); 398*98bc733bSsthen } 399*98bc733bSsthen /* remove further rrs */ 400*98bc733bSsthen rrset->rr_last = prev; 401*98bc733bSsthen rrset->rr_count = count; 402*98bc733bSsthen while(rr) { 403*98bc733bSsthen rrset->size -= rr->size; 404*98bc733bSsthen rr = rr->next; 405*98bc733bSsthen } 406*98bc733bSsthen if(rrset->rr_last) 407*98bc733bSsthen rrset->rr_last->next = NULL; 408*98bc733bSsthen else rrset->rr_first = NULL; 409*98bc733bSsthen } 410*98bc733bSsthen 411933707f3Ssthen /** 412933707f3Ssthen * This routine normalizes a response. This includes removing "irrelevant" 413933707f3Ssthen * records from the answer and additional sections and (re)synthesizing 414933707f3Ssthen * CNAMEs from DNAMEs, if present. 415933707f3Ssthen * 416933707f3Ssthen * @param pkt: packet. 417933707f3Ssthen * @param msg: msg to normalize. 418933707f3Ssthen * @param qinfo: original query. 419933707f3Ssthen * @param region: where to allocate synthesized CNAMEs. 4208b7325afSsthen * @param env: module env with config options. 421933707f3Ssthen * @return 0 on error. 422933707f3Ssthen */ 423933707f3Ssthen static int 4245d76a658Ssthen scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg, 4258b7325afSsthen struct query_info* qinfo, struct regional* region, 4268b7325afSsthen struct module_env* env) 427933707f3Ssthen { 428933707f3Ssthen uint8_t* sname = qinfo->qname; 429933707f3Ssthen size_t snamelen = qinfo->qname_len; 430933707f3Ssthen struct rrset_parse* rrset, *prev, *nsset=NULL; 431*98bc733bSsthen int cname_length = 0; /* number of CNAMEs, or DNAMEs */ 432933707f3Ssthen 433933707f3Ssthen if(FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NOERROR && 434933707f3Ssthen FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NXDOMAIN) 435933707f3Ssthen return 1; 436933707f3Ssthen 437933707f3Ssthen /* For the ANSWER section, remove all "irrelevant" records and add 438933707f3Ssthen * synthesized CNAMEs from DNAMEs 439933707f3Ssthen * This will strip out-of-order CNAMEs as well. */ 440933707f3Ssthen 441933707f3Ssthen /* walk through the parse packet rrset list, keep track of previous 442933707f3Ssthen * for insert and delete ease, and examine every RRset */ 443933707f3Ssthen prev = NULL; 444933707f3Ssthen rrset = msg->rrset_first; 445933707f3Ssthen while(rrset && rrset->section == LDNS_SECTION_ANSWER) { 446*98bc733bSsthen if(cname_length > 11 /* env->cfg.iter_scrub_cname */) { 447*98bc733bSsthen /* Too many CNAMEs, or DNAMEs, from the authority 448*98bc733bSsthen * server, scrub down the length to something 449*98bc733bSsthen * shorter. This deletes everything after the limit 450*98bc733bSsthen * is reached. The iterator is going to look up 451*98bc733bSsthen * the content one by one anyway. */ 452*98bc733bSsthen remove_rrset("normalize: removing because too many cnames:", 453*98bc733bSsthen pkt, msg, prev, &rrset); 454*98bc733bSsthen continue; 455*98bc733bSsthen } 456933707f3Ssthen if(rrset->type == LDNS_RR_TYPE_DNAME && 457933707f3Ssthen pkt_strict_sub(pkt, sname, rrset->dname)) { 458933707f3Ssthen /* check if next rrset is correct CNAME. else, 459933707f3Ssthen * synthesize a CNAME */ 460933707f3Ssthen struct rrset_parse* nx = rrset->rrset_all_next; 461933707f3Ssthen uint8_t alias[LDNS_MAX_DOMAINLEN+1]; 462933707f3Ssthen size_t aliaslen = 0; 463933707f3Ssthen if(rrset->rr_count != 1) { 464933707f3Ssthen verbose(VERB_ALGO, "Found DNAME rrset with " 465933707f3Ssthen "size > 1: %u", 466933707f3Ssthen (unsigned)rrset->rr_count); 467933707f3Ssthen return 0; 468933707f3Ssthen } 469933707f3Ssthen if(!synth_cname(sname, snamelen, rrset, alias, 470933707f3Ssthen &aliaslen, pkt)) { 471933707f3Ssthen verbose(VERB_ALGO, "synthesized CNAME " 472933707f3Ssthen "too long"); 473933707f3Ssthen return 0; 474933707f3Ssthen } 475*98bc733bSsthen cname_length++; 476933707f3Ssthen if(nx && nx->type == LDNS_RR_TYPE_CNAME && 477933707f3Ssthen dname_pkt_compare(pkt, sname, nx->dname) == 0) { 478933707f3Ssthen /* check next cname */ 479933707f3Ssthen uint8_t* t = NULL; 480933707f3Ssthen size_t tlen = 0; 48106a13c09Ssthen if(!parse_get_cname_target(nx, &t, &tlen, pkt)) 482933707f3Ssthen return 0; 483933707f3Ssthen if(dname_pkt_compare(pkt, alias, t) == 0) { 484933707f3Ssthen /* it's OK and better capitalized */ 485933707f3Ssthen prev = rrset; 486933707f3Ssthen rrset = nx; 487933707f3Ssthen continue; 488933707f3Ssthen } 489933707f3Ssthen /* synth ourselves */ 490933707f3Ssthen } 491933707f3Ssthen /* synth a CNAME rrset */ 492933707f3Ssthen prev = synth_cname_rrset(&sname, &snamelen, alias, 493933707f3Ssthen aliaslen, region, msg, rrset, rrset, nx, pkt); 494933707f3Ssthen if(!prev) { 495933707f3Ssthen log_err("out of memory synthesizing CNAME"); 496933707f3Ssthen return 0; 497933707f3Ssthen } 498933707f3Ssthen /* FIXME: resolve the conflict between synthesized 499933707f3Ssthen * CNAME ttls and the cache. */ 500933707f3Ssthen rrset = nx; 501933707f3Ssthen continue; 502933707f3Ssthen 503933707f3Ssthen } 504933707f3Ssthen 505933707f3Ssthen /* The only records in the ANSWER section not allowed to */ 506933707f3Ssthen if(dname_pkt_compare(pkt, sname, rrset->dname) != 0) { 507933707f3Ssthen remove_rrset("normalize: removing irrelevant RRset:", 508933707f3Ssthen pkt, msg, prev, &rrset); 509933707f3Ssthen continue; 510933707f3Ssthen } 511933707f3Ssthen 512933707f3Ssthen /* Follow the CNAME chain. */ 513933707f3Ssthen if(rrset->type == LDNS_RR_TYPE_CNAME) { 51424893edcSsthen struct rrset_parse* nx = rrset->rrset_all_next; 515933707f3Ssthen uint8_t* oldsname = sname; 516*98bc733bSsthen cname_length++; 51724893edcSsthen /* see if the next one is a DNAME, if so, swap them */ 51824893edcSsthen if(nx && nx->section == LDNS_SECTION_ANSWER && 51924893edcSsthen nx->type == LDNS_RR_TYPE_DNAME && 52024893edcSsthen nx->rr_count == 1 && 52124893edcSsthen pkt_strict_sub(pkt, sname, nx->dname)) { 52224893edcSsthen /* there is a DNAME after this CNAME, it 52324893edcSsthen * is in the ANSWER section, and the DNAME 52424893edcSsthen * applies to the name we cover */ 52524893edcSsthen /* check if the alias of the DNAME equals 52624893edcSsthen * this CNAME */ 52724893edcSsthen uint8_t alias[LDNS_MAX_DOMAINLEN+1]; 52824893edcSsthen size_t aliaslen = 0; 52924893edcSsthen uint8_t* t = NULL; 53024893edcSsthen size_t tlen = 0; 53124893edcSsthen if(synth_cname(sname, snamelen, nx, alias, 53224893edcSsthen &aliaslen, pkt) && 53306a13c09Ssthen parse_get_cname_target(rrset, &t, &tlen, pkt) && 53424893edcSsthen dname_pkt_compare(pkt, alias, t) == 0) { 53524893edcSsthen /* the synthesized CNAME equals the 53624893edcSsthen * current CNAME. This CNAME is the 53724893edcSsthen * one that the DNAME creates, and this 53824893edcSsthen * CNAME is better capitalised */ 53924893edcSsthen verbose(VERB_ALGO, "normalize: re-order of DNAME and its CNAME"); 54024893edcSsthen if(prev) prev->rrset_all_next = nx; 54124893edcSsthen else msg->rrset_first = nx; 54224893edcSsthen if(nx->rrset_all_next == NULL) 54324893edcSsthen msg->rrset_last = rrset; 54424893edcSsthen rrset->rrset_all_next = 54524893edcSsthen nx->rrset_all_next; 54624893edcSsthen nx->rrset_all_next = rrset; 547452a1548Ssthen /* prev = nx; unused, enable if there 548452a1548Ssthen * is other rrset removal code after 549452a1548Ssthen * this */ 55024893edcSsthen } 55124893edcSsthen } 55224893edcSsthen 55324893edcSsthen /* move to next name in CNAME chain */ 55406a13c09Ssthen if(!parse_get_cname_target(rrset, &sname, &snamelen, pkt)) 555933707f3Ssthen return 0; 556933707f3Ssthen prev = rrset; 557933707f3Ssthen rrset = rrset->rrset_all_next; 558933707f3Ssthen /* in CNAME ANY response, can have data after CNAME */ 559933707f3Ssthen if(qinfo->qtype == LDNS_RR_TYPE_ANY) { 560933707f3Ssthen while(rrset && rrset->section == 561933707f3Ssthen LDNS_SECTION_ANSWER && 562933707f3Ssthen dname_pkt_compare(pkt, oldsname, 563933707f3Ssthen rrset->dname) == 0) { 564*98bc733bSsthen if(rrset->type == LDNS_RR_TYPE_NS && 565*98bc733bSsthen rrset->rr_count > 20 /* env->cfg->iter_scrub_ns */) { 566*98bc733bSsthen shorten_rrset(pkt, rrset, 20 /* env->cfg->iter_scrub_ns */); 567*98bc733bSsthen } 568933707f3Ssthen prev = rrset; 569933707f3Ssthen rrset = rrset->rrset_all_next; 570933707f3Ssthen } 571933707f3Ssthen } 572933707f3Ssthen continue; 573933707f3Ssthen } 574933707f3Ssthen 575933707f3Ssthen /* Otherwise, make sure that the RRset matches the qtype. */ 576933707f3Ssthen if(qinfo->qtype != LDNS_RR_TYPE_ANY && 577933707f3Ssthen qinfo->qtype != rrset->type) { 578933707f3Ssthen remove_rrset("normalize: removing irrelevant RRset:", 579933707f3Ssthen pkt, msg, prev, &rrset); 580933707f3Ssthen continue; 581933707f3Ssthen } 582933707f3Ssthen 583*98bc733bSsthen if(rrset->type == LDNS_RR_TYPE_NS && 584*98bc733bSsthen rrset->rr_count > 20 /* env->cfg->iter_scrub_ns */) { 585*98bc733bSsthen shorten_rrset(pkt, rrset, 20 /* env->cfg->iter_scrub_ns */); 586*98bc733bSsthen } 587*98bc733bSsthen 588933707f3Ssthen /* Mark the additional names from relevant rrset as OK. */ 589933707f3Ssthen /* only for RRsets that match the query name, other ones 590933707f3Ssthen * will be removed by sanitize, so no additional for them */ 591933707f3Ssthen if(dname_pkt_compare(pkt, qinfo->qname, rrset->dname) == 0) 592933707f3Ssthen mark_additional_rrset(pkt, msg, rrset); 593933707f3Ssthen 594933707f3Ssthen prev = rrset; 595933707f3Ssthen rrset = rrset->rrset_all_next; 596933707f3Ssthen } 597933707f3Ssthen 598933707f3Ssthen /* Mark additional names from AUTHORITY */ 599933707f3Ssthen while(rrset && rrset->section == LDNS_SECTION_AUTHORITY) { 6008b7325afSsthen /* protect internals of recursor by making sure to del these */ 601933707f3Ssthen if(rrset->type==LDNS_RR_TYPE_DNAME || 602933707f3Ssthen rrset->type==LDNS_RR_TYPE_CNAME || 603933707f3Ssthen rrset->type==LDNS_RR_TYPE_A || 604933707f3Ssthen rrset->type==LDNS_RR_TYPE_AAAA) { 605933707f3Ssthen remove_rrset("normalize: removing irrelevant " 606933707f3Ssthen "RRset:", pkt, msg, prev, &rrset); 607933707f3Ssthen continue; 608933707f3Ssthen } 6098b7325afSsthen /* Allowed list of types in the authority section */ 6108b7325afSsthen if(env->cfg->harden_unknown_additional && 6118b7325afSsthen !type_allowed_in_authority_section(rrset->type)) { 6128b7325afSsthen remove_rrset("normalize: removing irrelevant " 6138b7325afSsthen "RRset:", pkt, msg, prev, &rrset); 6148b7325afSsthen continue; 6158b7325afSsthen } 616933707f3Ssthen /* only one NS set allowed in authority section */ 617933707f3Ssthen if(rrset->type==LDNS_RR_TYPE_NS) { 618933707f3Ssthen /* NS set must be pertinent to the query */ 619933707f3Ssthen if(!sub_of_pkt(pkt, qinfo->qname, rrset->dname)) { 620933707f3Ssthen remove_rrset("normalize: removing irrelevant " 621933707f3Ssthen "RRset:", pkt, msg, prev, &rrset); 622933707f3Ssthen continue; 623933707f3Ssthen } 6243150e5f6Ssthen /* we don't want NS sets for NXDOMAIN answers, 6253150e5f6Ssthen * because they could contain poisonous contents, 6263150e5f6Ssthen * from. eg. fragmentation attacks, inserted after 6273150e5f6Ssthen * long RRSIGs in the packet get to the packet 6283150e5f6Ssthen * border and such */ 6293150e5f6Ssthen /* also for NODATA answers */ 6303150e5f6Ssthen if(FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NXDOMAIN || 6313150e5f6Ssthen (FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NOERROR 6323150e5f6Ssthen && soa_in_auth(msg) && msg->an_rrsets == 0)) { 6333150e5f6Ssthen remove_rrset("normalize: removing irrelevant " 6343150e5f6Ssthen "RRset:", pkt, msg, prev, &rrset); 6353150e5f6Ssthen continue; 6363150e5f6Ssthen } 637933707f3Ssthen if(nsset == NULL) { 638933707f3Ssthen nsset = rrset; 639933707f3Ssthen } else { 640933707f3Ssthen remove_rrset("normalize: removing irrelevant " 641933707f3Ssthen "RRset:", pkt, msg, prev, &rrset); 642933707f3Ssthen continue; 643933707f3Ssthen } 644*98bc733bSsthen if(rrset->rr_count > 20 /* env->cfg->iter_scrub_ns */) { 645*98bc733bSsthen /* If this is not a referral, and the NS RRset 646*98bc733bSsthen * is signed, then remove it entirely, so 647*98bc733bSsthen * that when it becomes bogus it does not 648*98bc733bSsthen * make the message that is otherwise fine 649*98bc733bSsthen * into a bogus message. */ 650*98bc733bSsthen if(!(msg->an_rrsets == 0 && 651*98bc733bSsthen FLAGS_GET_RCODE(msg->flags) == 652*98bc733bSsthen LDNS_RCODE_NOERROR && 653*98bc733bSsthen !soa_in_auth(msg) && 654*98bc733bSsthen !(msg->flags & BIT_AA)) && 655*98bc733bSsthen rrset->rrsig_count != 0) { 656*98bc733bSsthen remove_rrset("normalize: removing too large NS " 657*98bc733bSsthen "RRset:", pkt, msg, prev, &rrset); 658*98bc733bSsthen continue; 659*98bc733bSsthen } else { 660*98bc733bSsthen shorten_rrset(pkt, rrset, 20 /* env->cfg->iter_scrub_ns */); 661*98bc733bSsthen } 662*98bc733bSsthen } 663933707f3Ssthen } 664bdfc4d55Sflorian /* if this is type DS and we query for type DS we just got 665bdfc4d55Sflorian * a referral answer for our type DS query, fix packet */ 666bdfc4d55Sflorian if(rrset->type==LDNS_RR_TYPE_DS && 667bdfc4d55Sflorian qinfo->qtype == LDNS_RR_TYPE_DS && 668bdfc4d55Sflorian dname_pkt_compare(pkt, qinfo->qname, rrset->dname) == 0) { 669bdfc4d55Sflorian rrset->section = LDNS_SECTION_ANSWER; 670bdfc4d55Sflorian msg->ancount = rrset->rr_count + rrset->rrsig_count; 671bdfc4d55Sflorian msg->nscount = 0; 672bdfc4d55Sflorian msg->arcount = 0; 673bdfc4d55Sflorian msg->an_rrsets = 1; 674bdfc4d55Sflorian msg->ns_rrsets = 0; 675bdfc4d55Sflorian msg->ar_rrsets = 0; 676bdfc4d55Sflorian msg->rrset_count = 1; 677bdfc4d55Sflorian msg->rrset_first = rrset; 678bdfc4d55Sflorian msg->rrset_last = rrset; 679bdfc4d55Sflorian rrset->rrset_all_next = NULL; 680bdfc4d55Sflorian return 1; 681bdfc4d55Sflorian } 682933707f3Ssthen mark_additional_rrset(pkt, msg, rrset); 683933707f3Ssthen prev = rrset; 684933707f3Ssthen rrset = rrset->rrset_all_next; 685933707f3Ssthen } 686933707f3Ssthen 687933707f3Ssthen /* For each record in the additional section, remove it if it is an 688933707f3Ssthen * address record and not in the collection of additional names 689933707f3Ssthen * found in ANSWER and AUTHORITY. */ 690933707f3Ssthen /* These records have not been marked OK previously */ 691933707f3Ssthen while(rrset && rrset->section == LDNS_SECTION_ADDITIONAL) { 692933707f3Ssthen if(rrset->type==LDNS_RR_TYPE_A || 693933707f3Ssthen rrset->type==LDNS_RR_TYPE_AAAA) 694933707f3Ssthen { 695933707f3Ssthen if((rrset->flags & RRSET_SCRUB_OK)) { 696933707f3Ssthen /* remove flag to clean up flags variable */ 697933707f3Ssthen rrset->flags &= ~RRSET_SCRUB_OK; 698933707f3Ssthen } else { 699933707f3Ssthen remove_rrset("normalize: removing irrelevant " 700933707f3Ssthen "RRset:", pkt, msg, prev, &rrset); 701933707f3Ssthen continue; 702933707f3Ssthen } 703933707f3Ssthen } 7048b7325afSsthen /* protect internals of recursor by making sure to del these */ 705933707f3Ssthen if(rrset->type==LDNS_RR_TYPE_DNAME || 706933707f3Ssthen rrset->type==LDNS_RR_TYPE_CNAME || 707933707f3Ssthen rrset->type==LDNS_RR_TYPE_NS) { 708933707f3Ssthen remove_rrset("normalize: removing irrelevant " 709933707f3Ssthen "RRset:", pkt, msg, prev, &rrset); 710933707f3Ssthen continue; 711933707f3Ssthen } 7128b7325afSsthen /* Allowed list of types in the additional section */ 7138b7325afSsthen if(env->cfg->harden_unknown_additional && 7148b7325afSsthen !type_allowed_in_additional_section(rrset->type)) { 7158b7325afSsthen remove_rrset("normalize: removing irrelevant " 7168b7325afSsthen "RRset:", pkt, msg, prev, &rrset); 7178b7325afSsthen continue; 7188b7325afSsthen } 719933707f3Ssthen prev = rrset; 720933707f3Ssthen rrset = rrset->rrset_all_next; 721933707f3Ssthen } 722933707f3Ssthen 723933707f3Ssthen return 1; 724933707f3Ssthen } 725933707f3Ssthen 726933707f3Ssthen /** 727933707f3Ssthen * Store potential poison in the cache (only if hardening disabled). 728933707f3Ssthen * The rrset is stored in the cache but removed from the message. 729933707f3Ssthen * So that it will be used for infrastructure purposes, but not be 730933707f3Ssthen * returned to the client. 731933707f3Ssthen * @param pkt: packet 732933707f3Ssthen * @param msg: message parsed 733933707f3Ssthen * @param env: environment with cache 734933707f3Ssthen * @param rrset: to store. 735933707f3Ssthen */ 736933707f3Ssthen static void 7375d76a658Ssthen store_rrset(sldns_buffer* pkt, struct msg_parse* msg, struct module_env* env, 738933707f3Ssthen struct rrset_parse* rrset) 739933707f3Ssthen { 740933707f3Ssthen struct ub_packed_rrset_key* k; 741933707f3Ssthen struct packed_rrset_data* d; 742933707f3Ssthen struct rrset_ref ref; 743229e174cSsthen time_t now = *env->now; 744933707f3Ssthen 745933707f3Ssthen k = alloc_special_obtain(env->alloc); 746933707f3Ssthen if(!k) 747933707f3Ssthen return; 748933707f3Ssthen k->entry.data = NULL; 749933707f3Ssthen if(!parse_copy_decompress_rrset(pkt, msg, rrset, NULL, k)) { 750933707f3Ssthen alloc_special_release(env->alloc, k); 751933707f3Ssthen return; 752933707f3Ssthen } 753933707f3Ssthen d = (struct packed_rrset_data*)k->entry.data; 754933707f3Ssthen packed_rrset_ttl_add(d, now); 755933707f3Ssthen ref.key = k; 756933707f3Ssthen ref.id = k->id; 757933707f3Ssthen /*ignore ret: it was in the cache, ref updated */ 758933707f3Ssthen (void)rrset_cache_update(env->rrset_cache, &ref, env->alloc, now); 759933707f3Ssthen } 760933707f3Ssthen 761933707f3Ssthen /** 762933707f3Ssthen * Check if right hand name in NSEC is within zone 763191f22c6Ssthen * @param pkt: the packet buffer for decompression. 764933707f3Ssthen * @param rrset: the NSEC rrset 765933707f3Ssthen * @param zonename: the zone name. 766933707f3Ssthen * @return true if BAD. 767933707f3Ssthen */ 768191f22c6Ssthen static int sanitize_nsec_is_overreach(sldns_buffer* pkt, 769191f22c6Ssthen struct rrset_parse* rrset, uint8_t* zonename) 770933707f3Ssthen { 771933707f3Ssthen struct rr_parse* rr; 772933707f3Ssthen uint8_t* rhs; 773933707f3Ssthen size_t len; 774933707f3Ssthen log_assert(rrset->type == LDNS_RR_TYPE_NSEC); 775933707f3Ssthen for(rr = rrset->rr_first; rr; rr = rr->next) { 776191f22c6Ssthen size_t pos = sldns_buffer_position(pkt); 777191f22c6Ssthen size_t rhspos; 778933707f3Ssthen rhs = rr->ttl_data+4+2; 7795d76a658Ssthen len = sldns_read_uint16(rr->ttl_data+4); 780191f22c6Ssthen rhspos = rhs-sldns_buffer_begin(pkt); 781191f22c6Ssthen sldns_buffer_set_position(pkt, rhspos); 782191f22c6Ssthen if(pkt_dname_len(pkt) == 0) { 783191f22c6Ssthen /* malformed */ 784191f22c6Ssthen sldns_buffer_set_position(pkt, pos); 785933707f3Ssthen return 1; 786933707f3Ssthen } 787191f22c6Ssthen if(sldns_buffer_position(pkt)-rhspos > len) { 788191f22c6Ssthen /* outside of rdata boundaries */ 789191f22c6Ssthen sldns_buffer_set_position(pkt, pos); 790191f22c6Ssthen return 1; 791191f22c6Ssthen } 792191f22c6Ssthen sldns_buffer_set_position(pkt, pos); 793191f22c6Ssthen if(!pkt_sub(pkt, rhs, zonename)) { 794933707f3Ssthen /* overreaching */ 795933707f3Ssthen return 1; 796933707f3Ssthen } 797933707f3Ssthen } 798933707f3Ssthen /* all NSEC RRs OK */ 799933707f3Ssthen return 0; 800933707f3Ssthen } 801933707f3Ssthen 802d896b962Ssthen /** Remove individual RRs, if the length is wrong. Returns true if the RRset 803d896b962Ssthen * has been removed. */ 804d896b962Ssthen static int 805d896b962Ssthen scrub_sanitize_rr_length(sldns_buffer* pkt, struct msg_parse* msg, 806d896b962Ssthen struct rrset_parse* prev, struct rrset_parse** rrset, int* added_ede, 807d896b962Ssthen struct module_qstate* qstate) 808d896b962Ssthen { 809d896b962Ssthen struct rr_parse* rr, *rr_prev = NULL; 810d896b962Ssthen for(rr = (*rrset)->rr_first; rr; rr = rr->next) { 811d896b962Ssthen 812d896b962Ssthen /* Sanity check for length of records 813d896b962Ssthen * An A record should be 6 bytes only 814d896b962Ssthen * (2 bytes for length and 4 for IPv4 addr)*/ 815d896b962Ssthen if((*rrset)->type == LDNS_RR_TYPE_A && rr->size != 6 ) { 816d896b962Ssthen if(!*added_ede) { 817d896b962Ssthen *added_ede = 1; 818d896b962Ssthen errinf_ede(qstate, "sanitize: records of inappropriate length have been removed.", 819d896b962Ssthen LDNS_EDE_OTHER); 820d896b962Ssthen } 821d896b962Ssthen if(msgparse_rrset_remove_rr("sanitize: removing type A RR of inappropriate length:", 822d896b962Ssthen pkt, *rrset, rr_prev, rr, NULL, 0)) { 823d896b962Ssthen remove_rrset("sanitize: removing type A RRset of inappropriate length:", 824d896b962Ssthen pkt, msg, prev, rrset); 825d896b962Ssthen return 1; 826d896b962Ssthen } 827d896b962Ssthen continue; 828d896b962Ssthen } 829d896b962Ssthen 830d896b962Ssthen /* Sanity check for length of records 831d896b962Ssthen * An AAAA record should be 18 bytes only 832d896b962Ssthen * (2 bytes for length and 16 for IPv6 addr)*/ 833d896b962Ssthen if((*rrset)->type == LDNS_RR_TYPE_AAAA && rr->size != 18 ) { 834d896b962Ssthen if(!*added_ede) { 835d896b962Ssthen *added_ede = 1; 836d896b962Ssthen errinf_ede(qstate, "sanitize: records of inappropriate length have been removed.", 837d896b962Ssthen LDNS_EDE_OTHER); 838d896b962Ssthen } 839d896b962Ssthen if(msgparse_rrset_remove_rr("sanitize: removing type AAAA RR of inappropriate length:", 840d896b962Ssthen pkt, *rrset, rr_prev, rr, NULL, 0)) { 841d896b962Ssthen remove_rrset("sanitize: removing type AAAA RRset of inappropriate length:", 842d896b962Ssthen pkt, msg, prev, rrset); 843d896b962Ssthen return 1; 844d896b962Ssthen } 845d896b962Ssthen continue; 846d896b962Ssthen } 847d896b962Ssthen rr_prev = rr; 848d896b962Ssthen } 849d896b962Ssthen return 0; 850d896b962Ssthen } 851d896b962Ssthen 852933707f3Ssthen /** 853933707f3Ssthen * Given a response event, remove suspect RRsets from the response. 854933707f3Ssthen * "Suspect" rrsets are potentially poison. Note that this routine expects 855933707f3Ssthen * the response to be in a "normalized" state -- that is, all "irrelevant" 856933707f3Ssthen * RRsets have already been removed, CNAMEs are in order, etc. 857933707f3Ssthen * 858933707f3Ssthen * @param pkt: packet. 859933707f3Ssthen * @param msg: msg to normalize. 860933707f3Ssthen * @param qinfo: the question originally asked. 861933707f3Ssthen * @param zonename: name of server zone. 862933707f3Ssthen * @param env: module environment with config and cache. 863933707f3Ssthen * @param ie: iterator environment with private address data. 864d896b962Ssthen * @param qstate: for setting errinf for EDE error messages. 865933707f3Ssthen * @return 0 on error. 866933707f3Ssthen */ 867933707f3Ssthen static int 8685d76a658Ssthen scrub_sanitize(sldns_buffer* pkt, struct msg_parse* msg, 869933707f3Ssthen struct query_info* qinfo, uint8_t* zonename, struct module_env* env, 870d896b962Ssthen struct iter_env* ie, struct module_qstate* qstate) 871933707f3Ssthen { 872933707f3Ssthen int del_addi = 0; /* if additional-holding rrsets are deleted, we 873933707f3Ssthen do not trust the normalized additional-A-AAAA any more */ 874d896b962Ssthen int added_rrlen_ede = 0; 875933707f3Ssthen struct rrset_parse* rrset, *prev; 876933707f3Ssthen prev = NULL; 877933707f3Ssthen rrset = msg->rrset_first; 878933707f3Ssthen 879933707f3Ssthen /* the first DNAME is allowed to stay. It needs checking before 880933707f3Ssthen * it can be used from the cache. After normalization, an initial 881933707f3Ssthen * DNAME will have a correctly synthesized CNAME after it. */ 882933707f3Ssthen if(rrset && rrset->type == LDNS_RR_TYPE_DNAME && 883933707f3Ssthen rrset->section == LDNS_SECTION_ANSWER && 884933707f3Ssthen pkt_strict_sub(pkt, qinfo->qname, rrset->dname) && 885933707f3Ssthen pkt_sub(pkt, rrset->dname, zonename)) { 886933707f3Ssthen prev = rrset; /* DNAME allowed to stay in answer section */ 887933707f3Ssthen rrset = rrset->rrset_all_next; 888933707f3Ssthen } 889933707f3Ssthen 890933707f3Ssthen /* remove all records from the answer section that are 891933707f3Ssthen * not the same domain name as the query domain name. 892933707f3Ssthen * The answer section should contain rrsets with the same name 893933707f3Ssthen * as the question. For DNAMEs a CNAME has been synthesized. 894933707f3Ssthen * Wildcards have the query name in answer section. 895933707f3Ssthen * ANY queries get query name in answer section. 896933707f3Ssthen * Remainders of CNAME chains are cut off and resolved by iterator. */ 897933707f3Ssthen while(rrset && rrset->section == LDNS_SECTION_ANSWER) { 898933707f3Ssthen if(dname_pkt_compare(pkt, qinfo->qname, rrset->dname) != 0) { 899933707f3Ssthen if(has_additional(rrset->type)) del_addi = 1; 900933707f3Ssthen remove_rrset("sanitize: removing extraneous answer " 901933707f3Ssthen "RRset:", pkt, msg, prev, &rrset); 902933707f3Ssthen continue; 903933707f3Ssthen } 904933707f3Ssthen prev = rrset; 905933707f3Ssthen rrset = rrset->rrset_all_next; 906933707f3Ssthen } 907933707f3Ssthen 908933707f3Ssthen /* At this point, we brutally remove ALL rrsets that aren't 909933707f3Ssthen * children of the originating zone. The idea here is that, 910933707f3Ssthen * as far as we know, the server that we contacted is ONLY 911933707f3Ssthen * authoritative for the originating zone. It, of course, MAY 9124bfc71b0Ssthen * be authoritative for any other zones, and of course, MAY 913933707f3Ssthen * NOT be authoritative for some subdomains of the originating 914933707f3Ssthen * zone. */ 915933707f3Ssthen prev = NULL; 916933707f3Ssthen rrset = msg->rrset_first; 917933707f3Ssthen while(rrset) { 918933707f3Ssthen 919d896b962Ssthen /* Sanity check for length of records */ 920d896b962Ssthen if(rrset->type == LDNS_RR_TYPE_A || 921d896b962Ssthen rrset->type == LDNS_RR_TYPE_AAAA) { 922d896b962Ssthen if(scrub_sanitize_rr_length(pkt, msg, prev, &rrset, 923d896b962Ssthen &added_rrlen_ede, qstate)) 924d896b962Ssthen continue; 925d896b962Ssthen } 926d896b962Ssthen 927933707f3Ssthen /* remove private addresses */ 928933707f3Ssthen if( (rrset->type == LDNS_RR_TYPE_A || 929229e174cSsthen rrset->type == LDNS_RR_TYPE_AAAA)) { 930933707f3Ssthen 931933707f3Ssthen /* do not set servfail since this leads to too 932933707f3Ssthen * many drops of other people using rfc1918 space */ 933229e174cSsthen /* also do not remove entire rrset, unless all records 934229e174cSsthen * in it are bad */ 935229e174cSsthen if(priv_rrset_bad(ie->priv, pkt, rrset)) { 936229e174cSsthen remove_rrset(NULL, pkt, msg, prev, &rrset); 937933707f3Ssthen continue; 938933707f3Ssthen } 939229e174cSsthen } 940933707f3Ssthen 941933707f3Ssthen /* skip DNAME records -- they will always be followed by a 942933707f3Ssthen * synthesized CNAME, which will be relevant. 943933707f3Ssthen * FIXME: should this do something differently with DNAME 944933707f3Ssthen * rrsets NOT in Section.ANSWER? */ 945933707f3Ssthen /* But since DNAME records are also subdomains of the zone, 946933707f3Ssthen * same check can be used */ 947933707f3Ssthen 948933707f3Ssthen if(!pkt_sub(pkt, rrset->dname, zonename)) { 949933707f3Ssthen if(msg->an_rrsets == 0 && 950933707f3Ssthen rrset->type == LDNS_RR_TYPE_NS && 951933707f3Ssthen rrset->section == LDNS_SECTION_AUTHORITY && 952933707f3Ssthen FLAGS_GET_RCODE(msg->flags) == 953933707f3Ssthen LDNS_RCODE_NOERROR && !soa_in_auth(msg) && 954933707f3Ssthen sub_of_pkt(pkt, zonename, rrset->dname)) { 955933707f3Ssthen /* noerror, nodata and this NS rrset is above 956933707f3Ssthen * the zone. This is LAME! 957933707f3Ssthen * Leave in the NS for lame classification. */ 958933707f3Ssthen /* remove everything from the additional 959933707f3Ssthen * (we dont want its glue that was approved 960933707f3Ssthen * during the normalize action) */ 961933707f3Ssthen del_addi = 1; 9623e38fc85Sbrad } else if(!env->cfg->harden_glue && ( 9633e38fc85Sbrad rrset->type == LDNS_RR_TYPE_A || 9643e38fc85Sbrad rrset->type == LDNS_RR_TYPE_AAAA)) { 965933707f3Ssthen /* store in cache! Since it is relevant 966933707f3Ssthen * (from normalize) it will be picked up 967933707f3Ssthen * from the cache to be used later */ 968933707f3Ssthen store_rrset(pkt, msg, env, rrset); 969933707f3Ssthen remove_rrset("sanitize: storing potential " 970933707f3Ssthen "poison RRset:", pkt, msg, prev, &rrset); 971933707f3Ssthen continue; 972933707f3Ssthen } else { 973933707f3Ssthen if(has_additional(rrset->type)) del_addi = 1; 974933707f3Ssthen remove_rrset("sanitize: removing potential " 975933707f3Ssthen "poison RRset:", pkt, msg, prev, &rrset); 976933707f3Ssthen continue; 977933707f3Ssthen } 978933707f3Ssthen } 979933707f3Ssthen if(del_addi && rrset->section == LDNS_SECTION_ADDITIONAL) { 980933707f3Ssthen remove_rrset("sanitize: removing potential " 981933707f3Ssthen "poison reference RRset:", pkt, msg, prev, &rrset); 982933707f3Ssthen continue; 983933707f3Ssthen } 984933707f3Ssthen /* check if right hand side of NSEC is within zone */ 985933707f3Ssthen if(rrset->type == LDNS_RR_TYPE_NSEC && 986191f22c6Ssthen sanitize_nsec_is_overreach(pkt, rrset, zonename)) { 987933707f3Ssthen remove_rrset("sanitize: removing overreaching NSEC " 988933707f3Ssthen "RRset:", pkt, msg, prev, &rrset); 989933707f3Ssthen continue; 990933707f3Ssthen } 991933707f3Ssthen prev = rrset; 992933707f3Ssthen rrset = rrset->rrset_all_next; 993933707f3Ssthen } 994933707f3Ssthen return 1; 995933707f3Ssthen } 996933707f3Ssthen 997933707f3Ssthen int 9985d76a658Ssthen scrub_message(sldns_buffer* pkt, struct msg_parse* msg, 999933707f3Ssthen struct query_info* qinfo, uint8_t* zonename, struct regional* region, 1000d896b962Ssthen struct module_env* env, struct module_qstate* qstate, 1001d896b962Ssthen struct iter_env* ie) 1002933707f3Ssthen { 1003933707f3Ssthen /* basic sanity checks */ 1004933707f3Ssthen log_nametypeclass(VERB_ALGO, "scrub for", zonename, LDNS_RR_TYPE_NS, 1005933707f3Ssthen qinfo->qclass); 1006933707f3Ssthen if(msg->qdcount > 1) 1007933707f3Ssthen return 0; 1008933707f3Ssthen if( !(msg->flags&BIT_QR) ) 1009933707f3Ssthen return 0; 1010933707f3Ssthen msg->flags &= ~(BIT_AD|BIT_Z); /* force off bit AD and Z */ 1011933707f3Ssthen 1012933707f3Ssthen /* make sure that a query is echoed back when NOERROR or NXDOMAIN */ 1013933707f3Ssthen /* this is not required for basic operation but is a forgery 1014933707f3Ssthen * resistance (security) feature */ 1015933707f3Ssthen if((FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NOERROR || 1016933707f3Ssthen FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NXDOMAIN) && 1017933707f3Ssthen msg->qdcount == 0) 1018933707f3Ssthen return 0; 1019933707f3Ssthen 1020933707f3Ssthen /* if a query is echoed back, make sure it is correct. Otherwise, 1021933707f3Ssthen * this may be not a reply to our query. */ 1022933707f3Ssthen if(msg->qdcount == 1) { 1023933707f3Ssthen if(dname_pkt_compare(pkt, msg->qname, qinfo->qname) != 0) 1024933707f3Ssthen return 0; 1025933707f3Ssthen if(msg->qtype != qinfo->qtype || msg->qclass != qinfo->qclass) 1026933707f3Ssthen return 0; 1027933707f3Ssthen } 1028933707f3Ssthen 1029933707f3Ssthen /* normalize the response, this cleans up the additional. */ 10308b7325afSsthen if(!scrub_normalize(pkt, msg, qinfo, region, env)) 1031933707f3Ssthen return 0; 1032933707f3Ssthen /* delete all out-of-zone information */ 1033d896b962Ssthen if(!scrub_sanitize(pkt, msg, qinfo, zonename, env, ie, qstate)) 1034933707f3Ssthen return 0; 1035933707f3Ssthen return 1; 1036933707f3Ssthen } 1037