Author: delphij
Date: Wed Dec 17 06:55:44 2014
New Revision: 275852
URL: https://svnweb.freebsd.org/changeset/base/275852

Log:
  MFV r275844:
  
  Fix unbound remote denial of service vulnerability.
  
  Security:     FreeBSD-SA-14:30.unbound
  Security:     CVE-2014-8602

Modified:
  head/contrib/unbound/iterator/iterator.c
  head/contrib/unbound/iterator/iterator.h
Directory Properties:
  head/contrib/unbound/   (props changed)

Modified: head/contrib/unbound/iterator/iterator.c
==============================================================================
--- head/contrib/unbound/iterator/iterator.c    Wed Dec 17 05:36:34 2014        
(r275851)
+++ head/contrib/unbound/iterator/iterator.c    Wed Dec 17 06:55:44 2014        
(r275852)
@@ -120,6 +120,7 @@ iter_new(struct module_qstate* qstate, i
        iq->query_restart_count = 0;
        iq->referral_count = 0;
        iq->sent_count = 0;
+       iq->target_count = NULL;
        iq->wait_priming_stub = 0;
        iq->refetch_glue = 0;
        iq->dnssec_expected = 0;
@@ -445,6 +446,26 @@ handle_cname_response(struct module_qsta
        return 1;
 }
 
+/** create target count structure for this query */
+static void
+target_count_create(struct iter_qstate* iq)
+{
+       if(!iq->target_count) {
+               iq->target_count = (int*)calloc(2, sizeof(int));
+               /* if calloc fails we simply do not track this number */
+               if(iq->target_count)
+                       iq->target_count[0] = 1;
+       }
+}
+
+static void
+target_count_increase(struct iter_qstate* iq, int num)
+{
+       target_count_create(iq);
+       if(iq->target_count)
+               iq->target_count[1] += num;
+}
+
 /**
  * Generate a subrequest.
  * Generate a local request event. Local events are tied to this module, and
@@ -516,6 +537,10 @@ generate_sub_request(uint8_t* qname, siz
                subiq = (struct iter_qstate*)subq->minfo[id];
                memset(subiq, 0, sizeof(*subiq));
                subiq->num_target_queries = 0;
+               target_count_create(iq);
+               subiq->target_count = iq->target_count;
+               if(iq->target_count)
+                       iq->target_count[0] ++; /* extra reference */
                subiq->num_current_queries = 0;
                subiq->depth = iq->depth+1;
                outbound_list_init(&subiq->outlist);
@@ -1342,6 +1367,12 @@ query_for_targets(struct module_qstate* 
 
        if(iq->depth == ie->max_dependency_depth)
                return 0;
+       if(iq->depth > 0 && iq->target_count &&
+               iq->target_count[1] > MAX_TARGET_COUNT) {
+               verbose(VERB_QUERY, "request has exceeded the maximum "
+                       "number of glue fetches %d", iq->target_count[1]);
+               return 0;
+       }
 
        iter_mark_cycle_targets(qstate, iq->dp);
        missing = (int)delegpt_count_missing_targets(iq->dp);
@@ -1524,6 +1555,7 @@ processLastResort(struct module_qstate* 
                        return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
                }
                iq->num_target_queries += qs;
+               target_count_increase(iq, qs);
                if(qs != 0) {
                        qstate->ext_state[id] = module_wait_subquery;
                        return 0; /* and wait for them */
@@ -1533,6 +1565,12 @@ processLastResort(struct module_qstate* 
                verbose(VERB_QUERY, "maxdepth and need more nameservers, fail");
                return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
        }
+       if(iq->depth > 0 && iq->target_count &&
+               iq->target_count[1] > MAX_TARGET_COUNT) {
+               verbose(VERB_QUERY, "request has exceeded the maximum "
+                       "number of glue fetches %d", iq->target_count[1]);
+               return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
+       }
        /* mark cycle targets for parent-side lookups */
        iter_mark_pside_cycle_targets(qstate, iq->dp);
        /* see if we can issue queries to get nameserver addresses */
@@ -1562,6 +1600,7 @@ processLastResort(struct module_qstate* 
                if(query_count != 0) { /* suspend to await results */
                        verbose(VERB_ALGO, "try parent-side glue lookup");
                        iq->num_target_queries += query_count;
+                       target_count_increase(iq, query_count);
                        qstate->ext_state[id] = module_wait_subquery;
                        return 0;
                }
@@ -1717,6 +1756,7 @@ processQueryTargets(struct module_qstate
                        return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
                }
                iq->num_target_queries += extra;
+               target_count_increase(iq, extra);
                if(iq->num_target_queries > 0) {
                        /* wait to get all targets, we want to try em */
                        verbose(VERB_ALGO, "wait for all targets for fallback");
@@ -1757,6 +1797,7 @@ processQueryTargets(struct module_qstate
                /* errors ignored, these targets are not strictly necessary for
                 * this result, we do not have to reply with SERVFAIL */
                iq->num_target_queries += extra;
+               target_count_increase(iq, extra);
        }
 
        /* Add the current set of unused targets to our queue. */
@@ -1802,6 +1843,7 @@ processQueryTargets(struct module_qstate
                                        return 1;
                                }
                                iq->num_target_queries += qs;
+                               target_count_increase(iq, qs);
                        }
                        /* Since a target query might have been made, we 
                         * need to check again. */
@@ -2894,6 +2936,8 @@ iter_clear(struct module_qstate* qstate,
        iq = (struct iter_qstate*)qstate->minfo[id];
        if(iq) {
                outbound_list_clear(&iq->outlist);
+               if(iq->target_count && --iq->target_count[0] == 0)
+                       free(iq->target_count);
                iq->num_current_queries = 0;
        }
        qstate->minfo[id] = NULL;

Modified: head/contrib/unbound/iterator/iterator.h
==============================================================================
--- head/contrib/unbound/iterator/iterator.h    Wed Dec 17 05:36:34 2014        
(r275851)
+++ head/contrib/unbound/iterator/iterator.h    Wed Dec 17 06:55:44 2014        
(r275852)
@@ -52,6 +52,8 @@ struct iter_donotq;
 struct iter_prep_list;
 struct iter_priv;
 
+/** max number of targets spawned for a query and its subqueries */
+#define MAX_TARGET_COUNT       32
 /** max number of query restarts. Determines max number of CNAME chain. */
 #define MAX_RESTART_COUNT       8
 /** max number of referrals. Makes sure resolver does not run away */
@@ -254,6 +256,10 @@ struct iter_qstate {
 
        /** number of queries fired off */
        int sent_count;
+       
+       /** number of target queries spawned in [1], for this query and its
+        * subqueries, the malloced-array is shared, [0] refcount. */
+       int* target_count;
 
        /**
         * The query must store NS records from referrals as parentside RRs
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to