diff options
author | Simon Kelley <simon@thekelleys.org.uk> | 2022-11-26 22:19:29 +0000 |
---|---|---|
committer | Simon Kelley <simon@thekelleys.org.uk> | 2022-11-26 22:19:29 +0000 |
commit | e939b45c9facb1b2dad688de1ce14457247615e9 (patch) | |
tree | 244e891a490d1661cae1ab1cd2e4b2297918384b | |
parent | e3068ed111fb5c3d338026406dd6ab24363edea3 (diff) | |
download | dnsmasq-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-- | CHANGELOG | 3 | ||||
-rw-r--r-- | src/forward.c | 12 | ||||
-rw-r--r-- | src/rfc1035.c | 34 |
3 files changed, 32 insertions, 17 deletions
@@ -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))) |