diff --git a/CHANGELOG b/CHANGELOG index b74f0d6..4acc376 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -33,7 +33,17 @@ version 2.93 This means that the permissions used when creating the watches are the same as used for accessing watched files, which makes more sense and avoids odd and confusing error conditions. - + + Rework storage allocation for domain names. This fixes a security + bug that can cause heap-overwrite with long domain names. + CVE-2026-2291 covers this and a simple patch for existing + releases was released with the CVE. This patch reworks the whole + code base to make it cleaner and less liable to future + confusions. Either upgrading to 2.93 or applying the patch + to earlier versions is sufficient to close the security hole. + Thanks to Andrew S. Fasano for spotting this problem in the + first place. + version 2.92 Redesign the interaction between DNSSEC validation and per-domain diff --git a/src/cache.c b/src/cache.c index f85b7a8..32192c1 100644 --- a/src/cache.c +++ b/src/cache.c @@ -22,6 +22,7 @@ static struct crec *new_chain = NULL; static int insert_error; static union bigname *big_free = NULL; static int bignames_left, hash_size; + struct nameblock { struct nameblock *next; unsigned int last, index; @@ -221,6 +222,9 @@ static char *store_name(unsigned int namelen, unsigned int index) struct nameblock *block; char *ret = NULL; + if (namelen > NAMEBLOCK_CHARS) + return NULL; + for (block = hostblocks; block; block = block->next) if (block->index == index && NAMEBLOCK_CHARS - block->last >= namelen) break; @@ -1418,7 +1422,7 @@ static int gettok(FILE *f, char *token) return eatspace(f); } - if (count < (MAXDNAME - 1)) + if (count < (MAXDNAMESTR - 1)) { token[count++] = c; token[count] = 0; @@ -1793,7 +1797,7 @@ void cache_add_dhcp_entry(char *host_name, int prot, /* Name in hosts, address doesn't match */ if (fail_crec) { - inet_ntop(prot, &fail_crec->addr, daemon->namebuff, MAXDNAME); + inet_ntop(prot, &fail_crec->addr, daemon->namebuff, MAXDNAMESTR); my_syslog(MS_DHCP | LOG_WARNING, _("not giving name %s to the DHCP lease of %s because " "the name exists in %s with address %s"), diff --git a/src/config.h b/src/config.h index bdd8acb..a010c8c 100644 --- a/src/config.h +++ b/src/config.h @@ -43,7 +43,7 @@ #define PING_CACHE_TIME 30 /* Ping test assumed to be valid this long. */ #define DECLINE_BACKOFF 600 /* disable DECLINEd static addresses for this long */ #define DHCP_PACKET_MAX 16384 /* hard limit on DHCP packet size */ -#define SMALLDNAME 50 /* most domain names are smaller than this */ +#define SMALLDNAME 75 /* most domain names are smaller than this */ #define CNAME_CHAIN 10 /* chains longer than this atr dropped for loop protection */ #define DNSSEC_MIN_TTL 60 /* DNSKEY and DS records in cache last at least this long */ #define HOSTSFILE "/etc/hosts" diff --git a/src/dbus.c b/src/dbus.c index afdadf2..aa59eb8 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -736,7 +736,7 @@ static void add_dict_entry(DBusMessageIter *container, const char *key, const ch static void add_dict_int(DBusMessageIter *container, const char *key, const unsigned int val) { - snprintf(daemon->namebuff, MAXDNAME, "%u", val); + snprintf(daemon->namebuff, MAXDNAMESTR, "%u", val); add_dict_entry(container, key, daemon->namebuff); } diff --git a/src/dhcp-common.c b/src/dhcp-common.c index a14c415..ef8fc44 100644 --- a/src/dhcp-common.c +++ b/src/dhcp-common.c @@ -278,9 +278,9 @@ void log_tags(struct dhcp_netid *netid, u32 xid) if (!n) { - strncat (s, netid->net, (MAXDNAME-1) - strlen(s)); + strncat (s, netid->net, MAXDNAMESTR - strlen(s)); if (netid->next) - strncat (s, ", ", (MAXDNAME-1) - strlen(s)); + strncat (s, ", ", MAXDNAMESTR - strlen(s)); } } my_syslog(MS_DHCP | LOG_INFO, _("%u tags: %s"), xid, s); diff --git a/src/dhcp.c b/src/dhcp.c index 7b25835..3f8dc29 100644 --- a/src/dhcp.c +++ b/src/dhcp.c @@ -958,7 +958,7 @@ void dhcp_read_ethers(void) up = &config->next; } - while (fgets(buff, MAXDNAME, f)) + while (fgets(buff, MAXDNAMESTR, f)) { char *host = NULL; diff --git a/src/dns-protocol.h b/src/dns-protocol.h index 9ef4083..43dddcf 100644 --- a/src/dns-protocol.h +++ b/src/dns-protocol.h @@ -22,8 +22,34 @@ #define IN6ADDRSZ 16 #define INADDRSZ 4 +/* Dnsmasq handles domains names internally as NULL-terminated C strings. + The '.' characters in these strings are significant as label-seperators. + '.' and /0 characters _within_ labels are escaped and take up two + characters to allow labels to contain arbitrary characters. + + The maximum length of a domain name in wire format is 255 bytes. + and the maximum length of this when coverted to a