aboutsummaryrefslogtreecommitdiff
path: root/libexec/blocklistd-helper/blacklistd-helper
blob: 4195f070e8eecc04429c6826e38a8173675a0788 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
#!/bin/sh
#echo "run $@" 1>&2
#set -x
# $1 command
# $2 rulename
# $3 protocol
# $4 address
# $5 mask
# $6 port
# $7 id

pf=
if [ -f "/etc/ipfw-blacklist.rc" ]; then
	echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" >&2
	echo "@  WARNING: rename /etc/ipfw-blacklist.rc to               @" >&2
	echo "@                  /etc/ipfw-blocklist.rc                  @" >&2
	echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" >&2

	pf="ipfw"
	. /etc/ipfw-blacklist.rc
	ipfw_offset=${ipfw_offset:-2000}
fi

if [ -z "$pf" ]; then
	for f in npf pf ipfilter ipfw; do
		if [ -x /etc/rc.d/$f ]; then
			if /etc/rc.d/$f status >/dev/null 2>&1; then
				pf="$f"
				break
			fi
		elif [ -f "/etc/$f.conf" ]; then
			# xxx assume a config file means it can be enabled --
			# and the first one wins!
			pf="$f"
			break
		fi
	done
fi

if [ -z "$pf" -a -x "/sbin/iptables" ]; then
	pf="iptables"
fi

if [ -z "$pf" ]; then
	echo "$0: Unsupported packet filter" 1>&2
	exit 1
fi

flags=
if [ -n "$3" ]; then
	raw_proto="$3"
	proto="proto $3"
	if [ $3 = "tcp" ]; then
		flags="flags S/SAFR"
	fi
fi

if [ -n "$6" ]; then
	raw_port="$6"
	port="port $6"
fi

addr="$4"
mask="$5"
case "$4" in
::ffff:*.*.*.*)
	if [ "$5" = 128 ]; then
		mask=32
		addr=${4#::ffff:}
	fi;;
esac

if [ "$pf" = "pf" ]; then
	for anchor in $(/sbin/pfctl -s Anchors 2> /dev/null); do
		if [ "$anchor" = "blacklistd" ]; then
			echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" >&2
			echo "@  WARNING: rename the blacklist anchor to blocklist       @" >&2
			echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" >&2
		fi
	done
fi

if [ "$pf" = "ipfilter" ]; then
	echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" >&2
	echo "@  WARNING: blacklist has been renamed to blocklist        @" >&2
	echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" >&2
fi

case "$1" in
add)
	case "$pf" in
	ipfilter)
		# N.B.:  If you reload /etc/ipf.conf then you need to stop and
		# restart blacklistd (and make sure blacklistd_flags="-r").
		# This should normally already be implemented in
		# /etc/rc.d/ipfilter, but if then not add the following lines to
		# the end of the ipfilter_reload() function:
		#
		#	if checkyesnox blacklistd; then
		#		/etc/rc.d/blacklistd restart
		#	fi
		#
		# XXX we assume the following rule is present in /etc/ipf.conf:
		# (should we check? -- it probably cannot be added dynamically)
		#
		#	block in proto tcp/udp from any to any head blacklistd
		#
		# where "blacklistd" is the default rulename (i.e. "$2")
		#
		# This rule can come before any rule that logs connections,
		# etc., and should be followed by final rules such as:
		#
		#	# log all as-yet unblocked incoming TCP connection
		#	# attempts
		#	log in proto tcp from any to any flags S/SAFR
		#	# last "pass" match wins for all non-blocked packets
		#	pass in all
		#	pass out all
		#
		# I.e. a "pass" rule which will be the final match and override
		# the "block".  This way the rules added by blacklistd will
		# actually block packets, and prevent logging of them as
		# connections, because they include the "quick" flag.
		#
		# N.b.:  $port is not included/used in rules -- abusers are cut
		# off completely from all services!
		#
		# Note RST packets are not returned for blocked SYN packets of
		# active attacks, so the port will not appear to be closed.
		# This will probably give away the fact that a firewall has been
		# triggered to block connections, but it prevents generating
		# extra outbound traffic, and it may also slow down the attacker
		# somewhat.
		#
		# Note also that we don't block all packets, just new attempts
		# to open connections (see $flags above).  This allows us to do
		# counterespionage against the attacker (or continue to make use
		# of any other services that might be on the same subnet as the
		# supposed attacker).  However it does not kill any active
		# connections -- we rely on the reporting daemon to do its own
		# protection and cleanup.
		#
		# N.B.:  The rule generated here must exactly match the
		# corresponding rule generated for the "rem" command below!
		#
		echo block in log quick $proto \
		    from $addr/$mask to any $flags group $2 | \
		    /sbin/ipf -A -f - >/dev/null 2>&1 && echo OK
		;;

	ipfw)
		# use $ipfw_offset+$port for rule number
		rule=$(($ipfw_offset + $6))
		tname="port$6"
		/sbin/ipfw table $tname create type addr 2>/dev/null
		/sbin/ipfw -q table $tname add "$addr/$mask"
		# if rule number $rule does not already exist, create it
		/sbin/ipfw show $rule >/dev/null 2>&1 || \
			/sbin/ipfw add $rule drop $3 from \
			table"("$tname")" to any dst-port $6 >/dev/null && \
			echo OK
		;;

	iptables)
		if ! /sbin/iptables --list "$2" >/dev/null 2>&1; then
			/sbin/iptables --new-chain "$2"
		fi
		/sbin/iptables --append INPUT --proto "$raw_proto" \
		    --dport "$raw_port" --jump "$2"
		/sbin/iptables --append "$2" --proto "$raw_proto" \
		    --source "$addr/$mask" --dport "$raw_port" --jump DROP
		echo OK
		;;

	npf)
		/sbin/npfctl rule "$2" add block in final $proto from \
		    "$addr/$mask" to any $port
		;;

	pf)
		# if the filtering rule does not exist, create it
		/sbin/pfctl -a "$2/$6" -sr 2>/dev/null | \
		    grep -q "<port$6>" || \
		    echo "block in quick $proto from <port$6> to any $port" | \
		    /sbin/pfctl -a "$2/$6" -f -
		# insert $ip/$mask into per-protocol/port anchored table
		/sbin/pfctl -qa "$2/$6" -t "port$6" -T add "$addr/$mask" && \
		    /sbin/pfctl -qk "$addr" && echo OK
		;;

	esac
	;;
rem)
	case "$pf" in
	ipfilter)
		# N.B.:  The rule generated here must exactly match the
		# corresponding rule generated for the "add" command above!
		#
		echo block in log quick $proto \
		    from $addr/$mask to any $flags group $2 | \
		    /sbin/ipf -A -r -f - >/dev/null 2>&1 && echo OK
		;;

	ipfw)
		/sbin/ipfw table "port$6" delete "$addr/$mask" 2>/dev/null && \
		    echo OK
		;;

	iptables)
		if /sbin/iptables --list "$2" >/dev/null 2>&1; then
			/sbin/iptables --delete "$2" --proto "$raw_proto" \
			    --source "$addr/$mask" --dport "$raw_port" \
			    --jump DROP
		fi
		echo OK
		;;

	npf)
		/sbin/npfctl rule "$2" rem-id "$7"
		;;

	pf)
		/sbin/pfctl -qa "$2/$6" -t "port$6" -T delete "$addr/$mask" && \
		    echo OK
		;;

	esac
	;;
flush)
	case "$pf" in
	ipfilter)
		#
		# N.B. WARNING:  This is obviously not reentrant!
		#
		# First we flush all the rules from the inactive set, then we
		# reload the ones that do not belong to the group "$2", and
		# finally we swap the active and inactive rule sets.
		#
		/sbin/ipf -I -F a
		#
		# "ipf -I -F a" also flushes active accounting rules!
		#
		# Note that accounting rule groups are unique to accounting
		# rules and have nothing to do with filter rules, though of
		# course theoretically one could use the same group name for
		# them too.
		#
		# In theory anyone using any such accounting rules should have a
		# wrapper /etc/rc.conf.d/blacklistd script (and corresponding
		# /etc/rc.conf.d/ipfilter script) that will record and
		# consolidate the values accumulated by such accounting rules
		# before they are flushed, since otherwise their counts will be
		# lost forever.
		#
		/usr/sbin/ipfstat -io | fgrep -v "group $2" | \
		    /sbin/ipf -I -f - >/dev/null 2>&1
		#
		# This MUST be done last and separately as "-s" is executed
		# _while_ the command arguments are being processed!
		#
		/sbin/ipf -s && echo OK
		;;

	ipfw)
		/sbin/ipfw table "port$6" flush 2>/dev/null && echo OK
		;;

	iptables)
		if /sbin/iptables --list "$2" >/dev/null 2>&1; then
			/sbin/iptables --flush "$2"
		fi
		echo OK
		;;

	npf)
		/sbin/npfctl rule "$2" flush
		;;

	pf)
		# dynamically determine which anchors exist
		for anchor in $(/sbin/pfctl -a "$2" -s Anchors 2> /dev/null); do
			/sbin/pfctl -a "$anchor" -t "port${anchor##*/}" -T flush
			/sbin/pfctl -a "$anchor" -F rules
		done
		echo OK
		;;
	esac
	;;
*)
	echo "$0: Unknown command '$1'" 1>&2
	exit 1
	;;
esac