From a7d301333470b4d359bb884797543c7d0bd3defc Mon Sep 17 00:00:00 2001 From: Wouter Wijngaards Date: Thu, 31 May 2007 14:57:24 +0000 Subject: [PATCH] scrubber work. git-svn-id: file:///svn/unbound/trunk@353 be551aaa-1e26-0410-a405-d3ace91eadb9 --- iterator/iter_scrub.c | 165 ++++++++++++++++++++++++++++++++++++++++++ iterator/iter_scrub.h | 65 +++++++++++++++++ iterator/iterator.c | 63 ++++++++++++++-- 3 files changed, 286 insertions(+), 7 deletions(-) create mode 100644 iterator/iter_scrub.c create mode 100644 iterator/iter_scrub.h diff --git a/iterator/iter_scrub.c b/iterator/iter_scrub.c new file mode 100644 index 000000000..5d7151aaf --- /dev/null +++ b/iterator/iter_scrub.c @@ -0,0 +1,165 @@ +/* + * iterator/iter_scrub.c - scrubbing, normalization, sanitization of DNS msgs. + * + * Copyright (c) 2007, NLnet Labs. All rights reserved. + * + * This software is open source. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NLNET LABS nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file + * + * This file has routine(s) for cleaning up incoming DNS messages from + * possible useless or malicious junk in it. + */ +#include "config.h" +#include "iterator/iter_scrub.h" +#include "util/log.h" +#include "util/net_help.h" +#include "util/data/msgparse.h" +#include "util/data/dname.h" +#include "util/data/msgreply.h" + +/** + * This routine normalizes a response. This includes removing "irrelevant" + * records from the answer and additional sections and (re)synthesizing + * CNAMEs from DNAMEs, if present. + * + * @param pkt: packet. + * @param msg: msg to normalize. + * @param qinfo: original query. + * @param region: where to allocate synthesized CNAMEs. + * @return 0 on error. + */ +static int +scrub_normalize(ldns_buffer* pkt, struct msg_parse* msg, + struct query_info* qinfo, struct region* region) +{ + uint8_t* sname = qinfo->qname; + size_t snamelen = qinfo->qname_len; + struct rrset_parse* rrset, *prev=NULL; + + if(FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NOERROR && + FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NXDOMAIN) + return 1; + + /* For the ANSWER section, remove all "irrelevant" records and add + * synthesized CNAMEs from DNAMEs + * This will strip out-of-order CNAMEs as well. */ + + for(rrset = msg->rrset_first; rrset; (prev=rrset),(rrset=rrset->rrset_all_next)) + { + if(rrset->section != LDNS_SECTION_ANSWER) + continue; + if(rrset->type == LDNS_RR_TYPE_DNAME && + dname_strict_subdomain_c(sname, rrset->dname)) { + if(rrset->rr_count != 1) { + verbose(VERB_ALGO, "Found DNAME rrset with " + "size > 1: %d", rrset->rr_count); + return 0; + } + /* check if next rrset is correct CNAME. else, + * synthesize a CNAME */ + /* if nametoolong, return 0 (should been YXDOMAIN) */ + continue; + } + + /* The only records in the ANSWER section not allowed to */ + if(dname_pkt_compare(pkt, sname, rrset->dname) != 0) { + /* TODO remove_rrset(pkt, msg, prev, rrset); */ + continue; + } + + /* Follow the CNAME chain. */ + if(rrset->type == LDNS_RR_TYPE_CNAME) { + if(rrset->rr_count != 1) { + verbose(VERB_ALGO, "Found CNAME rrset with " + "size > 1: %d", rrset->rr_count); + return 0; + } + if(rrset->rr_first->size < sizeof(uint16_t)+1) + return 0; /* CNAME rdata too small */ + sname = rrset->rr_first->ttl_data + sizeof(uint32_t) + + sizeof(uint16_t); /* skip ttl, rdatalen */ + snamelen = rrset->rr_first->size - sizeof(uint16_t); + } + + /* Otherwise, make sure that the RRset matches the qtype. */ + if(qinfo->qtype != LDNS_RR_TYPE_ANY && + qinfo->qtype != rrset->type) { + /*TODO: remove_rrset(); */ + } + + /* Otherwise, fetch the additional names from the + * relevant rrset. */ + /* store additional names from rrset rdata */ + } + + /* Get additional names from AUTHORITY */ + /* go through authority section, store add. names from rrdat rdata */ + + /* For each record in the additional section, remove it if it is an + * address record and not in the collection of additional names + * found in ANSWER and AUTHORITY. */ + + /* FIXME: what about other types? */ + return 1; +} + +int +scrub_message(ldns_buffer* pkt, struct msg_parse* msg, + struct query_info* qinfo, uint8_t* zonename, size_t zonelen, + struct region* region) +{ + /* things to check: + * if qdcount > 0 : qinfo. + * from normalize() from NSclient. + * from sanitize() from iterator. + */ + /* basic sanity checks */ + if(msg->qdcount > 1) + return 0; + if( !(msg->flags&BIT_QR) ) + return 0; + + /* if a query is echoed back, make sure it is correct. Otherwise, + * this may be not a reply to our query. */ + if(msg->qdcount == 1) { + if(dname_pkt_compare(pkt, msg->qname, qinfo->qname) != 0) + return 0; + if(msg->qtype != qinfo->qtype || msg->qclass != qinfo->qclass) + return 0; + } + + /* normalize the response */ + if(!scrub_normalize(pkt, msg, qinfo, region)) + return 0; + return 1; +} diff --git a/iterator/iter_scrub.h b/iterator/iter_scrub.h new file mode 100644 index 000000000..8f32cea96 --- /dev/null +++ b/iterator/iter_scrub.h @@ -0,0 +1,65 @@ +/* + * iterator/iter_scrub.h - scrubbing, normalization, sanitization of DNS msgs. + * + * Copyright (c) 2007, NLnet Labs. All rights reserved. + * + * This software is open source. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NLNET LABS nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file + * + * This file has routine(s) for cleaning up incoming DNS messages from + * possible useless or malicious junk in it. + */ + +#ifndef ITERATOR_ITER_SCRUB_H +#define ITERATOR_ITER_SCRUB_H +struct msg_parse; +struct query_info; +struct region; + +/** + * Cleanup the passed dns message. + * @param pkt: the packet itself, for resolving name compression pointers. + * the packet buffer is unaltered. + * @param msg: the parsed packet, this structure is cleaned up. + * @param qinfo: the query info that was sent to the server. Checked. + * @param zonename: the name of the last delegation point. + * Used to determine out of bailiwick information. + * @param zonelen: length of zonename. + * @param region: where to allocate (new) parts of the message. + * @return: false if the message is total waste. true if scrubbed with success. + */ +int scrub_message(ldns_buffer* pkt, struct msg_parse* msg, + struct query_info* qinfo, uint8_t* zonename, size_t zonelen, + struct region* region); + +#endif /* ITERATOR_ITER_SCRUB_H */ diff --git a/iterator/iterator.c b/iterator/iterator.c index b6722319a..412b075ba 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -46,6 +46,7 @@ #include "iterator/iter_hints.h" #include "iterator/iter_delegpt.h" #include "iterator/iter_resptype.h" +#include "iterator/iter_scrub.h" #include "services/cache/dns.h" #include "util/module.h" #include "util/netevent.h" @@ -743,7 +744,7 @@ return nextState(event, req, state, IterEventState.INIT_REQUEST_STATE); * this query as the parent. So further processing for * this event will stop until reactivated by the results * of priming. */ - return false; + return 0; } /* Reset the RD flag. If this is a query restart, then the RD @@ -784,7 +785,7 @@ processInitRequest2(struct module_qstate* qstate, struct iter_qstate* iq, if(prime_stub(qstate, iq, ie, id, qstate->qinfo.qname, qstate->qinfo.qclass)) { /* A priming sub request was made */ - return false; + return 0; } /* most events just get forwarded to the next state. */ @@ -1042,7 +1043,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, else verbose(VERB_ALGO, "no current targets -- waiting " "for %d outstanding queries to respond.", iq->num_current_queries); - return false; + return 0; } /* We have a valid target. */ @@ -1060,6 +1061,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, } outbound_list_insert(&iq->outlist, outq); iq->num_current_queries++; + qstate->ext_state[id] = module_wait_reply; return 0; } @@ -1177,11 +1179,58 @@ process_request(struct module_qstate* qstate, struct iter_qstate* iq, /** process authoritative server reply */ static void process_response(struct module_qstate* qstate, struct iter_qstate* iq, - struct iter_env* ie, int id, struct outbound_entry* outbound) + struct iter_env* ie, int id, struct outbound_entry* outbound, + enum module_ev event) { + struct msg_parse* prs; + struct edns_data edns; + ldns_buffer* pkt; + verbose(VERB_ALGO, "process_response: new external response event"); - /* TODO outbound: use it for scrubbing and so on */ + iq->response = NULL; iq->state = QUERY_RESP_STATE; + if(event == module_event_timeout || event == module_event_error) { + goto handle_it; + } + if(event != module_event_reply || !qstate->reply) { + log_err("Bad event combined with response"); + outbound_list_remove(&iq->outlist, outbound); + qstate->ext_state[id] = module_error; + return; + } + + /* parse message */ + prs = (struct msg_parse*)region_alloc(qstate->scratch, + sizeof(struct msg_parse)); + if(!prs) { + log_err("out of memory on incoming message"); + /* like packet got dropped */ + goto handle_it; + } + memset(prs, 0, sizeof(*prs)); + memset(&edns, 0, sizeof(edns)); + pkt = qstate->reply->c->buffer; + ldns_buffer_set_position(pkt, 0); + if(!parse_packet(pkt, prs, qstate->scratch)) + goto handle_it; + /* edns is not examined, but removed from message to help cache */ + if(!parse_extract_edns(prs, &edns)) + goto handle_it; + + /* normalize and sanitize: easy to delete items from linked lists */ + if(!scrub_message(pkt, prs, &qstate->qinfo, + iq->dp->name, iq->dp->namelen, qstate->scratch)) + goto handle_it; + + /* allocate response dns_msg in region */ + /* TODO: + iq->response = dns_parse_to_msg(prs, qstate->region); + */ + if(!iq->response) + goto handle_it; + +handle_it: + outbound_list_remove(&iq->outlist, outbound); iter_handle(qstate, iq, ie, id); } @@ -1213,8 +1262,8 @@ iter_operate(struct module_qstate* qstate, enum module_ev event, int id, iter_handle(qstate, iq, ie, id); return; } - if(event == module_event_reply) { - process_response(qstate, iq, ie, id, outbound); + if(outbound) { + process_response(qstate, iq, ie, id, outbound, event); return; } /* TODO: uhh */ -- 2.47.2