From: Wouter Wijngaards Date: Fri, 20 Jul 2007 09:12:06 +0000 (+0000) Subject: Option to disable sanitize (normalize is still active), speeds up. But X-Git-Tag: release-0.4~23 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=84b0f323b5154f6742d1d1ab3b24c3ab14130fd4;p=thirdparty%2Funbound.git Option to disable sanitize (normalize is still active), speeds up. But some spoofing scenarios become possible. Default is the safe method. git-svn-id: file:///svn/unbound/trunk@443 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/doc/example.conf b/doc/example.conf index d18f0d38f..def4e1bca 100644 --- a/doc/example.conf +++ b/doc/example.conf @@ -139,6 +139,9 @@ server: # Harden against unseemly large queries. # harden-large-queries: no + # Harden against out of zone rrsets, to avoid spoofing attempts. + # harden-glue: yes + # Do not query the following addresses. No DNS queries are sent there. # List one address per entry. To block other ports than the default # DNS port, use "1.2.3.4@123" to block port 123 for 1.2.3.4. diff --git a/doc/unbound.conf.5 b/doc/unbound.conf.5 index 9d55faee4..0dc2d87df 100644 --- a/doc/unbound.conf.5 +++ b/doc/unbound.conf.5 @@ -146,6 +146,8 @@ small answers to these queries, where possible. Very large queries are ignored. Default is off, since it is legal protocol wise to send these, and could be necessary for operation if TSIG or EDNS payload is very large. +.It \fBharden-glue:\fR +Will trust glue only if it is within the servers authority. Default is on. .It \fBdo-not-query-address:\fR Do not query the given IP address. Can be IP4 or IP6. By default the DNS port is blocked for that address. Appending "@portnumber" will block diff --git a/iterator/iter_scrub.c b/iterator/iter_scrub.c index 7c2841174..4fb9ada5e 100644 --- a/iterator/iter_scrub.c +++ b/iterator/iter_scrub.c @@ -41,12 +41,16 @@ */ #include "config.h" #include "iterator/iter_scrub.h" +#include "services/cache/rrset.h" #include "util/log.h" #include "util/net_help.h" #include "util/region-allocator.h" +#include "util/config_file.h" +#include "util/module.h" #include "util/data/msgparse.h" #include "util/data/dname.h" #include "util/data/msgreply.h" +#include "util/alloc.h" /** RRset flag used during scrubbing. The RRset is OK. */ #define RRSET_SCRUB_OK 0x80 @@ -437,6 +441,42 @@ scrub_normalize(ldns_buffer* pkt, struct msg_parse* msg, return 1; } +/** + * Store potential poison in the cache (only if hardening disabled). + * The rrset is stored in the cache but removed from the message. + * So that it will be used for infrastructure purposes, but not be + * returned to the client. + * @param pkt: packet + * @param msg: message parsed + * @param env: environment with cache + * @param rrset: to store. + */ +static void +store_rrset(ldns_buffer* pkt, struct msg_parse* msg, struct module_env* env, + struct rrset_parse* rrset) +{ + struct ub_packed_rrset_key* k; + struct packed_rrset_data* d; + struct rrset_ref ref; + uint32_t now = time(NULL); + + k = alloc_special_obtain(env->alloc); + if(!k) + return; + k->entry.data = NULL; + if(!parse_copy_decompress_rrset(pkt, msg, rrset, NULL, k)) { + alloc_special_release(env->alloc, k); + return; + } + d = (struct packed_rrset_data*)k->entry.data; + packed_rrset_ttl_add(d, now); + ref.key = k; + ref.id = k->id; + /*ignore ret: it was in the cache, ref updated */ + (void)rrset_cache_update(env->rrset_cache, &ref, + env->alloc, now); +} + /** * Given a response event, remove suspect RRsets from the response. * "Suspect" rrsets are potentially poison. Note that this routine expects @@ -446,10 +486,12 @@ scrub_normalize(ldns_buffer* pkt, struct msg_parse* msg, * @param pkt: packet. * @param msg: msg to normalize. * @param zonename: name of server zone. + * @param env: module environment with config and cache. * @return 0 on error. */ static int -scrub_sanitize(ldns_buffer* pkt, struct msg_parse* msg, uint8_t* zonename) +scrub_sanitize(ldns_buffer* pkt, struct msg_parse* msg, uint8_t* zonename, + struct module_env* env) { struct rrset_parse* rrset, *prev; prev = NULL; @@ -472,8 +514,16 @@ scrub_sanitize(ldns_buffer* pkt, struct msg_parse* msg, uint8_t* zonename) * same check can be used */ if(!pkt_sub(pkt, rrset->dname, zonename)) { - remove_rrset("sanitize: removing potential poison " - "RRset:", pkt, msg, prev, &rrset); + if(!env->cfg->harden_glue) { + /* store in cache! Since it is relevant + * (from normalize) it will be picked up + * from the cache to be used later */ + store_rrset(pkt, msg, env, rrset); + remove_rrset("sanitize: storing potential " + "poison RRset:", pkt, msg, prev, &rrset); + } else + remove_rrset("sanitize: removing potential " + "poison RRset:", pkt, msg, prev, &rrset); continue; } prev = rrset; @@ -484,7 +534,8 @@ scrub_sanitize(ldns_buffer* pkt, struct msg_parse* msg, uint8_t* zonename) int scrub_message(ldns_buffer* pkt, struct msg_parse* msg, - struct query_info* qinfo, uint8_t* zonename, struct region* region) + struct query_info* qinfo, uint8_t* zonename, struct region* region, + struct module_env* env) { /* basic sanity checks */ log_nametypeclass(VERB_ALGO, "scrub for", zonename, LDNS_RR_TYPE_NS, @@ -507,7 +558,7 @@ scrub_message(ldns_buffer* pkt, struct msg_parse* msg, if(!scrub_normalize(pkt, msg, qinfo, region)) return 0; /* delete all out-of-zone information */ - if(!scrub_sanitize(pkt, msg, zonename)) + if(!scrub_sanitize(pkt, msg, zonename, env)) return 0; return 1; } diff --git a/iterator/iter_scrub.h b/iterator/iter_scrub.h index 68f3ce9b2..0ac5768bf 100644 --- a/iterator/iter_scrub.h +++ b/iterator/iter_scrub.h @@ -45,6 +45,7 @@ struct msg_parse; struct query_info; struct region; +struct module_env; /** * Cleanup the passed dns message. @@ -55,9 +56,11 @@ struct region; * @param zonename: the name of the last delegation point. * Used to determine out of bailiwick information. * @param region: where to allocate (new) parts of the message. + * @param env: module environment with config settings and cache. * @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, struct region* region); + struct query_info* qinfo, uint8_t* zonename, struct region* region, + struct module_env* env); #endif /* ITERATOR_ITER_SCRUB_H */ diff --git a/iterator/iterator.c b/iterator/iterator.c index f6d6b9b9d..1d4769873 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -1480,7 +1480,7 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq, /* normalize and sanitize: easy to delete items from linked lists */ if(!scrub_message(pkt, prs, &iq->qchase, iq->dp->name, - qstate->env->scratch)) + qstate->env->scratch, qstate->env)) goto handle_it; /* allocate response dns_msg in region */ diff --git a/util/config_file.c b/util/config_file.c index a4ab1cf44..ef5a9b225 100644 --- a/util/config_file.c +++ b/util/config_file.c @@ -106,6 +106,7 @@ config_create() cfg->forwards = NULL; cfg->harden_short_bufsize = 0; cfg->harden_large_queries = 0; + cfg->harden_glue = 1; cfg->hide_identity = 0; cfg->hide_version = 0; cfg->identity = NULL; diff --git a/util/config_file.h b/util/config_file.h index f0a799661..037695d13 100644 --- a/util/config_file.h +++ b/util/config_file.h @@ -113,6 +113,8 @@ struct config_file { int harden_short_bufsize; /** harden against very large query sizes */ int harden_large_queries; + /** harden against spoofed glue (out of zone data) */ + int harden_glue; /** chrootdir, if not "" or chroot will be done */ char* chrootdir; diff --git a/util/configlexer.lex b/util/configlexer.lex index 5631c0478..1c6594f87 100644 --- a/util/configlexer.lex +++ b/util/configlexer.lex @@ -128,6 +128,7 @@ num-queries-per-thread{COLON} { YDOUT; return VAR_NUM_QUERIES_PER_THREAD;} target-fetch-policy{COLON} { YDOUT; return VAR_TARGET_FETCH_POLICY;} harden-short-bufsize{COLON} { YDOUT; return VAR_HARDEN_SHORT_BUFSIZE;} harden-large-queries{COLON} { YDOUT; return VAR_HARDEN_LARGE_QUERIES;} +harden-glue{COLON} { YDOUT; return VAR_HARDEN_GLUE;} stub-zone{COLON} { YDOUT; return VAR_STUB_ZONE;} name{COLON} { YDOUT; return VAR_NAME;} stub-addr{COLON} { YDOUT; return VAR_STUB_ADDR;} diff --git a/util/configparser.y b/util/configparser.y index 31b982dd5..56b94412f 100644 --- a/util/configparser.y +++ b/util/configparser.y @@ -79,7 +79,7 @@ extern struct config_parser_state* cfg_parser; %token VAR_HARDEN_SHORT_BUFSIZE VAR_HARDEN_LARGE_QUERIES %token VAR_FORWARD_ZONE VAR_FORWARD_HOST VAR_FORWARD_ADDR %token VAR_DO_NOT_QUERY_ADDRESS VAR_HIDE_IDENTITY VAR_HIDE_VERSION -%token VAR_IDENTITY VAR_VERSION +%token VAR_IDENTITY VAR_VERSION VAR_HARDEN_GLUE %% toplevelvars: /* empty */ | toplevelvars toplevelvar ; @@ -111,7 +111,8 @@ content_server: server_num_threads | server_verbosity | server_port | server_infra_cache_numlame | server_target_fetch_policy | server_harden_short_bufsize | server_harden_large_queries | server_do_not_query_address | server_hide_identity | - server_hide_version | server_identity | server_version + server_hide_version | server_identity | server_version | + server_harden_glue ; stubstart: VAR_STUB_ZONE { @@ -444,6 +445,16 @@ server_harden_large_queries: VAR_HARDEN_LARGE_QUERIES STRING free($2); } ; +server_harden_glue: VAR_HARDEN_GLUE STRING + { + OUTYY(("P(server_harden_glue:%s)\n", $2)); + if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) + yyerror("expected yes or no."); + else cfg_parser->cfg->harden_glue = + (strcmp($2, "yes")==0); + free($2); + } + ; server_do_not_query_address: VAR_DO_NOT_QUERY_ADDRESS STRING { OUTYY(("P(server_do_not_query_address:%s)\n", $2)); diff --git a/util/data/msgreply.c b/util/data/msgreply.c index c634b4e0b..64b0c9ef2 100644 --- a/util/data/msgreply.c +++ b/util/data/msgreply.c @@ -281,19 +281,13 @@ parse_create_rrset(ldns_buffer* pkt, struct rrset_parse* pset, /** get trust value for rrset */ static enum rrset_trust -get_rrset_trust(struct reply_info* rep, size_t i) +get_rrset_trust(struct msg_parse* msg, struct rrset_parse* rrset) { - uint16_t AA = rep->flags & BIT_AA; - /* TODO: need scrubber that knows what zone the server serves, so that - * it can check if AA bit is warranted. - * it can check if rrset_trust_nonauth_ans_AA should be used */ - if(i < rep->an_numrrsets) { - /* answer section */ + uint16_t AA = msg->flags & BIT_AA; + if(rrset->section == LDNS_SECTION_ANSWER) { if(AA) return rrset_trust_ans_AA; else return rrset_trust_ans_noAA; - - } else if(i < rep->an_numrrsets+rep->ns_numrrsets) { - /* authority section */ + } else if(rrset->section == LDNS_SECTION_AUTHORITY) { if(AA) return rrset_trust_auth_AA; else return rrset_trust_auth_noAA; } else { @@ -304,6 +298,36 @@ get_rrset_trust(struct reply_info* rep, size_t i) return rrset_trust_none; } +int +parse_copy_decompress_rrset(ldns_buffer* pkt, struct msg_parse* msg, + struct rrset_parse *pset, struct region* region, + struct ub_packed_rrset_key* pk) +{ + struct packed_rrset_data* data; + pk->rk.flags = pset->flags; + pk->rk.dname_len = pset->dname_len; + if(region) + pk->rk.dname = (uint8_t*)region_alloc( + region, pset->dname_len); + else pk->rk.dname = + (uint8_t*)malloc(pset->dname_len); + if(!pk->rk.dname) + return 0; + /** copy & decompress dname */ + dname_pkt_copy(pkt, pk->rk.dname, pset->dname); + /** copy over type and class */ + pk->rk.type = htons(pset->type); + pk->rk.rrset_class = pset->rrset_class; + /** read data part. */ + if(!parse_create_rrset(pkt, pset, &data, region)) + return 0; + pk->entry.data = (void*)data; + pk->entry.key = (void*)pk; + pk->entry.hash = pset->hash; + data->trust = get_rrset_trust(msg, pset); + return 1; +} + /** * Copy and decompress rrs * @param pkt: the packet for compression pointer resolution. @@ -325,27 +349,10 @@ parse_copy_decompress(ldns_buffer* pkt, struct msg_parse* msg, rep->ttl = NORR_TTL; for(i=0; irrset_count; i++) { - rep->rrsets[i]->rk.flags = pset->flags; - rep->rrsets[i]->rk.dname_len = pset->dname_len; - if(region) - rep->rrsets[i]->rk.dname = (uint8_t*)region_alloc( - region, pset->dname_len); - else rep->rrsets[i]->rk.dname = - (uint8_t*)malloc(pset->dname_len); - if(!rep->rrsets[i]->rk.dname) - return 0; - /** copy & decompress dname */ - dname_pkt_copy(pkt, rep->rrsets[i]->rk.dname, pset->dname); - /** copy over type and class */ - rep->rrsets[i]->rk.type = htons(pset->type); - rep->rrsets[i]->rk.rrset_class = pset->rrset_class; - /** read data part. */ - if(!parse_create_rrset(pkt, pset, &data, region)) + if(!parse_copy_decompress_rrset(pkt, msg, pset, region, + rep->rrsets[i])) return 0; - rep->rrsets[i]->entry.data = (void*)data; - rep->rrsets[i]->entry.key = (void*)rep->rrsets[i]; - rep->rrsets[i]->entry.hash = pset->hash; - data->trust = get_rrset_trust(rep, i); + data = (struct packed_rrset_data*)rep->rrsets[i]->entry.data; if(data->ttl < rep->ttl) rep->ttl = data->ttl; diff --git a/util/data/msgreply.h b/util/data/msgreply.h index 04f471e21..c75afe36d 100644 --- a/util/data/msgreply.h +++ b/util/data/msgreply.h @@ -49,6 +49,7 @@ struct iovec; struct region; struct edns_data; struct msg_parse; +struct rrset_parse; /** * Structure to store query information that makes answers to queries @@ -290,6 +291,21 @@ struct msgreply_entry* query_info_entrysetup(struct query_info* q, struct reply_info* reply_info_copy(struct reply_info* rep, struct alloc_cache* alloc, struct region* region); +/** + * Copy a parsed rrset into given key, decompressing and allocating rdata. + * @param pkt: packet for decompression + * @param msg: the parser message (for flags for trust). + * @param pset: the parsed rrset to copy. + * @param region: if NULL - malloc, else data is allocated in this region. + * @param pk: a freshly obtained rrsetkey structure. No dname is set yet, + * will be set on return. + * Note that TTL will still be relative on return. + * @return false on alloc failure. + */ +int parse_copy_decompress_rrset(ldns_buffer* pkt, struct msg_parse* msg, + struct rrset_parse *pset, struct region* region, + struct ub_packed_rrset_key* pk); + /** * Find answer rrset in reply, the one matching qinfo. Follows CNAMEs, so the * result may have a different owner name.