From ac927469a0807ce78e294a96ffb2cd5f8b6fed4b Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Jun 08 2020 10:01:09 +0000 Subject: import unbound-1.6.6-4.el7_8 --- diff --git a/SOURCES/unbound-1.6.6-amplifying-an-incoming-query.patch b/SOURCES/unbound-1.6.6-amplifying-an-incoming-query.patch new file mode 100644 index 0000000..00a8ddc --- /dev/null +++ b/SOURCES/unbound-1.6.6-amplifying-an-incoming-query.patch @@ -0,0 +1,906 @@ +diff --git a/iterator/iter_delegpt.c b/iterator/iter_delegpt.c +index ecf88b2..1ababa1 100644 +--- a/iterator/iter_delegpt.c ++++ b/iterator/iter_delegpt.c +@@ -84,7 +84,7 @@ struct delegpt* delegpt_copy(struct delegpt* dp, struct regional* region) + } + for(a = dp->target_list; a; a = a->next_target) { + if(!delegpt_add_addr(copy, region, &a->addr, a->addrlen, +- a->bogus, a->lame)) ++ a->bogus, a->lame, NULL)) + return NULL; + } + return copy; +@@ -161,7 +161,7 @@ delegpt_find_addr(struct delegpt* dp, struct sockaddr_storage* addr, + int + delegpt_add_target(struct delegpt* dp, struct regional* region, + uint8_t* name, size_t namelen, struct sockaddr_storage* addr, +- socklen_t addrlen, uint8_t bogus, uint8_t lame) ++ socklen_t addrlen, uint8_t bogus, uint8_t lame, int* additions) + { + struct delegpt_ns* ns = delegpt_find_ns(dp, name, namelen); + log_assert(!dp->dp_type_mlc); +@@ -176,13 +176,13 @@ delegpt_add_target(struct delegpt* dp, struct regional* region, + if(ns->got4 && ns->got6) + ns->resolved = 1; + } +- return delegpt_add_addr(dp, region, addr, addrlen, bogus, lame); ++ return delegpt_add_addr(dp, region, addr, addrlen, bogus, lame, additions); + } + + int + delegpt_add_addr(struct delegpt* dp, struct regional* region, + struct sockaddr_storage* addr, socklen_t addrlen, uint8_t bogus, +- uint8_t lame) ++ uint8_t lame, int* additions) + { + struct delegpt_addr* a; + log_assert(!dp->dp_type_mlc); +@@ -195,6 +195,9 @@ delegpt_add_addr(struct delegpt* dp, struct regional* region, + return 1; + } + ++ if(additions) ++ *additions = 1; ++ + a = (struct delegpt_addr*)regional_alloc(region, + sizeof(struct delegpt_addr)); + if(!a) +@@ -370,10 +373,10 @@ delegpt_from_message(struct dns_msg* msg, struct regional* region) + continue; + + if(ntohs(s->rk.type) == LDNS_RR_TYPE_A) { +- if(!delegpt_add_rrset_A(dp, region, s, 0)) ++ if(!delegpt_add_rrset_A(dp, region, s, 0, NULL)) + return NULL; + } else if(ntohs(s->rk.type) == LDNS_RR_TYPE_AAAA) { +- if(!delegpt_add_rrset_AAAA(dp, region, s, 0)) ++ if(!delegpt_add_rrset_AAAA(dp, region, s, 0, NULL)) + return NULL; + } + } +@@ -404,7 +407,7 @@ delegpt_rrset_add_ns(struct delegpt* dp, struct regional* region, + + int + delegpt_add_rrset_A(struct delegpt* dp, struct regional* region, +- struct ub_packed_rrset_key* ak, uint8_t lame) ++ struct ub_packed_rrset_key* ak, uint8_t lame, int* additions) + { + struct packed_rrset_data* d=(struct packed_rrset_data*)ak->entry.data; + size_t i; +@@ -420,7 +423,7 @@ delegpt_add_rrset_A(struct delegpt* dp, struct regional* region, + memmove(&sa.sin_addr, d->rr_data[i]+2, INET_SIZE); + if(!delegpt_add_target(dp, region, ak->rk.dname, + ak->rk.dname_len, (struct sockaddr_storage*)&sa, +- len, (d->security==sec_status_bogus), lame)) ++ len, (d->security==sec_status_bogus), lame, additions)) + return 0; + } + return 1; +@@ -428,7 +431,7 @@ delegpt_add_rrset_A(struct delegpt* dp, struct regional* region, + + int + delegpt_add_rrset_AAAA(struct delegpt* dp, struct regional* region, +- struct ub_packed_rrset_key* ak, uint8_t lame) ++ struct ub_packed_rrset_key* ak, uint8_t lame, int* additions) + { + struct packed_rrset_data* d=(struct packed_rrset_data*)ak->entry.data; + size_t i; +@@ -444,7 +447,7 @@ delegpt_add_rrset_AAAA(struct delegpt* dp, struct regional* region, + memmove(&sa.sin6_addr, d->rr_data[i]+2, INET6_SIZE); + if(!delegpt_add_target(dp, region, ak->rk.dname, + ak->rk.dname_len, (struct sockaddr_storage*)&sa, +- len, (d->security==sec_status_bogus), lame)) ++ len, (d->security==sec_status_bogus), lame, additions)) + return 0; + } + return 1; +@@ -452,20 +455,32 @@ delegpt_add_rrset_AAAA(struct delegpt* dp, struct regional* region, + + int + delegpt_add_rrset(struct delegpt* dp, struct regional* region, +- struct ub_packed_rrset_key* rrset, uint8_t lame) ++ struct ub_packed_rrset_key* rrset, uint8_t lame, int* additions) + { + if(!rrset) + return 1; + if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_NS) + return delegpt_rrset_add_ns(dp, region, rrset, lame); + else if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_A) +- return delegpt_add_rrset_A(dp, region, rrset, lame); ++ return delegpt_add_rrset_A(dp, region, rrset, lame, additions); + else if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_AAAA) +- return delegpt_add_rrset_AAAA(dp, region, rrset, lame); ++ return delegpt_add_rrset_AAAA(dp, region, rrset, lame, additions); + log_warn("Unknown rrset type added to delegpt"); + return 1; + } + ++void delegpt_mark_neg(struct delegpt_ns* ns, uint16_t qtype) ++{ ++ if(ns) { ++ if(qtype == LDNS_RR_TYPE_A) ++ ns->got4 = 2; ++ else if(qtype == LDNS_RR_TYPE_AAAA) ++ ns->got6 = 2; ++ if(ns->got4 && ns->got6) ++ ns->resolved = 1; ++ } ++} ++ + void delegpt_add_neg_msg(struct delegpt* dp, struct msgreply_entry* msg) + { + struct reply_info* rep = (struct reply_info*)msg->entry.data; +@@ -475,14 +490,7 @@ void delegpt_add_neg_msg(struct delegpt* dp, struct msgreply_entry* msg) + if(FLAGS_GET_RCODE(rep->flags) != 0 || rep->an_numrrsets == 0) { + struct delegpt_ns* ns = delegpt_find_ns(dp, msg->key.qname, + msg->key.qname_len); +- if(ns) { +- if(msg->key.qtype == LDNS_RR_TYPE_A) +- ns->got4 = 1; +- else if(msg->key.qtype == LDNS_RR_TYPE_AAAA) +- ns->got6 = 1; +- if(ns->got4 && ns->got6) +- ns->resolved = 1; +- } ++ delegpt_mark_neg(ns, msg->key.qtype); + } + } + +diff --git a/iterator/iter_delegpt.h b/iterator/iter_delegpt.h +index 4bd79c8..e640fc6 100644 +--- a/iterator/iter_delegpt.h ++++ b/iterator/iter_delegpt.h +@@ -102,9 +102,10 @@ struct delegpt_ns { + * and marked true if got4 and got6 are both true. + */ + int resolved; +- /** if the ipv4 address is in the delegpt */ ++ /** if the ipv4 address is in the delegpt, 0=not, 1=yes 2=negative, ++ * * negative means it was done, but no content. */ + uint8_t got4; +- /** if the ipv6 address is in the delegpt */ ++ /** if the ipv6 address is in the delegpt, 0=not, 1=yes 2=negative */ + uint8_t got6; + /** + * If the name is parent-side only and thus dispreferred. +@@ -209,11 +210,12 @@ int delegpt_rrset_add_ns(struct delegpt* dp, struct regional* regional, + * @param addrlen: the length of addr. + * @param bogus: security status for the address, pass true if bogus. + * @param lame: address is lame. ++ * @param additions: will be set to 1 if a new address is added + * @return false on error. + */ + int delegpt_add_target(struct delegpt* dp, struct regional* regional, + uint8_t* name, size_t namelen, struct sockaddr_storage* addr, +- socklen_t addrlen, uint8_t bogus, uint8_t lame); ++ socklen_t addrlen, uint8_t bogus, uint8_t lame, int* additions); + + /** + * Add A RRset to delegpt. +@@ -221,21 +223,23 @@ int delegpt_add_target(struct delegpt* dp, struct regional* regional, + * @param regional: where to allocate the info. + * @param rrset: RRset A to add. + * @param lame: rrset is lame, disprefer it. ++ * @param additions: will be set to 1 if a new address is added + * @return 0 on alloc error. + */ + int delegpt_add_rrset_A(struct delegpt* dp, struct regional* regional, +- struct ub_packed_rrset_key* rrset, uint8_t lame); ++ struct ub_packed_rrset_key* rrset, uint8_t lame, int* additions); + + /** + * Add AAAA RRset to delegpt. + * @param dp: delegation point. + * @param regional: where to allocate the info. + * @param rrset: RRset AAAA to add. ++ * @param additions: will be set to 1 if a new address is added + * @param lame: rrset is lame, disprefer it. + * @return 0 on alloc error. + */ + int delegpt_add_rrset_AAAA(struct delegpt* dp, struct regional* regional, +- struct ub_packed_rrset_key* rrset, uint8_t lame); ++ struct ub_packed_rrset_key* rrset, uint8_t lame, int* additions); + + /** + * Add any RRset to delegpt. +@@ -244,10 +248,11 @@ int delegpt_add_rrset_AAAA(struct delegpt* dp, struct regional* regional, + * @param regional: where to allocate the info. + * @param rrset: RRset to add, NS, A, AAAA. + * @param lame: rrset is lame, disprefer it. ++ * @param additions: will be set to 1 if a new address is added + * @return 0 on alloc error. + */ + int delegpt_add_rrset(struct delegpt* dp, struct regional* regional, +- struct ub_packed_rrset_key* rrset, uint8_t lame); ++ struct ub_packed_rrset_key* rrset, uint8_t lame, int* additions); + + /** + * Add address to the delegation point. No servername is associated or checked. +@@ -257,11 +262,13 @@ int delegpt_add_rrset(struct delegpt* dp, struct regional* regional, + * @param addrlen: the length of addr. + * @param bogus: if address is bogus. + * @param lame: if address is lame. ++ * @param additions: will be set to 1 if a new address is added ++ * @return 0 on alloc error. + * @return false on error. + */ + int delegpt_add_addr(struct delegpt* dp, struct regional* regional, + struct sockaddr_storage* addr, socklen_t addrlen, +- uint8_t bogus, uint8_t lame); ++ uint8_t bogus, uint8_t lame, int* additions); + + /** + * Find NS record in name list of delegation point. +@@ -334,6 +341,14 @@ size_t delegpt_count_targets(struct delegpt* dp); + struct delegpt* delegpt_from_message(struct dns_msg* msg, + struct regional* regional); + ++/** ++* Mark negative return in delegation point for specific nameserver. ++* sets the got4 or got6 to negative, updates the ns->resolved. ++* @param ns: the nameserver in the delegpt. ++* @param qtype: A or AAAA (host order). ++*/ ++void delegpt_mark_neg(struct delegpt_ns* ns, uint16_t qtype); ++ + /** + * Add negative message to delegation point. + * @param dp: delegation point. +diff --git a/iterator/iter_scrub.c b/iterator/iter_scrub.c +index 6bdfe41..36ff766 100644 +--- a/iterator/iter_scrub.c ++++ b/iterator/iter_scrub.c +@@ -185,8 +185,9 @@ mark_additional_rrset(sldns_buffer* pkt, struct msg_parse* msg, + /** Get target name of a CNAME */ + static int + parse_get_cname_target(struct rrset_parse* rrset, uint8_t** sname, +- size_t* snamelen) ++ size_t* snamelen, sldns_buffer* pkt) + { ++ size_t oldpos, dlen; + if(rrset->rr_count != 1) { + struct rr_parse* sig; + verbose(VERB_ALGO, "Found CNAME rrset with " +@@ -204,6 +205,19 @@ parse_get_cname_target(struct rrset_parse* rrset, uint8_t** sname, + *sname = rrset->rr_first->ttl_data + sizeof(uint32_t) + + sizeof(uint16_t); /* skip ttl, rdatalen */ + *snamelen = rrset->rr_first->size - sizeof(uint16_t); ++ ++ if(rrset->rr_first->outside_packet) { ++ if(!dname_valid(*sname, *snamelen)) ++ return 0; ++ return 1; ++ } ++ oldpos = sldns_buffer_position(pkt); ++ sldns_buffer_set_position(pkt, (size_t)(*sname - sldns_buffer_begin(pkt))); ++ dlen = pkt_dname_len(pkt); ++ sldns_buffer_set_position(pkt, oldpos); ++ if(dlen == 0) ++ return 0; /* parse fail on the rdata name */ ++ *snamelen = dlen; + return 1; + } + +@@ -215,7 +229,7 @@ synth_cname(uint8_t* qname, size_t qnamelen, struct rrset_parse* dname_rrset, + /* we already know that sname is a strict subdomain of DNAME owner */ + uint8_t* dtarg = NULL; + size_t dtarglen; +- if(!parse_get_cname_target(dname_rrset, &dtarg, &dtarglen)) ++ if(!parse_get_cname_target(dname_rrset, &dtarg, &dtarglen, pkt)) + return 0; + log_assert(qnamelen > dname_rrset->dname_len); + /* DNAME from com. to net. with qname example.com. -> example.net. */ +@@ -372,7 +386,7 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg, + /* check next cname */ + uint8_t* t = NULL; + size_t tlen = 0; +- if(!parse_get_cname_target(nx, &t, &tlen)) ++ if(!parse_get_cname_target(nx, &t, &tlen, pkt)) + return 0; + if(dname_pkt_compare(pkt, alias, t) == 0) { + /* it's OK and better capitalized */ +@@ -423,7 +437,7 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg, + size_t tlen = 0; + if(synth_cname(sname, snamelen, nx, alias, + &aliaslen, pkt) && +- parse_get_cname_target(rrset, &t, &tlen) && ++ parse_get_cname_target(rrset, &t, &tlen, pkt) && + dname_pkt_compare(pkt, alias, t) == 0) { + /* the synthesized CNAME equals the + * current CNAME. This CNAME is the +@@ -441,7 +455,7 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg, + } + + /* move to next name in CNAME chain */ +- if(!parse_get_cname_target(rrset, &sname, &snamelen)) ++ if(!parse_get_cname_target(rrset, &sname, &snamelen, pkt)) + return 0; + prev = rrset; + rrset = rrset->rrset_all_next; +diff --git a/iterator/iter_utils.c b/iterator/iter_utils.c +index 70cab40..fc7d625 100644 +--- a/iterator/iter_utils.c ++++ b/iterator/iter_utils.c +@@ -997,7 +997,7 @@ int iter_lookup_parent_glue_from_cache(struct module_env* env, + log_rrset_key(VERB_ALGO, "found parent-side", akey); + ns->done_pside4 = 1; + /* a negative-cache-element has no addresses it adds */ +- if(!delegpt_add_rrset_A(dp, region, akey, 1)) ++ if(!delegpt_add_rrset_A(dp, region, akey, 1, NULL)) + log_err("malloc failure in lookup_parent_glue"); + lock_rw_unlock(&akey->entry.lock); + } +@@ -1009,7 +1009,7 @@ int iter_lookup_parent_glue_from_cache(struct module_env* env, + log_rrset_key(VERB_ALGO, "found parent-side", akey); + ns->done_pside6 = 1; + /* a negative-cache-element has no addresses it adds */ +- if(!delegpt_add_rrset_AAAA(dp, region, akey, 1)) ++ if(!delegpt_add_rrset_AAAA(dp, region, akey, 1, NULL)) + log_err("malloc failure in lookup_parent_glue"); + lock_rw_unlock(&akey->entry.lock); + } +diff --git a/iterator/iterator.c b/iterator/iterator.c +index 01ac883..7fd4d32 100644 +--- a/iterator/iterator.c ++++ b/iterator/iterator.c +@@ -68,6 +68,8 @@ + #include "sldns/parseutil.h" + #include "sldns/sbuffer.h" + ++static void target_count_increase_nx(struct iter_qstate* iq, int num); ++ + int + iter_init(struct module_env* env, int id) + { +@@ -146,6 +148,7 @@ iter_new(struct module_qstate* qstate, int id) + iq->sent_count = 0; + iq->ratelimit_ok = 0; + iq->target_count = NULL; ++ iq->dp_target_count = 0; + iq->wait_priming_stub = 0; + iq->refetch_glue = 0; + iq->dnssec_expected = 0; +@@ -217,6 +220,7 @@ final_state(struct iter_qstate* iq) + static void + error_supers(struct module_qstate* qstate, int id, struct module_qstate* super) + { ++ struct iter_env* ie = (struct iter_env*)qstate->env->modinfo[id]; + struct iter_qstate* super_iq = (struct iter_qstate*)super->minfo[id]; + + if(qstate->qinfo.qtype == LDNS_RR_TYPE_A || +@@ -241,7 +245,11 @@ error_supers(struct module_qstate* qstate, int id, struct module_qstate* super) + super->region, super_iq->dp)) + log_err("out of memory adding missing"); + } ++ delegpt_mark_neg(dpns, qstate->qinfo.qtype); + dpns->resolved = 1; /* mark as failed */ ++ if((dpns->got4 == 2 || !ie->supports_ipv4) && ++ (dpns->got6 == 2 || !ie->supports_ipv6)) ++ target_count_increase_nx(super_iq, 1); + } + if(qstate->qinfo.qtype == LDNS_RR_TYPE_NS) { + /* prime failed to get delegation */ +@@ -574,7 +582,7 @@ static void + target_count_create(struct iter_qstate* iq) + { + if(!iq->target_count) { +- iq->target_count = (int*)calloc(2, sizeof(int)); ++ iq->target_count = (int*)calloc(3, sizeof(int)); + /* if calloc fails we simply do not track this number */ + if(iq->target_count) + iq->target_count[0] = 1; +@@ -587,6 +595,15 @@ target_count_increase(struct iter_qstate* iq, int num) + target_count_create(iq); + if(iq->target_count) + iq->target_count[1] += num; ++ iq->dp_target_count++; ++} ++ ++static void ++target_count_increase_nx(struct iter_qstate* iq, int num) ++{ ++ target_count_create(iq); ++ if(iq->target_count) ++ iq->target_count[2] += num; + } + + /** +@@ -609,13 +626,15 @@ target_count_increase(struct iter_qstate* iq, int num) + * @param subq_ret: if newly allocated, the subquerystate, or NULL if it does + * not need initialisation. + * @param v: if true, validation is done on the subquery. ++ * @param detached: true if this qstate should not attach to the subquery + * @return false on error (malloc). + */ + static int + generate_sub_request(uint8_t* qname, size_t qnamelen, uint16_t qtype, + uint16_t qclass, struct module_qstate* qstate, int id, + struct iter_qstate* iq, enum iter_state initial_state, +- enum iter_state finalstate, struct module_qstate** subq_ret, int v) ++ enum iter_state finalstate, struct module_qstate** subq_ret, int v, ++ int detached) + { + struct module_qstate* subq = NULL; + struct iter_qstate* subiq = NULL; +@@ -642,12 +661,24 @@ generate_sub_request(uint8_t* qname, size_t qnamelen, uint16_t qtype, + valrec = 1; + } + +- /* attach subquery, lookup existing or make a new one */ +- fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub)); +- if(!(*qstate->env->attach_sub)(qstate, &qinf, qflags, prime, valrec, +- &subq)) { +- return 0; +- } ++ if(detached) { ++ struct mesh_state* sub = NULL; ++ fptr_ok(fptr_whitelist_modenv_add_sub( ++ qstate->env->add_sub)); ++ if(!(*qstate->env->add_sub)(qstate, &qinf, ++ qflags, prime, valrec, &subq, &sub)){ ++ return 0; ++ } ++ } ++ else { ++ /* attach subquery, lookup existing or make a new one */ ++ fptr_ok(fptr_whitelist_modenv_attach_sub( ++ qstate->env->attach_sub)); ++ if(!(*qstate->env->attach_sub)(qstate, &qinf, qflags, prime, ++ valrec, &subq)) { ++ return 0; ++ } ++ } + *subq_ret = subq; + if(subq) { + /* initialise the new subquery */ +@@ -669,6 +700,7 @@ generate_sub_request(uint8_t* qname, size_t qnamelen, uint16_t qtype, + subiq->target_count = iq->target_count; + if(iq->target_count) + iq->target_count[0] ++; /* extra reference */ ++ subiq->dp_target_count = 0; + subiq->num_current_queries = 0; + subiq->depth = iq->depth+1; + outbound_list_init(&subiq->outlist); +@@ -712,7 +744,7 @@ prime_root(struct module_qstate* qstate, struct iter_qstate* iq, int id, + * the normal INIT state logic (which would cause an infloop). */ + if(!generate_sub_request((uint8_t*)"\000", 1, LDNS_RR_TYPE_NS, + qclass, qstate, id, iq, QUERYTARGETS_STATE, PRIME_RESP_STATE, +- &subq, 0)) { ++ &subq, 0, 0)) { + verbose(VERB_ALGO, "could not prime root"); + return 0; + } +@@ -797,7 +829,7 @@ prime_stub(struct module_qstate* qstate, struct iter_qstate* iq, int id, + * redundant INIT state processing. */ + if(!generate_sub_request(stub_dp->name, stub_dp->namelen, + LDNS_RR_TYPE_NS, qclass, qstate, id, iq, +- QUERYTARGETS_STATE, PRIME_RESP_STATE, &subq, 0)) { ++ QUERYTARGETS_STATE, PRIME_RESP_STATE, &subq, 0, 0)) { + verbose(VERB_ALGO, "could not prime stub"); + (void)error_response(qstate, id, LDNS_RCODE_SERVFAIL); + return 1; /* return 1 to make module stop, with error */ +@@ -878,7 +910,7 @@ generate_a_aaaa_check(struct module_qstate* qstate, struct iter_qstate* iq, + if(!generate_sub_request(s->rk.dname, s->rk.dname_len, + ntohs(s->rk.type), ntohs(s->rk.rrset_class), + qstate, id, iq, +- INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1)) { ++ INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1, 0)) { + verbose(VERB_ALGO, "could not generate addr check"); + return; + } +@@ -919,7 +951,7 @@ generate_ns_check(struct module_qstate* qstate, struct iter_qstate* iq, int id) + iq->dp->name, LDNS_RR_TYPE_NS, iq->qchase.qclass); + if(!generate_sub_request(iq->dp->name, iq->dp->namelen, + LDNS_RR_TYPE_NS, iq->qchase.qclass, qstate, id, iq, +- INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1)) { ++ INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1, 0)) { + verbose(VERB_ALGO, "could not generate ns check"); + return; + } +@@ -976,7 +1008,7 @@ generate_dnskey_prefetch(struct module_qstate* qstate, + iq->dp->name, LDNS_RR_TYPE_DNSKEY, iq->qchase.qclass); + if(!generate_sub_request(iq->dp->name, iq->dp->namelen, + LDNS_RR_TYPE_DNSKEY, iq->qchase.qclass, qstate, id, iq, +- INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0)) { ++ INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0, 0)) { + /* we'll be slower, but it'll work */ + verbose(VERB_ALGO, "could not generate dnskey prefetch"); + return; +@@ -1150,6 +1182,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, + iq->refetch_glue = 0; + iq->query_restart_count++; + iq->sent_count = 0; ++ iq->dp_target_count = 0; + sock_list_insert(&qstate->reply_origin, NULL, 0, qstate->region); + if(qstate->env->cfg->qname_minimisation) + iq->minimisation_state = INIT_MINIMISE_STATE; +@@ -1468,7 +1501,7 @@ generate_parentside_target_query(struct module_qstate* qstate, + { + struct module_qstate* subq; + if(!generate_sub_request(name, namelen, qtype, qclass, qstate, +- id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0)) ++ id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0, 0)) + return 0; + if(subq) { + struct iter_qstate* subiq = +@@ -1519,7 +1552,7 @@ generate_target_query(struct module_qstate* qstate, struct iter_qstate* iq, + { + struct module_qstate* subq; + if(!generate_sub_request(name, namelen, qtype, qclass, qstate, +- id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0)) ++ id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0, 0)) + return 0; + log_nametypeclass(VERB_QUERY, "new target", name, qtype, qclass); + return 1; +@@ -1558,6 +1591,14 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq, + "number of glue fetches %d", s, iq->target_count[1]); + return 0; + } ++ if(iq->dp_target_count > MAX_DP_TARGET_COUNT) { ++ char s[LDNS_MAX_DOMAINLEN+1]; ++ dname_str(qstate->qinfo.qname, s); ++ verbose(VERB_QUERY, "request %s has exceeded the maximum " ++ "number of glue fetches %d to a single delegation point", ++ s, iq->dp_target_count); ++ return 0; ++ } + + iter_mark_cycle_targets(qstate, iq->dp); + missing = (int)delegpt_count_missing_targets(iq->dp); +@@ -1670,7 +1711,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq, + for(a = p->target_list; a; a=a->next_target) { + (void)delegpt_add_addr(iq->dp, qstate->region, + &a->addr, a->addrlen, a->bogus, +- a->lame); ++ a->lame, NULL); + } + } + iq->dp->has_parent_side_NS = 1; +@@ -1687,6 +1728,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq, + iq->refetch_glue = 1; + iq->query_restart_count++; + iq->sent_count = 0; ++ iq->dp_target_count = 0; + if(qstate->env->cfg->qname_minimisation) + iq->minimisation_state = INIT_MINIMISE_STATE; + return next_state(iq, INIT_REQUEST_STATE); +@@ -1841,7 +1883,7 @@ processDSNSFind(struct module_qstate* qstate, struct iter_qstate* iq, int id) + iq->dsns_point, LDNS_RR_TYPE_NS, iq->qchase.qclass); + if(!generate_sub_request(iq->dsns_point, iq->dsns_point_len, + LDNS_RR_TYPE_NS, iq->qchase.qclass, qstate, id, iq, +- INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0)) { ++ INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0, 0)) { + return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL); + } + +@@ -1893,6 +1935,15 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, + "number of sends with %d", iq->sent_count); + return error_response(qstate, id, LDNS_RCODE_SERVFAIL); + } ++ ++ if(iq->target_count && iq->target_count[2] > MAX_TARGET_NX) { ++ verbose(VERB_QUERY, "request has exceeded the maximum " ++ " number of nxdomain nameserver lookups with %d", ++ iq->target_count[2]); ++ errinf(qstate, "exceeded the maximum nameserver nxdomains"); ++ return error_response(qstate, id, LDNS_RCODE_SERVFAIL); ++ } ++ + + /* Make sure we have a delegation point, otherwise priming failed + * or another failure occurred */ +@@ -2160,12 +2211,41 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, + iq->qinfo_out.qtype, iq->qinfo_out.qclass, + qstate->query_flags, qstate->region, + qstate->env->scratch); +- if(msg && msg->rep->an_numrrsets == 0 +- && FLAGS_GET_RCODE(msg->rep->flags) == ++ if(msg && FLAGS_GET_RCODE(msg->rep->flags) == + LDNS_RCODE_NOERROR) + /* no need to send query if it is already +- * cached as NOERROR/NODATA */ ++ * cached as NOERROR */ + return 1; ++ if(msg && FLAGS_GET_RCODE(msg->rep->flags) == ++ LDNS_RCODE_NXDOMAIN && ++ qstate->env->need_to_validate && ++ qstate->env->cfg->harden_below_nxdomain) { ++ if(msg->rep->security == sec_status_secure) { ++ iq->response = msg; ++ return final_state(iq); ++ } ++ if(msg->rep->security == sec_status_unchecked) { ++ struct module_qstate* subq = NULL; ++ if(!generate_sub_request( ++ iq->qinfo_out.qname, ++ iq->qinfo_out.qname_len, ++ iq->qinfo_out.qtype, ++ iq->qinfo_out.qclass, ++ qstate, id, iq, ++ INIT_REQUEST_STATE, ++ FINISHED_STATE, &subq, 1, 1)) ++ verbose(VERB_ALGO, ++ "could not validate NXDOMAIN " ++ "response"); ++ } ++ } ++ if(msg && FLAGS_GET_RCODE(msg->rep->flags) == ++ LDNS_RCODE_NXDOMAIN) { ++ /* return and add a label in the next ++ * minimisation iteration. ++ */ ++ return 1; ++ } + } + } + if(iq->minimisation_state == SKIP_MINIMISE_STATE) { +@@ -2216,6 +2296,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, + outbound_list_insert(&iq->outlist, outq); + iq->num_current_queries++; + iq->sent_count++; ++ iq->dp_target_count = 0; + qstate->ext_state[id] = module_wait_reply; + + return 0; +@@ -2404,7 +2485,8 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, + /* Make subrequest to validate intermediate + * NXDOMAIN if harden-below-nxdomain is + * enabled. */ +- if(qstate->env->cfg->harden_below_nxdomain) { ++ if(qstate->env->cfg->harden_below_nxdomain && ++ qstate->env->need_to_validate) { + struct module_qstate* subq = NULL; + log_query_info(VERB_QUERY, + "schedule NXDOMAIN validation:", +@@ -2416,7 +2498,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, + iq->response->qinfo.qclass, + qstate, id, iq, + INIT_REQUEST_STATE, +- FINISHED_STATE, &subq, 1)) ++ FINISHED_STATE, &subq, 1, 1)) + verbose(VERB_ALGO, + "could not validate NXDOMAIN " + "response"); +@@ -2499,6 +2581,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, + /* Count this as a referral. */ + iq->referral_count++; + iq->sent_count = 0; ++ iq->dp_target_count = 0; + /* see if the next dp is a trust anchor, or a DS was sent + * along, indicating dnssec is expected for next zone */ + iq->dnssec_expected = iter_indicates_dnssec(qstate->env, +@@ -2739,7 +2822,7 @@ processPrimeResponse(struct module_qstate* qstate, int id) + if(!generate_sub_request(qstate->qinfo.qname, + qstate->qinfo.qname_len, qstate->qinfo.qtype, + qstate->qinfo.qclass, qstate, id, iq, +- INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1)) { ++ INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1, 0)) { + verbose(VERB_ALGO, "could not generate prime check"); + } + generate_a_aaaa_check(qstate, iq, id); +@@ -2767,6 +2850,7 @@ static void + processTargetResponse(struct module_qstate* qstate, int id, + struct module_qstate* forq) + { ++ struct iter_env* ie = (struct iter_env*)qstate->env->modinfo[id]; + struct iter_qstate* iq = (struct iter_qstate*)qstate->minfo[id]; + struct iter_qstate* foriq = (struct iter_qstate*)forq->minfo[id]; + struct ub_packed_rrset_key* rrset; +@@ -2804,7 +2888,7 @@ processTargetResponse(struct module_qstate* qstate, int id, + log_rrset_key(VERB_ALGO, "add parentside glue to dp", + iq->pside_glue); + if(!delegpt_add_rrset(foriq->dp, forq->region, +- iq->pside_glue, 1)) ++ iq->pside_glue, 1, NULL)) + log_err("out of memory adding pside glue"); + } + +@@ -2815,6 +2899,7 @@ processTargetResponse(struct module_qstate* qstate, int id, + * response type was ANSWER. */ + rrset = reply_find_answer_rrset(&iq->qchase, qstate->return_msg->rep); + if(rrset) { ++ int additions = 0; + /* if CNAMEs have been followed - add new NS to delegpt. */ + /* BTW. RFC 1918 says NS should not have got CNAMEs. Robust. */ + if(!delegpt_find_ns(foriq->dp, rrset->rk.dname, +@@ -2826,13 +2911,23 @@ processTargetResponse(struct module_qstate* qstate, int id, + } + /* if dpns->lame then set the address(es) lame too */ + if(!delegpt_add_rrset(foriq->dp, forq->region, rrset, +- dpns->lame)) ++ dpns->lame, &additions)) + log_err("out of memory adding targets"); ++ if(!additions) { ++ /* no new addresses, increase the nxns counter, like ++ * this could be a list of wildcards with no new ++ * addresses */ ++ target_count_increase_nx(foriq, 1); ++ } + verbose(VERB_ALGO, "added target response"); + delegpt_log(VERB_ALGO, foriq->dp); + } else { + verbose(VERB_ALGO, "iterator TargetResponse failed"); ++ delegpt_mark_neg(dpns, qstate->qinfo.qtype); + dpns->resolved = 1; /* fail the target */ ++ if((dpns->got4 == 2 || !ie->supports_ipv4) && ++ (dpns->got6 == 2 || !ie->supports_ipv6)) ++ target_count_increase_nx(foriq, 1); + } + } + +@@ -3003,7 +3098,7 @@ processCollectClass(struct module_qstate* qstate, int id) + qstate->qinfo.qname_len, qstate->qinfo.qtype, + c, qstate, id, iq, INIT_REQUEST_STATE, + FINISHED_STATE, &subq, +- (int)!(qstate->query_flags&BIT_CD))) { ++ (int)!(qstate->query_flags&BIT_CD), 0)) { + return error_response(qstate, id, + LDNS_RCODE_SERVFAIL); + } +diff --git a/iterator/iterator.h b/iterator/iterator.h +index 75aafee..3534b5c 100644 +--- a/iterator/iterator.h ++++ b/iterator/iterator.h +@@ -55,6 +55,11 @@ struct rbtree_type; + + /** max number of targets spawned for a query and its subqueries */ + #define MAX_TARGET_COUNT 64 ++/** max number of target lookups per qstate, per delegation point */ ++#define MAX_DP_TARGET_COUNT 16 ++/** max number of nxdomains allowed for target lookups for a query and ++ * its subqueries */ ++#define MAX_TARGET_NX 5 + /** 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 */ +@@ -305,9 +310,14 @@ struct iter_qstate { + int sent_count; + + /** number of target queries spawned in [1], for this query and its +- * subqueries, the malloced-array is shared, [0] refcount. */ ++ * subqueries, the malloced-array is shared, [0] refcount. ++ * in [2] the number of nxdomains is counted. */ + int* target_count; + ++ /** number of target lookups per delegation point. Reset to 0 after ++ * receiving referral answer. Not shared with subqueries. */ ++ int dp_target_count; ++ + /** if true, already tested for ratelimiting and passed the test */ + int ratelimit_ok; + +diff --git a/services/cache/dns.c b/services/cache/dns.c +index da43c50..ce138f1 100644 +--- a/services/cache/dns.c ++++ b/services/cache/dns.c +@@ -223,7 +223,7 @@ find_add_addrs(struct module_env* env, uint16_t qclass, + akey = rrset_cache_lookup(env->rrset_cache, ns->name, + ns->namelen, LDNS_RR_TYPE_A, qclass, 0, now, 0); + if(akey) { +- if(!delegpt_add_rrset_A(dp, region, akey, 0)) { ++ if(!delegpt_add_rrset_A(dp, region, akey, 0, NULL)) { + lock_rw_unlock(&akey->entry.lock); + return 0; + } +@@ -243,7 +243,7 @@ find_add_addrs(struct module_env* env, uint16_t qclass, + akey = rrset_cache_lookup(env->rrset_cache, ns->name, + ns->namelen, LDNS_RR_TYPE_AAAA, qclass, 0, now, 0); + if(akey) { +- if(!delegpt_add_rrset_AAAA(dp, region, akey, 0)) { ++ if(!delegpt_add_rrset_AAAA(dp, region, akey, 0, NULL)) { + lock_rw_unlock(&akey->entry.lock); + return 0; + } +@@ -277,7 +277,8 @@ cache_fill_missing(struct module_env* env, uint16_t qclass, + akey = rrset_cache_lookup(env->rrset_cache, ns->name, + ns->namelen, LDNS_RR_TYPE_A, qclass, 0, now, 0); + if(akey) { +- if(!delegpt_add_rrset_A(dp, region, akey, ns->lame)) { ++ if(!delegpt_add_rrset_A(dp, region, akey, ns->lame, ++ NULL)) { + lock_rw_unlock(&akey->entry.lock); + return 0; + } +@@ -297,7 +298,8 @@ cache_fill_missing(struct module_env* env, uint16_t qclass, + akey = rrset_cache_lookup(env->rrset_cache, ns->name, + ns->namelen, LDNS_RR_TYPE_AAAA, qclass, 0, now, 0); + if(akey) { +- if(!delegpt_add_rrset_AAAA(dp, region, akey, ns->lame)) { ++ if(!delegpt_add_rrset_AAAA(dp, region, akey, ns->lame, ++ NULL)) { + lock_rw_unlock(&akey->entry.lock); + return 0; + } +diff --git a/util/data/dname.c b/util/data/dname.c +index 517af28..a80a4e2 100644 +--- a/util/data/dname.c ++++ b/util/data/dname.c +@@ -231,17 +231,28 @@ int + dname_pkt_compare(sldns_buffer* pkt, uint8_t* d1, uint8_t* d2) + { + uint8_t len1, len2; ++ int count1 = 0, count2 = 0; + log_assert(pkt && d1 && d2); + len1 = *d1++; + len2 = *d2++; + while( len1 != 0 || len2 != 0 ) { + /* resolve ptrs */ + if(LABEL_IS_PTR(len1)) { ++ if((size_t)PTR_OFFSET(len1, *d1) ++ >= sldns_buffer_limit(pkt)) ++ return -1; ++ if(count1++ > MAX_COMPRESS_PTRS) ++ return -1; + d1 = sldns_buffer_at(pkt, PTR_OFFSET(len1, *d1)); + len1 = *d1++; + continue; + } + if(LABEL_IS_PTR(len2)) { ++ if((size_t)PTR_OFFSET(len2, *d2) ++ >= sldns_buffer_limit(pkt)) ++ return 1; ++ if(count2++ > MAX_COMPRESS_PTRS) ++ return 1; + d2 = sldns_buffer_at(pkt, PTR_OFFSET(len2, *d2)); + len2 = *d2++; + continue; +@@ -276,6 +287,7 @@ dname_query_hash(uint8_t* dname, hashvalue_type h) + uint8_t labuf[LDNS_MAX_LABELLEN+1]; + uint8_t lablen; + int i; ++ int count = 0; + + /* preserve case of query, make hash label by label */ + lablen = *dname++; +@@ -333,6 +345,9 @@ void dname_pkt_copy(sldns_buffer* pkt, uint8_t* to, uint8_t* dname) + while(lablen) { + if(LABEL_IS_PTR(lablen)) { + /* follow pointer */ ++ if((size_t)PTR_OFFSET(lablen, *dname) ++ >= sldns_buffer_limit(pkt)) ++ return; + dname = sldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname)); + lablen = *dname++; + continue; +@@ -357,6 +372,7 @@ void dname_pkt_copy(sldns_buffer* pkt, uint8_t* to, uint8_t* dname) + void dname_print(FILE* out, struct sldns_buffer* pkt, uint8_t* dname) + { + uint8_t lablen; ++ int count = 0; + if(!out) out = stdout; + if(!dname) return; + +@@ -370,6 +386,15 @@ void dname_print(FILE* out, struct sldns_buffer* pkt, uint8_t* dname) + fputs("??compressionptr??", out); + return; + } ++ if((size_t)PTR_OFFSET(lablen, *dname) ++ >= sldns_buffer_limit(pkt)) { ++ fputs("??compressionptr??", out); ++ return; ++ } ++ if(count++ > MAX_COMPRESS_PTRS) { ++ fputs("??compressionptr??", out); ++ return; ++ } + dname = sldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname)); + lablen = *dname++; + continue; +diff --git a/util/data/msgparse.c b/util/data/msgparse.c +index 2887200..783f3a7 100644 +--- a/util/data/msgparse.c ++++ b/util/data/msgparse.c +@@ -55,7 +55,11 @@ smart_compare(sldns_buffer* pkt, uint8_t* dnow, + { + if(LABEL_IS_PTR(*dnow)) { + /* ptr points to a previous dname */ +- uint8_t* p = sldns_buffer_at(pkt, PTR_OFFSET(dnow[0], dnow[1])); ++ uint8_t* p; ++ if((size_t)PTR_OFFSET(dnow[0], dnow[1]) ++ >= sldns_buffer_limit(pkt)) ++ return -1; ++ p = sldns_buffer_at(pkt, PTR_OFFSET(dnow[0], dnow[1])); + if( p == dprfirst || p == dprlast ) + return 0; + /* prev dname is also a ptr, both ptrs are the same. */ diff --git a/SPECS/unbound.spec b/SPECS/unbound.spec index 353575d..cf08cf0 100644 --- a/SPECS/unbound.spec +++ b/SPECS/unbound.spec @@ -12,7 +12,7 @@ Summary: Validating, recursive, and caching DNS(SEC) resolver Name: unbound Version: 1.6.6 -Release: 3%{?dist} +Release: 4%{?dist} License: BSD Url: https://unbound.net/ Source: https://www.unbound.net/downloads/%{name}-%{version}.tar.gz @@ -41,6 +41,7 @@ Patch4: unbound-1.6.3-coverity.patch Patch5: unbound-1.6.6-test-fwd_oneport.patch Patch6: unbound-1.6.6-fix-domain-insecure-for-stub-zone.patch Patch7: unbound-1.6.6-rh1775706.patch +Patch8: unbound-1.6.6-amplifying-an-incoming-query.patch Group: System Environment/Daemons BuildRequires: openssl-devel @@ -142,6 +143,7 @@ Python modules and extensions for unbound %patch5 -p1 -b .test-fwd_oneport %patch6 -p1 -b .domain-insecure %patch7 -p1 -b .rh1775706 +%patch8 -p1 -b .amplifying-an-incoming-query # regrnerate config parser due to new options added echo "#include \"config.h\"" > util/configlexer.c || echo "Failed to create configlexer" @@ -353,6 +355,10 @@ fi %endif %changelog +* Sun May 31 2020 Anna Khaitovich - 1.6.6-4 +- Fix amplifying an incoming query into a large number of queries directed to a target +- Resolves: rhbz#1839172 (CVE-2020-12662), rhbz#1840258 (CVE-2020-12663) + * Tue Dec 03 2019 Petr Menšík - 1.6.6-3 - Lower CPU usage on slow log I/O (#1775706)