1/* 2 * iterator/iter_scrub.c - scrubbing, normalization, sanitization of DNS msgs. 3 * 4 * Copyright (c) 2007, NLnet Labs. All rights reserved. 5 * 6 * This software is open source. 7 * 8 * Redistribution and use in source and binary forms, with or without --- 7 unchanged lines hidden (view full) --- 16 * this list of conditions and the following disclaimer in the documentation 17 * and/or other materials provided with the distribution. 18 * 19 * Neither the name of the NLNET LABS nor the names of its contributors may 20 * be used to endorse or promote products derived from this software without 21 * specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 29 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
34 */ 35 36/** 37 * \file 38 * 39 * This file has routine(s) for cleaning up incoming DNS messages from 40 * possible useless or malicious junk in it. 41 */ --- 6 unchanged lines hidden (view full) --- 48#include "util/net_help.h" 49#include "util/regional.h" 50#include "util/config_file.h" 51#include "util/module.h" 52#include "util/data/msgparse.h" 53#include "util/data/dname.h" 54#include "util/data/msgreply.h" 55#include "util/alloc.h" |
56#include "ldns/sbuffer.h" |
57 58/** RRset flag used during scrubbing. The RRset is OK. */ 59#define RRSET_SCRUB_OK 0x80 60 61/** remove rrset, update loop variables */ 62static void |
63remove_rrset(const char* str, sldns_buffer* pkt, struct msg_parse* msg, |
64 struct rrset_parse* prev, struct rrset_parse** rrset) 65{ |
66 if(verbosity >= VERB_QUERY && str |
67 && (*rrset)->dname_len <= LDNS_MAX_DOMAINLEN) { 68 uint8_t buf[LDNS_MAX_DOMAINLEN+1]; 69 dname_pkt_copy(pkt, buf, (*rrset)->dname); 70 log_nametypeclass(VERB_QUERY, str, buf, 71 (*rrset)->type, ntohs((*rrset)->rrset_class)); 72 } 73 if(prev) 74 prev->rrset_all_next = (*rrset)->rrset_all_next; --- 29 unchanged lines hidden (view full) --- 104 return 0; 105 } 106 return 0; 107} 108 109/** get additional name from rrset RR, return false if no name present */ 110static int 111get_additional_name(struct rrset_parse* rrset, struct rr_parse* rr, |
112 uint8_t** nm, size_t* nmlen, sldns_buffer* pkt) |
113{ 114 size_t offset = 0; 115 size_t len, oldpos; 116 switch(rrset->type) { 117 case LDNS_RR_TYPE_MB: 118 case LDNS_RR_TYPE_MD: 119 case LDNS_RR_TYPE_MF: 120 case LDNS_RR_TYPE_NS: --- 7 unchanged lines hidden (view full) --- 128 offset = 6; 129 break; 130 case LDNS_RR_TYPE_NAPTR: 131 /* TODO: NAPTR not supported, glue stripped off */ 132 return 0; 133 default: 134 return 0; 135 } |
136 len = sldns_read_uint16(rr->ttl_data+sizeof(uint32_t)); |
137 if(len < offset+1) 138 return 0; /* rdata field too small */ 139 *nm = rr->ttl_data+sizeof(uint32_t)+sizeof(uint16_t)+offset; |
140 oldpos = sldns_buffer_position(pkt); 141 sldns_buffer_set_position(pkt, (size_t)(*nm - sldns_buffer_begin(pkt))); |
142 *nmlen = pkt_dname_len(pkt); |
143 sldns_buffer_set_position(pkt, oldpos); |
144 if(*nmlen == 0) 145 return 0; 146 return 1; 147} 148 149/** Place mark on rrsets in additional section they are OK */ 150static void |
151mark_additional_rrset(sldns_buffer* pkt, struct msg_parse* msg, |
152 struct rrset_parse* rrset) 153{ 154 /* Mark A and AAAA for NS as appropriate additional section info. */ 155 uint8_t* nm = NULL; 156 size_t nmlen = 0; 157 struct rr_parse* rr; 158 159 if(!has_additional(rrset->type)) --- 45 unchanged lines hidden (view full) --- 205 + sizeof(uint16_t); /* skip ttl, rdatalen */ 206 *snamelen = rrset->rr_first->size - sizeof(uint16_t); 207 return 1; 208} 209 210/** Synthesize CNAME from DNAME, false if too long */ 211static int 212synth_cname(uint8_t* qname, size_t qnamelen, struct rrset_parse* dname_rrset, |
213 uint8_t* alias, size_t* aliaslen, sldns_buffer* pkt) |
214{ 215 /* we already know that sname is a strict subdomain of DNAME owner */ 216 uint8_t* dtarg = NULL; 217 size_t dtarglen; 218 if(!parse_get_cname_target(dname_rrset, &dtarg, &dtarglen)) 219 return 0; 220 log_assert(qnamelen > dname_rrset->dname_len); 221 /* DNAME from com. to net. with qname example.com. -> example.net. */ --- 7 unchanged lines hidden (view full) --- 229 return 1; 230} 231 232/** synthesize a CNAME rrset */ 233static struct rrset_parse* 234synth_cname_rrset(uint8_t** sname, size_t* snamelen, uint8_t* alias, 235 size_t aliaslen, struct regional* region, struct msg_parse* msg, 236 struct rrset_parse* rrset, struct rrset_parse* prev, |
237 struct rrset_parse* nx, sldns_buffer* pkt) |
238{ 239 struct rrset_parse* cn = (struct rrset_parse*)regional_alloc(region, 240 sizeof(struct rrset_parse)); 241 if(!cn) 242 return NULL; 243 memset(cn, 0, sizeof(*cn)); 244 cn->rr_first = (struct rr_parse*)regional_alloc(region, 245 sizeof(struct rr_parse)); --- 14 unchanged lines hidden (view full) --- 260 cn->hash=pkt_hash_rrset(pkt, cn->dname, cn->type, cn->rrset_class, 0); 261 /* allocate TTL + rdatalen + uncompressed dname */ 262 memset(cn->rr_first, 0, sizeof(struct rr_parse)); 263 cn->rr_first->outside_packet = 1; 264 cn->rr_first->ttl_data = (uint8_t*)regional_alloc(region, 265 sizeof(uint32_t)+sizeof(uint16_t)+aliaslen); 266 if(!cn->rr_first->ttl_data) 267 return NULL; |
268 sldns_write_uint32(cn->rr_first->ttl_data, 0); /* TTL = 0 */ 269 sldns_write_uint16(cn->rr_first->ttl_data+4, aliaslen); |
270 memmove(cn->rr_first->ttl_data+6, alias, aliaslen); 271 cn->rr_first->size = sizeof(uint16_t)+aliaslen; 272 273 /* link it in */ 274 cn->rrset_all_next = nx; 275 if(prev) 276 prev->rrset_all_next = cn; 277 else msg->rrset_first = cn; --- 5 unchanged lines hidden (view full) --- 283 284 *sname = cn->rr_first->ttl_data + sizeof(uint32_t)+sizeof(uint16_t); 285 *snamelen = aliaslen; 286 return cn; 287} 288 289/** check if DNAME applies to a name */ 290static int |
291pkt_strict_sub(sldns_buffer* pkt, uint8_t* sname, uint8_t* dr) |
292{ 293 uint8_t buf1[LDNS_MAX_DOMAINLEN+1]; 294 uint8_t buf2[LDNS_MAX_DOMAINLEN+1]; 295 /* decompress names */ 296 dname_pkt_copy(pkt, buf1, sname); 297 dname_pkt_copy(pkt, buf2, dr); 298 return dname_strict_subdomain_c(buf1, buf2); 299} 300 301/** check subdomain with decompression */ 302static int |
303pkt_sub(sldns_buffer* pkt, uint8_t* comprname, uint8_t* zone) |
304{ 305 uint8_t buf[LDNS_MAX_DOMAINLEN+1]; 306 dname_pkt_copy(pkt, buf, comprname); 307 return dname_subdomain_c(buf, zone); 308} 309 310/** check subdomain with decompression, compressed is parent */ 311static int |
312sub_of_pkt(sldns_buffer* pkt, uint8_t* zone, uint8_t* comprname) |
313{ 314 uint8_t buf[LDNS_MAX_DOMAINLEN+1]; 315 dname_pkt_copy(pkt, buf, comprname); 316 return dname_subdomain_c(zone, buf); 317} 318 319/** 320 * This routine normalizes a response. This includes removing "irrelevant" 321 * records from the answer and additional sections and (re)synthesizing 322 * CNAMEs from DNAMEs, if present. 323 * 324 * @param pkt: packet. 325 * @param msg: msg to normalize. 326 * @param qinfo: original query. 327 * @param region: where to allocate synthesized CNAMEs. 328 * @return 0 on error. 329 */ 330static int |
331scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg, |
332 struct query_info* qinfo, struct regional* region) 333{ 334 uint8_t* sname = qinfo->qname; 335 size_t snamelen = qinfo->qname_len; 336 struct rrset_parse* rrset, *prev, *nsset=NULL; 337 338 if(FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NOERROR && 339 FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NXDOMAIN) --- 170 unchanged lines hidden (view full) --- 510 * So that it will be used for infrastructure purposes, but not be 511 * returned to the client. 512 * @param pkt: packet 513 * @param msg: message parsed 514 * @param env: environment with cache 515 * @param rrset: to store. 516 */ 517static void |
518store_rrset(sldns_buffer* pkt, struct msg_parse* msg, struct module_env* env, |
519 struct rrset_parse* rrset) 520{ 521 struct ub_packed_rrset_key* k; 522 struct packed_rrset_data* d; 523 struct rrset_ref ref; |
524 time_t now = *env->now; |
525 526 k = alloc_special_obtain(env->alloc); 527 if(!k) 528 return; 529 k->entry.data = NULL; 530 if(!parse_copy_decompress_rrset(pkt, msg, rrset, NULL, k)) { 531 alloc_special_release(env->alloc, k); 532 return; --- 28 unchanged lines hidden (view full) --- 561 uint8_t* zonename) 562{ 563 struct rr_parse* rr; 564 uint8_t* rhs; 565 size_t len; 566 log_assert(rrset->type == LDNS_RR_TYPE_NSEC); 567 for(rr = rrset->rr_first; rr; rr = rr->next) { 568 rhs = rr->ttl_data+4+2; |
569 len = sldns_read_uint16(rr->ttl_data+4); |
570 if(!dname_valid(rhs, len)) { 571 /* malformed domain name in rdata */ 572 return 1; 573 } 574 if(!dname_subdomain_c(rhs, zonename)) { 575 /* overreaching */ 576 return 1; 577 } --- 12 unchanged lines hidden (view full) --- 590 * @param msg: msg to normalize. 591 * @param qinfo: the question originally asked. 592 * @param zonename: name of server zone. 593 * @param env: module environment with config and cache. 594 * @param ie: iterator environment with private address data. 595 * @return 0 on error. 596 */ 597static int |
598scrub_sanitize(sldns_buffer* pkt, struct msg_parse* msg, |
599 struct query_info* qinfo, uint8_t* zonename, struct module_env* env, 600 struct iter_env* ie) 601{ 602 int del_addi = 0; /* if additional-holding rrsets are deleted, we 603 do not trust the normalized additional-A-AAAA any more */ 604 struct rrset_parse* rrset, *prev; 605 prev = NULL; 606 rrset = msg->rrset_first; --- 35 unchanged lines hidden (view full) --- 642 * NOT be authoritative for some subdomains of the originating 643 * zone. */ 644 prev = NULL; 645 rrset = msg->rrset_first; 646 while(rrset) { 647 648 /* remove private addresses */ 649 if( (rrset->type == LDNS_RR_TYPE_A || |
650 rrset->type == LDNS_RR_TYPE_AAAA)) { |
651 652 /* do not set servfail since this leads to too 653 * many drops of other people using rfc1918 space */ |
654 /* also do not remove entire rrset, unless all records 655 * in it are bad */ 656 if(priv_rrset_bad(ie->priv, pkt, rrset)) { 657 remove_rrset(NULL, pkt, msg, prev, &rrset); 658 continue; 659 } |
660 } 661 662 /* skip DNAME records -- they will always be followed by a 663 * synthesized CNAME, which will be relevant. 664 * FIXME: should this do something differently with DNAME 665 * rrsets NOT in Section.ANSWER? */ 666 /* But since DNAME records are also subdomains of the zone, 667 * same check can be used */ --- 41 unchanged lines hidden (view full) --- 709 } 710 prev = rrset; 711 rrset = rrset->rrset_all_next; 712 } 713 return 1; 714} 715 716int |
717scrub_message(sldns_buffer* pkt, struct msg_parse* msg, |
718 struct query_info* qinfo, uint8_t* zonename, struct regional* region, 719 struct module_env* env, struct iter_env* ie) 720{ 721 /* basic sanity checks */ 722 log_nametypeclass(VERB_ALGO, "scrub for", zonename, LDNS_RR_TYPE_NS, 723 qinfo->qclass); 724 if(msg->qdcount > 1) 725 return 0; --- 29 unchanged lines hidden --- |