!28 backport some community patches and fix CVE-2021-3448

From: @eaglegai
Reviewed-by: @zengwefeng
Signed-off-by: @zengwefeng
This commit is contained in:
openeuler-ci-bot 2021-04-23 11:26:21 +08:00 committed by Gitee
commit 08a481842f
11 changed files with 2209 additions and 1 deletions

View File

@ -0,0 +1,361 @@
From 25e63f1e56f5acdcf91893a1b92ad1e0f2f552d8 Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Wed, 25 Nov 2020 21:17:52 +0000
Subject: [PATCH] Handle caching with EDNS options better.
If we add the EDNS client subnet option, or the client's
MAC address, then the reply we get back may very depending on
that. Since the cache is ignorant of such things, it's not safe to
cache such replies. This patch determines when a dangerous EDNS
option is being added and disables caching.
Note that for much the same reason, we can't combine multiple
queries for the same question when dangerous EDNS options are
being added, and the code now handles that in the same way. This
query combining is required for security against cache poisoning,
so disabling the cache has a security function as well as a
correctness one.
---
man/dnsmasq.8 | 4 ++--
src/dnsmasq.h | 3 ++-
src/edns0.c | 75 +++++++++++++++++++++++++++++++++++++----------------------
src/forward.c | 41 +++++++++++++++++++++-----------
4 files changed, 78 insertions(+), 45 deletions(-)
diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
index 7c6b405..ac7c9fa 100644
--- a/man/dnsmasq.8
+++ b/man/dnsmasq.8
@@ -692,8 +692,8 @@ still marks the request so that no upstream nameserver will add client
address information either. The default is zero for both IPv4 and
IPv6. Note that upstream nameservers may be configured to return
different results based on this information, but the dnsmasq cache
-does not take account. If a dnsmasq instance is configured such that
-different results may be encountered, caching should be disabled.
+does not take account. Caching is therefore disabled for such replies,
+unless the subnet address being added is constant.
For example,
.B --add-subnet=24,96
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index fa8c5ea..9f74c7a 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -655,6 +655,7 @@ struct hostsfile {
#define FREC_TEST_PKTSZ 256
#define FREC_HAS_EXTRADATA 512
#define FREC_HAS_PHEADER 1024
+#define FREC_NO_CACHE 2048
#define HASH_SIZE 32 /* SHA-256 digest size */
@@ -1658,7 +1659,7 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l
unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do, int replace);
size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit);
size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit,
- union mysockaddr *source, time_t now, int *check_subnet);
+ union mysockaddr *source, time_t now, int *check_subnet, int *cacheable);
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer);
/* arp.c */
diff --git a/src/edns0.c b/src/edns0.c
index d75d3cc..53cfe24 100644
--- a/src/edns0.c
+++ b/src/edns0.c
@@ -264,7 +264,8 @@ static void encoder(unsigned char *in, char *out)
out[3] = char64(in[2]);
}
-static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *l3, time_t now)
+static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned char *limit,
+ union mysockaddr *l3, time_t now, int *cacheablep)
{
int maclen, replace = 2; /* can't get mac address, just delete any incoming. */
unsigned char mac[DHCP_CHADDR_MAX];
@@ -273,6 +274,7 @@ static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned ch
if ((maclen = find_mac(l3, mac, 1, now)) == 6)
{
replace = 1;
+ *cacheablep = 0;
if (option_bool(OPT_MAC_HEX))
print_mac(encode, mac, maclen);
@@ -288,14 +290,18 @@ static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned ch
}
-static size_t add_mac(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *l3, time_t now)
+static size_t add_mac(struct dns_header *header, size_t plen, unsigned char *limit,
+ union mysockaddr *l3, time_t now, int *cacheablep)
{
int maclen;
unsigned char mac[DHCP_CHADDR_MAX];
if ((maclen = find_mac(l3, mac, 1, now)) != 0)
- plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_MAC, mac, maclen, 0, 0);
-
+ {
+ *cacheablep = 0;
+ plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_MAC, mac, maclen, 0, 0);
+ }
+
return plen;
}
@@ -313,17 +319,18 @@ static void *get_addrp(union mysockaddr *addr, const short family)
return &addr->in.sin_addr;
}
-static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source)
+static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source, int *cacheablep)
{
/* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
int len;
void *addrp = NULL;
int sa_family = source->sa.sa_family;
-
+ int cacheable = 0;
+
opt->source_netmask = 0;
opt->scope_netmask = 0;
-
+
if (source->sa.sa_family == AF_INET6 && daemon->add_subnet6)
{
opt->source_netmask = daemon->add_subnet6->mask;
@@ -331,6 +338,7 @@ static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source)
{
sa_family = daemon->add_subnet6->addr.sa.sa_family;
addrp = get_addrp(&daemon->add_subnet6->addr, sa_family);
+ cacheable = 1;
}
else
addrp = &source->in6.sin6_addr;
@@ -343,6 +351,7 @@ static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source)
{
sa_family = daemon->add_subnet4->addr.sa.sa_family;
addrp = get_addrp(&daemon->add_subnet4->addr, sa_family);
+ cacheable = 1; /* Address is constant */
}
else
addrp = &source->in.sin_addr;
@@ -350,8 +359,6 @@ static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source)
opt->family = htons(sa_family == AF_INET6 ? 2 : 1);
- len = 0;
-
if (addrp && opt->source_netmask != 0)
{
len = ((opt->source_netmask - 1) >> 3) + 1;
@@ -359,18 +366,26 @@ static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source)
if (opt->source_netmask & 7)
opt->addr[len-1] &= 0xff << (8 - (opt->source_netmask & 7));
}
+ else
+ {
+ cacheable = 1; /* No address ever supplied. */
+ len = 0;
+ }
+
+ if (cacheablep)
+ *cacheablep = cacheable;
return len + 4;
}
-static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source)
+static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source, int *cacheable)
{
/* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
int len;
struct subnet_opt opt;
- len = calc_subnet_opt(&opt, source);
+ len = calc_subnet_opt(&opt, source, cacheable);
return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0, 0);
}
@@ -383,18 +398,18 @@ int check_source(struct dns_header *header, size_t plen, unsigned char *pseudohe
unsigned char *p;
int code, i, rdlen;
- calc_len = calc_subnet_opt(&opt, peer);
-
- if (!(p = skip_name(pseudoheader, header, plen, 10)))
- return 1;
-
- p += 8; /* skip UDP length and RCODE */
+ calc_len = calc_subnet_opt(&opt, peer, NULL);
- GETSHORT(rdlen, p);
- if (!CHECK_LEN(header, p, plen, rdlen))
- return 1; /* bad packet */
-
- /* check if option there */
+ if (!(p = skip_name(pseudoheader, header, plen, 10)))
+ return 1;
+
+ p += 8; /* skip UDP length and RCODE */
+
+ GETSHORT(rdlen, p);
+ if (!CHECK_LEN(header, p, plen, rdlen))
+ return 1; /* bad packet */
+
+ /* check if option there */
for (i = 0; i + 4 < rdlen; i += len + 4)
{
GETSHORT(code, p);
@@ -412,24 +427,28 @@ int check_source(struct dns_header *header, size_t plen, unsigned char *pseudohe
return 1;
}
+/* Set *check_subnet if we add a client subnet option, which needs to checked
+ in the reply. Set *cacheable to zero if we add an option which the answer
+ may depend on. */
size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit,
- union mysockaddr *source, time_t now, int *check_subnet)
+ union mysockaddr *source, time_t now, int *check_subnet, int *cacheable)
{
*check_subnet = 0;
-
+ *cacheable = 1;
+
if (option_bool(OPT_ADD_MAC))
- plen = add_mac(header, plen, limit, source, now);
+ plen = add_mac(header, plen, limit, source, now, cacheable);
if (option_bool(OPT_MAC_B64) || option_bool(OPT_MAC_HEX))
- plen = add_dns_client(header, plen, limit, source, now);
-
+ plen = add_dns_client(header, plen, limit, source, now, cacheable);
+
if (daemon->dns_client_id)
plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMCPEID,
(unsigned char *)daemon->dns_client_id, strlen(daemon->dns_client_id), 0, 1);
if (option_bool(OPT_CLIENT_SUBNET))
{
- plen = add_source_addr(header, plen, limit, source);
+ plen = add_source_addr(header, plen, limit, source, cacheable);
*check_subnet = 1;
}
diff --git a/src/forward.c b/src/forward.c
index d9b32b3..70b84d7 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -352,13 +352,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
{
/* Query from new source, but the same query may be in progress
from another source. If so, just add this client to the
- list that will get the reply.
+ list that will get the reply.*/
- Note that is the EDNS client subnet option is in use, we can't do this,
- as the clients (and therefore query EDNS options) will be different
- for each query. The EDNS subnet code has checks to avoid
- attacks in this case. */
- if (!option_bool(OPT_CLIENT_SUBNET) && (forward = lookup_frec_by_query(hash, fwd_flags)))
+ if (!option_bool(OPT_ADD_MAC) && !option_bool(OPT_MAC_B64) &&
+ (forward = lookup_frec_by_query(hash, fwd_flags)))
{
/* Note whine_malloc() zeros memory. */
if (!daemon->free_frec_src &&
@@ -455,18 +452,21 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
if (!flags && forward)
{
struct server *firstsentto = start;
- int subnet, forwarded = 0;
+ int subnet, cacheable, forwarded = 0;
size_t edns0_len;
unsigned char *pheader;
/* If a query is retried, use the log_id for the retry when logging the answer. */
forward->frec_src.log_id = daemon->log_id;
- plen = add_edns0_config(header, plen, ((unsigned char *)header) + PACKETSZ, &forward->frec_src.source, now, &subnet);
+ plen = add_edns0_config(header, plen, ((unsigned char *)header) + PACKETSZ, &forward->frec_src.source, now, &subnet, &cacheable);
if (subnet)
forward->flags |= FREC_HAS_SUBNET;
-
+
+ if (!cacheable)
+ forward->flags |= FREC_NO_CACHE;
+
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && do_dnssec)
{
@@ -650,7 +650,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
}
}
#endif
-
+
if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign, NULL)))
{
/* Get extended RCODE. */
@@ -1260,6 +1260,11 @@ void reply_query(int fd, int family, time_t now)
header->hb4 |= HB4_CD;
else
header->hb4 &= ~HB4_CD;
+
+ /* Never cache answers which are contingent on the source or MAC address EDSN0 option,
+ since the cache is ignorant of such things. */
+ if (forward->flags & FREC_NO_CACHE)
+ no_cache_dnssec = 1;
if ((nn = process_reply(header, now, forward->sentto, (size_t)n, check_rebind, no_cache_dnssec, cache_secure, bogusanswer,
forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION,
@@ -1821,7 +1826,7 @@ unsigned char *tcp_request(int confd, time_t now,
int local_auth = 0;
#endif
int checking_disabled, do_bit, added_pheader = 0, have_pseudoheader = 0;
- int check_subnet, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0;
+ int check_subnet, cacheable, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0;
size_t m;
unsigned short qtype;
unsigned int gotname;
@@ -1992,7 +1997,7 @@ unsigned char *tcp_request(int confd, time_t now,
char *domain = NULL;
unsigned char *oph = find_pseudoheader(header, size, NULL, NULL, NULL, NULL);
- size = add_edns0_config(header, size, ((unsigned char *) header) + 65536, &peer_addr, now, &check_subnet);
+ size = add_edns0_config(header, size, ((unsigned char *) header) + 65536, &peer_addr, now, &check_subnet, &cacheable);
if (gotname)
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
@@ -2171,6 +2176,11 @@ unsigned char *tcp_request(int confd, time_t now,
break;
}
+ /* Never cache answers which are contingent on the source or MAC address EDSN0 option,
+ since the cache is ignorant of such things. */
+ if (!cacheable)
+ no_cache_dnssec = 1;
+
m = process_reply(header, now, last_server, (unsigned int)m,
option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, cache_secure, bogusanswer,
ad_reqd, do_bit, added_pheader, check_subnet, &peer_addr);
@@ -2435,10 +2445,13 @@ static struct frec *lookup_frec_by_query(void *hash, unsigned int flags)
struct frec *f;
/* FREC_DNSKEY and FREC_DS_QUERY are never set in flags, so the test below
- ensures that no frec created for internal DNSSEC query can be returned here. */
+ ensures that no frec created for internal DNSSEC query can be returned here.
+
+ Similarly FREC_NO_CACHE is never set in flags, so a query which is
+ contigent on a particular source address EDNS0 option will never be matched. */
#define FLAGMASK (FREC_CHECKING_DISABLED | FREC_AD_QUESTION | FREC_DO_QUESTION \
- | FREC_HAS_PHEADER | FREC_DNSKEY_QUERY | FREC_DS_QUERY)
+ | FREC_HAS_PHEADER | FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_NO_CACHE)
for(f = daemon->frec_list; f; f = f->next)
if (f->sentto &&
--
1.8.3.1

View File

@ -0,0 +1,24 @@
From 12af2b171de0d678d98583e2190789e544440e02 Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Fri, 22 Jan 2021 18:24:03 +0000
Subject: [PATCH] Fix to 75e2f0aec33e58ef5b8d4d107d821c215a52827c
---
src/forward.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/forward.c b/src/forward.c
index 43d0ae7..1def931 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -378,6 +378,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
new->dest = *dst_addr;
new->log_id = daemon->log_id;
new->iface = dst_iface;
+ forward->frec_src.fd = udpfd;
}
return 1;
--
1.8.3.1

View File

@ -0,0 +1,25 @@
From 3f535da79e7a42104543ef5c7b5fa2bed819a78b Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Fri, 22 Jan 2021 22:26:25 +0000
Subject: [PATCH] Fix for 12af2b171de0d678d98583e2190789e544440e02
---
src/forward.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/forward.c b/src/forward.c
index 1def931..5c9cbbb 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -378,7 +378,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
new->dest = *dst_addr;
new->log_id = daemon->log_id;
new->iface = dst_iface;
- forward->frec_src.fd = udpfd;
+ new->fd = udpfd;
}
return 1;
--
1.8.3.1

View File

@ -0,0 +1,126 @@
From 141a26f979b4bc959d8e866a295e24f8cf456920 Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Wed, 17 Feb 2021 23:56:32 +0000
Subject: [PATCH] Fix problem with DNS retries in 2.83/2.84.
The new logic in 2.83/2.84 which merges distinct requests for the
same domain causes problems with clients which do retries as distinct
requests (differing IDs and/or source ports.) The retries just get
piggy-backed on the first, failed, request.
The logic is now changed so that distinct requests for repeated
queries still get merged into a single ID/source port, but they now
always trigger a re-try upstream.
Thanks to Nicholas Mu for his analysis.
---
src/forward.c | 79 ++++++++++++++++++++++++++++++++---------------------------
1 file changed, 43 insertions(+), 36 deletions(-)
diff --git a/src/forward.c b/src/forward.c
index 8fb0327..e82e14a 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -278,8 +278,46 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
fwd_flags |= FREC_DO_QUESTION;
#endif
- /* may be no servers available. */
- if (forward || (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash)))
+ /* Check for retry on existing query from same source */
+ if (!forward && (!(forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash))))
+ {
+ /* Maybe query from new source, but the same query may be in progress
+ from another source. If so, just add this client to the
+ list that will get the reply.*/
+
+ if (!option_bool(OPT_ADD_MAC) && !option_bool(OPT_MAC_B64) &&
+ (forward = lookup_frec_by_query(hash, fwd_flags)))
+ {
+ struct frec_src *new;
+
+ /* Note whine_malloc() zeros memory. */
+ if (!daemon->free_frec_src &&
+ daemon->frec_src_count < daemon->ftabsize &&
+ (daemon->free_frec_src = whine_malloc(sizeof(struct frec_src))))
+ {
+ daemon->frec_src_count++;
+ daemon->free_frec_src->next = NULL;
+ }
+
+ /* If we've been spammed with many duplicates, just drop the query. */
+ if (!daemon->free_frec_src)
+ return 0;
+
+ new = daemon->free_frec_src;
+ daemon->free_frec_src = new->next;
+ new->next = forward->frec_src.next;
+ forward->frec_src.next = new;
+ new->orig_id = ntohs(header->id);
+ new->source = *udpaddr;
+ new->dest = *dst_addr;
+ new->log_id = daemon->log_id;
+ new->iface = dst_iface;
+ new->fd = udpfd;
+ }
+ }
+
+ /* retry existing query */
+ if (forward)
{
/* If we didn't get an answer advertising a maximal packet in EDNS,
fall back to 1280, which should work everywhere on IPv6.
@@ -350,40 +388,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
}
else
{
- /* Query from new source, but the same query may be in progress
- from another source. If so, just add this client to the
- list that will get the reply.*/
-
- if (!option_bool(OPT_ADD_MAC) && !option_bool(OPT_MAC_B64) &&
- (forward = lookup_frec_by_query(hash, fwd_flags)))
- {
- /* Note whine_malloc() zeros memory. */
- if (!daemon->free_frec_src &&
- daemon->frec_src_count < daemon->ftabsize &&
- (daemon->free_frec_src = whine_malloc(sizeof(struct frec_src))))
- {
- daemon->frec_src_count++;
- daemon->free_frec_src->next = NULL;
- }
-
- /* If we've been spammed with many duplicates, just drop the query. */
- if (daemon->free_frec_src)
- {
- struct frec_src *new = daemon->free_frec_src;
- daemon->free_frec_src = new->next;
- new->next = forward->frec_src.next;
- forward->frec_src.next = new;
- new->orig_id = ntohs(header->id);
- new->source = *udpaddr;
- new->dest = *dst_addr;
- new->log_id = daemon->log_id;
- new->iface = dst_iface;
- new->fd = udpfd;
- }
-
- return 1;
- }
-
+ /* new query */
+
if (gotname)
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
@@ -392,6 +398,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
#endif
type &= ~SERV_DO_DNSSEC;
+ /* may be no servers available. */
if (daemon->servers && !flags)
forward = get_new_frec(now, NULL, NULL);
/* table full - flags == 0, return REFUSED */
--
1.8.3.1

View File

@ -0,0 +1,114 @@
From 305cb79c5754d5554729b18a2c06fe7ce699687a Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Thu, 18 Feb 2021 21:35:09 +0000
Subject: [PATCH] Simplify preceding fix.
Remove distinction between retry with same QID/SP and
retry for same query with different QID/SP. If the
QID/SP are the same as an existing one, simply retry,
if a new QID/SP is seen, add to the list to be replied to.
---
src/forward.c | 64 ++++++++++++++++++++---------------------------------------
1 file changed, 22 insertions(+), 42 deletions(-)
diff --git a/src/forward.c b/src/forward.c
index e82e14a..6bbf8a4 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -17,9 +17,6 @@
#include "dnsmasq.h"
static struct frec *lookup_frec(unsigned short id, int fd, int family, void *hash);
-static struct frec *lookup_frec_by_sender(unsigned short id,
- union mysockaddr *addr,
- void *hash);
static struct frec *lookup_frec_by_query(void *hash, unsigned int flags);
static unsigned short get_id(void);
@@ -278,18 +275,20 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
fwd_flags |= FREC_DO_QUESTION;
#endif
- /* Check for retry on existing query from same source */
- if (!forward && (!(forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash))))
+ /* Check for retry on existing query */
+ if (!forward && (forward = lookup_frec_by_query(hash, fwd_flags)))
{
- /* Maybe query from new source, but the same query may be in progress
- from another source. If so, just add this client to the
- list that will get the reply.*/
-
- if (!option_bool(OPT_ADD_MAC) && !option_bool(OPT_MAC_B64) &&
- (forward = lookup_frec_by_query(hash, fwd_flags)))
- {
- struct frec_src *new;
+ struct frec_src *src;
+
+ for (src = &forward->frec_src; src; src = src->next)
+ if (src->orig_id == ntohs(header->id) &&
+ sockaddr_isequal(&src->source, udpaddr))
+ break;
+ /* Existing query, but from new source, just add this
+ client to the list that will get the reply.*/
+ if (!src)
+ {
/* Note whine_malloc() zeros memory. */
if (!daemon->free_frec_src &&
daemon->frec_src_count < daemon->ftabsize &&
@@ -303,16 +302,16 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
if (!daemon->free_frec_src)
return 0;
- new = daemon->free_frec_src;
- daemon->free_frec_src = new->next;
- new->next = forward->frec_src.next;
- forward->frec_src.next = new;
- new->orig_id = ntohs(header->id);
- new->source = *udpaddr;
- new->dest = *dst_addr;
- new->log_id = daemon->log_id;
- new->iface = dst_iface;
- new->fd = udpfd;
+ src = daemon->free_frec_src;
+ daemon->free_frec_src = src->next;
+ src->next = forward->frec_src.next;
+ forward->frec_src.next = src;
+ src->orig_id = ntohs(header->id);
+ src->source = *udpaddr;
+ src->dest = *dst_addr;
+ src->log_id = daemon->log_id;
+ src->iface = dst_iface;
+ src->fd = udpfd;
}
}
@@ -2433,25 +2432,6 @@ static struct frec *lookup_frec(unsigned short id, int fd, int family, void *has
return NULL;
}
-static struct frec *lookup_frec_by_sender(unsigned short id,
- union mysockaddr *addr,
- void *hash)
-{
- struct frec *f;
- struct frec_src *src;
-
- for (f = daemon->frec_list; f; f = f->next)
- if (f->sentto &&
- !(f->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) &&
- memcmp(hash, f->hash, HASH_SIZE) == 0)
- for (src = &f->frec_src; src; src = src->next)
- if (src->orig_id == id &&
- sockaddr_isequal(&src->source, addr))
- return f;
-
- return NULL;
-}
-
static struct frec *lookup_frec_by_query(void *hash, unsigned int flags)
{
struct frec *f;
--
1.8.3.1

View File

@ -0,0 +1,34 @@
From cc0b4489c782f6b90ca118abb18e716a7a831289 Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Fri, 15 Jan 2021 22:21:52 +0000
Subject: [PATCH] Update to new struct frec fields in conntrack code.
---
src/forward.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/forward.c b/src/forward.c
index f94c4cf..7a95ddf 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -538,7 +538,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
if (option_bool(OPT_CONNTRACK))
{
unsigned int mark;
- if (get_incoming_mark(&forward->source, &forward->dest, 0, &mark))
+ if (get_incoming_mark(&forward->frec_src.source, &forward->frec_src.dest, 0, &mark))
setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
}
#endif
@@ -1193,7 +1193,7 @@ void reply_query(int fd, int family, time_t now)
if (option_bool(OPT_CONNTRACK))
{
unsigned int mark;
- if (get_incoming_mark(&orig->source, &orig->dest, 0, &mark))
+ if (get_incoming_mark(&orig->frec_src.source, &orig->frec_src.dest, 0, &mark))
setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
}
#endif
--
1.8.3.1

View File

@ -0,0 +1,84 @@
From a2a7e040b128a8ec369ba8f22beca2705435b85b Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Sat, 12 Dec 2020 23:26:45 +0000
Subject: [PATCH] Use the values of --min-port and --max-port in TCP
connections.
Rather that letting the kernel pick source ports, do it ourselves
so that the --min-port and --max-port parameters are be obeyed.
---
CHANGELOG | 5 +++++
src/network.c | 37 +++++++++++++++++++++++++++++++++----
2 files changed, 38 insertions(+), 4 deletions(-)
diff --git a/CHANGELOG b/CHANGELOG
index e6a2231..34ad22e 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,8 @@
+version 2.83
+ Use the values of --min-port and --max-port in outgoing
+ TCP connections to upstream DNS servers.
+
+
version 2.82
Improve behaviour in the face of network interfaces which come
and go and change index. Thanks to Petr Mensik for the patch.
diff --git a/src/network.c b/src/network.c
index c7d002b..7cf2546 100644
--- a/src/network.c
+++ b/src/network.c
@@ -1262,17 +1262,46 @@ int random_sock(int family)
int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifindex, int is_tcp)
{
union mysockaddr addr_copy = *addr;
+ unsigned short port;
+ int tries = 1, done = 0;
+ unsigned int ports_avail = ((unsigned short)daemon->max_port - (unsigned short)daemon->min_port) + 1;
+
+ if (addr_copy.sa.sa_family == AF_INET)
+ port = addr_copy.in.sin_port;
+ else
+ port = addr_copy.in6.sin6_port;
/* cannot set source _port_ for TCP connections. */
if (is_tcp)
+ port = 0;
+
+ /* Bind a random port within the range given by min-port and max-port */
+ if (port == 0)
+ {
+ tries = ports_avail < 30 ? 3 * ports_avail : 100;
+ port = htons(daemon->min_port + (rand16() % ((unsigned short)ports_avail)));
+ }
+
+ while (tries--)
{
if (addr_copy.sa.sa_family == AF_INET)
- addr_copy.in.sin_port = 0;
+ addr_copy.in.sin_port = port;
else
- addr_copy.in6.sin6_port = 0;
+ addr_copy.in6.sin6_port = port;
+
+ if (bind(fd, (struct sockaddr *)&addr_copy, sa_len(&addr_copy)) != -1)
+ {
+ done = 1;
+ break;
+ }
+
+ if (errno != EADDRINUSE && errno != EACCES)
+ return 0;
+
+ port = htons(daemon->min_port + (rand16() % ((unsigned short)ports_avail)));
}
-
- if (bind(fd, (struct sockaddr *)&addr_copy, sa_len(&addr_copy)) == -1)
+
+ if (!done)
return 0;
if (!is_tcp && ifindex > 0)
--
1.8.3.1

View File

@ -0,0 +1,252 @@
From 4c0aecc68524c5fd74053244a605e905dc644228 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemensik@redhat.com>
Date: Tue, 2 Mar 2021 18:21:32 +0000
Subject: [PATCH] Correct occasional --bind-dynamic synchronization break
Request only one re-read of addresses and/or routes
Previous implementation re-reads systemd addresses exactly the same
number of time equal number of notifications received.
This is not necessary, we need just notification of change, then re-read
the current state and adapt listeners. Repeated re-reading slows netlink
processing and highers CPU usage on mass interface changes.
Continue reading multicast events from netlink, even when ENOBUFS
arrive. Broadcasts are not trusted anyway and refresh would be done in
iface_enumerate. Save queued events sent again.
Remove sleeping on netlink ENOBUFS
With reduced number of written events netlink should receive ENOBUFS
rarely. It does not make sense to wait if it is received. It is just a
signal some packets got missing. Fast reading all pending packets is required,
seq checking ensures it already. Finishes changes by
commit 1d07667ac77c55b9de56b1b2c385167e0e0ec27a.
Move restart from iface_enumerate to enumerate_interfaces
When ENOBUFS is received, restart of reading addresses is done. But
previously found addresses might not have been found this time. In order
to catch this, restart both IPv4 and IPv6 enumeration with clearing
found interfaces first. It should deliver up-to-date state also after
ENOBUFS.
Read all netlink messages before netlink restart
Before writing again into netlink socket, try fetching all pending
messages. They would be ignored, only might trigger new address
synchronization. Should ensure new try has better chance to succeed.
ENOBUFS error handling was improved. Netlink is correctly drained before
sending a new request again. It seems ENOBUFS supression is no longer
necessary or wanted. Let kernel tell us when it failed and handle it a
good way.
---
src/netlink.c | 72 +++++++++++++++++++++++++++++++++++++++--------------------
src/network.c | 14 ++++++++----
2 files changed, 58 insertions(+), 28 deletions(-)
diff --git a/src/netlink.c b/src/netlink.c
index 0494070..3ad18a6 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -41,19 +41,26 @@
#ifndef NDA_RTA
# define NDA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
-#endif
+#endif
+
+/* Used to request refresh of addresses or routes just once,
+ * when multiple changes might be announced. */
+enum async_states {
+ STATE_NEWADDR = (1 << 0),
+ STATE_NEWROUTE = (1 << 1),
+};
static struct iovec iov;
static u32 netlink_pid;
-static void nl_async(struct nlmsghdr *h);
+static unsigned nl_async(struct nlmsghdr *h, unsigned state);
+static void nl_multicast_state(unsigned state);
char *netlink_init(void)
{
struct sockaddr_nl addr;
socklen_t slen = sizeof(addr);
- int opt = 1;
addr.nl_family = AF_NETLINK;
addr.nl_pad = 0;
@@ -92,10 +99,6 @@ char *netlink_init(void)
iov.iov_len = 100;
iov.iov_base = safe_malloc(iov.iov_len);
- if (daemon->kernel_version >= KERNEL_VERSION(2,6,30) &&
- setsockopt(daemon->netlinkfd, SOL_NETLINK, NETLINK_NO_ENOBUFS, &opt, sizeof(opt)) == -1)
- return _("warning: failed to set NETLINK_NO_ENOBUFS on netlink socket");
-
return NULL;
}
@@ -151,7 +154,9 @@ static ssize_t netlink_recv(void)
/* family = AF_UNSPEC finds ARP table entries.
- family = AF_LOCAL finds MAC addresses. */
+ family = AF_LOCAL finds MAC addresses.
+ returns 0 on failure, 1 on success, -1 when restart is required
+*/
int iface_enumerate(int family, void *parm, int (*callback)())
{
struct sockaddr_nl addr;
@@ -159,6 +164,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
ssize_t len;
static unsigned int seq = 0;
int callback_ok = 1;
+ unsigned state = 0;
struct {
struct nlmsghdr nlh;
@@ -170,7 +176,6 @@ int iface_enumerate(int family, void *parm, int (*callback)())
addr.nl_family = AF_NETLINK;
- again:
if (family == AF_UNSPEC)
req.nlh.nlmsg_type = RTM_GETNEIGH;
else if (family == AF_LOCAL)
@@ -197,8 +202,8 @@ int iface_enumerate(int family, void *parm, int (*callback)())
{
if (errno == ENOBUFS)
{
- sleep(1);
- goto again;
+ nl_multicast_state(state);
+ return -1;
}
return 0;
}
@@ -207,7 +212,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
if (h->nlmsg_pid != netlink_pid || h->nlmsg_type == NLMSG_ERROR)
{
/* May be multicast arriving async */
- nl_async(h);
+ state = nl_async(h, state);
}
else if (h->nlmsg_seq != seq)
{
@@ -341,26 +346,36 @@ int iface_enumerate(int family, void *parm, int (*callback)())
}
}
-void netlink_multicast(void)
+static void nl_multicast_state(unsigned state)
{
ssize_t len;
struct nlmsghdr *h;
int flags;
-
- /* don't risk blocking reading netlink messages here. */
+
if ((flags = fcntl(daemon->netlinkfd, F_GETFL)) == -1 ||
fcntl(daemon->netlinkfd, F_SETFL, flags | O_NONBLOCK) == -1)
return;
+
+ do {
+ /* don't risk blocking reading netlink messages here. */
+ while ((len = netlink_recv()) != -1)
- if ((len = netlink_recv()) != -1)
- for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
- nl_async(h);
-
+ for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
+ state = nl_async(h, state);
+ } while (errno == ENOBUFS);
+
/* restore non-blocking status */
fcntl(daemon->netlinkfd, F_SETFL, flags);
}
-static void nl_async(struct nlmsghdr *h)
+void netlink_multicast(void)
+{
+ unsigned state = 0;
+ nl_multicast_state(state);
+}
+
+
+static unsigned nl_async(struct nlmsghdr *h, unsigned state)
{
if (h->nlmsg_type == NLMSG_ERROR)
{
@@ -368,7 +383,8 @@ static void nl_async(struct nlmsghdr *h)
if (err->error != 0)
my_syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error)));
}
- else if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE)
+ else if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE &&
+ (state & STATE_NEWROUTE)==0)
{
/* We arrange to receive netlink multicast messages whenever the network route is added.
If this happens and we still have a DNS packet in the buffer, we re-send it.
@@ -380,10 +396,18 @@ static void nl_async(struct nlmsghdr *h)
if (rtm->rtm_type == RTN_UNICAST && rtm->rtm_scope == RT_SCOPE_LINK &&
(rtm->rtm_table == RT_TABLE_MAIN ||
rtm->rtm_table == RT_TABLE_LOCAL))
- queue_event(EVENT_NEWROUTE);
+ {
+ queue_event(EVENT_NEWROUTE);
+ state |= STATE_NEWROUTE;
+ }
+ }
+ else if ((h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR) &&
+ (state & STATE_NEWADDR)==0)
+ {
+ queue_event(EVENT_NEWADDR);
+ state |= STATE_NEWADDR;
}
- else if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR)
- queue_event(EVENT_NEWADDR);
+ return state;
}
#endif
diff --git a/src/network.c b/src/network.c
index 6cf15a9..77eb6ad 100644
--- a/src/network.c
+++ b/src/network.c
@@ -638,7 +638,8 @@ int enumerate_interfaces(int reset)
if ((param.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
return 0;
-
+
+again:
/* Mark interfaces for garbage collection */
for (iface = daemon->interfaces; iface; iface = iface->next)
iface->found = 0;
@@ -690,9 +691,14 @@ int enumerate_interfaces(int reset)
param.spare = spare;
ret = iface_enumerate(AF_INET6, &param, iface_allowed_v6);
-
- if (ret)
- ret = iface_enumerate(AF_INET, &param, iface_allowed_v4);
+ if (ret < 0)
+ goto again;
+ else if (ret)
+ {
+ ret = iface_enumerate(AF_INET, &param, iface_allowed_v4);
+ if (ret < 0)
+ goto again;
+ }
errsave = errno;
close(param.fd);
--
1.8.3.1

View File

@ -0,0 +1,65 @@
From 04490bf622ac84891aad6f2dd2edf83725decdee Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Fri, 22 Jan 2021 16:49:12 +0000
Subject: [PATCH] Move fd into frec_src, fixes
15b60ddf935a531269bb8c68198de012a4967156
If identical queries from IPv4 and IPv6 sources are combined by the
new code added in 15b60ddf935a531269bb8c68198de012a4967156 then replies
can end up being sent via the wrong family of socket. The ->fd
should be per query, not per-question.
In bind-interfaces mode, this could also result in replies being sent
via the wrong socket even when IPv4/IPV6 issues are not in play.
---
src/dnsmasq.h | 3 ++-
src/forward.c | 4 ++--
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 914f469..360c226 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -664,6 +664,7 @@ struct frec {
union mysockaddr source;
union all_addr dest;
unsigned int iface, log_id;
+ int fd;
unsigned short orig_id;
struct frec_src *next;
} frec_src;
@@ -671,7 +672,7 @@ struct frec {
struct randfd *rfd4;
struct randfd *rfd6;
unsigned short new_id;
- int fd, forwardall, flags;
+ int forwardall, flags;
time_t time;
unsigned char *hash[HASH_SIZE];
#ifdef HAVE_DNSSEC
diff --git a/src/forward.c b/src/forward.c
index 7a95ddf..43d0ae7 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -402,8 +402,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
forward->frec_src.dest = *dst_addr;
forward->frec_src.iface = dst_iface;
forward->frec_src.next = NULL;
+ forward->frec_src.fd = udpfd;
forward->new_id = get_id();
- forward->fd = udpfd;
memcpy(forward->hash, hash, HASH_SIZE);
forward->forwardall = 0;
forward->flags = fwd_flags;
@@ -1300,7 +1300,7 @@ void reply_query(int fd, int family, time_t now)
dump_packet(DUMP_REPLY, daemon->packet, (size_t)nn, NULL, &src->source);
#endif
- send_from(forward->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn,
+ send_from(src->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn,
&src->source, &src->dest, src->iface);
if (option_bool(OPT_EXTRALOG) && src != &forward->frec_src)
--
1.8.3.1

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
Name: dnsmasq
Version: 2.82
Release: 6
Release: 7
Summary: Dnsmasq provides network infrastructure for small networks
License: GPLv2 or GPLv3
URL: http://www.thekelleys.org.uk/dnsmasq/
@ -22,6 +22,16 @@ Patch11: backport-fix-regression-in-s_config_in_context-method.patch
Patch12: backport-Add-missing-check-for-NULL-return-from-allocate_rfd.patch
Patch13: backport-Fix-DNS-reply-when-asking-for-DNSSEC-and-a-validated.patch
Patch14: backport-Handle-DHCPREBIND-requests-in-the-DHCPv6-server.patch
Patch15: backport-0001-Handle-caching-with-EDNS-options-better.patch
Patch16: backport-0002-Fix-to-75e2f0aec33e58ef5b8d4d107d821c215a52827c.patch
Patch17: backport-0003-Fix-for-12af2b171de0d678d98583e2190789e544440e02.patch
Patch18: backport-0004-Fix-problem-with-DNS-retries-in-2.83-2.84.patch
Patch19: backport-0005-Simplify-preceding-fix.patch
Patch20: backport-0006-Update-to-new-struct-frec-fields-in-conntrack-code.patch
Patch21: backport-0007-Use-the-values-of-min-port-and-max-port-in-TCP-conne.patch
Patch22: backport-0008-Correct-occasional-bind-dynamic-synchronization-brea.patch
Patch23: backport-0009-Move-fd-into-frec_src-fixes-15b60ddf935a531269bb8c68.patch
Patch24: backport-0010-CVE-2021-3448.patch
BuildRequires: dbus-devel pkgconfig libidn2-devel nettle-devel systemd
Requires: nettle >= 3.4
@ -114,6 +124,21 @@ install -Dpm644 %{SOURCE2} $RPM_BUILD_ROOT%{_sysusersdir}/dnsmasq.conf
%{_mandir}/man8/dnsmasq*
%changelog
* Wed Apr 21 2021 gaihuiying <gaihuiying1@huawei.com> - 2.82-7
- Type:CVE
- Id:NA
- SUG:NA
- DESC: Handle caching with EDNS options better
Fix to 75e2f0aec33e58ef5b8d4d107d821c215a52827c
Fix for 12af2b171de0d678d98583e2190789e544440e02
Fix problem with DNS retries in 2.83 2.84 version
Simplify preceding fix
Update to new struct frec fields in conntrack code
Use the values of min port and max port in TCP connection
Correct occasional bind dynamic synchronization
Move fd into frec_src fixes 15b60ddf935a531269bb8c68
fix CVE-2021-3448
* Tue Apr 12 2021 seuzw <930zhaowei@163.com> - 2.82-6
- Type:requirement
- Id:NA