From d390dc033804f6b09009d68d33b6d358db92717b Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Sat, 15 Mar 2025 16:47:55 +0000 Subject: [PATCH] Implement RFC6672 para 5.3.2. check for DNAME. Also fix overflow checking of NSEC type maps. --- src/dnssec.c | 49 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/src/dnssec.c b/src/dnssec.c index e4a268a..9712034 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -1254,6 +1254,7 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi p += 8; /* class, type, TTL */ GETSHORT(rdlen, p); psave = p; + if (!extract_name(header, plen, &p, workspace2, EXTR_NAME_EXTRACT, 0)) return DNSSEC_FAIL_BADPACKET; @@ -1276,7 +1277,22 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi 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); 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 that the type in question doesn't appear in the type map */ - 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() */ - - /* 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 (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) @@ -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) 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; 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 */ 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 (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; } - while (rdlen >= 2) + while (rdlen > 0) { + if (rdlen < 2 || rdlen < p[1] + 2) + return DNSSEC_FAIL_BADPACKET; + if (p[0] == type >> 8) { /* Does the NSEC3 say our type exists? */