Implement RFC6672 para 5.3.2. check for DNAME.

Also fix overflow checking of NSEC type maps.
This commit is contained in:
Simon Kelley
2025-03-15 16:47:55 +00:00
parent 105c25e561
commit d390dc0338

View File

@@ -1254,6 +1254,7 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
p += 8; /* class, type, TTL */ p += 8; /* class, type, TTL */
GETSHORT(rdlen, p); GETSHORT(rdlen, p);
psave = p; psave = p;
if (!extract_name(header, plen, &p, workspace2, EXTR_NAME_EXTRACT, 0)) if (!extract_name(header, plen, &p, workspace2, EXTR_NAME_EXTRACT, 0))
return DNSSEC_FAIL_BADPACKET; return DNSSEC_FAIL_BADPACKET;
@@ -1276,7 +1277,22 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
workspace1--; workspace1--;
*workspace1 = '*'; *workspace1 = '*';
} }
rdlen -= p - psave;
/* 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); rc = hostname_cmp(workspace1, name);
if (rc == 0) if (rc == 0)
@@ -1287,16 +1303,12 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
/* NSEC with the same name as the RR we're testing, check /* NSEC with the same name as the RR we're testing, check
that the type in question doesn't appear in the type map */ that the type in question doesn't appear in the type map */
rdlen -= p - psave; if (p[0] == 0 && p[1] >= 1)
/* 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() */
/* If we can prove that there's no NS record, return that information. */
if (nons && rdlen >= 2 && p[0] == 0 && (p[2] & (0x80 >> T_NS)) != 0)
*nons = 0;
if (rdlen >= 2 && p[0] == 0)
{ {
/* 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 /* A CNAME answer would also be valid, so if there's a CNAME is should
have been returned. */ have been returned. */
if ((p[2] & (0x80 >> T_CNAME)) != 0) if ((p[2] & (0x80 >> T_CNAME)) != 0)
@@ -1308,10 +1320,10 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
if (name_labels != 0 && type == T_DS && (p[2] & (0x80 >> T_SOA)) != 0) if (name_labels != 0 && type == T_DS && (p[2] & (0x80 >> T_SOA)) != 0)
return DNSSEC_FAIL_NONSEC; return DNSSEC_FAIL_NONSEC;
} }
while (rdlen >= 2) while (rdlen > 0)
{ {
if (!CHECK_LEN(header, p, plen, rdlen)) if (rdlen < 2 || rdlen < p[1] + 2)
return DNSSEC_FAIL_BADPACKET; return DNSSEC_FAIL_BADPACKET;
if (p[0] == type >> 8) if (p[0] == type >> 8)
@@ -1451,7 +1463,11 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige
p += hash_len; /* skip next-domain hash */ p += hash_len; /* skip next-domain hash */
rdlen -= p - psave; rdlen -= p - psave;
if (rdlen >= 2 && p[0] == 0) /* 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 we can prove that there's no NS record, return that information. */
if (nons && (p[2] & (0x80 >> T_NS)) != 0) if (nons && (p[2] & (0x80 >> T_NS)) != 0)
@@ -1469,8 +1485,11 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige
return 0; return 0;
} }
while (rdlen >= 2) while (rdlen > 0)
{ {
if (rdlen < 2 || rdlen < p[1] + 2)
return DNSSEC_FAIL_BADPACKET;
if (p[0] == type >> 8) if (p[0] == type >> 8)
{ {
/* Does the NSEC3 say our type exists? */ /* Does the NSEC3 say our type exists? */