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