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