From 429959a4c9aa249ddd5ed4190313b61b00c6c69a Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Aug 06 2019 10:57:38 +0000 Subject: import dnsmasq-2.76-9.el7 --- diff --git a/SOURCES/dnsmasq-2.76-dnssec-cache.patch b/SOURCES/dnsmasq-2.76-dnssec-cache.patch new file mode 100644 index 0000000..8752c89 --- /dev/null +++ b/SOURCES/dnsmasq-2.76-dnssec-cache.patch @@ -0,0 +1,548 @@ +From b09d07ee2aa891c5b8dd2469c4a73c9dd61e2384 Mon Sep 17 00:00:00 2001 +From: Simon Kelley +Date: Wed, 25 Oct 2017 17:48:19 +0100 +Subject: [PATCH 1/2] Fix caching logic for validated answers. + +The current logic is naive in the case that there is more than +one RRset in an answer (Typically, when a non-CNAME query is answered +by one or more CNAME RRs, and then then an answer RRset.) + +If all the RRsets validate, then they are cached and marked as validated, +but if any RRset doesn't validate, then the AD flag is not set (good) and +ALL the RRsets are cached marked as not validated. + +This breaks when, eg, the answer contains a validated CNAME, pointing +to a non-validated answer. A subsequent query for the CNAME without do +will get an answer with the AD flag wrongly reset, and worse, the same +query with do will get a cached answer without RRSIGS, rather than +being forwarded. + +The code now records the validation of individual RRsets and that +is used to correctly set the "validated" bits in the cache entries. + +(cherry picked from commit a6004d7f17687ac2455f724d0b57098c413f128d) +--- + src/dnsmasq.c | 2 + + src/dnsmasq.h | 5 +- + src/dnssec.c | 174 +++++++++++++++++++++++++++++--------------------- + src/forward.c | 19 ++++-- + src/rfc1035.c | 58 ++++++++++++----- + 5 files changed, 162 insertions(+), 96 deletions(-) + +diff --git a/src/dnsmasq.c b/src/dnsmasq.c +index 50b2029..f3d2671 100644 +--- a/src/dnsmasq.c ++++ b/src/dnsmasq.c +@@ -118,6 +118,8 @@ int main (int argc, char **argv) + daemon->namebuff = safe_malloc(MAXDNAME * 2); + daemon->keyname = safe_malloc(MAXDNAME * 2); + daemon->workspacename = safe_malloc(MAXDNAME * 2); ++ /* one char flag per possible RR in answer section. */ ++ daemon->rr_status = safe_malloc(256); + } + #endif + +diff --git a/src/dnsmasq.h b/src/dnsmasq.h +index 5a68162..89d138a 100644 +--- a/src/dnsmasq.h ++++ b/src/dnsmasq.h +@@ -1006,6 +1006,7 @@ extern struct daemon { + #ifdef HAVE_DNSSEC + char *keyname; /* MAXDNAME size buffer */ + char *workspacename; /* ditto */ ++ char *rr_status; /* 256 bytes as flags for individual RRs */ + #endif + unsigned int local_answer, queries_forwarded, auth_answer; + struct frec *frec_list; +@@ -1118,7 +1119,7 @@ size_t setup_reply(struct dns_header *header, size_t qlen, + unsigned long local_ttl); + int extract_addresses(struct dns_header *header, size_t qlen, char *namebuff, + time_t now, char **ipsets, int is_sign, int checkrebind, +- int no_cache, int secure, int *doctored); ++ int no_cache_dnssec, int secure, int *doctored, char *rr_status); + size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + struct in_addr local_addr, struct in_addr local_netmask, + time_t now, int ad_reqd, int do_bit, int have_pseudoheader); +@@ -1151,7 +1152,7 @@ size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char + int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t n, char *name, char *keyname, int class); + int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class); + int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class, +- int check_unsigned, int *neganswer, int *nons); ++ int check_unsigned, int *neganswer, int *nons, char *rr_status); + int dnskey_keytag(int alg, int flags, unsigned char *rdata, int rdlen); + size_t filter_rrsigs(struct dns_header *header, size_t plen); + unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name); +diff --git a/src/dnssec.c b/src/dnssec.c +index f45c804..3121eb1 100644 +--- a/src/dnssec.c ++++ b/src/dnssec.c +@@ -1177,8 +1177,7 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char + if (qtype != T_DS || qclass != class) + rc = STAT_BOGUS; + else +- rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons); +- /* Note dnssec_validate_reply() will have cached positive answers */ ++ rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons, NULL); + + if (rc == STAT_INSECURE) + rc = STAT_BOGUS; +@@ -1962,18 +1961,25 @@ static int zone_status(char *name, int class, char *keyname, time_t now) + STAT_INSECURE at least one RRset not validated, because in unsigned zone. + STAT_BOGUS signature is wrong, bad packet, no validation where there should be. + STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname, class in *class) +- STAT_NEED_DS need DS to complete validation (name is returned in keyname) ++ STAT_NEED_DS need DS to complete validation (name is returned in keyname) ++ ++ If non-NULL, rr_status points to a char array which corressponds to the RRs in the ++ answer section (only). This is set to 1 for each RR which is validated, and 0 for any which aren't. + */ + int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, +- int *class, int check_unsigned, int *neganswer, int *nons) ++ int *class, int check_unsigned, int *neganswer, int *nons, char *rr_status) + { + static unsigned char **targets = NULL; + static int target_sz = 0; + + unsigned char *ans_start, *p1, *p2; +- int type1, class1, rdlen1, type2, class2, rdlen2, qclass, qtype, targetidx; ++ int type1, class1, rdlen1 = 0, type2, class2, rdlen2, qclass, qtype, targetidx; + int i, j, rc; ++ int secure = STAT_SECURE; + ++ if (rr_status) ++ memset(rr_status, 0, ntohs(header->ancount)); ++ + if (neganswer) + *neganswer = 0; + +@@ -2030,7 +2036,10 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch + + for (p1 = ans_start, i = 0; i < ntohs(header->ancount) + ntohs(header->nscount); i++) + { +- if (!extract_name(header, plen, &p1, name, 1, 10)) ++ if (i != 0 && !ADD_RDLEN(header, p1, plen, rdlen1)) ++ return STAT_BOGUS; ++ ++ if (!extract_name(header, plen, &p1, name, 1, 10)) + return STAT_BOGUS; /* bad packet */ + + GETSHORT(type1, p1); +@@ -2039,106 +2048,125 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch + GETSHORT(rdlen1, p1); + + /* Don't try and validate RRSIGs! */ +- if (type1 != T_RRSIG) ++ if (type1 == T_RRSIG) ++ continue; ++ ++ /* Check if we've done this RRset already */ ++ for (p2 = ans_start, j = 0; j < i; j++) + { +- /* Check if we've done this RRset already */ +- for (p2 = ans_start, j = 0; j < i; j++) +- { +- if (!(rc = extract_name(header, plen, &p2, name, 0, 10))) +- return STAT_BOGUS; /* bad packet */ +- +- GETSHORT(type2, p2); +- GETSHORT(class2, p2); +- p2 += 4; /* TTL */ +- GETSHORT(rdlen2, p2); +- +- if (type2 == type1 && class2 == class1 && rc == 1) +- break; /* Done it before: name, type, class all match. */ +- +- if (!ADD_RDLEN(header, p2, plen, rdlen2)) +- return STAT_BOGUS; +- } ++ if (!(rc = extract_name(header, plen, &p2, name, 0, 10))) ++ return STAT_BOGUS; /* bad packet */ ++ ++ GETSHORT(type2, p2); ++ GETSHORT(class2, p2); ++ p2 += 4; /* TTL */ ++ GETSHORT(rdlen2, p2); ++ ++ if (type2 == type1 && class2 == class1 && rc == 1) ++ break; /* Done it before: name, type, class all match. */ + ++ if (!ADD_RDLEN(header, p2, plen, rdlen2)) ++ return STAT_BOGUS; ++ } ++ ++ if (j != i) ++ { ++ /* Done already: copy the validation status */ ++ if (rr_status && (i < ntohs(header->ancount))) ++ rr_status[i] = rr_status[j]; ++ } ++ else ++ { + /* Not done, validate now */ +- if (j == i) ++ int sigcnt, rrcnt; ++ char *wildname; ++ ++ if (!explore_rrset(header, plen, class1, type1, name, keyname, &sigcnt, &rrcnt)) ++ return STAT_BOGUS; ++ ++ /* No signatures for RRset. We can be configured to assume this is OK and return a INSECURE result. */ ++ if (sigcnt == 0) + { +- int sigcnt, rrcnt; +- char *wildname; +- +- if (!explore_rrset(header, plen, class1, type1, name, keyname, &sigcnt, &rrcnt)) +- return STAT_BOGUS; +- +- /* No signatures for RRset. We can be configured to assume this is OK and return a INSECURE result. */ +- if (sigcnt == 0) ++ if (check_unsigned) + { +- if (check_unsigned) +- { +- rc = zone_status(name, class1, keyname, now); +- if (rc == STAT_SECURE) +- rc = STAT_BOGUS; +- if (class) +- *class = class1; /* Class for NEED_DS or NEED_KEY */ +- } +- else +- rc = STAT_INSECURE; +- +- return rc; ++ rc = zone_status(name, class1, keyname, now); ++ if (rc == STAT_SECURE) ++ rc = STAT_BOGUS; ++ if (class) ++ *class = class1; /* Class for NEED_DS or NEED_KEY */ + } ++ else ++ rc = STAT_INSECURE; + ++ if (rc != STAT_INSECURE) ++ return rc; ++ } ++ else ++ { + /* explore_rrset() gives us key name from sigs in keyname. + Can't overwrite name here. */ + strcpy(daemon->workspacename, keyname); + rc = zone_status(daemon->workspacename, class1, keyname, now); +- +- if (rc != STAT_SECURE) ++ ++ if (rc == STAT_BOGUS || rc == STAT_NEED_KEY || rc == STAT_NEED_DS) + { + /* Zone is insecure, don't need to validate RRset */ + if (class) + *class = class1; /* Class for NEED_DS or NEED_KEY */ + return rc; +- } +- +- rc = validate_rrset(now, header, plen, class1, type1, sigcnt, rrcnt, name, keyname, &wildname, NULL, 0, 0, 0); ++ } + +- if (rc == STAT_BOGUS || rc == STAT_NEED_KEY || rc == STAT_NEED_DS) +- { +- if (class) +- *class = class1; /* Class for DS or DNSKEY */ +- return rc; +- } +- else ++ /* Zone is insecure, don't need to validate RRset */ ++ if (rc == STAT_SECURE) + { ++ rc = validate_rrset(now, header, plen, class1, type1, sigcnt, ++ rrcnt, name, keyname, &wildname, NULL, 0, 0, 0); ++ ++ if (rc == STAT_BOGUS || rc == STAT_NEED_KEY || rc == STAT_NEED_DS) ++ { ++ if (class) ++ *class = class1; /* Class for DS or DNSKEY */ ++ return rc; ++ } ++ + /* rc is now STAT_SECURE or STAT_SECURE_WILDCARD */ +- ++ ++ /* Note that RR is validated */ ++ if (rr_status && (i < ntohs(header->ancount))) ++ rr_status[i] = 1; ++ + /* Note if we've validated either the answer to the question + or the target of a CNAME. Any not noted will need NSEC or + to be in unsigned space. */ +- + for (j = 0; j flags |= SERV_WARNED_RECURSIVE; + } + ++#ifdef HAVE_DNSSEC ++ if (option_bool(OPT_DNSSEC_VALID)) ++ rr_status = daemon->rr_status; ++#endif ++ + if (daemon->bogus_addr && RCODE(header) != NXDOMAIN && + check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now)) + { +@@ -676,7 +682,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server + cache_secure = 0; + } + +- if (extract_addresses(header, n, daemon->namebuff, now, sets, is_sign, check_rebind, no_cache, cache_secure, &doctored)) ++ if (extract_addresses(header, n, daemon->namebuff, now, sets, is_sign, check_rebind, no_cache, cache_secure, &doctored, rr_status)) + { + my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff); + munged = 1; +@@ -856,7 +862,7 @@ void reply_query(int fd, int family, time_t now) + if (forward->forwardall == 0 || --forward->forwardall == 1 || RCODE(header) != REFUSED) + { + int check_rebind = 0, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0; +- ++ + if (option_bool(OPT_NO_REBIND)) + check_rebind = !(forward->flags & FREC_NOREBIND); + +@@ -896,7 +902,8 @@ void reply_query(int fd, int family, time_t now) + status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class); + else + status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, +- option_bool(OPT_DNSSEC_NO_SIGN), NULL, NULL); ++ option_bool(OPT_DNSSEC_NO_SIGN) && (server->flags & SERV_DO_DNSSEC), ++ NULL, NULL, daemon->rr_status); + } + + /* Can't validate, as we're missing key data. Put this +@@ -1480,7 +1487,9 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si + else if (status == STAT_NEED_DS) + new_status = dnssec_validate_ds(now, header, n, name, keyname, class); + else +- new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, option_bool(OPT_DNSSEC_NO_SIGN), NULL, NULL); ++ new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, ++ option_bool(OPT_DNSSEC_NO_SIGN) && (server->flags & SERV_DO_DNSSEC), ++ NULL, NULL, daemon->rr_status); + + if (new_status != STAT_NEED_DS && new_status != STAT_NEED_KEY) + break; +diff --git a/src/rfc1035.c b/src/rfc1035.c +index f78b5cb..607412f 100644 +--- a/src/rfc1035.c ++++ b/src/rfc1035.c +@@ -571,7 +571,8 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *doc + expired and cleaned out that way. + Return 1 if we reject an address because it look like part of dns-rebinding attack. */ + int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now, +- char **ipsets, int is_sign, int check_rebind, int no_cache_dnssec, int secure, int *doctored) ++ char **ipsets, int is_sign, int check_rebind, int no_cache_dnssec, ++ int secure, int *doctored, char *rr_status) + { + unsigned char *p, *p1, *endrr, *namep; + int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0; +@@ -582,6 +583,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t + #else + (void)ipsets; /* unused */ + #endif ++ + + cache_start_insert(); + +@@ -590,10 +592,16 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t + { + searched_soa = 1; + ttl = find_soa(header, qlen, name, doctored); +-#ifdef HAVE_DNSSEC +- if (*doctored && secure) +- return 0; +-#endif ++ ++ if (*doctored) ++ { ++ if (secure) ++ return 0; ++ if (rr_status) ++ for (i = 0; i < ntohs(header->ancount); i++) ++ if (rr_status[i]) ++ return 0; ++ } + } + + /* go through the questions. */ +@@ -604,7 +612,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t + int found = 0, cname_count = CNAME_CHAIN; + struct crec *cpp = NULL; + int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0; +- int secflag = secure ? F_DNSSECOK : 0; ++ int cname_short = 0; + unsigned long cttl = ULONG_MAX, attl; + + namep = p; +@@ -632,8 +640,9 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t + if (!(p1 = skip_questions(header, qlen))) + return 0; + +- for (j = ntohs(header->ancount); j != 0; j--) ++ for (j = 0; j < ntohs(header->ancount); j++) + { ++ int secflag = 0; + unsigned char *tmp = namep; + /* the loop body overwrites the original name, so get it back here. */ + if (!extract_name(header, qlen, &tmp, name, 1, 0) || +@@ -659,11 +668,21 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t + { + if (!extract_name(header, qlen, &p1, name, 1, 0)) + return 0; +- ++ ++ if (rr_status && rr_status[j]) ++ { ++ /* validated RR anywhere in CNAME chain, don't cache. */ ++ if (cname_short || aqtype == T_CNAME) ++ return 0; ++ ++ secflag = F_DNSSECOK; ++ } ++ + if (aqtype == T_CNAME) + { +- if (!cname_count-- || secure) +- return 0; /* looped CNAMES, or DNSSEC, which we can't cache. */ ++ if (!cname_count--) ++ return 0; /* looped CNAMES, we can't cache. */ ++ cname_short = 1; + goto cname_loop; + } + +@@ -685,7 +704,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t + ttl = find_soa(header, qlen, NULL, doctored); + } + if (ttl) +- cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags | secflag); ++ cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags | (secure ? F_DNSSECOK : 0)); + } + } + else +@@ -713,8 +732,10 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t + if (!(p1 = skip_questions(header, qlen))) + return 0; + +- for (j = ntohs(header->ancount); j != 0; j--) ++ for (j = 0; j < ntohs(header->ancount); j++) + { ++ int secflag = 0; ++ + if (!(res = extract_name(header, qlen, &p1, name, 0, 10))) + return 0; /* bad packet */ + +@@ -731,6 +752,10 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t + + if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == qtype)) + { ++#ifdef HAVE_DNSSEC ++ if (rr_status && rr_status[j]) ++ secflag = F_DNSSECOK; ++#endif + if (aqtype == T_CNAME) + { + if (!cname_count--) +@@ -822,7 +847,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t + pointing at this, inherit its TTL */ + if (ttl || cpp) + { +- newc = cache_insert(name, NULL, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags | secflag); ++ newc = cache_insert(name, NULL, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0)); + if (newc && cpp) + { + cpp->addr.cname.target.cache = newc; +@@ -937,7 +962,7 @@ int check_for_local_domain(char *name, time_t now) + /* Note: the call to cache_find_by_name is intended to find any record which matches + ie A, AAAA, CNAME. */ + +- if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME |F_NO_RR)) && ++ if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME | F_NO_RR)) && + (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))) + return 1; + +@@ -1689,8 +1714,9 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + + if (qtype == T_CNAME || qtype == T_ANY) + { +- if ((crecp = cache_find_by_name(NULL, name, now, F_CNAME)) && +- (qtype == T_CNAME || (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG | (dryrun ? F_NO_RR : 0))))) ++ if ((crecp = cache_find_by_name(NULL, name, now, F_CNAME | (dryrun ? F_NO_RR : 0))) && ++ (qtype == T_CNAME || (crecp->flags & F_CONFIG)) && ++ ((crecp->flags & F_CONFIG) || !do_bit || !(crecp->flags & F_DNSSECOK))) + { + if (!(crecp->flags & F_DNSSECOK)) + sec_data = 0; +-- +2.20.1 + diff --git a/SOURCES/dnsmasq-2.76-dnssec-passthru.patch b/SOURCES/dnsmasq-2.76-dnssec-passthru.patch new file mode 100644 index 0000000..b8e4365 --- /dev/null +++ b/SOURCES/dnsmasq-2.76-dnssec-passthru.patch @@ -0,0 +1,75 @@ +From 5fe8a0fc4d817fa6be42cd28c40752e38ee678b1 Mon Sep 17 00:00:00 2001 +From: Simon Kelley +Date: Fri, 29 Jun 2018 14:39:41 +0100 +Subject: [PATCH 2/2] Fix sometimes missing DNSSEC RRs when DNSSEC validation + not enabled. + +Dnsmasq does pass on the do-bit, and return DNSSEC RRs, irrespective +of of having DNSSEC validation compiled in or enabled. + +The thing to understand here is that the cache does not store all the +DNSSEC RRs, and dnsmasq doesn't have the (very complex) logic required +to determine the set of DNSSEC RRs required in an answer. Therefore if +the client wants the DNSSEC RRs, the query can not be answered from +the cache. When DNSSEC validation is enabled, any query with the +do-bit set is never answered from the cache, unless the domain is +known not to be signed: the query is always forwarded. This ensures +that the DNSEC RRs are included. + +The same thing should be true when DNSSEC validation is not enabled, +but there's a bug in the logic. + +line 1666 of src/rfc1035.c looks like this + + if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || !do_bit || !(crecp->flags & F_DNSSECOK)) + +{ ...answer from cache ... } + +So local stuff (hosts, DHCP, ) get answered. If the do_bit is not set +then the query is answered, and if the domain is known not to be +signed, the query is answered. + +Unfortunately, if DNSSEC validation is not turned on then the +F_DNSSECOK bit is not valid, and it's always zero, so the question +always gets answered from the cache, even when the do-bit is set. + +This code should look like that at line 1468, dealing with PTR queries + + if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || + !do_bit || + (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK))) + +where the F_DNSSECOK bit is only used when validation is enabled. + +(cherry picked from commit a997ca0da044719a0ce8a232d14da8b30022592b) +--- + src/rfc1035.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/src/rfc1035.c b/src/rfc1035.c +index 607412f..96acae9 100644 +--- a/src/rfc1035.c ++++ b/src/rfc1035.c +@@ -1632,7 +1632,9 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + } + + /* If the client asked for DNSSEC don't use cached data. */ +- if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || !do_bit || !(crecp->flags & F_DNSSECOK)) ++ if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || ++ !do_bit || ++ (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK))) + do + { + /* don't answer wildcard queries with data not from /etc/hosts +@@ -1716,7 +1718,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, + { + if ((crecp = cache_find_by_name(NULL, name, now, F_CNAME | (dryrun ? F_NO_RR : 0))) && + (qtype == T_CNAME || (crecp->flags & F_CONFIG)) && +- ((crecp->flags & F_CONFIG) || !do_bit || !(crecp->flags & F_DNSSECOK))) ++ ((crecp->flags & F_CONFIG) || !do_bit || (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK)))) + { + if (!(crecp->flags & F_DNSSECOK)) + sec_data = 0; +-- +2.20.1 + diff --git a/SOURCES/dnsmasq-2.76-min-query-port.patch b/SOURCES/dnsmasq-2.76-min-query-port.patch new file mode 100644 index 0000000..856770c --- /dev/null +++ b/SOURCES/dnsmasq-2.76-min-query-port.patch @@ -0,0 +1,85 @@ +From 333856b1c1b032f937dd24d604f98cdb6dfe3d91 Mon Sep 17 00:00:00 2001 +From: Simon Kelley +Date: Mon, 29 Jan 2018 22:49:27 +0000 +Subject: [PATCH] Default min-port to 1024 to avoid reserved ports. + +(cherry picked from commit baf553db0cdb50707ddab464fb3eff7786ea576c) +--- + man/dnsmasq.8 | 3 ++- + src/dns-protocol.h | 1 + + src/dnsmasq.c | 3 --- + src/network.c | 5 +---- + src/option.c | 1 + + 5 files changed, 5 insertions(+), 8 deletions(-) + +diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 +index 1f1b048..9b7adde 100644 +--- a/man/dnsmasq.8 ++++ b/man/dnsmasq.8 +@@ -182,7 +182,8 @@ OS: this was the default behaviour in versions prior to 2.43. + Do not use ports less than that given as source for outbound DNS + queries. Dnsmasq picks random ports as source for outbound queries: + when this option is given, the ports used will always to larger +-than that specified. Useful for systems behind firewalls. ++than that specified. Useful for systems behind firewalls. If not specified, ++defaults to 1024. + .TP + .B --max-port= + Use ports lower than that given as source for outbound DNS queries. +diff --git a/src/dns-protocol.h b/src/dns-protocol.h +index 75d8ffb..dd69b28 100644 +--- a/src/dns-protocol.h ++++ b/src/dns-protocol.h +@@ -16,6 +16,7 @@ + + #define NAMESERVER_PORT 53 + #define TFTP_PORT 69 ++#define MIN_PORT 1024 /* first non-reserved port */ + #define MAX_PORT 65535u + + #define IN6ADDRSZ 16 +diff --git a/src/dnsmasq.c b/src/dnsmasq.c +index 83631ef..ae1aa96 100644 +--- a/src/dnsmasq.c ++++ b/src/dnsmasq.c +@@ -220,9 +220,6 @@ int main (int argc, char **argv) + die(_("loop detection not available: set HAVE_LOOP in src/config.h"), NULL, EC_BADCONF); + #endif + +- if (daemon->max_port != MAX_PORT && daemon->min_port == 0) +- daemon->min_port = 1024u; +- + if (daemon->max_port < daemon->min_port) + die(_("max_port cannot be smaller than min_port"), NULL, EC_BADCONF); + +diff --git a/src/network.c b/src/network.c +index fcd9d8d..d75f560 100644 +--- a/src/network.c ++++ b/src/network.c +@@ -1149,10 +1149,7 @@ int random_sock(int family) + if (fix_fd(fd)) + while(tries--) + { +- unsigned short port = rand16(); +- +- if (daemon->min_port != 0 || daemon->max_port != MAX_PORT) +- port = htons(daemon->min_port + (port % ((unsigned short)ports_avail))); ++ unsigned short port = htons(daemon->min_port + (rand16() % ((unsigned short)ports_avail))); + + if (family == AF_INET) + { +diff --git a/src/option.c b/src/option.c +index 3469f53..22846f6 100644 +--- a/src/option.c ++++ b/src/option.c +@@ -4521,6 +4521,7 @@ void read_opts(int argc, char **argv, char *compile_opts) + daemon->soa_retry = SOA_RETRY; + daemon->soa_expiry = SOA_EXPIRY; + daemon->max_port = MAX_PORT; ++ daemon->min_port = MIN_PORT; + + add_txt("version.bind", "dnsmasq-" VERSION, 0 ); + add_txt("authors.bind", "Simon Kelley", 0); +-- +2.20.1 + diff --git a/SPECS/dnsmasq.spec b/SPECS/dnsmasq.spec index 4ae8196..b715e18 100644 --- a/SPECS/dnsmasq.spec +++ b/SPECS/dnsmasq.spec @@ -13,7 +13,7 @@ Name: dnsmasq Version: 2.76 -Release: 7%{?extraversion}%{?dist} +Release: 9%{?extraversion}%{?dist} Summary: A lightweight DHCP/caching DNS server Group: System Environment/Daemons @@ -56,6 +56,11 @@ Patch18: dnsmasq-2.76-underflow.patch Patch19: dnsmasq-2.76-misc-cleanups.patch Patch20: dnsmasq-2.76-CVE-2017-14491-2.patch Patch21: dnsmasq-2.76-inotify.patch +Patch22: dnsmasq-2.76-min-query-port.patch +# commit a6004d7f17687ac2455f724d0b57098c413f128d +Patch23: dnsmasq-2.76-dnssec-cache.patch +# commit a997ca0da044719a0ce8a232d14da8b30022592b +Patch24: dnsmasq-2.76-dnssec-passthru.patch BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) @@ -112,6 +117,9 @@ query/remove a DHCP server's leases. %patch19 -p1 -b .misc %patch20 -p1 -b .CVE-2017-14491-2 %patch21 -p1 -b .inotify +%patch22 -p1 -b .rh1614331 +%patch23 -p1 -b .dnssec-cache +%patch24 -p1 -b .dnssec-passthru # use /var/lib/dnsmasq instead of /var/lib/misc for file in dnsmasq.conf.example man/dnsmasq.8 man/es/dnsmasq.8 src/config.h; do @@ -198,6 +206,12 @@ rm -rf $RPM_BUILD_ROOT %{_mandir}/man1/dhcp_* %changelog +* Tue Mar 26 2019 Petr Menšík - 2.79-9 +- Fix passing of dnssec enabled queries (#1638703) + +* Mon Mar 18 2019 Petr Menšík - 2.76-8 +- Stop using privileged port for outbound queries (#1614331) + * Wed May 09 2018 Martin Sehnoutka - 2.76-7 - Resolves: #1474515 dhcp-agent dnsmasq max files