board/common: only apply explicitly requested DHCP options in client

Validate that DHCP options were requested in the parameter request
list before applying them. This prevents malicious or misconfigured
DHCP servers from forcing unwanted configuration changes.

Validates: hostname (12), DNS (6), domain (15), search (119),
router (3), static routes (121), and NTP (42).

Fail-safe behavior: rejects options if config file unavailable.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg
2025-11-20 09:34:00 +01:00
parent 52320a973a
commit 3aa22a7c63
@@ -50,6 +50,26 @@ wait_for_ipv6_default_route()
err "Timed out waiting for IPv6 default route!"
}
# Check if a DHCP option was requested in the parameter request list
# Returns: 0 if requested, 1 if not requested or config unavailable
was_option_requested()
{
local opt_num="$1"
local config="/etc/finit.d/available/dhcp-client-${interface}.conf"
if [ ! -f "$config" ]; then
dbg "config file not found: $config"
return 1
fi
# Extract udhcpc command line and check for -O <option_num>
if grep -q -- "-O ${opt_num}\b" "$config"; then
return 0
fi
return 1
}
# RFC3442: If the DHCP server returns both a Classless
# Static Routes option and a Router option, the DHCP
# client MUST ignore the Router option.
@@ -57,17 +77,25 @@ set_dhcp_routes()
{
echo "! Generated by udhcpc" > "$NEXT"
if [ -n "$staticroutes" ]; then
# format: dest1/mask gw1 ... destn/mask gwn
set -- $staticroutes
while [ -n "$1" -a -n "$2" ]; do
dbg "adding route $1 via $2 metric $metric tag 100"
echo "ip route $1 $2 $metric tag 100" >> "$NEXT"
shift 2
done
if was_option_requested 121; then
# format: dest1/mask gw1 ... destn/mask gwn
set -- $staticroutes
while [ -n "$1" -a -n "$2" ]; do
dbg "adding route $1 via $2 metric $metric tag 100"
echo "ip route $1 $2 $metric tag 100" >> "$NEXT"
shift 2
done
else
log "ignoring unrequested staticroutes (option 121)"
fi
elif [ -n "$router" ] ; then
for i in $router ; do
echo "ip route 0.0.0.0/0 $i $metric tag 100" >> "$NEXT"
done
if was_option_requested 3; then
for i in $router ; do
echo "ip route 0.0.0.0/0 $i $metric tag 100" >> "$NEXT"
done
else
log "ignoring unrequested router (option 3)"
fi
fi
# Reduce changes needed by comparing with previous route(s)
@@ -109,8 +137,9 @@ case "$ACTION" in
rm -f "$RESOLV_CONF"
rm -f "$NTPFILE"
if [ -f "/etc/hostname.d/90-dhcp-${interface}" ]; then
rm -f "/etc/hostname.d/90-dhcp-${interface}"
/usr/libexec/infix/hostname dhcp
log "removing /etc/hostname.d/90-dhcp-${interface}"
rm -f "/etc/hostname.d/90-dhcp-${interface}"
/usr/libexec/infix/hostname dhcp
fi
if [ -x /usr/sbin/avahi-autoipd ]; then
/usr/sbin/avahi-autoipd -c $interface && /usr/sbin/avahi-autoipd -k $interface
@@ -137,22 +166,35 @@ case "$ACTION" in
set_dhcp_routes
# set hostname if given
# set hostname if given and requested
if [ -n "$hostname" ]; then
log "received DHCP hostname: $hostname"
mkdir -p /etc/hostname.d
echo "$hostname" > "/etc/hostname.d/90-dhcp-${interface}"
/usr/libexec/infix/hostname dhcp
if was_option_requested 12; then
log "received DHCP hostname: $hostname"
mkdir -p /etc/hostname.d
echo "$hostname" > "/etc/hostname.d/90-dhcp-${interface}"
/usr/libexec/infix/hostname dhcp
else
log "ignoring unrequested hostname (option 12): $hostname"
fi
fi
# drop info from this interface
truncate -s 0 "$RESOLV_CONF"
# prefer rfc3397 domain search list (option 119) if available
search_list=""
if [ -n "$search" ]; then
search_list=$search
if was_option_requested 119; then
search_list=$search
else
log "ignoring unrequested search (option 119): $search"
fi
elif [ -n "$domain" ]; then
search_list=$domain
if was_option_requested 15; then
search_list=$domain
else
log "ignoring unrequested domain (option 15): $domain"
fi
fi
if [ -n "$search_list" ]; then
@@ -160,19 +202,29 @@ case "$ACTION" in
echo "search $search_list # $interface" >> $RESOLV_CONF
fi
for i in $dns ; do
dbg "adding dns $i"
echo "nameserver $i # $interface" >> $RESOLV_CONF
resolvconf -u
done
if [ -n "$dns" ]; then
if was_option_requested 6; then
for i in $dns ; do
dbg "adding dns $i"
echo "nameserver $i # $interface" >> $RESOLV_CONF
resolvconf -u
done
else
log "ignoring unrequested dns (option 6): $dns"
fi
fi
if [ -n "$ntpsrv" ]; then
truncate -s 0 "$NTPFILE"
for srv in $ntpsrv; do
dbg "got NTP server $srv"
echo "server $srv iburst" >> "$NTPFILE"
done
chronyc reload sources >/dev/null
if was_option_requested 42; then
truncate -s 0 "$NTPFILE"
for srv in $ntpsrv; do
dbg "got NTP server $srv"
echo "server $srv iburst" >> "$NTPFILE"
done
chronyc reload sources >/dev/null
else
log "ignoring unrequested ntpsrv (option 42): $ntpsrv"
fi
fi
esac