From owner-svn-src-releng@FreeBSD.ORG Wed Dec 17 06:59:51 2014 Return-Path: Delivered-To: svn-src-releng@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id F1A334C1; Wed, 17 Dec 2014 06:59:50 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id D256F252; Wed, 17 Dec 2014 06:59:50 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.9/8.14.9) with ESMTP id sBH6xoXE095219; Wed, 17 Dec 2014 06:59:50 GMT (envelope-from delphij@FreeBSD.org) Received: (from delphij@localhost) by svn.freebsd.org (8.14.9/8.14.9/Submit) id sBH6xmgm095205; Wed, 17 Dec 2014 06:59:48 GMT (envelope-from delphij@FreeBSD.org) Message-Id: <201412170659.sBH6xmgm095205@svn.freebsd.org> X-Authentication-Warning: svn.freebsd.org: delphij set sender to delphij@FreeBSD.org using -f From: Xin LI Date: Wed, 17 Dec 2014 06:59:48 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-releng@freebsd.org Subject: svn commit: r275854 - in releng: 10.0 10.0/contrib/unbound/iterator 10.0/sys/conf 10.1 10.1/contrib/unbound/iterator 10.1/sys/conf X-SVN-Group: releng MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-releng@freebsd.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: SVN commit messages for the release engineering / security commits to the src tree List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 17 Dec 2014 06:59:51 -0000 Author: delphij Date: Wed Dec 17 06:59:47 2014 New Revision: 275854 URL: https://svnweb.freebsd.org/changeset/base/275854 Log: Fix unbound remote denial of service vulnerability. Security: FreeBSD-SA-14:30.unbound Security: CVE-2014-8602 Approved by: so Modified: releng/10.0/UPDATING releng/10.0/contrib/unbound/iterator/iterator.c releng/10.0/contrib/unbound/iterator/iterator.h releng/10.0/sys/conf/newvers.sh releng/10.1/UPDATING releng/10.1/contrib/unbound/iterator/iterator.c releng/10.1/contrib/unbound/iterator/iterator.h releng/10.1/sys/conf/newvers.sh Modified: releng/10.0/UPDATING ============================================================================== --- releng/10.0/UPDATING Wed Dec 17 06:58:00 2014 (r275853) +++ releng/10.0/UPDATING Wed Dec 17 06:59:47 2014 (r275854) @@ -16,6 +16,9 @@ from older versions of FreeBSD, try WITH stable/10, and then rebuild without this option. The bootstrap process from older version of current is a bit fragile. +20141217: p14 FreeBSD-SA-14:30.unbound + Fix unbound remote denial of service vulnerability. + 20141210: p13 FreeBSD-SA-14:28.file Fix multiple vulnerabilities in file(1) and libmagic(3). Modified: releng/10.0/contrib/unbound/iterator/iterator.c ============================================================================== --- releng/10.0/contrib/unbound/iterator/iterator.c Wed Dec 17 06:58:00 2014 (r275853) +++ releng/10.0/contrib/unbound/iterator/iterator.c Wed Dec 17 06:59:47 2014 (r275854) @@ -117,6 +117,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; @@ -442,6 +443,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 @@ -513,6 +534,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); @@ -1339,6 +1364,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); @@ -1487,6 +1518,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 */ @@ -1496,6 +1528,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 */ @@ -1525,6 +1563,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; } @@ -1680,6 +1719,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"); @@ -1720,6 +1760,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. */ @@ -1765,6 +1806,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. */ @@ -2847,6 +2889,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: releng/10.0/contrib/unbound/iterator/iterator.h ============================================================================== --- releng/10.0/contrib/unbound/iterator/iterator.h Wed Dec 17 06:58:00 2014 (r275853) +++ releng/10.0/contrib/unbound/iterator/iterator.h Wed Dec 17 06:59:47 2014 (r275854) @@ -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 Modified: releng/10.0/sys/conf/newvers.sh ============================================================================== --- releng/10.0/sys/conf/newvers.sh Wed Dec 17 06:58:00 2014 (r275853) +++ releng/10.0/sys/conf/newvers.sh Wed Dec 17 06:59:47 2014 (r275854) @@ -32,7 +32,7 @@ TYPE="FreeBSD" REVISION="10.0" -BRANCH="RELEASE-p13" +BRANCH="RELEASE-p14" if [ "X${BRANCH_OVERRIDE}" != "X" ]; then BRANCH=${BRANCH_OVERRIDE} fi Modified: releng/10.1/UPDATING ============================================================================== --- releng/10.1/UPDATING Wed Dec 17 06:58:00 2014 (r275853) +++ releng/10.1/UPDATING Wed Dec 17 06:59:47 2014 (r275854) @@ -16,6 +16,9 @@ from older versions of FreeBSD, try WITH stable/10, and then rebuild without this option. The bootstrap process from older version of current is a bit fragile. +20141217: p2 FreeBSD-SA-14:30.unbound + Fix unbound remote denial of service vulnerability. + 20141210: p1 FreeBSD-SA-14:27.stdio FreeBSD-SA-14:28.file Modified: releng/10.1/contrib/unbound/iterator/iterator.c ============================================================================== --- releng/10.1/contrib/unbound/iterator/iterator.c Wed Dec 17 06:58:00 2014 (r275853) +++ releng/10.1/contrib/unbound/iterator/iterator.c Wed Dec 17 06:59:47 2014 (r275854) @@ -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: releng/10.1/contrib/unbound/iterator/iterator.h ============================================================================== --- releng/10.1/contrib/unbound/iterator/iterator.h Wed Dec 17 06:58:00 2014 (r275853) +++ releng/10.1/contrib/unbound/iterator/iterator.h Wed Dec 17 06:59:47 2014 (r275854) @@ -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 Modified: releng/10.1/sys/conf/newvers.sh ============================================================================== --- releng/10.1/sys/conf/newvers.sh Wed Dec 17 06:58:00 2014 (r275853) +++ releng/10.1/sys/conf/newvers.sh Wed Dec 17 06:59:47 2014 (r275854) @@ -32,7 +32,7 @@ TYPE="FreeBSD" REVISION="10.1" -BRANCH="RELEASE-p1" +BRANCH="RELEASE-p2" if [ "X${BRANCH_OVERRIDE}" != "X" ]; then BRANCH=${BRANCH_OVERRIDE} fi