Fix DNS name buffer overflow.

There are a couple of instances in the DNSSEC validation code where
the code steps along a DNS name to remove some number if left-hand
labels and replace them with * to make a wildcard. The name then
gets passed to to_wire() to translate it in-place to the wire format.

If external parameters dictate that all the labels are removed, and the name
is of exactly the maximum size, then this causes to_wire() to overwrite the
buffer by one byte.

Having created the wildcard, move it to the start of the buffer to avoid
this problem.

Thanks to Lucas Celant <lcelant@redhat.com> for spotting this.

CVE-2026-93011 may apply.
This commit is contained in:
Simon Kelley committed 2026-10-20 00:07:34 +01:00
1 parent a627a7847f
commit 145abca7f6
1 file changed
+12 -9
+12 -9
View File
@@ -486,7 +486,6 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
int i, wire_len, sig_len;
const struct nettle_hash *hash;
void *ctx;
char *name_start;
u32 nsigttl, ttl, orig_ttl;
failflags &= ~DNSSEC_FAIL_NOSIG;
@@ -576,13 +575,13 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
if (!extract_name(header, plen, &p, name, EXTR_NAME_EXTRACT, 10))
return STAT_BOGUS;
name_start = name;
/* if more labels than in RRsig name, hash *.<no labels in rrsig labels field> 4035 5.3.2 */
/* If the name is already the wildcard, we're not going to change it. */
if (labels < name_labels &&
!(name_labels - labels == 1 && name_start[0] == '*' && name_start[1] == '.'))
!(name_labels - labels == 1 && name[0] == '*' && name[1] == '.'))
{
char *name_start = name;
for (j = name_labels - labels; j != 0; j--)
{
while (*name_start != '.' && *name_start != 0)
@@ -595,11 +594,13 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
*wildcard_offset_out = name_start - name + 1;
name_start--;
*name_start = '*';
*name_start = '*';
/* move our name to the start of the buffer to avoid pathalogical cases over-running the end in to_wire(). */
memmove(name, name_start, strlen(name_start) + 1);
}
wire_len = to_wire(name_start);
hash->update(ctx, (unsigned int)wire_len, (unsigned char *)name_start);
wire_len = to_wire(name);
hash->update(ctx, (unsigned int)wire_len, (unsigned char *)name);
hash->update(ctx, 4, p); /* class and type */
hash->update(ctx, 4, (unsigned char *)&nsigttl);
@@ -1694,11 +1695,13 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
wildcard--;
*wildcard = '*';
/* move our name to the start of the buffer to avoid pathalogical cases over-running the end in to_wire(). */
memmove(name, wildcard, strlen(wildcard) + 1);
if (dec_counter(validate_counter, NULL))
return DNSSEC_FAIL_WORK;
if ((digest_len = hash_name(wildcard, &digest, hash, salt, salt_len, iterations)) == 0)
if ((digest_len = hash_name(name, &digest, hash, salt, salt_len, iterations)) == 0)
return DNSSEC_FAIL_NONSEC;
if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, NULL, 1))