mirror of
http://thekelleys.org.uk/git/dnsmasq.git
synced 2026-10-23 11:54:39 +00:00
Fix NSEC bitmap parsing infinite loop. CVE-2026-4890
Report from Royce M <royce@xchglabs.com>. Location: dnssec.c:1290-1306, dnssec.c:1450-1463 The bitmap window iteration advances by p[1] instead of p[1]+2 (missing the 2-byte window header). With bitmap_length=0, both rdlen and p are unchanged, causing an infinite loop and dnsmasq stops responding to all queries. Bug is reachable before RRSIG validation (confirmed by the source comment at line 2125), so no valid DNSSEC signatures are needed.
This commit is contained in:
1 file changed
+58
-81
+58
-81
@@ -1243,15 +1243,23 @@ static int hostname_cmp(const char *a, const char *b)
|
||||
}
|
||||
}
|
||||
|
||||
static int check_type_bitmap(unsigned char *p, int type)
|
||||
{
|
||||
int offset = (type & 0xff) >> 3;
|
||||
|
||||
if (p[0] == type >> 8 && p[1] > offset && (p[offset + 2] & (0x80 >> (type & 0x07))))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* returns 0 on success, or DNSSEC_FAIL_* value on failure. */
|
||||
static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsigned char **nsecs, unsigned char **labels, int nsec_count,
|
||||
char *workspace1_in, char *workspace2, char *name, int type, int *nons)
|
||||
{
|
||||
int i, rc, rdlen;
|
||||
unsigned char *p, *psave;
|
||||
int offset = (type & 0xff) >> 3;
|
||||
int mask = 0x80 >> (type & 0x07);
|
||||
|
||||
|
||||
if (nons)
|
||||
*nons = 1;
|
||||
|
||||
@@ -1295,17 +1303,6 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
|
||||
/* rdlen is now length of type map, and p points to it
|
||||
packet checked to be as long as rdlen implies in prove_non_existence() */
|
||||
|
||||
/* check that the first typemap is complete. */
|
||||
if (rdlen < 2 || rdlen < p[1] + 2)
|
||||
return DNSSEC_FAIL_BADPACKET;
|
||||
|
||||
/* RFC 6672 5.3.4.1. */
|
||||
#define DNAME_OFFSET (T_DNAME >> 3)
|
||||
#define DNAME_MASK (0x80 >> (T_DNAME & 0x07))
|
||||
if (p[0] == 0 && (p[1] >= DNAME_OFFSET + 1) && (p[2 + DNAME_OFFSET] & DNAME_MASK) != 0 &&
|
||||
hostname_issubdomain(name, workspace1) == 1)
|
||||
return DNSSEC_FAIL_NONSEC;
|
||||
|
||||
rc = hostname_cmp(workspace1, name);
|
||||
|
||||
if (rc == 0)
|
||||
@@ -1314,42 +1311,37 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
|
||||
if (type == T_NSEC || type == T_RRSIG)
|
||||
return 0;
|
||||
|
||||
/* NSEC with the same name as the RR we're testing, check
|
||||
that the type in question doesn't appear in the type map */
|
||||
if (p[0] == 0 && p[1] >= 1)
|
||||
{
|
||||
/* If we can prove that there's no NS record, return that information. */
|
||||
if (nons && (p[2] & (0x80 >> T_NS)) != 0)
|
||||
*nons = 0;
|
||||
|
||||
/* A CNAME answer would also be valid, so if there's a CNAME is should
|
||||
have been returned. */
|
||||
if ((p[2] & (0x80 >> T_CNAME)) != 0)
|
||||
return DNSSEC_FAIL_NONSEC;
|
||||
|
||||
/* If the SOA bit is set for a DS record, then we have the
|
||||
DS from the wrong side of the delegation. For the root DS,
|
||||
this is expected. */
|
||||
if (name_labels != 0 && type == T_DS && (p[2] & (0x80 >> T_SOA)) != 0)
|
||||
return DNSSEC_FAIL_NONSEC;
|
||||
}
|
||||
|
||||
while (rdlen > 0)
|
||||
{
|
||||
if (rdlen < 2 || rdlen < p[1] + 2)
|
||||
return DNSSEC_FAIL_BADPACKET;
|
||||
|
||||
if (p[0] == type >> 8)
|
||||
{
|
||||
/* Does the NSEC say our type exists? */
|
||||
if (offset < p[1] && (p[offset+2] & mask) != 0)
|
||||
return DNSSEC_FAIL_NONSEC;
|
||||
|
||||
break; /* finished checking */
|
||||
}
|
||||
/* NSEC with the same name as the RR we're testing, check
|
||||
that the type in question doesn't appear in the type map */
|
||||
if (check_type_bitmap(p, type))
|
||||
return DNSSEC_FAIL_NONSEC;
|
||||
|
||||
/* If we can prove that there's no NS record, return that information. */
|
||||
if (nons && check_type_bitmap(p, T_NS))
|
||||
*nons = 0;
|
||||
|
||||
rdlen -= p[1];
|
||||
p += p[1];
|
||||
/* A CNAME answer would also be valid, so if there's a CNAME it should
|
||||
have been returned. */
|
||||
if (check_type_bitmap(p, T_CNAME))
|
||||
return DNSSEC_FAIL_NONSEC;
|
||||
|
||||
/* If the SOA bit is set for a DS record, then we have the
|
||||
DS from the wrong side of the delegation. For the root DS,
|
||||
this is expected. */
|
||||
if (name_labels != 0 && check_type_bitmap(p, T_DS))
|
||||
return DNSSEC_FAIL_NONSEC;
|
||||
|
||||
/* RFC 6672 5.3.4.1. */
|
||||
if (check_type_bitmap(p, T_DNAME) && hostname_issubdomain(name, workspace1) == 1)
|
||||
return DNSSEC_FAIL_NONSEC;
|
||||
|
||||
rdlen -= p[1] + 2;
|
||||
p += p[1] + 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -1473,50 +1465,35 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige
|
||||
we just need to check the type map. p points to the RR data for the record.
|
||||
Note we have packet length up to rdlen bytes checked. */
|
||||
|
||||
int offset = (type & 0xff) >> 3;
|
||||
int mask = 0x80 >> (type & 0x07);
|
||||
|
||||
p += hash_len; /* skip next-domain hash */
|
||||
rdlen -= p - psave;
|
||||
|
||||
/* check that the first typemap is complete. */
|
||||
if (rdlen < 2 || rdlen < p[1] + 2)
|
||||
return DNSSEC_FAIL_BADPACKET;
|
||||
|
||||
if (p[0] == 0 && p[1] >= 1)
|
||||
{
|
||||
/* If we can prove that there's no NS record, return that information. */
|
||||
if (nons && (p[2] & (0x80 >> T_NS)) != 0)
|
||||
*nons = 0;
|
||||
|
||||
/* A CNAME answer would also be valid, so if there's a CNAME is should
|
||||
have been returned. */
|
||||
if ((p[2] & (0x80 >> T_CNAME)) != 0)
|
||||
return 0;
|
||||
|
||||
/* If the SOA bit is set for a DS record, then we have the
|
||||
DS from the wrong side of the delegation. For the root DS,
|
||||
this is expected. */
|
||||
if (name_labels != 0 && type == T_DS && (p[2] & (0x80 >> T_SOA)) != 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (rdlen > 0)
|
||||
{
|
||||
if (rdlen < 2 || rdlen < p[1] + 2)
|
||||
return DNSSEC_FAIL_BADPACKET;
|
||||
return 0;
|
||||
|
||||
/* Does the NSEC3 say our type exists? */
|
||||
if (check_type_bitmap(p, type))
|
||||
return 0;
|
||||
|
||||
if (p[0] == type >> 8)
|
||||
{
|
||||
/* Does the NSEC3 say our type exists? */
|
||||
if (offset < p[1] && (p[offset+2] & mask) != 0)
|
||||
return 0;
|
||||
|
||||
break; /* finished checking */
|
||||
}
|
||||
|
||||
rdlen -= p[1];
|
||||
p += p[1];
|
||||
/* If we can prove that there's no NS record, return that information. */
|
||||
if (nons && check_type_bitmap(p, T_NS))
|
||||
*nons = 0;
|
||||
|
||||
/* A CNAME answer would also be valid, so if there's a CNAME it should
|
||||
have been returned. */
|
||||
if (check_type_bitmap(p, T_CNAME))
|
||||
return DNSSEC_FAIL_NONSEC;
|
||||
|
||||
/* If the SOA bit is set for a DS record, then we have the
|
||||
DS from the wrong side of the delegation. For the root DS,
|
||||
this is expected. */
|
||||
if (name_labels != 0 && check_type_bitmap(p, T_DS))
|
||||
return 0;
|
||||
|
||||
rdlen -= p[1] + 2;
|
||||
p += p[1] + 2;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
Reference in new issue
Block a user