diff options
| author | Hauke Mehrtens | 2026-05-23 12:07:37 +0000 |
|---|---|---|
| committer | Hauke Mehrtens | 2026-06-18 00:34:59 +0000 |
| commit | 8637f4cbb15c56c69f9ac85c021d9624850cbabf (patch) | |
| tree | c9c166a28f64c4856afc2dacc6cae78f123375d8 | |
| parent | f6571a42f24c7151e99ece24afe97d718ec96d07 (diff) | |
| download | odhcpd-8637f4cbb15c56c69f9ac85c021d9624850cbabf.tar.gz | |
dhcpv6: reject undersized encapsulated DHCPv4 messages in 4o6 path
dhcpv6_4o6_query() passes the inner OPTION_DHCPV4_MSG payload straight
to dhcpv4_handle_msg(), which immediately reads the BOOTP fixed header
(op, htype, hlen, ..., cookie — the bytes before dhcp.options). On the
normal DHCPv4 UDP socket those reads are safe because a BPF filter
drops anything shorter than offsetof(options); on the 4o6 path that
filter is bypassed and any olen >= 1 was accepted, allowing an out-of-
bounds read past the option buffer. Reject the message here so we keep
the same minimum-length guarantee the BPF filter provides on the
native path.
Assisted-by: Claude:claude-opus-4-7
Link: https://github.com/openwrt/odhcpd/pull/401
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
(cherry picked from commit d89caa59c00516d5b5803ed87a86204e4c992799)
| -rw-r--r-- | src/dhcpv6.c | 13 |
1 files changed, 13 insertions, 0 deletions
diff --git a/src/dhcpv6.c b/src/dhcpv6.c index b4fea34..38f9c15 100644 --- a/src/dhcpv6.c +++ b/src/dhcpv6.c @@ -317,6 +317,19 @@ static ssize_t dhcpv6_4o6_query(uint8_t *buf, size_t buflen, return -1; } + /* dhcpv4_handle_msg() reads the full BOOTP fixed header (op, htype, + * hlen, hops, xid, secs, flags, ciaddr, yiaddr, siaddr, giaddr, + * chaddr, sname, file, cookie = offsetof(options) bytes) before + * parsing options. The normal DHCPv4 socket path enforces this via a + * BPF filter, but the DHCPv4-over-DHCPv6 path bypasses that filter, + * so validate the length here to prevent an out-of-bounds read on a + * truncated DHCPV4_MSG option. RFC7341 §7.1 defines the DHCPv4 Message + * option but mandates no minimum length, so this floor is ours. */ + if (msgv4_len < offsetof(struct dhcpv4_message, options)) { + error("4o6: encapsulated DHCPv4 message too short (%u)", msgv4_len); + return -1; + } + // Dummy IPv4 address memset(&addrv4, 0, sizeof(addrv4)); addrv4.sin_family = AF_INET; |