mirror of
https://github.com/pi-hole/dnsmasq.git
synced 2025-12-19 18:28:25 +00:00
Handle malformed DNS replies better.
If we detect that that reply from usptream is malformed, transform it into a SERVFAIL reply before sending to the original requestor.
This commit is contained in:
@@ -59,6 +59,9 @@ version 2.88
|
|||||||
needed is O(n^2). Handle this case more intelligently.
|
needed is O(n^2). Handle this case more intelligently.
|
||||||
Thanks to Ye Zhou for spotting the problem and an initial patch.
|
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
|
version 2.87
|
||||||
Allow arbitrary prefix lengths in --rev-server and
|
Allow arbitrary prefix lengths in --rev-server and
|
||||||
|
|||||||
@@ -821,12 +821,22 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
|||||||
n = rrfilter(header, n, RRFILTER_AAAA);
|
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);
|
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
|
||||||
munged = 1;
|
munged = 1;
|
||||||
cache_secure = 0;
|
cache_secure = 0;
|
||||||
ede = EDE_BLOCKED;
|
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)
|
if (doctored)
|
||||||
|
|||||||
@@ -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,
|
/* 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
|
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.
|
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,
|
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,
|
struct ipsets *ipsets, struct ipsets *nftsets, int is_sign, int check_rebind,
|
||||||
int no_cache_dnssec, int secure, int *doctored)
|
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);
|
namep = p = (unsigned char *)(header+1);
|
||||||
|
|
||||||
if (ntohs(header->qdcount) != 1 || !extract_name(header, qlen, &p, name, 1, 4))
|
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(qtype, p);
|
||||||
GETSHORT(qclass, p);
|
GETSHORT(qclass, p);
|
||||||
@@ -607,13 +609,13 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
|||||||
{
|
{
|
||||||
cname_loop:
|
cname_loop:
|
||||||
if (!(p1 = skip_questions(header, qlen)))
|
if (!(p1 = skip_questions(header, qlen)))
|
||||||
return 0;
|
return 2;
|
||||||
|
|
||||||
for (j = 0; j < ntohs(header->ancount); j++)
|
for (j = 0; j < ntohs(header->ancount); j++)
|
||||||
{
|
{
|
||||||
int secflag = 0;
|
int secflag = 0;
|
||||||
if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
|
if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
|
||||||
return 0; /* bad packet */
|
return 2; /* bad packet */
|
||||||
|
|
||||||
GETSHORT(aqtype, p1);
|
GETSHORT(aqtype, p1);
|
||||||
GETSHORT(aqclass, 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);
|
log_query(secflag | F_CNAME | F_FORWARD | F_UPSTREAM, name, NULL, NULL, 0);
|
||||||
|
|
||||||
if (!extract_name(header, qlen, &p1, name, 1, 0))
|
if (!extract_name(header, qlen, &p1, name, 1, 0))
|
||||||
return 0;
|
return 2;
|
||||||
|
|
||||||
if (aqtype == T_CNAME)
|
if (aqtype == T_CNAME)
|
||||||
{
|
{
|
||||||
@@ -677,7 +679,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
|||||||
|
|
||||||
p1 = endrr;
|
p1 = endrr;
|
||||||
if (!CHECK_LEN(header, p1, qlen, 0))
|
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:
|
cname_loop1:
|
||||||
if (!(p1 = skip_questions(header, qlen)))
|
if (!(p1 = skip_questions(header, qlen)))
|
||||||
return 0;
|
return 2;
|
||||||
|
|
||||||
for (j = 0; j < ntohs(header->ancount); j++)
|
for (j = 0; j < ntohs(header->ancount); j++)
|
||||||
{
|
{
|
||||||
int secflag = 0;
|
int secflag = 0;
|
||||||
|
|
||||||
if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
|
if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
|
||||||
return 0; /* bad packet */
|
return 2; /* bad packet */
|
||||||
|
|
||||||
GETSHORT(aqtype, p1);
|
GETSHORT(aqtype, p1);
|
||||||
GETSHORT(aqclass, p1);
|
GETSHORT(aqclass, p1);
|
||||||
@@ -747,7 +749,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
|||||||
{
|
{
|
||||||
p1 = endrr;
|
p1 = endrr;
|
||||||
if (!CHECK_LEN(header, p1, qlen, 0))
|
if (!CHECK_LEN(header, p1, qlen, 0))
|
||||||
return 0; /* bad packet */
|
return 2; /* bad packet */
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -790,7 +792,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
|||||||
|
|
||||||
namep = p1;
|
namep = p1;
|
||||||
if (!extract_name(header, qlen, &p1, name, 1, 0))
|
if (!extract_name(header, qlen, &p1, name, 1, 0))
|
||||||
return 0;
|
return 2;
|
||||||
|
|
||||||
if (qtype != T_CNAME)
|
if (qtype != T_CNAME)
|
||||||
goto cname_loop1;
|
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;
|
unsigned char *tmp = namep;
|
||||||
|
|
||||||
if (!CHECK_LEN(header, p1, qlen, 6))
|
if (!CHECK_LEN(header, p1, qlen, 6))
|
||||||
return 0; /* bad packet */
|
return 2; /* bad packet */
|
||||||
GETSHORT(addr.srv.priority, p1);
|
GETSHORT(addr.srv.priority, p1);
|
||||||
GETSHORT(addr.srv.weight, p1);
|
GETSHORT(addr.srv.weight, p1);
|
||||||
GETSHORT(addr.srv.srvport, p1);
|
GETSHORT(addr.srv.srvport, p1);
|
||||||
if (!extract_name(header, qlen, &p1, name, 1, 0))
|
if (!extract_name(header, qlen, &p1, name, 1, 0))
|
||||||
return 0;
|
return 2;
|
||||||
addr.srv.targetlen = strlen(name) + 1; /* include terminating zero */
|
addr.srv.targetlen = strlen(name) + 1; /* include terminating zero */
|
||||||
if (!(addr.srv.target = blockdata_alloc(name, addr.srv.targetlen)))
|
if (!(addr.srv.target = blockdata_alloc(name, addr.srv.targetlen)))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* we overwrote the original name, so get it back here. */
|
/* we overwrote the original name, so get it back here. */
|
||||||
if (!extract_name(header, qlen, &tmp, name, 1, 0))
|
if (!extract_name(header, qlen, &tmp, name, 1, 0))
|
||||||
return 0;
|
return 2;
|
||||||
}
|
}
|
||||||
else if (flags & (F_IPV4 | F_IPV6))
|
else if (flags & (F_IPV4 | F_IPV6))
|
||||||
{
|
{
|
||||||
/* copy address into aligned storage */
|
/* copy address into aligned storage */
|
||||||
if (!CHECK_LEN(header, p1, qlen, addrlen))
|
if (!CHECK_LEN(header, p1, qlen, addrlen))
|
||||||
return 0; /* bad packet */
|
return 2; /* bad packet */
|
||||||
memcpy(&addr, p1, addrlen);
|
memcpy(&addr, p1, addrlen);
|
||||||
|
|
||||||
/* check for returned address in private space */
|
/* 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 (aqtype == T_TXT)
|
||||||
{
|
{
|
||||||
if (!print_txt(header, qlen, name, p1, ardlen, secflag))
|
if (!print_txt(header, qlen, name, p1, ardlen, secflag))
|
||||||
return 0;
|
return 2;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
log_query(flags | F_FORWARD | secflag | F_UPSTREAM, name, &addr, NULL, aqtype);
|
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;
|
p1 = endrr;
|
||||||
if (!CHECK_LEN(header, p1, qlen, 0))
|
if (!CHECK_LEN(header, p1, qlen, 0))
|
||||||
return 0; /* bad packet */
|
return 2; /* bad packet */
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found && (qtype != T_ANY || (flags & F_NXDOMAIN)))
|
if (!found && (qtype != T_ANY || (flags & F_NXDOMAIN)))
|
||||||
|
|||||||
Reference in New Issue
Block a user