mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-30 20:43:02 +02:00
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>
238 lines
5.7 KiB
Bash
Executable File
238 lines
5.7 KiB
Bash
Executable File
#!/bin/sh
|
|
# This script expect a system with resolvconf (openresolv) and iproute2
|
|
|
|
[ -z "$1" ] && echo "Error: should be called from udhcpc" && exit 1
|
|
|
|
ACTION="$1"
|
|
IP_CACHE="/var/lib/misc/${interface}.cache"
|
|
RESOLV_CONF="/run/resolvconf/interfaces/${interface}.conf"
|
|
NTPFILE="/run/chrony/dhcp-sources.d/${interface}.sources"
|
|
NAME="/etc/frr/static.d/${interface}-dhcp.conf"
|
|
NEXT="${NAME}+"
|
|
|
|
[ -n "$broadcast" ] && BROADCAST="broadcast $broadcast"
|
|
[ -n "$subnet" ] || subnet=32
|
|
[ -n "$metric" ] || metric=5
|
|
|
|
# Handle stateful DHCPv6 like DHCPv4
|
|
[ -n "$ipv6" ] && ip="$ipv6/128"
|
|
|
|
if [ -z "${IF_WAIT_DELAY}" ]; then
|
|
IF_WAIT_DELAY=10
|
|
fi
|
|
|
|
log()
|
|
{
|
|
logger -I $$ -t udhcpc -p user.notice "${interface}: $*"
|
|
}
|
|
|
|
dbg()
|
|
{
|
|
logger -I $$ -t udhcpc -p user.debug "${interface}: $*"
|
|
}
|
|
|
|
err()
|
|
{
|
|
logger -I $$ -t udhcpc -p user.err "${interface}: $*"
|
|
}
|
|
|
|
wait_for_ipv6_default_route()
|
|
{
|
|
dbg "waiting for IPv6 default route to be installed."
|
|
while [ $IF_WAIT_DELAY -gt 0 ]; do
|
|
if ip -6 route list proto dhcp dev $interface | grep -q default; then
|
|
return
|
|
fi
|
|
sleep 1
|
|
printf "."
|
|
: $((IF_WAIT_DELAY -= 1))
|
|
done
|
|
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.
|
|
set_dhcp_routes()
|
|
{
|
|
echo "! Generated by udhcpc" > "$NEXT"
|
|
if [ -n "$staticroutes" ]; then
|
|
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
|
|
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)
|
|
cmp -s "$NAME" "$NEXT" && return
|
|
mv "$NEXT" "$NAME"
|
|
|
|
initctl -nbq restart staticd
|
|
}
|
|
|
|
clr_dhcp_routes()
|
|
{
|
|
log "deleting DHCP routes"
|
|
[ -f "$NAME" ] || return
|
|
rm "$NAME"
|
|
|
|
initctl -nbq restart staticd
|
|
}
|
|
|
|
clr_dhcp_addresses()
|
|
{
|
|
addrs=$(ip -j addr show dev $interface \
|
|
| jq -c '.[0].addr_info[] | select(.family == "inet") | select(.protocol == "dhcp")')
|
|
|
|
for addr in $addrs; do
|
|
ip="$(echo "$addr" | jq -r '."local"')"
|
|
prefix="$(echo "$addr" | jq -r '."prefixlen"')"
|
|
log "removing $ip/$prefix"
|
|
ip addr del "$ip/$prefix" dev "$interface"
|
|
done
|
|
}
|
|
|
|
log "action $ACTION"
|
|
case "$ACTION" in
|
|
deconfig)
|
|
clr_dhcp_addresses
|
|
clr_dhcp_routes
|
|
|
|
# drop info from this interface
|
|
rm -f "$RESOLV_CONF"
|
|
rm -f "$NTPFILE"
|
|
if [ -f "/etc/hostname.d/90-dhcp-${interface}" ]; then
|
|
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
|
|
fi
|
|
;;
|
|
|
|
leasefail|nak)
|
|
if [ -x /usr/sbin/avahi-autoipd ]; then
|
|
/usr/sbin/avahi-autoipd -c $interface || /usr/sbin/avahi-autoipd -wD $interface --no-chroot
|
|
fi
|
|
;;
|
|
|
|
renew|bound)
|
|
if [ -x /usr/sbin/avahi-autoipd ]; then
|
|
/usr/sbin/avahi-autoipd -c $interface && /usr/sbin/avahi-autoipd -k $interface
|
|
fi
|
|
|
|
if /bin/ip addr add dev $interface $ip/$subnet $BROADCAST proto dhcp; then
|
|
echo "$ip" > "$IP_CACHE"
|
|
fi
|
|
if [ -n "$ipv6" ] ; then
|
|
wait_for_ipv6_default_route
|
|
fi
|
|
|
|
set_dhcp_routes
|
|
|
|
# set hostname if given and requested
|
|
if [ -n "$hostname" ]; then
|
|
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
|
|
if was_option_requested 119; then
|
|
search_list=$search
|
|
else
|
|
log "ignoring unrequested search (option 119): $search"
|
|
fi
|
|
elif [ -n "$domain" ]; then
|
|
if was_option_requested 15; then
|
|
search_list=$domain
|
|
else
|
|
log "ignoring unrequested domain (option 15): $domain"
|
|
fi
|
|
fi
|
|
|
|
if [ -n "$search_list" ]; then
|
|
dbg "adding search $search_list"
|
|
echo "search $search_list # $interface" >> $RESOLV_CONF
|
|
fi
|
|
|
|
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
|
|
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
|
|
|
|
HOOK_DIR="$0.d"
|
|
for hook in "${HOOK_DIR}/"*; do
|
|
[ -f "${hook}" -a -x "${hook}" ] || continue
|
|
"${hook}" "$ACTION"
|
|
done
|
|
|
|
exit 0
|