xref: /netbsd-src/external/bsd/blocklist/libexec/blocklistd-helper (revision 2718af68c3efc72c9769069b5c7f9ed36f6b9def)
1#!/bin/sh
2#echo "run $@" 1>&2
3#set -x
4# $1 command
5# $2 rulename
6# $3 protocol
7# $4 address
8# $5 mask
9# $6 port
10# $7 id
11
12pf=
13if [ -f "/etc/ipfw-blocklist.rc" ]; then
14	pf="ipfw"
15	. /etc/ipfw-blocklist.rc
16	ipfw_offset=${ipfw_offset:-2000}
17fi
18
19if [ -z "$pf" ]; then
20	for f in npf pf ipfilter; do
21		if [ -f "/etc/$f.conf" ]; then
22			pf="$f"
23			break
24		fi
25	done
26fi
27
28if [ -z "$pf" -a -x "/sbin/iptables" ]; then
29	pf="iptables"
30fi
31
32if [ -z "$pf" ]; then
33	echo "$0: Unsupported packet filter" 1>&2
34	exit 1
35fi
36
37flags=
38if [ -n "$3" ]; then
39	raw_proto="$3"
40	proto="proto $3"
41	if [ $3 = "tcp" ]; then
42		flags="flags S/SAFR"
43	fi
44fi
45
46if [ -n "$6" ]; then
47	raw_port="$6"
48	port="port $6"
49fi
50
51addr="$4"
52mask="$5"
53case "$4" in
54::ffff:*.*.*.*)
55	if [ "$5" = 128 ]; then
56		mask=32
57		addr=${4#::ffff:}
58	fi;;
59esac
60
61case "$1" in
62add)
63	case "$pf" in
64	ipf)
65		# N.B.:  If you reload /etc/ipf.conf then you need to stop and
66		# restart blocklistd (and make sure blocklistd_flags="-r"):
67		#
68		#	/etc/rc.d/ipfilter reload
69		#	/etc/rc.d/blocklistd restart
70		#
71		# XXX we assume the following rule is present in /etc/ipf.conf:
72		#
73		#	block in proto tcp/udp from any to any head blocklistd
74		#
75		# where "blocklistd" is the default rulename (i.e. "$2")
76		#
77		# This rule can come before any rule that logs connections,
78		# etc., and should be followed by final rules such as:
79		#
80		#	# log all as-yet unblocked incoming TCP connection
81		#	# attempts
82		#	log in proto tcp from any to any flags S/SAFR
83		#	# last "pass" match wins for all non-blocked packets
84		#	pass in all
85		#	pass out all
86		#
87		# I.e. a "pass" rule which will be the final match and override
88		# the "block".  This way the rules added by blocklistd will
89		# actually block packets, and prevent logging of them as
90		# connections, because they include the "quick" flag.
91		#
92		# N.b.:  $port is not included -- abusers are cut off completely
93		# from all services!
94		#
95		# Note RST packets are not returned for blocked SYN packets of
96		# active attacks, so the port will not appear to be closed.
97		# This will probably give away the fact that a firewall has been
98		# triggered to block connections, but it prevents generating
99		# extra outbound traffic, and it may also slow down the attacker
100		# somewhat.
101		#
102		# Note also that we don't block all packets, just new attempts
103		# to open connections (see $flags above).  This allows us to do
104		# counterespionage against the attacker (or continue to make use
105		# of any other services that might be on the same subnet as the
106		# attacker).  However it does not kill any active connections --
107		# we rely on the reporting daemon to do its own protection and
108		# cleanup.
109		#
110		# N.B.:  The generated must exactly match the rule generated for
111		# the "rem" command below!
112		#
113		echo block in log quick $proto \
114		     from $addr/$mask to any $flags group $2 | \
115		    /sbin/ipf -A -f - >/dev/null 2>&1 && echo OK
116		;;
117
118	ipfw)
119		# use $ipfw_offset+$port for rule number
120		rule=$(($ipfw_offset + $6))
121		tname="port$6"
122		/sbin/ipfw table $tname create type addr 2>/dev/null
123		/sbin/ipfw -q table $tname add "$addr/$mask"
124		# if rule number $rule does not already exist, create it
125		/sbin/ipfw show $rule >/dev/null 2>&1 || \
126			/sbin/ipfw add $rule drop $3 from \
127			table"("$tname")" to any dst-port $6 >/dev/null && \
128			echo OK
129		;;
130
131	iptables)
132		if ! /sbin/iptables --list "$2" >/dev/null 2>&1; then
133			/sbin/iptables --new-chain "$2"
134		fi
135		/sbin/iptables --append INPUT --proto "$raw_proto" \
136		    --dport "$raw_port" --jump "$2"
137		/sbin/iptables --append "$2" --proto "$raw_proto" \
138		    --source "$addr/$mask" --dport "$raw_port" --jump DROP
139		echo OK
140		;;
141
142	npf)
143		/sbin/npfctl rule "$2" add block in final $proto from \
144		    "$addr/$mask" to any $port
145		;;
146
147	pf)
148		# if the filtering rule does not exist, create it
149		/sbin/pfctl -a "$2/$6" -sr 2>/dev/null | \
150		    grep -q "<port$6>" || \
151		    echo "block in quick $proto from <port$6> to any $port" | \
152		    /sbin/pfctl -a "$2/$6" -f -
153		# insert $ip/$mask into per-protocol/port anchored table
154		/sbin/pfctl -a "$2/$6" -t "port$6" -T add "$addr/$mask" && \
155		    echo OK
156		;;
157
158	esac
159	;;
160rem)
161	case "$pf" in
162	ipf)
163		echo block in log quick $proto \
164		     from $addr/$mask to any $flags group $2 | \
165		    /sbin/ipf -A -r -f - >/dev/null 2>&1 && echo OK
166		;;
167
168	ipfw)
169		/sbin/ipfw table "port$6" delete "$addr/$mask" 2>/dev/null && \
170		    echo OK
171		;;
172
173	iptables)
174		if /sbin/iptables --list "$2" >/dev/null 2>&1; then
175			/sbin/iptables --delete "$2" --proto "$raw_proto" \
176			    --source "$addr/$mask" --dport "$raw_port" \
177			    --jump DROP
178		fi
179		echo OK
180		;;
181
182	npf)
183		/sbin/npfctl rule "$2" rem-id "$7"
184		;;
185
186	pf)
187		/sbin/pfctl -a "$2/$6" -t "port$6" -T delete "$addr/$mask" && \
188		    echo OK
189		;;
190
191	esac
192	;;
193flush)
194	case "$pf" in
195	ipf)
196		#
197		# XXX this is a slightly convoluted way to remove all the rules
198		# in the group added for "$2" (i.e. normally by default
199		# "blocklistd").
200		#
201		# N.B. WARNING:  This is obviously not reentrant!
202		#
203		/sbin/ipf -I -F a
204		/usr/sbin/ipfstat -io | fgrep -v "group $2" | \
205		    /sbin/ipf -I -f - >/dev/null 2>&1
206		# XXX this MUST be done last and separately as "-s" is executed
207		# _while_ the command arguments are being processed!
208		/sbin/ipf -s && echo OK
209		;;
210
211	ipfw)
212		/sbin/ipfw table "port$6" flush 2>/dev/null && echo OK
213		;;
214
215	iptables)
216		if /sbin/iptables --list "$2" >/dev/null 2>&1; then
217			/sbin/iptables --flush "$2"
218		fi
219		echo OK
220		;;
221
222	npf)
223		/sbin/npfctl rule "$2" flush
224		;;
225
226	pf)
227		/sbin/pfctl -a "$2/$6" -t "port$6" -T flush && echo OK
228		;;
229	esac
230	;;
231*)
232	echo "$0: Unknown command '$1'" 1>&2
233	exit 1
234	;;
235esac
236