]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
Option to disable sanitize (normalize is still active), speeds up. But
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Fri, 20 Jul 2007 09:12:06 +0000 (09:12 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Fri, 20 Jul 2007 09:12:06 +0000 (09:12 +0000)
some spoofing scenarios become possible. Default is the safe method.

git-svn-id: file:///svn/unbound/trunk@443 be551aaa-1e26-0410-a405-d3ace91eadb9

doc/example.conf
doc/unbound.conf.5
iterator/iter_scrub.c
iterator/iter_scrub.h
iterator/iterator.c
util/config_file.c
util/config_file.h
util/configlexer.lex
util/configparser.y
util/data/msgreply.c
util/data/msgreply.h

index d18f0d38fe89939f224d1d214dbf7b4a48161745..def4e1bca76066284be6caefa2caa31183a923be 100644 (file)
@@ -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.
index 9d55faee44e2ed87f2e2cbe82a149702e53c183f..0dc2d87dfba938fdca2b5ed03bb3536a9c37cacb 100644 (file)
@@ -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 <yes or no>
+Will trust glue only if it is within the servers authority. Default is on.
 .It \fBdo-not-query-address:\fR <IP address>
 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 
index 7c28411740a952f79691bfb4e908b2aba19d4d54..4fb9ada5e4d676526e206086179306e5305c4237 100644 (file)
  */
 #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;
 }
index 68f3ce9b239b53d0b1f8175fcbea0313204bee4e..0ac5768bf04ae814d98ef06d6a954176c2bef687 100644 (file)
@@ -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 */
index f6d6b9b9d041bd5e68ba79929c9303e252ed9535..1d4769873cef75f06e19b7d70c8617d142fe79b7 100644 (file)
@@ -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 */
index a4ab1cf449e052b7622fa89758dc012cff92ff7a..ef5a9b225f52844def9bfbac582fba91b06da1ba 100644 (file)
@@ -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;
index f0a7996613b56d5e60d3e6cb19febbb446f9d2ed..037695d13ab78d2bf33bbe23fd07238f9c4227f9 100644 (file)
@@ -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;
index 5631c04785725c62904afe11bda58ea594c9d09f..1c6594f87b2cdc04af464a4aec822851d8e4cbe9 100644 (file)
@@ -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;}
index 31b982dd596c08182ebe12f8973b6e111d1cfce8..56b94412f48e98b6cf8efd1087b566d703b72d0e 100644 (file)
@@ -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));
index c634b4e0b3ed047bc87d261d6667899f48f56ea8..64b0c9ef2c3e04d6131e402af3dd20a1193abfd3 100644 (file)
@@ -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; i<rep->rrset_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;
 
index 04f471e215257fbf6dd74edd873d75386680775b..c75afe36d8f1a9c338800348229e5fd3df2a79ab 100644 (file)
@@ -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.