Hi Packages for testing can be found at:
https://people.debian.org/~carnil/tmp/bind9/ (amd64 build only), and attached the debdiff. I would appreciate any testing feedback from people mentioning in this bug that they are affected by the issue. Thanks already in advance, Regards, Salvatore
diff -u bind9-9.9.5.dfsg/bin/named/query.c bind9-9.9.5.dfsg/bin/named/query.c --- bind9-9.9.5.dfsg/bin/named/query.c +++ bind9-9.9.5.dfsg/bin/named/query.c @@ -7330,6 +7330,7 @@ result = query_dns64(client, &fname, rdataset, sigrdataset, dbuf, DNS_SECTION_ANSWER); + noqname = NULL; dns_rdataset_disassociate(rdataset); dns_message_puttemprdataset(client->message, &rdataset); if (result == ISC_R_NOMORE) { diff -u bind9-9.9.5.dfsg/bin/tests/system/dname/ns2/example.db bind9-9.9.5.dfsg/bin/tests/system/dname/ns2/example.db --- bind9-9.9.5.dfsg/bin/tests/system/dname/ns2/example.db +++ bind9-9.9.5.dfsg/bin/tests/system/dname/ns2/example.db @@ -29,6 +29,7 @@ short-dname DNAME short a.longlonglonglonglonglonglonglonglonglonglonglonglong A 10.0.0.2 long-dname DNAME longlonglonglonglonglonglonglonglonglonglonglonglong +toolong-dname DNAME longlonglonglonglonglonglonglonglonglonglonglonglong cname CNAME a.cnamedname cnamedname DNAME target a.target A 10.0.0.3 diff -u bind9-9.9.5.dfsg/bin/tests/system/dname/tests.sh bind9-9.9.5.dfsg/bin/tests/system/dname/tests.sh --- bind9-9.9.5.dfsg/bin/tests/system/dname/tests.sh +++ bind9-9.9.5.dfsg/bin/tests/system/dname/tests.sh @@ -56,10 +56,19 @@ if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` -echo "I:checking (too) long dname from recursive" +echo "I:checking (too) long dname from recursive with cached DNAME" +ret=0 +$DIG 01234567890123456789012345678901234567890123456789.longlonglonglonglonglonglonglonglonglonglonglonglonglonglong.longlonglonglonglonglonglonglonglonglonglonglonglonglonglong.longlonglonglonglonglonglonglonglonglonglonglonglonglonglong.long-dname.example @10.53.0.4 a -p 5300 > dig.out.ns4.cachedtoolong || ret=1 +grep "status: YXDOMAIN" dig.out.ns4.cachedtoolong > /dev/null || ret=1 +grep '^long-dname\.example\..*DNAME.*long' dig.out.ns4.cachedtoolong > /dev/null || ret=1 +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +echo "I:checking (too) long dname from recursive without cached DNAME" ret=0 -$DIG 01234567890123456789012345678901234567890123456789.longlonglonglonglonglonglonglonglonglonglonglonglonglonglong.longlonglonglonglonglonglonglonglonglonglonglonglonglonglong.longlonglonglonglonglonglonglonglonglonglonglonglonglonglong.long-dname.example @10.53.0.4 a -p 5300 > dig.out.ns4.toolong || ret=1 -grep "status: YXDOMAIN" dig.out.ns4.toolong > /dev/null || ret=1 +$DIG 01234567890123456789012345678901234567890123456789.longlonglonglonglonglonglonglonglonglonglonglonglonglonglong.longlonglonglonglonglonglonglonglonglonglonglonglonglonglong.longlonglonglonglonglonglonglonglonglonglonglonglonglong.toolong-dname.example @10.53.0.4 a -p 5300 > dig.out.ns4.uncachedtoolong || ret=1 +grep "status: YXDOMAIN" dig.out.ns4.uncachedtoolong > /dev/null || ret=1 +grep '^toolong-dname\.example\..*DNAME.*long' dig.out.ns4.uncachedtoolong > /dev/null || ret=1 if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` diff -u bind9-9.9.5.dfsg/debian/changelog bind9-9.9.5.dfsg/debian/changelog --- bind9-9.9.5.dfsg/debian/changelog +++ bind9-9.9.5.dfsg/debian/changelog @@ -1,3 +1,22 @@ +bind9 (1:9.9.5.dfsg-9+deb8u10) jessie-security; urgency=high + + * Non-maintainer upload by the Security Team. + * Dns64 with "break-dnssec yes;" can result in a assertion failure. + (CVE-2017-3136) (Closes: #860224) + * Prerequisite for CVE-2017-3137 cherry-picked from upstream change #4190. + If not cherry-picking this chane the fix for CVE-2017-3137 can causs an + assertion failure to appear in name.c. + * Some chaining (CNAME or DNAME) responses to upstream queries could trigger + assertion failures (CVE-2017-3137) (Closes: #860225) + * Reimplement: Some chaining (CNAME or DNAME) responses to upstream queries + could trigger assertion failures. (CVE-2017-3137) + * Fix regression introduced when handling CNAME to referral below the + current domain + * 'rndc ""' could trigger a assertion failure in named. (CVE-2017-3138) + (Closes: #860226) + + -- Salvatore Bonaccorso <car...@debian.org> Thu, 11 May 2017 07:40:56 +0200 + bind9 (1:9.9.5.dfsg-9+deb8u9) jessie-security; urgency=medium * Apply patches from ISC. diff -u bind9-9.9.5.dfsg/lib/dns/resolver.c bind9-9.9.5.dfsg/lib/dns/resolver.c --- bind9-9.9.5.dfsg/lib/dns/resolver.c +++ bind9-9.9.5.dfsg/lib/dns/resolver.c @@ -3821,6 +3821,7 @@ isc_result_t result; if (message->rcode != dns_rcode_noerror && + message->rcode != dns_rcode_yxdomain && message->rcode != dns_rcode_nxdomain) return (ISC_FALSE); @@ -5396,56 +5397,6 @@ goto again; } -static inline isc_result_t -cname_target(dns_rdataset_t *rdataset, dns_name_t *tname) { - isc_result_t result; - dns_rdata_t rdata = DNS_RDATA_INIT; - dns_rdata_cname_t cname; - - result = dns_rdataset_first(rdataset); - if (result != ISC_R_SUCCESS) - return (result); - dns_rdataset_current(rdataset, &rdata); - result = dns_rdata_tostruct(&rdata, &cname, NULL); - if (result != ISC_R_SUCCESS) - return (result); - dns_name_init(tname, NULL); - dns_name_clone(&cname.cname, tname); - dns_rdata_freestruct(&cname); - - return (ISC_R_SUCCESS); -} - -static inline isc_result_t -dname_target(dns_rdataset_t *rdataset, dns_name_t *qname, - unsigned int nlabels, dns_fixedname_t *fixeddname) -{ - isc_result_t result; - dns_rdata_t rdata = DNS_RDATA_INIT; - dns_rdata_dname_t dname; - dns_fixedname_t prefix; - - /* - * Get the target name of the DNAME. - */ - result = dns_rdataset_first(rdataset); - if (result != ISC_R_SUCCESS) - return (result); - dns_rdataset_current(rdataset, &rdata); - result = dns_rdata_tostruct(&rdata, &dname, NULL); - if (result != ISC_R_SUCCESS) - return (result); - - dns_fixedname_init(&prefix); - dns_name_split(qname, nlabels, dns_fixedname_name(&prefix), NULL); - dns_fixedname_init(fixeddname); - result = dns_name_concatenate(dns_fixedname_name(&prefix), - &dname.dname, - dns_fixedname_name(fixeddname), NULL); - dns_rdata_freestruct(&dname); - return (result); -} - static isc_boolean_t is_answeraddress_allowed(dns_view_t *view, dns_name_t *name, dns_rdataset_t *rdataset) @@ -5521,9 +5472,8 @@ } static isc_boolean_t -is_answertarget_allowed(dns_view_t *view, dns_name_t *name, - dns_rdatatype_t type, dns_name_t *tname, - dns_name_t *domain) +is_answertarget_allowed(fetchctx_t *fctx, dns_name_t *qname, dns_name_t *rname, + dns_rdataset_t *rdataset, isc_boolean_t *chainingp) { isc_result_t result; dns_rbtnode_t *node = NULL; @@ -5531,8 +5481,57 @@ char tnamebuf[DNS_NAME_FORMATSIZE]; char classbuf[64]; char typebuf[64]; + dns_name_t *tname = NULL; + dns_rdata_cname_t cname; + dns_rdata_dname_t dname; + dns_view_t *view = fctx->res->view; + dns_rdata_t rdata = DNS_RDATA_INIT; + unsigned int nlabels; + dns_fixedname_t fixed; + dns_name_t prefix; + + REQUIRE(rdataset != NULL); + REQUIRE(rdataset->type == dns_rdatatype_cname || + rdataset->type == dns_rdatatype_dname); + + /* + * By default, we allow any target name. + * If newqname != NULL we also need to extract the newqname. + */ + if (chainingp == NULL && view->denyanswernames == NULL) + return (ISC_TRUE); + + result = dns_rdataset_first(rdataset); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + dns_rdataset_current(rdataset, &rdata); + switch (rdataset->type) { + case dns_rdatatype_cname: + result = dns_rdata_tostruct(&rdata, &cname, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + tname = &cname.cname; + break; + case dns_rdatatype_dname: + result = dns_rdata_tostruct(&rdata, &dname, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + dns_name_init(&prefix, NULL); + dns_fixedname_init(&fixed); + tname = dns_fixedname_name(&fixed); + nlabels = dns_name_countlabels(qname) - + dns_name_countlabels(rname); + dns_name_split(qname, nlabels, &prefix, NULL); + result = dns_name_concatenate(&prefix, &dname.dname, tname, + NULL); + if (result == DNS_R_NAMETOOLONG) + return (ISC_TRUE); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + break; + default: + INSIST(0); + } + + if (chainingp != NULL) + *chainingp = ISC_TRUE; - /* By default, we allow any target name. */ if (view->denyanswernames == NULL) return (ISC_TRUE); @@ -5541,8 +5540,8 @@ * or partially, allow it. */ if (view->answernames_exclude != NULL) { - result = dns_rbt_findnode(view->answernames_exclude, name, NULL, - &node, NULL, 0, NULL, NULL); + result = dns_rbt_findnode(view->answernames_exclude, qname, + NULL, &node, NULL, 0, NULL, NULL); if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) return (ISC_TRUE); } @@ -5550,7 +5549,7 @@ /* * If the target name is a subdomain of the search domain, allow it. */ - if (dns_name_issubdomain(tname, domain)) + if (dns_name_issubdomain(tname, &fctx->domain)) return (ISC_TRUE); /* @@ -5559,9 +5558,9 @@ result = dns_rbt_findnode(view->denyanswernames, tname, NULL, &node, NULL, 0, NULL, NULL); if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) { - dns_name_format(name, qnamebuf, sizeof(qnamebuf)); + dns_name_format(qname, qnamebuf, sizeof(qnamebuf)); dns_name_format(tname, tnamebuf, sizeof(tnamebuf)); - dns_rdatatype_format(type, typebuf, sizeof(typebuf)); + dns_rdatatype_format(rdataset->type, typebuf, sizeof(typebuf)); dns_rdataclass_format(view->rdclass, classbuf, sizeof(classbuf)); isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, @@ -6044,438 +6043,300 @@ return (ISC_R_SUCCESS); } +static isc_boolean_t +validinanswer(dns_rdataset_t *rdataset, fetchctx_t *fctx) { + if (rdataset->type == dns_rdatatype_nsec3) { + /* + * NSEC3 records are not allowed to + * appear in the answer section. + */ + log_formerr(fctx, "NSEC3 in answer"); + return (ISC_FALSE); + } + if (rdataset->type == dns_rdatatype_tkey) { + /* + * TKEY is not a valid record in a + * response to any query we can make. + */ + log_formerr(fctx, "TKEY in answer"); + return (ISC_FALSE); + } + if (rdataset->rdclass != fctx->res->rdclass) { + log_formerr(fctx, "Mismatched class in answer"); + return (ISC_FALSE); + } + return (ISC_TRUE); +} + static isc_result_t answer_response(fetchctx_t *fctx) { isc_result_t result; - dns_message_t *message; - dns_name_t *name, *dname = NULL, *qname, tname, *ns_name; - dns_name_t *cname = NULL; - dns_rdataset_t *rdataset, *ns_rdataset; - isc_boolean_t done, external, chaining, aa, found, want_chaining; - isc_boolean_t have_answer, found_cname, found_dname, found_type; - isc_boolean_t wanted_chaining; - unsigned int aflag; + dns_message_t *message = NULL; + dns_name_t *name = NULL, *qname = NULL, *ns_name = NULL; + dns_name_t *aname = NULL, *cname = NULL, *dname = NULL; + dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL; + dns_rdataset_t *ardataset = NULL, *crdataset = NULL; + dns_rdataset_t *drdataset = NULL, *ns_rdataset = NULL; + isc_boolean_t done = ISC_FALSE, aa; + unsigned int dname_labels, domain_labels; + isc_boolean_t chaining = ISC_FALSE; dns_rdatatype_t type; - dns_fixedname_t fdname, fqname; - dns_view_t *view; + dns_view_t *view = NULL; + dns_trust_t trust; + + REQUIRE(VALID_FCTX(fctx)); FCTXTRACE("answer_response"); message = fctx->rmessage; + qname = &fctx->name; + view = fctx->res->view; + type = fctx->type; + + /* + * There can be multiple RRSIG and SIG records at a name so + * we treat these types as a subset of ANY. + */ + if (type == dns_rdatatype_rrsig || type == dns_rdatatype_sig) { + type = dns_rdatatype_any; + } /* - * Examine the answer section, marking those rdatasets which are - * part of the answer and should be cached. + * Bigger than any valid DNAME label count. */ + dname_labels = dns_name_countlabels(qname); + domain_labels = dns_name_countlabels(&fctx->domain); - done = ISC_FALSE; - found_cname = ISC_FALSE; - found_dname = ISC_FALSE; - found_type = ISC_FALSE; - chaining = ISC_FALSE; - have_answer = ISC_FALSE; - want_chaining = ISC_FALSE; - POST(want_chaining); - if ((message->flags & DNS_MESSAGEFLAG_AA) != 0) - aa = ISC_TRUE; - else - aa = ISC_FALSE; - qname = &fctx->name; - type = fctx->type; - view = fctx->res->view; - result = dns_message_firstname(message, DNS_SECTION_ANSWER); - while (!done && result == ISC_R_SUCCESS) { - dns_namereln_t namereln; + /* + * Perform a single pass looking for the answer, cname or covering + * dname. + */ + for (result = dns_message_firstname(message, DNS_SECTION_ANSWER); + result == ISC_R_SUCCESS; + result = dns_message_nextname(message, DNS_SECTION_ANSWER)) + { int order; unsigned int nlabels; + dns_namereln_t namereln; name = NULL; dns_message_currentname(message, DNS_SECTION_ANSWER, &name); - external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain)); namereln = dns_name_fullcompare(qname, name, &order, &nlabels); - if (namereln == dns_namereln_equal) { - wanted_chaining = ISC_FALSE; + switch (namereln) { + case dns_namereln_equal: for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; - rdataset = ISC_LIST_NEXT(rdataset, link)) { - found = ISC_FALSE; - want_chaining = ISC_FALSE; - aflag = 0; - if (rdataset->type == dns_rdatatype_nsec3) { - /* - * NSEC3 records are not allowed to - * appear in the answer section. - */ - log_formerr(fctx, "NSEC3 in answer"); - return (DNS_R_FORMERR); - } - if (rdataset->type == dns_rdatatype_tkey) { - /* - * TKEY is not a valid record in a - * response to any query we can make. - */ - log_formerr(fctx, "TKEY in answer"); - return (DNS_R_FORMERR); - } - if (rdataset->rdclass != fctx->res->rdclass) { - log_formerr(fctx, "Mismatched class " - "in answer"); - return (DNS_R_FORMERR); - } - - /* - * Apply filters, if given, on answers to reject - * a malicious attempt of rebinding. - */ - if ((rdataset->type == dns_rdatatype_a || - rdataset->type == dns_rdatatype_aaaa) && - !is_answeraddress_allowed(view, name, - rdataset)) { - return (DNS_R_SERVFAIL); - } - - if (rdataset->type == type && !found_cname) { - /* - * We've found an ordinary answer. - */ - found = ISC_TRUE; - found_type = ISC_TRUE; - done = ISC_TRUE; - aflag = DNS_RDATASETATTR_ANSWER; - } else if (type == dns_rdatatype_any) { - /* - * We've found an answer matching - * an ANY query. There may be - * more. - */ - found = ISC_TRUE; - aflag = DNS_RDATASETATTR_ANSWER; - } else if (rdataset->type == dns_rdatatype_rrsig - && rdataset->covers == type - && !found_cname) { - /* - * We've found a signature that - * covers the type we're looking for. - */ - found = ISC_TRUE; - found_type = ISC_TRUE; - aflag = DNS_RDATASETATTR_ANSWERSIG; - } else if (rdataset->type == - dns_rdatatype_cname - && !found_type) { - /* - * We're looking for something else, - * but we found a CNAME. - * - * Getting a CNAME response for some - * query types is an error, see - * RFC 4035, Section 2.5. - */ - if (type == dns_rdatatype_rrsig || - type == dns_rdatatype_key || - type == dns_rdatatype_nsec) { - char buf[DNS_RDATATYPE_FORMATSIZE]; - dns_rdatatype_format(fctx->type, - buf, sizeof(buf)); - log_formerr(fctx, - "CNAME response " - "for %s RR", buf); - return (DNS_R_FORMERR); - } - found = ISC_TRUE; - found_cname = ISC_TRUE; - want_chaining = ISC_TRUE; - aflag = DNS_RDATASETATTR_ANSWER; - result = cname_target(rdataset, - &tname); - if (result != ISC_R_SUCCESS) - return (result); - /* Apply filters on the target name. */ - if (!is_answertarget_allowed(view, - name, - rdataset->type, - &tname, - &fctx->domain)) { - return (DNS_R_SERVFAIL); + rdataset = ISC_LIST_NEXT(rdataset, link)) + { + if (rdataset->type == type || + type == dns_rdatatype_any) + { + aname = name; + if (type != dns_rdatatype_any) { + ardataset = rdataset; } - } else if (rdataset->type == dns_rdatatype_rrsig - && rdataset->covers == - dns_rdatatype_cname - && !found_type) { - /* - * We're looking for something else, - * but we found a SIG CNAME. - */ - found = ISC_TRUE; - found_cname = ISC_TRUE; - aflag = DNS_RDATASETATTR_ANSWERSIG; + break; } - - if (found) { - /* - * We've found an answer to our - * question. - */ - name->attributes |= - DNS_NAMEATTR_CACHE; - rdataset->attributes |= - DNS_RDATASETATTR_CACHE; - rdataset->trust = dns_trust_answer; - if (!chaining) { - /* - * This data is "the" answer - * to our question only if - * we're not chaining (i.e. - * if we haven't followed - * a CNAME or DNAME). - */ - INSIST(!external); - /* - * Don't use found_cname here - * as we have just set it - * above. - */ - if (cname == NULL && - !found_dname && - aflag == - DNS_RDATASETATTR_ANSWER) - { - have_answer = ISC_TRUE; - if (found_cname && - cname == NULL) - cname = name; - name->attributes |= - DNS_NAMEATTR_ANSWER; - } - rdataset->attributes |= aflag; - if (aa) - rdataset->trust = - dns_trust_authanswer; - } else if (external) { - /* - * This data is outside of - * our query domain, and - * may not be cached. - */ - rdataset->attributes |= - DNS_RDATASETATTR_EXTERNAL; - } - - /* - * Mark any additional data related - * to this rdataset. - */ - (void)dns_rdataset_additionaldata( - rdataset, - check_related, - fctx); - - /* - * CNAME chaining. - */ - if (want_chaining) { - wanted_chaining = ISC_TRUE; - name->attributes |= - DNS_NAMEATTR_CHAINING; - rdataset->attributes |= - DNS_RDATASETATTR_CHAINING; - qname = &tname; - } + if (rdataset->type == dns_rdatatype_cname) { + cname = name; + crdataset = rdataset; + break; } - /* - * We could add an "else" clause here and - * log that we're ignoring this rdataset. - */ } + break; + + case dns_namereln_subdomain: /* - * If wanted_chaining is true, we've done - * some chaining as the result of processing - * this node, and thus we need to set - * chaining to true. - * - * We don't set chaining inside of the - * rdataset loop because doing that would - * cause us to ignore the signatures of - * CNAMEs. + * In-scope DNAME records must have at least + * as many labels as the domain being queried. + * They also must be less that qname's labels + * and any previously found dname. */ - if (wanted_chaining) - chaining = ISC_TRUE; - } else { - dns_rdataset_t *dnameset = NULL; - + if (nlabels >= dname_labels || nlabels < domain_labels) + { + continue; + } /* - * Look for a DNAME (or its SIG). Anything else is - * ignored. + * We are looking for the shortest DNAME if there + * are multiple ones (which there shouldn't be). */ - wanted_chaining = ISC_FALSE; for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { - if (rdataset->rdclass != fctx->res->rdclass) { - log_formerr(fctx, "Mismatched class " - "in answer"); - return (DNS_R_FORMERR); - } - - /* - * Only pass DNAME or RRSIG(DNAME). - */ - if (rdataset->type != dns_rdatatype_dname && - (rdataset->type != dns_rdatatype_rrsig || - rdataset->covers != dns_rdatatype_dname)) + if (rdataset->type != dns_rdatatype_dname) { continue; - - /* - * If we're not chaining, then the DNAME and - * its signature should not be external. - */ - if (!chaining && external) { - char qbuf[DNS_NAME_FORMATSIZE]; - char obuf[DNS_NAME_FORMATSIZE]; - - dns_name_format(name, qbuf, - sizeof(qbuf)); - dns_name_format(&fctx->domain, obuf, - sizeof(obuf)); - log_formerr(fctx, "external DNAME or " - "RRSIG covering DNAME " - "in answer: %s is " - "not in %s", qbuf, obuf); - return (DNS_R_FORMERR); } + dname = name; + drdataset = rdataset; + dname_labels = nlabels; + break; + } + break; + default: + break; + } + } - /* - * If DNAME + synthetic CNAME then the - * namereln is dns_namereln_subdomain. - * - * If synthetic CNAME + DNAME then the - * namereln is dns_namereln_commonancestor - * and the number of label must match the - * DNAME. This order is not RFC compliant. - */ - - if (namereln != dns_namereln_subdomain && - (namereln != dns_namereln_commonancestor || - nlabels != dns_name_countlabels(name))) - { - char qbuf[DNS_NAME_FORMATSIZE]; - char obuf[DNS_NAME_FORMATSIZE]; - - dns_name_format(qname, qbuf, - sizeof(qbuf)); - dns_name_format(name, obuf, - sizeof(obuf)); - log_formerr(fctx, "unrelated DNAME " - "in answer: %s is " - "not in %s", qbuf, obuf); - return (DNS_R_FORMERR); - } + if (dname != NULL) { + aname = NULL; + ardataset = NULL; + cname = NULL; + crdataset = NULL; + } else if (aname != NULL) { + cname = NULL; + crdataset = NULL; + } - aflag = 0; - if (rdataset->type == dns_rdatatype_dname) { - want_chaining = ISC_TRUE; - POST(want_chaining); - aflag = DNS_RDATASETATTR_ANSWER; - result = dname_target(rdataset, qname, - nlabels, &fdname); - if (result == ISC_R_NOSPACE) { - /* - * We can't construct the - * DNAME target. Do not - * try to continue. - */ - want_chaining = ISC_FALSE; - POST(want_chaining); - } else if (result != ISC_R_SUCCESS) - return (result); - else - dnameset = rdataset; - - dname = dns_fixedname_name(&fdname); - if (!is_answertarget_allowed(view, - qname, rdataset->type, - dname, &fctx->domain)) - { - return (DNS_R_SERVFAIL); - } - } else { - /* - * We've found a signature that - * covers the DNAME. - */ - aflag = DNS_RDATASETATTR_ANSWERSIG; - } + aa = ISC_TF((message->flags & DNS_MESSAGEFLAG_AA) != 0); + trust = aa ? dns_trust_authanswer : dns_trust_answer; - /* - * We've found an answer to our - * question. - */ - name->attributes |= DNS_NAMEATTR_CACHE; - rdataset->attributes |= DNS_RDATASETATTR_CACHE; - rdataset->trust = dns_trust_answer; - if (!chaining) { - /* - * This data is "the" answer to - * our question only if we're - * not chaining. - */ - INSIST(!external); - if (aflag == DNS_RDATASETATTR_ANSWER) { - have_answer = ISC_TRUE; - found_dname = ISC_TRUE; - if (cname != NULL) - cname->attributes &= - ~DNS_NAMEATTR_ANSWER; - name->attributes |= - DNS_NAMEATTR_ANSWER; - } - rdataset->attributes |= aflag; - if (aa) - rdataset->trust = - dns_trust_authanswer; - } else if (external) { - rdataset->attributes |= - DNS_RDATASETATTR_EXTERNAL; - } + if (aname != NULL && type == dns_rdatatype_any) { + for (rdataset = ISC_LIST_HEAD(aname->list); + rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) + { + if (!validinanswer(rdataset, fctx)) { + return (DNS_R_FORMERR); } - - /* - * DNAME chaining. - */ - if (dnameset != NULL) { - /* - * Copy the dname into the qname fixed name. - * - * Although we check for failure of the copy - * operation, in practice it should never fail - * since we already know that the result fits - * in a fixedname. - */ - dns_fixedname_init(&fqname); - qname = dns_fixedname_name(&fqname); - result = dns_name_copy(dname, qname, NULL); - if (result != ISC_R_SUCCESS) - return (result); - wanted_chaining = ISC_TRUE; - name->attributes |= DNS_NAMEATTR_CHAINING; - dnameset->attributes |= - DNS_RDATASETATTR_CHAINING; + if ((fctx->type == dns_rdatatype_sig || + fctx->type == dns_rdatatype_rrsig) && + rdataset->type != fctx->type) + { + continue; } - if (wanted_chaining) - chaining = ISC_TRUE; + if ((rdataset->type == dns_rdatatype_a || + rdataset->type == dns_rdatatype_aaaa) && + !is_answeraddress_allowed(view, aname, rdataset)) + { + return (DNS_R_SERVFAIL); + } + if ((rdataset->type == dns_rdatatype_cname || + rdataset->type == dns_rdatatype_dname) && + !is_answertarget_allowed(fctx, qname, aname, + rdataset, NULL)) + { + return (DNS_R_SERVFAIL); + } + aname->attributes |= DNS_NAMEATTR_CACHE; + aname->attributes |= DNS_NAMEATTR_ANSWER; + rdataset->attributes |= DNS_RDATASETATTR_ANSWER; + rdataset->attributes |= DNS_RDATASETATTR_CACHE; + rdataset->trust = trust; + (void)dns_rdataset_additionaldata(rdataset, + check_related, + fctx); } - result = dns_message_nextname(message, DNS_SECTION_ANSWER); - } - if (result == ISC_R_NOMORE) - result = ISC_R_SUCCESS; - if (result != ISC_R_SUCCESS) - return (result); - - /* - * We should have found an answer. - */ - if (!have_answer) { + } else if (aname != NULL) { + if (!validinanswer(ardataset, fctx)) + return (DNS_R_FORMERR); + if ((ardataset->type == dns_rdatatype_a || + ardataset->type == dns_rdatatype_aaaa) && + !is_answeraddress_allowed(view, aname, ardataset)) { + return (DNS_R_SERVFAIL); + } + if ((ardataset->type == dns_rdatatype_cname || + ardataset->type == dns_rdatatype_dname) && + !is_answertarget_allowed(fctx, qname, aname, ardataset, + NULL)) + { + return (DNS_R_SERVFAIL); + } + aname->attributes |= DNS_NAMEATTR_CACHE; + aname->attributes |= DNS_NAMEATTR_ANSWER; + ardataset->attributes |= DNS_RDATASETATTR_ANSWER; + ardataset->attributes |= DNS_RDATASETATTR_CACHE; + ardataset->trust = trust; + (void)dns_rdataset_additionaldata(ardataset, check_related, + fctx); + for (sigrdataset = ISC_LIST_HEAD(aname->list); + sigrdataset != NULL; + sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) { + if (!validinanswer(sigrdataset, fctx)) + return (DNS_R_FORMERR); + if (sigrdataset->type != dns_rdatatype_rrsig || + sigrdataset->covers != type) + continue; + sigrdataset->attributes |= DNS_RDATASETATTR_ANSWERSIG; + sigrdataset->attributes |= DNS_RDATASETATTR_CACHE; + sigrdataset->trust = trust; + break; + } + } else if (cname != NULL) { + if (!validinanswer(crdataset, fctx)) { + return (DNS_R_FORMERR); + } + if (type == dns_rdatatype_rrsig || type == dns_rdatatype_key || + type == dns_rdatatype_nsec) + { + char buf[DNS_RDATATYPE_FORMATSIZE]; + dns_rdatatype_format(type, buf, sizeof(buf)); + log_formerr(fctx, "CNAME response for %s RR", buf); + return (DNS_R_FORMERR); + } + if (!is_answertarget_allowed(fctx, qname, cname, crdataset, + NULL)) + { + return (DNS_R_SERVFAIL); + } + cname->attributes |= DNS_NAMEATTR_CACHE; + cname->attributes |= DNS_NAMEATTR_ANSWER; + cname->attributes |= DNS_NAMEATTR_CHAINING; + crdataset->attributes |= DNS_RDATASETATTR_ANSWER; + crdataset->attributes |= DNS_RDATASETATTR_CACHE; + crdataset->attributes |= DNS_RDATASETATTR_CHAINING; + crdataset->trust = trust; + for (sigrdataset = ISC_LIST_HEAD(cname->list); + sigrdataset != NULL; + sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) + { + if (!validinanswer(sigrdataset, fctx)) { + return (DNS_R_FORMERR); + } + if (sigrdataset->type != dns_rdatatype_rrsig || + sigrdataset->covers != dns_rdatatype_cname) + { + continue; + } + sigrdataset->attributes |= DNS_RDATASETATTR_ANSWERSIG; + sigrdataset->attributes |= DNS_RDATASETATTR_CACHE; + sigrdataset->trust = trust; + break; + } + chaining = ISC_TRUE; + } else if (dname != NULL) { + if (!validinanswer(drdataset, fctx)) { + return (DNS_R_FORMERR); + } + if (!is_answertarget_allowed(fctx, qname, dname, drdataset, + &chaining)) { + return (DNS_R_SERVFAIL); + } + dname->attributes |= DNS_NAMEATTR_CACHE; + dname->attributes |= DNS_NAMEATTR_ANSWER; + dname->attributes |= DNS_NAMEATTR_CHAINING; + drdataset->attributes |= DNS_RDATASETATTR_ANSWER; + drdataset->attributes |= DNS_RDATASETATTR_CACHE; + drdataset->attributes |= DNS_RDATASETATTR_CHAINING; + drdataset->trust = trust; + for (sigrdataset = ISC_LIST_HEAD(dname->list); + sigrdataset != NULL; + sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) + { + if (!validinanswer(sigrdataset, fctx)) { + return (DNS_R_FORMERR); + } + if (sigrdataset->type != dns_rdatatype_rrsig || + sigrdataset->covers != dns_rdatatype_dname) + { + continue; + } + sigrdataset->attributes |= DNS_RDATASETATTR_ANSWERSIG; + sigrdataset->attributes |= DNS_RDATASETATTR_CACHE; + sigrdataset->trust = trust; + break; + } + } else { log_formerr(fctx, "reply has no answer"); return (DNS_R_FORMERR); } @@ -6489,13 +6350,7 @@ * Did chaining end before we got the final answer? */ if (chaining) { - /* - * Yes. This may be a negative reply, so hand off - * authority section processing to the noanswer code. - * If it isn't a noanswer response, no harm will be - * done. - */ - return (noanswer_response(fctx, qname, 0)); + return (ISC_R_SUCCESS); } /* @@ -6514,11 +6369,9 @@ * We expect there to be only one owner name for all the rdatasets * in this section, and we expect that it is not external. */ - done = ISC_FALSE; - ns_name = NULL; - ns_rdataset = NULL; result = dns_message_firstname(message, DNS_SECTION_AUTHORITY); while (!done && result == ISC_R_SUCCESS) { + isc_boolean_t external; name = NULL; dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name); external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain)); @@ -6537,12 +6390,13 @@ DNS_NAMEATTR_CACHE; rdataset->attributes |= DNS_RDATASETATTR_CACHE; - if (aa && !chaining) + if (aa && !chaining) { rdataset->trust = dns_trust_authauthority; - else + } else { rdataset->trust = dns_trust_additional; + } if (rdataset->type == dns_rdatatype_ns) { @@ -7201,6 +7055,7 @@ * Is the remote server broken, or does it dislike us? */ if (message->rcode != dns_rcode_noerror && + message->rcode != dns_rcode_yxdomain && message->rcode != dns_rcode_nxdomain) { if (((message->rcode == dns_rcode_formerr || message->rcode == dns_rcode_notimp) || @@ -7245,13 +7100,6 @@ log_formerr(fctx, "server sent FORMERR"); result = DNS_R_FORMERR; } - } else if (message->rcode == dns_rcode_yxdomain) { - /* - * DNAME mapping failed because the new name - * was too long. There's no chance of success - * for this fetch. - */ - result = DNS_R_YXDOMAIN; } else if (message->rcode == dns_rcode_badvers) { unsigned int flags, mask; unsigned int version; @@ -7356,6 +7204,7 @@ */ if (message->counts[DNS_SECTION_ANSWER] > 0 && (message->rcode == dns_rcode_noerror || + message->rcode == dns_rcode_yxdomain || message->rcode == dns_rcode_nxdomain)) { /* * [normal case] only in patch2: unchanged: --- bind9-9.9.5.dfsg.orig/bin/tests/system/rndc/tests.sh +++ bind9-9.9.5.dfsg/bin/tests/system/rndc/tests.sh @@ -245,5 +245,13 @@ if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` +n=`expr $n + 1` +echo "I:check 'rndc \"\"' is handled ($n)" +ret=0 +$RNDCCMD "" > rndc.out.test$n 2>&1 && ret=1 +grep "rndc: '' failed: failure" rndc.out.test$n > /dev/null +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + echo "I:exit status: $status" exit $status only in patch2: unchanged: --- bind9-9.9.5.dfsg.orig/lib/dns/name.c +++ bind9-9.9.5.dfsg/lib/dns/name.c @@ -2120,11 +2120,9 @@ REQUIRE(prefix != NULL || suffix != NULL); REQUIRE(prefix == NULL || (VALID_NAME(prefix) && - prefix->buffer != NULL && BINDABLE(prefix))); REQUIRE(suffix == NULL || (VALID_NAME(suffix) && - suffix->buffer != NULL && BINDABLE(suffix))); splitlabel = name->labels - suffixlabels; only in patch2: unchanged: --- bind9-9.9.5.dfsg.orig/lib/isc/include/isc/lex.h +++ bind9-9.9.5.dfsg/lib/isc/include/isc/lex.h @@ -152,8 +152,6 @@ * Requires: *\li '*lexp' is a valid lexer. * - *\li max_token > 0. - * * Ensures: *\li On success, *lexp is attached to the newly created lexer. * only in patch2: unchanged: --- bind9-9.9.5.dfsg.orig/lib/isc/lex.c +++ bind9-9.9.5.dfsg/lib/isc/lex.c @@ -93,9 +93,10 @@ /* * Create a lexer. */ - REQUIRE(lexp != NULL && *lexp == NULL); - REQUIRE(max_token > 0U); + + if (max_token == 0U) + max_token = 1; lex = isc_mem_get(mctx, sizeof(*lex)); if (lex == NULL)