summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Kelley <simon@thekelleys.org.uk>2022-11-26 22:19:29 +0000
committerSimon Kelley <simon@thekelleys.org.uk>2022-11-26 22:19:29 +0000
commite939b45c9facb1b2dad688de1ce14457247615e9 (patch)
tree244e891a490d1661cae1ab1cd2e4b2297918384b
parente3068ed111fb5c3d338026406dd6ab24363edea3 (diff)
downloaddnsmasq-e939b45c9facb1b2dad688de1ce14457247615e9.tar.gz
Handle malformed DNS replies better.v2.88rc5
If we detect that that reply from usptream is malformed, transform it into a SERVFAIL reply before sending to the original requestor.
-rw-r--r--CHANGELOG3
-rw-r--r--src/forward.c12
-rw-r--r--src/rfc1035.c34
3 files changed, 32 insertions, 17 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 0aa6511..0f36a0f 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -59,6 +59,9 @@ version 2.88
needed is O(n^2). Handle this case more intelligently.
Thanks to Ye Zhou for spotting the problem and an initial patch.
+ If we detect that a DNS reply from upstream is malformed don't
+ return it to the requestor; send a SEVFAIL rcode instead.
+
version 2.87
Allow arbitrary prefix lengths in --rev-server and
diff --git a/src/forward.c b/src/forward.c
index b4e3c5a..0f03818 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -821,12 +821,22 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
n = rrfilter(header, n, RRFILTER_AAAA);
}
- if (extract_addresses(header, n, daemon->namebuff, now, ipsets, nftsets, is_sign, check_rebind, no_cache, cache_secure, &doctored))
+ switch (extract_addresses(header, n, daemon->namebuff, now, ipsets, nftsets, is_sign, check_rebind, no_cache, cache_secure, &doctored))
{
+ case 1:
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
munged = 1;
cache_secure = 0;
ede = EDE_BLOCKED;
+ break;
+
+ /* extract_addresses() found a malformed answer. */
+ case 2:
+ munged = 1;
+ SET_RCODE(header, SERVFAIL);
+ cache_secure = 0;
+ ede = EDE_OTHER;
+ break;
}
if (doctored)
diff --git a/src/rfc1035.c b/src/rfc1035.c
index d6d3b49..5c0df56 100644
--- a/src/rfc1035.c
+++ b/src/rfc1035.c
@@ -538,7 +538,9 @@ static int print_txt(struct dns_header *header, const size_t qlen, char *name,
/* Note that the following code can create CNAME chains that don't point to a real record,
either because of lack of memory, or lack of SOA records. These are treated by the cache code as
expired and cleaned out that way.
- Return 1 if we reject an address because it look like part of dns-rebinding attack. */
+ Return 1 if we reject an address because it look like part of dns-rebinding attack.
+ Return 2 if the packet is malformed.
+*/
int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now,
struct ipsets *ipsets, struct ipsets *nftsets, int is_sign, int check_rebind,
int no_cache_dnssec, int secure, int *doctored)
@@ -589,7 +591,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
namep = p = (unsigned char *)(header+1);
if (ntohs(header->qdcount) != 1 || !extract_name(header, qlen, &p, name, 1, 4))
- return 0; /* bad packet */
+ return 2; /* bad packet */
GETSHORT(qtype, p);
GETSHORT(qclass, p);
@@ -607,13 +609,13 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
{
cname_loop:
if (!(p1 = skip_questions(header, qlen)))
- return 0;
+ return 2;
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 */
+ return 2; /* bad packet */
GETSHORT(aqtype, p1);
GETSHORT(aqclass, p1);
@@ -651,7 +653,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
log_query(secflag | F_CNAME | F_FORWARD | F_UPSTREAM, name, NULL, NULL, 0);
if (!extract_name(header, qlen, &p1, name, 1, 0))
- return 0;
+ return 2;
if (aqtype == T_CNAME)
{
@@ -677,7 +679,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
p1 = endrr;
if (!CHECK_LEN(header, p1, qlen, 0))
- return 0; /* bad packet */
+ return 2; /* bad packet */
}
}
@@ -722,14 +724,14 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
cname_loop1:
if (!(p1 = skip_questions(header, qlen)))
- return 0;
+ return 2;
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 */
+ return 2; /* bad packet */
GETSHORT(aqtype, p1);
GETSHORT(aqclass, p1);
@@ -747,7 +749,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
{
p1 = endrr;
if (!CHECK_LEN(header, p1, qlen, 0))
- return 0; /* bad packet */
+ return 2; /* bad packet */
continue;
}
@@ -790,7 +792,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
namep = p1;
if (!extract_name(header, qlen, &p1, name, 1, 0))
- return 0;
+ return 2;
if (qtype != T_CNAME)
goto cname_loop1;
@@ -813,25 +815,25 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
unsigned char *tmp = namep;
if (!CHECK_LEN(header, p1, qlen, 6))
- return 0; /* bad packet */
+ return 2; /* bad packet */
GETSHORT(addr.srv.priority, p1);
GETSHORT(addr.srv.weight, p1);
GETSHORT(addr.srv.srvport, p1);
if (!extract_name(header, qlen, &p1, name, 1, 0))
- return 0;
+ return 2;
addr.srv.targetlen = strlen(name) + 1; /* include terminating zero */
if (!(addr.srv.target = blockdata_alloc(name, addr.srv.targetlen)))
return 0;
/* we overwrote the original name, so get it back here. */
if (!extract_name(header, qlen, &tmp, name, 1, 0))
- return 0;
+ return 2;
}
else if (flags & (F_IPV4 | F_IPV6))
{
/* copy address into aligned storage */
if (!CHECK_LEN(header, p1, qlen, addrlen))
- return 0; /* bad packet */
+ return 2; /* bad packet */
memcpy(&addr, p1, addrlen);
/* check for returned address in private space */
@@ -875,7 +877,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
if (aqtype == T_TXT)
{
if (!print_txt(header, qlen, name, p1, ardlen, secflag))
- return 0;
+ return 2;
}
else
log_query(flags | F_FORWARD | secflag | F_UPSTREAM, name, &addr, NULL, aqtype);
@@ -883,7 +885,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
p1 = endrr;
if (!CHECK_LEN(header, p1, qlen, 0))
- return 0; /* bad packet */
+ return 2; /* bad packet */
}
if (!found && (qtype != T_ANY || (flags & F_NXDOMAIN)))