xref: /illumos-gate/usr/src/cmd/svc/shell/ipf_include.sh (revision b1d7ec75953cd517f5b7c3d9cb427ff8ec5d7d07)
1#!/sbin/sh
2#
3# CDDL HEADER START
4#
5# The contents of this file are subject to the terms of the
6# Common Development and Distribution License (the "License").
7# You may not use this file except in compliance with the License.
8#
9# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10# or http://www.opensolaris.org/os/licensing.
11# See the License for the specific language governing permissions
12# and limitations under the License.
13#
14# When distributing Covered Code, include this CDDL HEADER in each
15# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16# If applicable, add the following below this CDDL HEADER, with the
17# fields enclosed by brackets "[]" replaced with your own identifying
18# information: Portions Copyright [yyyy] [name of copyright owner]
19#
20# CDDL HEADER END
21#
22# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
23#
24
25IPFILTER_FMRI="svc:/network/ipfilter:default"
26ETC_IPF_DIR=/etc/ipf
27IP6FILCONF=`/usr/bin/svcprop -p config/ipf6_config_file $IPFILTER_FMRI \
28    2>/dev/null`
29if [ $? -eq 1 ]; then
30	IP6FILCONF=$ETC_IPF_DIR/ipf6.conf
31fi
32IPNATCONF=`/usr/bin/svcprop -p config/ipnat_config_file $IPFILTER_FMRI \
33    2>/dev/null`
34if [ $? -eq 1 ]; then
35	IPNATCONF=$ETC_IPF_DIR/ipnat.conf
36fi
37IPPOOLCONF=`/usr/bin/svcprop -p config/ippool_config_file $IPFILTER_FMRI \
38    2>/dev/null`
39if [ $? -eq 1 ]; then
40	IPPOOLCONF=$ETC_IPF_DIR/ippool.conf
41fi
42VAR_IPF_DIR=/var/run/ipf
43IPFILCONF=$VAR_IPF_DIR/ipf.conf
44IPFILOVRCONF=$VAR_IPF_DIR/ipf_ovr.conf
45IPF_LOCK=/var/run/ipflock
46CONF_FILES=""
47NAT_FILES=""
48IPF_SUFFIX=".ipf"
49NAT_SUFFIX=".nat"
50
51# version for configuration upgrades
52CURRENT_VERSION=1
53
54IPF_FMRI="svc:/network/ipfilter:default"
55INETDFMRI="svc:/network/inetd:default"
56RPCBINDFMRI="svc:/network/rpc/bind:default"
57
58SMF_ONLINE="online"
59SMF_MAINT="maintenance"
60SMF_NONE="none"
61
62FW_CONTEXT_PG="firewall_context"
63METHOD_PROP="ipf_method"
64
65FW_CONFIG_PG="firewall_config"
66POLICY_PROP="policy"
67APPLY2_PROP="apply_to"
68EXCEPTIONS_PROP="exceptions"
69
70FW_CONFIG_DEF_PG="firewall_config_default"
71FW_CONFIG_OVR_PG="firewall_config_override"
72CUSTOM_FILE_PROP="custom_policy_file"
73OPEN_PORTS_PROP="open_ports"
74
75PREFIX_HOST="host:"
76PREFIX_NET="network:"
77PREFIX_POOL="pool:"
78PREFIX_IF="if:"
79
80SERVINFO=/usr/lib/servinfo
81
82#
83# Given a service, gets its config pg name
84#
85get_config_pg()
86{
87	if [ "$1" = "$IPF_FMRI" ]; then
88		echo "$FW_CONFIG_DEF_PG"
89	else
90		echo "$FW_CONFIG_PG"
91	fi
92	return 0
93}
94
95#
96# Given a service, gets its firewall policy
97#
98get_policy()
99{
100	config_pg=`get_config_pg $1`
101	svcprop -p $config_pg/${POLICY_PROP} $1 2>/dev/null
102}
103
104get_global_def_policy()
105{
106	svcprop -p ${FW_CONFIG_DEF_PG}/${POLICY_PROP} $IPF_FMRI 2>/dev/null
107}
108
109#
110# Given a service, gets its firewall policy
111#
112get_exceptions()
113{
114	config_pg=`get_config_pg $1`
115	svcprop -p $config_pg/${EXCEPTIONS_PROP} $1 2>/dev/null
116}
117
118#
119# Given a service, gets its firewall policy
120#
121get_apply2_list()
122{
123	config_pg=`get_config_pg $1`
124	svcprop -p $config_pg/${APPLY2_PROP} $1 2>/dev/null
125}
126
127check_ipf_dir()
128{
129	[ -d $VAR_IPF_DIR ] && return 0
130	mkdir $VAR_IPF_DIR >/dev/null 2>&1 || return 1
131}
132
133#
134# fmri_to_file fmri suffix
135#
136fmri_to_file()
137{
138	check_ipf_dir || return 1
139	fprefix="${VAR_IPF_DIR}/`echo $1 | tr -s '/:' '__'`"
140	echo "${fprefix}${2}"
141}
142
143#
144# Return service's enabled property
145#
146service_is_enabled()
147{
148	#
149	# Temporary enabled state overrides the persistent state
150	# so check it first.
151	#
152	enabled_ovr=`svcprop -c -p general_ovr/enabled $1 2>/dev/null`
153	if [ -n "$enabled_ovr" ]; then
154		[ "$enabled_ovr" = "true" ] && return 0 || return 1
155	fi
156
157	enabled=`svcprop -c -p general/enabled $1 2>/dev/null`
158	[ -n "$enabled" -a "$enabled" = "true" ] && return 0 || return 1
159}
160
161#
162# Return whether service is desired state
163#
164# Args: fmri state
165# Return:
166#  0 - desired state is service's current state
167#  1 - desired state is not service's current state
168#
169service_check_state()
170{
171	#
172	# Make sure we're done with ongoing state transition
173	#
174	while [ "`svcprop -p restarter/next_state $1`" != "$SMF_NONE" ]; do
175		sleep 1
176	done
177
178	[ "`svcprop -p restarter/state $1`" = "$2" ] && return 0 || return 1
179}
180
181#
182# Deny/Allow list stores values in the form "host:addr", "network:addr/netmask",
183# "pool:number", and "if:interface". This function returns the
184# IP(addr or addr/netmask) value or a pool number.
185#
186get_IP()
187{
188	value_is_interface $1 && return 1
189	echo "$1" | sed -n -e 's,^pool:\(.*\),pool/\1,p' \
190	    -e 's,^host:\(.*\),\1,p' \
191	    -e 's,^network:\(.*\),\1,p'
192}
193
194get_interface()
195{
196	value_is_interface $1 || return 1
197	scratch=`echo "$1" | sed -e 's/^if://'`
198
199	ifconfig $scratch >/dev/null 2>&1 || return 1
200	echo $scratch | sed -e 's/:.*//'
201}
202
203#
204#
205#
206value_is_interface()
207{
208	[ -z "$1" ] && return 1
209	echo $1 | grep "^if:" >/dev/null 2>&1
210}
211
212#
213# Remove rules in given file from active list without restarting ipfilter
214#
215remove_rules()
216{
217	[ -f "$1" ] && ipf -r -f $1 >/dev/null 2>&1
218}
219
220remove_nat_rules()
221{
222	[ -f "$1" ] && ipnat -r -f $1 >/dev/null 2>&1
223}
224
225check_ipf_syntax()
226{
227	ipf -n -f $1 >/dev/null 2>&1
228}
229
230check_nat_syntax()
231{
232	ipnat -n -f $1 >/dev/null 2>&1
233}
234
235file_get_ports()
236{
237	ipf -n -v -f $1 2>/dev/null | sed -n -e \
238	    's/.*to.* port = \([a-z0-9]*\).*/\1/p' | uniq | \
239	    awk '{if (length($0) > 1) {printf("%s ", $1)}}'
240}
241
242get_active_ports()
243{
244	ipfstat -io 2>/dev/null | sed -n -e \
245	    's/.*to.* port = \([a-z0-9]*\).*/\1/p' | uniq | \
246	    awk '{if (length($0) > 1) {printf("%s ",$1)}}'
247}
248
249#
250# Given two list of ports, return failure if there's a duplicate.
251#
252sets_check_duplicate()
253{
254	#
255	# If either list is empty, there isn't any conflict.
256	#
257	[ -z "$1" -o -z "$2" ] && return 0
258
259	for p in $1; do
260		for ap in $2; do
261			[ "$p" = "$ap" ] && return 1
262		done
263	done
264
265	return 0
266}
267
268#
269# Given a file containing ipf rules, check the syntax and verify
270# the rules don't conflict, use same port number, with active
271# rules (ipfstat -io output).
272#
273update_check_ipf_rules()
274{
275	check_ipf_syntax $1 || return 1
276
277	lports=`file_get_ports $1`
278	lactive_ports=`get_active_ports`
279
280	sets_check_duplicate "$lports" "$lactive_ports" || return 1
281}
282
283server_port_list=""
284
285#
286# Given a file containing ipf rules, check the syntax and verify
287# the rules don't conflict with already processed services.
288#
289# The list of processed services' ports are maintained in the global
290# variable 'server_port_list'.
291#
292check_ipf_rules()
293{
294	check_ipf_syntax $1 || return 1
295
296	lports=`file_get_ports $1`
297	sets_check_duplicate "$lports" "$server_port_list" || return 1
298	server_port_list="$server_port_list $lports"
299	return 0
300}
301
302prepend_new_rules()
303{
304	check_ipf_syntax $1 && tail -r $1 | sed -e 's/^[a-z]/@0 &/' | \
305	    ipf -f - >/dev/null 2>&1
306}
307
308append_new_rules()
309{
310	check_ipf_syntax $1 && ipf -f $1 >/dev/null 2>&1
311}
312
313append_new_nat_rules()
314{
315	check_nat_syntax $1 && ipnat -f $1 >/dev/null 2>&1
316}
317
318#
319# get port information from string of the form "proto:{port | port-port}"
320#
321tuple_get_port()
322{
323	port_str=`echo "$1" | sed -e 's/ //g; s/.*://' 2>/dev/null`
324	[ -z "$port_str" ] && return 1
325
326	echo $port_str | grep "-" >/dev/null
327	if  [ $? -eq  0 ]; then
328		echo $port_str | grep '^[0-9]\{1,5\}-[0-9]\{1,5\}$' >/dev/null || \
329		    return 1
330		ports=`echo $port_str | ( IFS=- read a b ; \
331		    [ $a \-le $b ] && echo $a $b || echo $b $a )`
332
333		for p in $ports; do
334			[ $p -gt 65535 ] && return 1
335		done
336		echo "$ports"
337	else
338		#
339		# port_str is a single port, verify and return it.
340		#
341		echo "$port_str" | grep '^[0-9]\{1,5\}$' >/dev/null || return 1
342		[ $port_str -gt 65535 ] && return 1
343		echo "$port_str"
344	fi
345}
346
347#
348# get proto info from string of the form "{tcp | udp}:port"
349#
350tuple_get_proto()
351{
352	proto=`echo "$1" | sed -e 's/ //g; s/:.*//' 2>/dev/null`
353	[ -z "$proto" ] && return 0
354
355	[ "$proto" = "tcp" -o "$proto" = "udp" ] && echo $proto || return 1
356	return 0
357}
358
359ipf_get_lock()
360{
361	newpid=$$
362
363	if [ -f "$IPF_LOCK/pid" ]; then
364		curpid=`cat $IPF_LOCK/pid 2>/dev/null`
365		[ "$curpid" = "$newpid" ] && return 0
366
367		#
368		# Clear lock if the owning process is no longer around.
369		#
370		ps -p $curpid >/dev/null 2>&1 || rm -r $IPF_LOCK >/dev/null 2>&1
371	fi
372
373	#
374	# Grab the lock
375	#
376	while :; do
377		mkdir $IPF_LOCK 2>/dev/null && break;
378		sleep 1
379	done
380	echo $newpid > $IPF_LOCK/pid
381}
382
383#
384# Remove lock if it's ours
385#
386ipf_remove_lock()
387{
388	if [ -f "$IPF_LOCK/pid" ]; then
389		[ "`cat $IPF_LOCK/pid`" = "$$" ] && rm -r $IPF_LOCK
390	fi
391	return 0
392}
393
394#
395# Make IPFILCONF, /var/tmp/ipf/ipf.conf, a symlink to the input file argument.
396#
397custom_set_symlink()
398{
399	#
400	# Nothing to do if the input file doesn't exist.
401	#
402	[ ! -f "$1" ] && return 0
403
404	check_ipf_dir || return 1
405
406	rm $IPFILCONF >/dev/null 2>&1
407	ln -s $1 $IPFILCONF >/dev/null 2>&1
408}
409
410#
411# New file replaces original file if they have different content
412#
413replace_file()
414{
415	orig=$1
416	new=$2
417
418	#
419	# IPFILCONF may be a symlink, remove it if that's the case
420	#
421	if [ -L "$orig" ]; then
422		rm $orig
423		touch $orig
424	fi
425
426	check_ipf_dir || return 1
427	mv $new $orig && return 0 || return 1
428}
429
430#
431# Given a service, gets the following details for ipf rule:
432# - policy
433# - protocol
434# - port(IANA port obtained by running servinfo)
435#
436process_server_svc()
437{
438	service=$1
439	ip="any"
440        policy=`get_policy ${service}`
441
442	#
443	# Empties service's rules file so callers won't use existing rule if
444	# we fail here.
445	#
446	file=`fmri_to_file $service $IPF_SUFFIX`
447	[ -z "$file" ] && return 1
448	echo "# $service" >${file}
449
450	#
451	# Nothing to do if policy is "use_global"
452	#
453	[ "$policy" = "use_global" ] && return 0
454
455	restarter=`svcprop -p general/restarter $service 2>/dev/null`
456	if [ "$restarter" = "$INETDFMRI" ]; then
457		iana_name=`svcprop -p inetd/name $service 2>/dev/null`
458		isrpc=`svcprop -p inetd/isrpc $service 2>/dev/null`
459	else
460		iana_name=`svcprop -p $FW_CONTEXT_PG/name $service 2>/dev/null`
461		isrpc=`svcprop -p $FW_CONTEXT_PG/isrpc $service 2>/dev/null`
462	fi
463
464	#
465	# Bail if iana_name isn't defined. Services with static rules
466	# like nis/client don't need to generate rules using
467	# iana name and protocol information.
468	#
469	[ -z "$iana_name" ] && return 1
470
471	#
472	# RPC services
473	#
474	if [ "$isrpc" = "true" ]; then
475		tports=`$SERVINFO -R -p -t -s $iana_name 2>/dev/null`
476		if [ -n "$tports" ]; then
477			for tport in $tports; do
478				generate_rules $service $policy "tcp" \
479				    $ip $tport $file
480			done
481		fi
482
483		uports=`$SERVINFO -R -p -u -s $iana_name 2>/dev/null`
484		if [ -n "$uports" ]; then
485			for uport in $uports; do
486				generate_rules $service $policy "udp" \
487				    $ip $uport $file
488			done
489		fi
490
491		return 0
492	fi
493
494	#
495	# Get the IANA port and supported protocols(tcp and udp)
496	# No support for IPv6 at this point.
497	#
498	tport=`$SERVINFO -p -t -s $iana_name 2>&1`
499	if [ $? -eq 0 -a -n "$tport" ]; then
500		generate_rules $service $policy "tcp" $ip $tport $file
501	fi
502
503	uport=`$SERVINFO -p -u -s $iana_name 2>&1`
504	if [ $? -eq 0 -a -n "$uport" ]; then
505		generate_rules $service $policy "udp" $ip $uport $file
506	fi
507
508	return 0
509}
510
511#
512# Given a service's name, policy, protocol and port, generate ipf rules
513# - list of host/network/interface to apply policy
514#
515# A 'use_global' policy inherits the system-wided Global Default policy
516# from network/ipfilter. For {deny | allow} policies, the rules are
517# ordered as:
518#
519# - make exceptions to policy for those in "exceptions" list
520# - apply policy to those specified in "apply_to" list
521# - policy rule
522#
523generate_rules()
524{
525	service=$1
526	mypolicy=$2
527	proto=$3
528	ip=$4
529	port=$5
530	out=$6
531
532	#
533	# Default mode is to inherit from global's policy
534	#
535	[ "$mypolicy" = "use_global" ] && return 0
536
537	tcp_opts=""
538	[ "$proto" = "tcp" ] && tcp_opts="flags S keep state keep frags"
539
540	#
541	# Allow all if policy is 'none'
542	#
543	if [ "$mypolicy" = "none" ]; then
544		echo "pass in log quick proto ${proto} from any to ${ip}" \
545		    "port = ${port} ${tcp_opts}" >>${out}
546		return 0
547	fi
548
549	#
550	# For now, let's concern only with incoming traffic.
551	#
552	[ "$mypolicy" = "deny" ] && { ecmd="pass"; acmd="block"; }
553	[ "$mypolicy" = "allow" ] && { ecmd="block"; acmd="pass"; }
554
555	for name in `get_exceptions $service`; do
556		[ -z "$name" -o "$name" = '""' ] && continue
557
558		ifc=`get_interface $name`
559		if [ $? -eq 0 -a -n "$ifc" ]; then
560			echo "${ecmd} in log quick on ${ifc} from any to" \
561			    "${ip} port = ${port}" >>${out}
562			continue
563		fi
564
565		addr=`get_IP ${name}`
566		if [ $? -eq 0 -a -n "$addr" ]; then
567			echo "${ecmd} in log quick proto ${proto} from ${addr}" \
568			    "to ${ip} port = ${port} ${tcp_opts}" >>${out}
569		fi
570	done
571
572	for name in `get_apply2_list $service`; do
573		[ -z "$name" -o "$name" = '""' ] && continue
574
575		ifc=`get_interface $name`
576		if [ $? -eq 0 -a -n "$ifc" ]; then
577			echo "${acmd} in log quick on ${ifc} from any to" \
578			    "${ip} port = ${port}" >>${out}
579			continue
580		fi
581
582		addr=`get_IP ${name}`
583		if [ $? -eq 0 -a -n "$addr" ]; then
584			echo "${acmd} in log quick proto ${proto} from ${addr}" \
585			    "to ${ip} port = ${port} ${tcp_opts}" >>${out}
586		fi
587	done
588
589	echo "${ecmd} in log quick proto ${proto} from any to ${ip}" \
590	    "port = ${port} ${tcp_opts}" >>${out}
591
592	return 0
593}
594
595#
596# Service has either IANA ports and proto or its own firewall method to
597# generate the rules.
598#
599# - if service has a custom method, use it to populate its rules
600# - if service has a firewall_config pg, use process_server_svc
601#
602# Argument - fmri
603#
604process_service()
605{
606	#
607	# Don't process network/ipfilter
608	#
609	[ "$1" = "$IPF_FMRI" ] && return 0
610
611	service_check_state $1 $SMF_MAINT && return 1
612
613	method=`svcprop -p $FW_CONTEXT_PG/$METHOD_PROP $1 2>/dev/null | \
614	    sed 's/\\\//g'`
615	if [ -n "$method" -a "$method" != '""' ]; then
616		( exec $method $1 >/dev/null )
617	else
618		svcprop -p $FW_CONFIG_PG $1 >/dev/null 2>&1 || return 1
619		process_server_svc $1 || return 1
620	fi
621	return 0
622}
623
624#
625# Generate rules for protocol/port defined in firewall_config_default/open_ports
626# property. These are non-service programs whose network resource info are
627# defined as "{tcp | upd}:{PORT | PORT-PORT}". Essentially, these programs need
628# some specific local ports to be opened. For example, BitTorrent clients need to
629# have 6881-6889 opened.
630#
631process_nonsvc_progs()
632{
633	out=$1
634	echo "# Non-service programs rules" >>${out}
635	progs=`svcprop -p ${FW_CONFIG_DEF_PG}/${OPEN_PORTS_PROP} \
636	    $SMF_FMRI 2>/dev/null`
637
638	for prog in $progs; do
639		[ -z "$prog" -o "$prog" = '""' ] && continue
640
641		port=`tuple_get_port $prog`
642		[ $? -eq 1 -o -z "$port" ] && continue
643
644		proto=`tuple_get_proto $prog`
645		[ $? -eq 1 ] && continue
646
647		set -- $port
648		if  [ $# -gt 1 ]; then
649			if [ -z "$proto" ]; then
650				echo "pass in log quick from any to any" \
651				    "port ${1} >< ${2}" >>${out}
652			else
653				echo "pass in log quick proto ${proto} from any" \
654				    "to any port ${1} >< ${2}" >>${out}
655			fi
656		else
657			if [ -z "$proto" ]; then
658				echo "pass in log quick from any to any" \
659				    "port = ${1}" >>${out}
660			else
661				echo "pass in log quick proto ${proto} from any" \
662				    "to any port = ${1}" >>${out}
663			fi
664		fi
665	done
666
667	return 0
668}
669
670#
671# Generate a new /etc/ipf/ipf.conf. If firewall policy is 'none',
672# ipf.conf is empty .
673#
674create_global_rules()
675{
676	policy=`get_global_def_policy`
677
678	if [ "$policy" = "custom" ]; then
679		file=`svcprop -p ${FW_CONFIG_DEF_PG}/${CUSTOM_FILE_PROP} $SMF_FMRI`
680
681		[ -n "$file" ] && custom_set_symlink $file
682		return 0
683	fi
684
685	TEMP=`mktemp /var/run/ipf.conf.pid$$.XXXXXX`
686	process_nonsvc_progs $TEMP
687
688	echo "# Global Default rules" >>${TEMP}
689	if [ "$policy" != "none" ]; then
690		echo "pass out log quick all keep state" >>${TEMP}
691	fi
692
693	case "$policy" in
694	'none')
695		# No rules
696		replace_file ${IPFILCONF} ${TEMP}
697		return $?
698		;;
699
700	'deny')
701		ecmd="pass"
702		acmd="block"
703		;;
704
705	'allow')
706		ecmd="block"
707		acmd="pass"
708		;;
709	*)
710		return 1;
711		;;
712	esac
713
714	for name in `get_exceptions $SMF_FMRI`; do
715		[ -z "$name" -o "$name" = '""' ] && continue
716
717		ifc=`get_interface $name`
718		if [ $? -eq 0 -a -n "$ifc" ]; then
719			echo "${ecmd} in log quick on ${ifc} all" >>${TEMP}
720			continue
721		fi
722
723		addr=`get_IP ${name}`
724		if [ $? -eq 0 -a -n "$addr" ]; then
725			echo "${ecmd} in log quick from ${addr} to any" >>${TEMP}
726		fi
727
728	done
729
730	for name in `get_apply2_list $SMF_FMRI`; do
731		[ -z "$name" -o "$name" = '""' ] && continue
732
733		ifc=`get_interface $name`
734		if [ $? -eq 0 -a -n "$ifc" ]; then
735			echo "${acmd} in log quick on ${ifc} all" >>${TEMP}
736			continue
737		fi
738
739		addr=`get_IP ${name}`
740		if [ $? -eq 0 -a -n "$addr" ]; then
741			echo "${acmd} in log quick from ${addr} to any" >>${TEMP}
742		fi
743	done
744
745	if [ "$policy" = "allow" ]; then
746		#
747		# Allow DHCP traffic if running as a DHCP client
748		#
749		/sbin/netstrategy | grep dhcp >/dev/null 2>&1
750		if [ $? -eq 0 ]; then
751			echo "pass out log quick from any port = 68" \
752			    "keep state" >>${TEMP}
753			echo "pass out log quick from any port = 546" \
754			    "keep state" >>${TEMP}
755			echo "pass in log quick from any to any port = 68" >>${TEMP}
756			echo "pass in log quick from any to any port = 546" >>${TEMP}
757		fi
758		echo "block in log all" >>${TEMP}
759	fi
760
761	replace_file ${IPFILCONF} ${TEMP}
762	return $?
763}
764
765#
766# Generate a new /etc/ipf/ipf_ovr.conf, the override system-wide policy. It's
767# a simplified policy that doesn't support 'exceptions' entities.
768#
769# If firewall policy is "none", no rules are generated.
770#
771# Note that "pass" rules don't have "quick" as we don't want
772# them to override services' block rules.
773#
774create_global_ovr_rules()
775{
776	#
777	# Simply empty override file if global policy is 'custom'
778	#
779	if [ "`get_global_def_policy`" = "custom" ]; then
780		echo "# 'custom' global policy" >$IPFILOVRCONF
781		return 0
782	fi
783
784	#
785	# Get and process override policy
786	#
787	ovr_policy=`svcprop -p ${FW_CONFIG_OVR_PG}/${POLICY_PROP} $IPF_FMRI`
788	if [ "$ovr_policy" = "none" ]; then
789		echo "# global override policy is 'none'" >$IPFILOVRCONF
790		return 0
791	fi
792
793	TEMP=`mktemp /var/run/ipf_ovr.conf.pid$$.XXXXXX`
794	[ "$ovr_policy" = "deny" ] && acmd="block in log quick"
795	[ "$ovr_policy" = "allow" ] && acmd="pass in log"
796
797	apply2_list=`svcprop -p $FW_CONFIG_OVR_PG/$APPLY2_PROP $IPF_FMRI`
798	for name in $apply2_list; do
799		[ -z "$name" -o "$name" = '""' ] && continue
800
801		ifc=`get_interface $name`
802		if [ $? -eq 0 -a -n "$ifc" ]; then
803			echo "${acmd} on ${ifc} all" >>${TEMP}
804			continue
805		fi
806
807		addr=`get_IP ${name}`
808		if [ $? -eq 0 -a -n "$addr" ]; then
809			echo "${acmd} from ${addr} to any" >>${TEMP}
810		fi
811	done
812
813	replace_file ${IPFILOVRCONF} ${TEMP}
814	return $?
815}
816
817#
818# Service is put into maintenance state due to its invalid firewall
819# definition and/or policy.
820#
821svc_mark_maintenance()
822{
823	svcadm mark maintenance $1 >/dev/null 2>&1
824
825	date=`date`
826	echo "[ $date ${0}: $1 has invalid ipf configuration. ]"
827	echo "[ $date ${0}: placing $1 in maintenance. ]"
828
829	#
830	# Move service's rule files to another location since
831	# they're most likely invalid.
832	#
833	ipfile=`fmri_to_file $1 $IPF_SUFFIX`
834	[ -f "$ipfile" ] && mv $ipfile "$ipfile.bak"
835
836	natfile=`fmri_to_file $1 $NAT_SUFFIX`
837	[ -f "$natfile" ] && mv $natfile "$natfile.bak"
838
839	return 0
840}
841
842svc_is_server()
843{
844	svcprop -p $FW_CONFIG_PG $1 >/dev/null 2>&1
845}
846
847#
848# Create rules for enabled firewalling and client services.
849# - obtain the list of enabled services and process them
850# - save the list of rules file for later use
851#
852create_services_rules()
853{
854	#
855	# Do nothing if global policy is 'custom'
856	#
857	global_policy=`get_global_def_policy`
858	[ "$global_policy" = "custom" ] && return 0
859
860	ipf_get_lock
861
862	#
863	# Get all enabled services
864	#
865	allsvcs=`svcprop -cf -p general/enabled -p general_ovr/enabled '*' \
866	    2>/dev/null | sed -n 's,^\(svc:.*\)/:properties/.* true$,\1,p' | sort -u`
867
868	#
869	# Process enabled services
870	#
871	for s in $allsvcs; do
872		service_is_enabled $s || continue
873		process_service $s || continue
874
875		ipfile=`fmri_to_file $s $IPF_SUFFIX`
876		if [ -n "$ipfile" -a -r "$ipfile" ]; then
877			check_ipf_syntax $ipfile
878			if [ $? -ne 0 ]; then
879				svc_mark_maintenance $s
880				continue
881			fi
882
883			svc_is_server $s
884			if [ $? -eq 0 ]; then
885				check_ipf_rules $ipfile
886				if [ $? -ne 0 ]; then
887					svc_mark_maintenance $s
888					continue
889				fi
890			fi
891			CONF_FILES="$CONF_FILES $ipfile"
892		fi
893
894		natfile=`fmri_to_file $s $NAT_SUFFIX`
895		if [ -n "$natfile" -a -r "$natfile" ]; then
896			check_nat_syntax $natfile
897			if [ $? -ne 0 ]; then
898				svc_mark_maintenance $s
899				continue
900			fi
901
902			NAT_FILES="$NAT_FILES $natfile"
903		fi
904	done
905
906	ipf_remove_lock
907	return 0
908}
909
910#
911# We update a services ipf ruleset in the following manners:
912# - service is disabled, tear down its rules.
913# - service is disable or refreshed(online), setup or update its rules.
914#
915service_update_rules()
916{
917	svc=$1
918
919	ipfile=`fmri_to_file $svc $IPF_SUFFIX`
920	[ -z "$ipfile" ] && return 0
921
922	remove_rules $ipfile
923
924	natfile=`fmri_to_file $svc $NAT_SUFFIX`
925	[ -n "$natfile" ] && remove_nat_rules $natfile
926
927	#
928	# Don't go further if service is disabled or in maintenance.
929	#
930	service_is_enabled $svc || return 0
931	service_check_state $1 $SMF_MAINT && return 0
932
933	process_service $svc || return 1
934	if [ -f "$ipfile" ]; then
935		check_ipf_syntax $ipfile
936		if [ $? -ne 0 ]; then
937			svc_mark_maintenance $svc
938			return 1
939		fi
940	fi
941
942	if [ -f "$natfile" ]; then
943		check_nat_syntax $natfile
944		if [ $? -ne 0 ]; then
945			svc_mark_maintenance $svc
946			return 1
947		fi
948	fi
949
950	if [ -f "$ipfile" ]; then
951		svc_is_server $svc
952		if [ $? -eq 0 ]; then
953			update_check_ipf_rules $ipfile
954			if [ $? -ne 0 ]; then
955				svc_mark_maintenance $svc
956				return 1
957			fi
958		fi
959
960		prepend_new_rules $ipfile
961
962		#
963		# reload Global Override rules to
964		# maintain correct ordering.
965		#
966		remove_rules $IPFILOVRCONF
967		prepend_new_rules $IPFILOVRCONF
968	fi
969
970	[ -f "$natfile" ] && append_new_nat_rules $natfile
971
972	return 0
973}
974
975#
976# Call the service_update_rules with appropriate svc fmri.
977#
978# This is called from '/lib/svc/method/ipfilter fw_update' whenever
979# a service is disabled/enabled/refreshed.
980#
981service_update()
982{
983	svc=$1
984	ret=0
985
986	#
987	# If ipfilter isn't online or global policy is 'custom',
988	# nothing should be done.
989	#
990	service_check_state $SMF_FMRI $SMF_ONLINE || return 0
991	[ "`get_global_def_policy`" = "custom" ] && return 0
992
993	ipf_get_lock
994	service_update_rules $svc || ret=1
995
996	ipf_remove_lock
997	return $ret
998}
999