print_mac() used to output into caller-supplied buffer with no
length checking. The various callers supplied different
length buffers and did their own, ad-hoc, length checking before
calling print_mac(). This was an accident waiting to happen and
it had actually happened in couple of places, with buffer
overflows possible.
print_mac() now returns the address of its own buffer, which
is grown as required.
Thanks to Nicholas Carlini <npc@anthropic.com> for spotting this problem.
Before this fix, the code could read a couple of bytes beyond
valid data when an invalid too-short packet was recieved.
Thanks to Sokhna Walo DIAKHATE <sokhnawalodiakhate@esp.sn>
for the bug report.
This is the stable release fix for CVE-2026-2291
Dnsmasq recieves and sends domain names within DNS packets in RFC-1035
format, a string of counted labels terminated by a zero length label.
Internally to dnsmasq, domain names are represented as zero-terminated C strings
with the labels seperated by '.' characters, like the normal presentation
format for domain names.
The RFC-1035 limit for a domain name in wire format is 255 octets, and
when converted to presentation format, this makes a string of
maximum 253 characters. However dnsmasq needs to be able to
represent any 8-bit character within a label, and this causes
problems with /000 (which would terminate the c-string early) and
'.' (which would terminate a label early). To avoid this, dnsmasq
escapes both of these characters, which means that they take two bytes
in the string. The domain name with must charaters in it has four labels
and the 255 octet wire length limits these four labels to a total of
250 characters. The other five octets are the four label length bytes
and the zero length byte terminator. If all 250 characters need escaping
that makes the maximum length of the dnsmasq internal format 503
characters (250 escaped characters in labels, amd three dots between
four labels) Since this format is zero-terminated c-string, buffers
have to be allocated as 504 bytes.
This arrangement grew in a somewhat ad-hoc manner and early dnsmasq
didn't do the escaping trick, and didn't really differentiate between
the lengths of the wire format and the presentation format, since
they were very similar. It also, for reasons lost in time, inherited
a defintion for MAXDNAME from early BIND headers, set at 1025 bytes.
This value was used as the buffer size for both wire and presentation
formats.
This patch makes everything consistent. It declares two constants
MAXDNAME 255 /* max size of wire format domain name */
MAXDNAMESTR 503 /* max size of internal format created from a 255 octet wire format name */
All internal name buffers are allocated as MAXDNAMESTR+1 bytes,
to hold a maximum size internal format name and zero termination.
Names parsed out of incoming packets are rejected if their
wire format is greater than 255 bytes, which ensures that
their internal representation cannot exceed 503 characters.
Names inserted into outgoing packets are failed similarly if
they exceed 255 bytes. (this would in theory be possible
for a legal internal-representaion name if it has at least
one unescaped character in a label.
Credit to Haiyang Huang for finding this.
The fix extends the improved framework for DHCPv6 options added in
b95d5a2577
to the encapsulated vendor and user options.
RFC 8415 doesn't exclude the possiblity that a relay-forward
message could contain _more_than_one relay-message option. The
existing code could, under certain circumstances, access freed
stack memory in this case.
Thanks to Dejan Alvadzijevic and his fuzzer for finding this
condition.
This patch fixes things so that dnsmasq does at least sensible
things with multiple relay-message options, and no longer
has the (remote) possibility of accessing dead memory.
In log6_opts(), the OPTION6_STATUS_CODE branch did:
memcpy(daemon->namebuff + len, opt6_ptr(opt, 2), opt6_len(opt)-2);
daemon->namebuff[len + opt6_len(opt) - 2] = 0;
Two bugs:
1. Integer underflow: if opt6_len(opt) < 2, the subtraction wraps to a
huge size_t, making memcpy write gigabytes and crash with SIGSEGV.
2. Heap overflow: no upper-bound check. opt6_len() can return up to
65535, but daemon->namebuff is only (MAXDNAME*2)+1 = 2051 bytes.
A STATUS_CODE payload larger than ~2045 bytes would overflow the
heap buffer.
Exploitability: log6_opts() is only ever called on daemon->outpacket,
the outgoing DHCPv6 reply built by dnsmasq itself — NOT on incoming
client packets. Consequently, these bugs are NOT directly triggerable
by a remote attacker under the current code. The fix is defensive:
put_opt6_short() (which writes STATUS_CODE options in outgoing packets)
always produces opt6_len >= 2, so neither bug fires in practice today.
However, the unchecked arithmetic is a latent hazard that could become
exploitable if future refactoring passes incoming option data through
log6_opts():
- Guard the block with opt6_len(opt) >= 2 to prevent underflow.
- Cap slen to the remaining space in daemon->namebuff.
Signed-off-by: Dominik <dl6er@dl6er.de>
The code sending vendorclass data to the DHCP script
was confused about the format of the vendorclass in
the DHCP packet, resulting in broken data passed
to the packet.
Change the behaviour of the DHVPv6 server when a REBIND message
is received but no lease exists. Under these circumstances a new
lease is created _only_ when the --dhcp-authoritative option is
set. This matches the behavior of the DHCPv4 server.
I am attaching an incremental git-am ready patch to go on top your Git HEAD,
to fix all sorts of issues and make this conforming C99 with default
options set,
and fix another load of warnings you receive when setting the compiler
to pick the nits,
-pedantic-errors -std=c99 (or c11, c18, c2x).
It changes many void * to uint8_t * to make the "increment by bytes"
explicit.
You can't do:
void *foo;
// ...
foo += 2.
This acts almost exactly like --dhcp-option except that the defined option
is only sent when replying to PXE clients. More importantly, these
options are sent in reply PXE clients when dnsmasq in acting in PXE
proxy mode. In PXE proxy mode, the set of options sent is defined by
the PXE standard and the normal set of options is not sent. This config
allows arbitrary options in PXE-proxy replies. A typical use-case is
to send option 175 to iPXE. Thanks to Jason Berry for finding the
requirement for this.
Tweak things so that packets relayed towards a server
have source address on the server-facing network, not the
client-facing network. Thanks to Luis Thomas for spotting this
and initial patch.
This change also removes a previous bug
where --dhcp-alternate-port would affect the port used
to relay _to_ as well as the port being listened on.
The new feature allows configuration to provide bug-for-bug
compatibility, if required. Thanks to Damian Kaczkowski
for the feature suggestion.
On machines with many interfaces, enumerating them
via netlink on each packet reciept is slow,
and unneccesary. All we need is the local address->interface
mapping, which can be cached in the relay structures.