ipf_include.sh revision 10058:735e53d134d0
155714Skris#!/sbin/sh
255714Skris#
355714Skris# CDDL HEADER START
455714Skris#
555714Skris# The contents of this file are subject to the terms of the
655714Skris# Common Development and Distribution License (the "License").
755714Skris# You may not use this file except in compliance with the License.
8280304Sjkim#
955714Skris# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
1055714Skris# or http://www.opensolaris.org/os/licensing.
1155714Skris# See the License for the specific language governing permissions
1255714Skris# and limitations under the License.
1355714Skris#
1455714Skris# When distributing Covered Code, include this CDDL HEADER in each
15280304Sjkim# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1655714Skris# If applicable, add the following below this CDDL HEADER, with the
1755714Skris# fields enclosed by brackets "[]" replaced with your own identifying
1855714Skris# information: Portions Copyright [yyyy] [name of copyright owner]
1955714Skris#
2055714Skris# CDDL HEADER END
2155714Skris#
22280304Sjkim#
2355714Skris# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2455714Skris# Use is subject to license terms.
2555714Skris#
2655714Skris
2755714SkrisETC_IPF_DIR=/etc/ipf
2855714SkrisIP6FILCONF=$ETC_IPF_DIR/ipf6.conf
2955714SkrisIPNATCONF=$ETC_IPF_DIR/ipnat.conf
3055714SkrisIPPOOLCONF=$ETC_IPF_DIR/ippool.conf
3155714SkrisVAR_IPF_DIR=/var/run/ipf
3255714SkrisIPFILCONF=$VAR_IPF_DIR/ipf.conf
3355714SkrisIPFILOVRCONF=$VAR_IPF_DIR/ipf_ovr.conf
3455714SkrisIPF_LOCK=/var/run/ipflock
3555714SkrisCONF_FILES=""
3655714SkrisNAT_FILES=""
37280304SjkimIPF_SUFFIX=".ipf"
3855714SkrisNAT_SUFFIX=".nat"
3955714Skris
40280304Sjkim# version for configuration upgrades
4155714SkrisCURRENT_VERSION=1
4255714Skris
4355714SkrisIPF_FMRI="svc:/network/ipfilter:default"
4455714SkrisINETDFMRI="svc:/network/inetd:default"
4555714SkrisRPCBINDFMRI="svc:/network/rpc/bind:default"
4655714Skris
4755714SkrisSMF_ONLINE="online"
4855714SkrisSMF_MAINT="maintenance"
4955714SkrisSMF_NONE="none"
5055714Skris
5155714SkrisFW_CONTEXT_PG="firewall_context"
52280304SjkimMETHOD_PROP="ipf_method"
5355714Skris
5455714SkrisFW_CONFIG_PG="firewall_config"
5555714SkrisPOLICY_PROP="policy"
5655714SkrisAPPLY2_PROP="apply_to"
5755714SkrisEXCEPTIONS_PROP="exceptions"
5855714Skris
5955714SkrisFW_CONFIG_DEF_PG="firewall_config_default"
6055714SkrisFW_CONFIG_OVR_PG="firewall_config_override"
6155714SkrisCUSTOM_FILE_PROP="custom_policy_file"
62110007SmarkmOPEN_PORTS_PROP="open_ports"
63280304Sjkim
64110007SmarkmPREFIX_HOST="host:"
6555714SkrisPREFIX_NET="network:"
6655714SkrisPREFIX_POOL="pool:"
67246772SjkimPREFIX_IF="if:"
68280304Sjkim
69246772SjkimSERVINFO=/usr/lib/servinfo
70110007Smarkm
71280304Sjkim#
7255714Skris# Given a service, gets its config pg name 
73110007Smarkm#
74280304Sjkimget_config_pg()
7555714Skris{
76110007Smarkm	if [ "$1" = "$IPF_FMRI" ]; then
77280304Sjkim		echo "$FW_CONFIG_DEF_PG"
7855714Skris	else
79160817Ssimon		echo "$FW_CONFIG_PG"
80280304Sjkim	fi
81160817Ssimon	return 0
82160817Ssimon}
83280304Sjkim
84160817Ssimon#
8555714Skris# Given a service, gets its firewall policy
8655714Skris#
8755714Skrisget_policy()
8855714Skris{
8955714Skris	config_pg=`get_config_pg $1`
9055714Skris	svcprop -p $config_pg/${POLICY_PROP} $1 2>/dev/null
9155714Skris}
9259194Skris
93110007Smarkmget_global_def_policy()
94111150Snectar{
95280304Sjkim	svcprop -p ${FW_CONFIG_DEF_PG}/${POLICY_PROP} $IPF_FMRI 2>/dev/null
96111150Snectar}
97160817Ssimon
98110007Smarkm#
9955714Skris# Given a service, gets its firewall policy
100246772Sjkim#
101280304Sjkimget_exceptions()
102246772Sjkim{
103238405Sjkim	config_pg=`get_config_pg $1`
104194206Ssimon	svcprop -p $config_pg/${EXCEPTIONS_PROP} $1 2>/dev/null
105280304Sjkim}
106194206Ssimon
107194206Ssimon#
108280304Sjkim# Given a service, gets its firewall policy
109194206Ssimon#
110194206Ssimonget_apply2_list()
11155714Skris{
112280304Sjkim	config_pg=`get_config_pg $1`
113110007Smarkm	svcprop -p $config_pg/${APPLY2_PROP} $1 2>/dev/null
114280304Sjkim}
115280304Sjkim
116280304Sjkimcheck_ipf_dir()
117280304Sjkim{
118280304Sjkim	[ -d $VAR_IPF_DIR ] && return 0
119280304Sjkim	mkdir $VAR_IPF_DIR >/dev/null 2>&1 || return 1
120280304Sjkim}
121280304Sjkim
122280304Sjkim#
123280304Sjkim# fmri_to_file fmri suffix
124280304Sjkim#
125280304Sjkimfmri_to_file()
126280304Sjkim{
127280304Sjkim	check_ipf_dir || return 1
128280304Sjkim	fprefix="${VAR_IPF_DIR}/`echo $1 | tr -s '/:' '__'`"
129280304Sjkim	echo "${fprefix}${2}"
130280304Sjkim}
131280304Sjkim
132280304Sjkim#
133280304Sjkim# Return service's enabled property
134280304Sjkim#
135280304Sjkimservice_is_enabled()
136280304Sjkim{
137280304Sjkim	#
138280304Sjkim	# Temporary enabled state overrides the persistent state
139280304Sjkim	# so check it first.
140280304Sjkim	#
141280304Sjkim	enabled_ovr=`svcprop -c -p general_ovr/enabled $1 2>/dev/null`
142280304Sjkim	if [ -n "$enabled_ovr" ]; then
143280304Sjkim		[ "$enabled_ovr" = "true" ] && return 0 || return 1
144280304Sjkim	fi
145280304Sjkim
146280304Sjkim	enabled=`svcprop -c -p general/enabled $1 2>/dev/null`
147280304Sjkim	[ -n "$enabled" -a "$enabled" = "true" ] && return 0 || return 1
148280304Sjkim}
149280304Sjkim
150280304Sjkim#
151280304Sjkim# Return whether service is desired state
152280304Sjkim#
153280304Sjkim# Args: fmri state
154280304Sjkim# Return:
155280304Sjkim#  0 - desired state is service's current state  
156280304Sjkim#  1 - desired state is not service's current state  
157280304Sjkim#
158280304Sjkimservice_check_state()
159280304Sjkim{
160280304Sjkim	#
161280304Sjkim	# Make sure we're done with ongoing state transition
162280304Sjkim	#
163280304Sjkim	while [ "`svcprop -p restarter/next_state $1`" != "$SMF_NONE" ]; do
164280304Sjkim		sleep 1
165280304Sjkim	done
166280304Sjkim	
16755714Skris	[ "`svcprop -p restarter/state $1`" = "$2" ] && return 0 || return 1
168280304Sjkim}
169
170#
171# Deny/Allow list stores values in the form "host:addr", "network:addr/netmask",
172# "pool:number", and "if:interface". This function returns the
173# IP(addr or addr/netmask) value or a pool number.
174#
175get_IP()
176{
177	value_is_interface $1 && return 1
178	echo "$1" | sed -n -e 's,^pool:\(.*\),pool/\1,p' \
179	    -e 's,^host:\(.*\),\1,p' \
180	    -e 's,^network:\(.*\),\1,p'
181}
182
183get_interface()
184{
185	value_is_interface $1 || return 1
186	scratch=`echo "$1" | sed -e 's/^if://'`
187
188	ifconfig $scratch >/dev/null 2>&1 || return 1
189	echo $scratch | sed -e 's/:.*//'
190}
191
192#
193#
194#
195value_is_interface()
196{
197	[ -z "$1" ] && return 1
198	echo $1 | grep "^if:" >/dev/null 2>&1
199}
200
201#
202# Remove rules in given file from active list without restarting ipfilter
203#
204remove_rules()
205{
206	[ -f "$1" ] && ipf -r -f $1 >/dev/null 2>&1
207}
208
209remove_nat_rules()
210{
211	[ -f "$1" ] && ipnat -r -f $1 >/dev/null 2>&1
212}
213
214check_ipf_syntax()
215{
216	ipf -n -f $1 >/dev/null 2>&1
217}
218
219check_nat_syntax()
220{
221	ipnat -n -f $1 >/dev/null 2>&1
222}
223
224file_get_ports()
225{
226	ipf -n -v -f $1 2>/dev/null | sed -n -e \
227	    's/.*to.* port = \([a-z0-9]*\).*/\1/p' | uniq | \
228	    awk '{if (length($0) > 1) {printf("%s ", $1)}}'
229}
230
231get_active_ports()
232{
233	ipfstat -io 2>/dev/null | sed -n -e \
234	    's/.*to.* port = \([a-z0-9]*\).*/\1/p' | uniq | \
235	    awk '{if (length($0) > 1) {printf("%s ",$1)}}'
236}
237
238#
239# Given two list of ports, return failure if there's a duplicate.
240#
241sets_check_duplicate()
242{
243	#
244	# If either list is empty, there isn't any conflict.
245	#
246	[ -z "$1" -o -z "$2" ] && return 0
247
248	for p in $1; do
249		for ap in $2; do
250			[ "$p" = "$ap" ] && return 1
251		done
252	done
253
254	return 0
255}
256
257#
258# Given a file containing ipf rules, check the syntax and verify
259# the rules don't conflict, use same port number, with active
260# rules (ipfstat -io output).
261#
262update_check_ipf_rules()
263{
264	check_ipf_syntax $1 || return 1
265
266	lports=`file_get_ports $1`
267	lactive_ports=`get_active_ports`
268
269	sets_check_duplicate "$lports" "$lactive_ports" || return 1
270}
271
272server_port_list=""
273
274#
275# Given a file containing ipf rules, check the syntax and verify
276# the rules don't conflict with already processed services.
277#
278# The list of processed services' ports are maintained in the global
279# variable 'server_port_list'.
280#
281check_ipf_rules()
282{
283	check_ipf_syntax $1 || return 1
284
285	lports=`file_get_ports $1`
286	sets_check_duplicate "$lports" "$server_port_list" || return 1
287	server_port_list="$server_port_list $lports"
288	return 0
289}
290
291prepend_new_rules()
292{
293	check_ipf_syntax $1 && tail -r $1 | sed -e 's/^[a-z]/@0 &/' | \
294	    ipf -f - >/dev/null 2>&1
295}
296
297append_new_rules()
298{
299	check_ipf_syntax $1 && ipf -f $1 >/dev/null 2>&1
300}
301
302append_new_nat_rules()
303{
304	check_nat_syntax $1 && ipnat -f $1 >/dev/null 2>&1
305}
306
307#
308# get port information from string of the form "proto:{port | port-port}"
309#
310tuple_get_port()
311{
312	port_str=`echo "$1" | sed -e 's/ //g; s/.*://' 2>/dev/null`
313	[ -z "$port_str" ] && return 1
314
315	echo $port_str | grep "-" >/dev/null
316	if  [ $? -eq  0 ]; then
317		echo $port_str | grep '^[0-9]\{1,5\}-[0-9]\{1,5\}$' >/dev/null || \
318		    return 1
319		ports=`echo $port_str | ( IFS=- read a b ; \
320		    [ $a \-le $b ] && echo $a $b || echo $b $a )`
321
322		for p in $ports; do
323			[ $p -gt 65535 ] && return 1
324		done
325		echo "$ports"
326	else
327		#
328		# port_str is a single port, verify and return it.
329		#
330		echo "$port_str" | grep '^[0-9]\{1,5\}$' >/dev/null || return 1
331		[ $port_str -gt 65535 ] && return 1
332		echo "$port_str"
333	fi
334}
335
336#
337# get proto info from string of the form "{tcp | udp}:port"
338#
339tuple_get_proto()
340{
341	proto=`echo "$1" | sed -e 's/ //g; s/:.*//' 2>/dev/null`
342	[ -z "$proto" ] && return 0
343
344	[ "$proto" = "tcp" -o "$proto" = "udp" ] && echo $proto || return 1
345	return 0
346}
347
348ipf_get_lock()
349{
350	newpid=$$
351
352	if [ -f "$IPF_LOCK/pid" ]; then
353		curpid=`cat $IPF_LOCK/pid 2>/dev/null`
354		[ "$curpid" = "$newpid" ] && return 0
355
356		#
357		# Clear lock if the owning process is no longer around.
358		#
359		ps -p $curpid >/dev/null 2>&1 || rm -r $IPF_LOCK >/dev/null 2>&1
360	fi
361
362	#
363	# Grab the lock
364	#
365	while :; do
366		mkdir $IPF_LOCK 2>/dev/null && break;
367		sleep 1
368	done
369	echo $newpid > $IPF_LOCK/pid
370}
371
372#
373# Remove lock if it's ours
374#
375ipf_remove_lock()
376{
377	if [ -f "$IPF_LOCK/pid" ]; then
378		[ "`cat $IPF_LOCK/pid`" = "$$" ] && rm -r $IPF_LOCK
379	fi
380	return 0
381}
382
383#
384# Make IPFILCONF, /var/tmp/ipf/ipf.conf, a symlink to the input file argument.
385#
386custom_set_symlink()
387{
388	#
389	# Nothing to do if the input file doesn't exist.
390	#
391	[ ! -f "$1" ] && return 0
392
393	check_ipf_dir || return 1
394
395	rm $IPFILCONF >/dev/null 2>&1
396	ln -s $1 $IPFILCONF >/dev/null 2>&1
397}
398
399#
400# New file replaces original file if they have different content
401#
402replace_file()
403{
404	orig=$1	
405	new=$2	
406
407	#
408	# IPFILCONF may be a symlink, remove it if that's the case
409	#
410	if [ -L "$orig" ]; then
411		rm $orig
412		touch $orig
413	fi
414
415	check_ipf_dir || return 1
416	mv $new $orig && return 0 || return 1
417}
418
419#
420# Given a service, gets the following details for ipf rule:
421# - policy
422# - protocol
423# - port(IANA port obtained by running servinfo)
424#
425process_server_svc()
426{
427	service=$1
428	ip="any"
429        policy=`get_policy ${service}`
430
431	#
432	# Empties service's rules file so callers won't use existing rule if
433	# we fail here.
434	#
435	file=`fmri_to_file $service $IPF_SUFFIX`
436	[ -z "$file" ] && return 1
437	echo "# $service" >${file}
438
439	#
440	# Nothing to do if policy is "use_global"
441	#
442	[ "$policy" = "use_global" ] && return 0
443
444	restarter=`svcprop -p general/restarter $service 2>/dev/null`
445	if [ "$restarter" = "$INETDFMRI" ]; then
446		iana_name=`svcprop -p inetd/name $service 2>/dev/null`
447		isrpc=`svcprop -p inetd/isrpc $service 2>/dev/null`
448	else
449		iana_name=`svcprop -p $FW_CONTEXT_PG/name $service 2>/dev/null`
450		isrpc=`svcprop -p $FW_CONTEXT_PG/isrpc $service 2>/dev/null`
451	fi
452
453	#
454	# Bail if iana_name isn't defined. Services with static rules
455	# like nis/client don't need to generate rules using
456	# iana name and protocol information.
457	# 
458	[ -z "$iana_name" ] && return 1
459
460	#
461	# RPC services
462	#
463	if [ "$isrpc" = "true" ]; then
464		tports=`$SERVINFO -R -p -t -s $iana_name 2>/dev/null`
465		if [ -n "$tports" ]; then
466			for tport in $tports; do 
467				generate_rules $service $policy "tcp" \
468				    $ip $tport $file
469			done
470		fi
471
472		uports=`$SERVINFO -R -p -u -s $iana_name 2>/dev/null`
473		if [ -n "$uports" ]; then
474			for uport in $uports; do 
475				generate_rules $service $policy "udp" \
476				    $ip $uport $file
477			done
478		fi
479
480		return 0
481	fi
482
483	#
484	# Get the IANA port and supported protocols(tcp and udp)
485	# No support for IPv6 at this point.
486	#
487	tport=`$SERVINFO -p -t -s $iana_name 2>&1`
488	if [ $? -eq 0 -a -n "$tport" ]; then
489		generate_rules $service $policy "tcp" $ip $tport $file
490	fi
491
492	uport=`$SERVINFO -p -u -s $iana_name 2>&1`
493	if [ $? -eq 0 -a -n "$uport" ]; then
494		generate_rules $service $policy "udp" $ip $uport $file
495	fi
496
497	return 0
498}
499
500#
501# Given a service's name, policy, protocol and port, generate ipf rules
502# - list of host/network/interface to apply policy
503#
504# A 'use_global' policy inherits the system-wided Global Default policy
505# from network/ipfilter. For {deny | allow} policies, the rules are
506# ordered as:
507#
508# - make exceptions to policy for those in "exceptions" list
509# - apply policy to those specified in "apply_to" list 
510# - policy rule
511#
512generate_rules()
513{
514	service=$1
515	mypolicy=$2
516	proto=$3
517	ip=$4
518	port=$5
519	out=$6
520
521	#
522	# Default mode is to inherit from global's policy
523	#
524	[ "$mypolicy" = "use_global" ] && return 0
525
526	tcp_opts=""
527	[ "$proto" = "tcp" ] && tcp_opts="flags S keep state keep frags"
528
529	#
530	# Allow all if policy is 'none'
531	#
532	if [ "$mypolicy" = "none" ]; then
533		echo "pass in log quick proto ${proto} from any to ${ip}" \
534		    "port = ${port} ${tcp_opts}" >>${out}
535		return 0
536	fi
537
538	#
539	# For now, let's concern only with incoming traffic.
540	#
541	[ "$mypolicy" = "deny" ] && { ecmd="pass"; acmd="block"; }
542	[ "$mypolicy" = "allow" ] && { ecmd="block"; acmd="pass"; }
543
544	for name in `get_exceptions $service`; do
545		[ -z "$name" -o "$name" = '""' ] && continue
546
547		ifc=`get_interface $name`
548		if [ $? -eq 0 -a -n "$ifc" ]; then
549			echo "${ecmd} in log quick on ${ifc} from any to" \
550			    "${ip} port = ${port}" >>${out}
551			continue
552		fi
553
554		addr=`get_IP ${name}`
555		if [ $? -eq 0 -a -n "$addr" ]; then
556			echo "${ecmd} in log quick proto ${proto} from ${addr}" \
557			    "to ${ip} port = ${port} ${tcp_opts}" >>${out}
558		fi
559	done
560
561	for name in `get_apply2_list $service`; do
562		[ -z "$name" -o "$name" = '""' ] && continue
563
564		ifc=`get_interface $name`
565		if [ $? -eq 0 -a -n "$ifc" ]; then
566			echo "${acmd} in log quick on ${ifc} from any to" \
567			    "${ip} port = ${port}" >>${out}
568			continue
569		fi
570
571		addr=`get_IP ${name}`
572		if [ $? -eq 0 -a -n "$addr" ]; then
573			echo "${acmd} in log quick proto ${proto} from ${addr}" \
574			    "to ${ip} port = ${port} ${tcp_opts}" >>${out}
575		fi
576	done
577
578	echo "${ecmd} in log quick proto ${proto} from any to ${ip}" \
579	    "port = ${port} ${tcp_opts}" >>${out}
580
581	return 0
582}
583
584#
585# Service has either IANA ports and proto or its own firewall method to
586# generate the rules.
587#
588# - if service has a custom method, use it to populate its rules
589# - if service has a firewall_config pg, use process_server_svc
590#
591# Argument - fmri
592#
593process_service()
594{
595	#
596	# Don't process network/ipfilter
597	#
598	[ "$1" = "$IPF_FMRI" ] && return 0
599
600	service_check_state $1 $SMF_MAINT && return 1
601
602	method=`svcprop -p $FW_CONTEXT_PG/$METHOD_PROP $1 2>/dev/null | \
603	    sed 's/\\\//g'`
604	if [ -n "$method" -a "$method" != '""' ]; then
605		( exec $method $1 >/dev/null )
606	else
607		svcprop -p $FW_CONFIG_PG $1 >/dev/null 2>&1 || return 1
608		process_server_svc $1 || return 1
609	fi
610	return 0
611}
612
613#
614# Generate rules for protocol/port defined in firewall_config_default/open_ports
615# property. These are non-service programs whose network resource info are
616# defined as "{tcp | upd}:{PORT | PORT-PORT}". Essentially, these programs need
617# some specific local ports to be opened. For example, BitTorrent clients need to
618# have 6881-6889 opened.
619#
620process_nonsvc_progs()
621{
622	out=$1
623	echo "# Non-service programs rules" >>${out}
624	progs=`svcprop -p ${FW_CONFIG_DEF_PG}/${OPEN_PORTS_PROP} \
625	    $SMF_FMRI 2>/dev/null`
626
627	for prog in $progs; do
628		[ -z "$prog" -o "$prog" = '""' ] && continue
629
630		port=`tuple_get_port $prog`
631		[ $? -eq 1 -o -z "$port" ] && continue
632
633		proto=`tuple_get_proto $prog`
634		[ $? -eq 1 ] && continue
635
636		set -- $port
637		if  [ $# -gt 1 ]; then
638			if [ -z "$proto" ]; then
639				echo "pass in log quick from any to any" \
640				    "port ${1} >< ${2}" >>${out}
641			else
642				echo "pass in log quick proto ${proto} from any" \
643				    "to any port ${1} >< ${2}" >>${out}
644			fi
645		else
646			if [ -z "$proto" ]; then
647				echo "pass in log quick from any to any" \
648				    "port = ${1}" >>${out}
649			else
650				echo "pass in log quick proto ${proto} from any" \
651				    "to any port = ${1}" >>${out}
652			fi
653		fi
654	done
655
656	return 0
657}
658
659#
660# Generate a new /etc/ipf/ipf.conf. If firewall policy is 'none',
661# ipf.conf is empty .
662#
663create_global_rules()
664{
665	policy=`get_global_def_policy`
666
667	if [ "$policy" = "custom" ]; then
668		file=`svcprop -p ${FW_CONFIG_DEF_PG}/${CUSTOM_FILE_PROP} $SMF_FMRI`
669
670		[ -n "$file" ] && custom_set_symlink $file
671		return 0
672	fi
673
674	TEMP=`mktemp /var/run/ipf.conf.pid$$.XXXXXX`
675	process_nonsvc_progs $TEMP
676
677	echo "# Global Default rules" >>${TEMP}
678	if [ "$policy" != "none" ]; then
679		echo "pass out log quick all keep state" >>${TEMP}
680	fi
681
682	case "$policy" in
683	'none')
684		# No rules
685		replace_file ${IPFILCONF} ${TEMP}
686		return $?
687		;;
688	
689	'deny')
690		ecmd="pass"
691		acmd="block"
692		;;
693
694	'allow')
695		ecmd="block"
696		acmd="pass"
697		;;
698	*)
699		return 1;
700		;;
701	esac
702
703	for name in `get_exceptions $SMF_FMRI`; do
704		[ -z "$name" -o "$name" = '""' ] && continue
705
706		ifc=`get_interface $name`
707		if [ $? -eq 0 -a -n "$ifc" ]; then
708			echo "${ecmd} in log quick on ${ifc} all" >>${TEMP} 
709			continue
710		fi
711
712		addr=`get_IP ${name}`
713		if [ $? -eq 0 -a -n "$addr" ]; then
714			echo "${ecmd} in log quick from ${addr} to any" >>${TEMP}
715		fi
716
717	done
718
719	for name in `get_apply2_list $SMF_FMRI`; do
720		[ -z "$name" -o "$name" = '""' ] && continue
721
722		ifc=`get_interface $name`
723		if [ $? -eq 0 -a -n "$ifc" ]; then
724			echo "${acmd} in log quick on ${ifc} all" >>${TEMP}
725			continue
726		fi
727
728		addr=`get_IP ${name}`
729		if [ $? -eq 0 -a -n "$addr" ]; then
730			echo "${acmd} in log quick from ${addr} to any" >>${TEMP}
731		fi
732	done
733
734	if [ "$policy" = "allow" ]; then
735		#
736		# Allow DHCP traffic if running as a DHCP client 
737		#
738		/sbin/netstrategy | grep dhcp >/dev/null 2>&1
739		if [ $? -eq 0 ]; then
740			echo "pass out log quick from any port = 68" \
741			    "keep state" >>${TEMP}
742			echo "pass out log quick from any port = 546" \
743			    "keep state" >>${TEMP}
744			echo "pass in log quick from any to any port = 68" >>${TEMP}
745			echo "pass in log quick from any to any port = 546" >>${TEMP}
746		fi
747		echo "block in log all" >>${TEMP}
748	fi
749
750	replace_file ${IPFILCONF} ${TEMP}
751	return $?
752}
753
754#
755# Generate a new /etc/ipf/ipf_ovr.conf, the override system-wide policy. It's
756# a simplified policy that doesn't support 'exceptions' entities.
757#
758# If firewall policy is "none", no rules are generated.
759#
760# Note that "pass" rules don't have "quick" as we don't want
761# them to override services' block rules.
762#
763create_global_ovr_rules()
764{
765	#
766	# Simply empty override file if global policy is 'custom'
767	#
768	if [ "`get_global_def_policy`" = "custom" ]; then 
769		echo "# 'custom' global policy" >$IPFILOVRCONF
770		return 0
771	fi
772
773	#
774	# Get and process override policy
775	#
776	ovr_policy=`svcprop -p ${FW_CONFIG_OVR_PG}/${POLICY_PROP} $IPF_FMRI`
777	TEMP=`mktemp /var/run/ipf_ovr.conf.pid$$.XXXXXX`
778
779	[ "$ovr_policy" = "deny" ] && acmd="block in log quick"
780	[ "$ovr_policy" = "allow" ] && acmd="pass in log"
781
782	apply2_list=`svcprop -p $FW_CONFIG_OVR_PG/$APPLY2_PROP $IPF_FMRI`
783	for name in $apply2_list; do
784		[ -z "$name" -o "$name" = '""' ] && continue
785
786		ifc=`get_interface $name`
787		if [ $? -eq 0 -a -n "$ifc" ]; then
788			echo "${acmd} on ${ifc} all" >>${TEMP}
789			continue
790		fi
791
792		addr=`get_IP ${name}`
793		if [ $? -eq 0 -a -n "$addr" ]; then
794			echo "${acmd} from ${addr} to any" >>${TEMP}
795		fi
796	done
797
798	replace_file ${IPFILOVRCONF} ${TEMP}
799	return $?
800}
801
802#
803# Service is put into maintenance state due to its invalid firewall
804# definition and/or policy.
805#
806svc_mark_maintenance()
807{
808	svcadm mark maintenance $1 >/dev/null 2>&1
809
810	date=`date`
811	echo "[ $date ${0}: $1 has invalid ipf configuration. ]"
812	echo "[ $date ${0}: placing $1 in maintenance. ]"
813
814	#
815	# Move service's rule files to another location since
816	# they're most likely invalid.
817	#
818	ipfile=`fmri_to_file $1 $IPF_SUFFIX`
819	[ -f "$ipfile" ] && mv $ipfile "$ipfile.bak"
820
821	natfile=`fmri_to_file $1 $NAT_SUFFIX`
822	[ -f "$natfile" ] && mv $natfile "$natfile.bak"
823
824	return 0
825}
826
827svc_is_server()
828{
829	svcprop -p $FW_CONFIG_PG $1 >/dev/null 2>&1
830}
831
832#
833# Create rules for enabled firewalling and client services.
834# - obtain the list of enabled services and process them
835# - save the list of rules file for later use
836#
837create_services_rules()
838{
839	#
840	# Do nothing if global policy is 'custom'
841	#
842	global_policy=`get_global_def_policy`
843	[ "$global_policy" = "custom" ] && return 0
844
845	ipf_get_lock
846
847	#
848	# Get all enabled services
849	#
850	allsvcs=`svcprop -cf -p general/enabled -p general_ovr/enabled '*' \
851	    2>/dev/null | sed -n 's,^\(svc:.*\)/:properties/.* true$,\1,p' | sort -u`
852
853	#
854	# Process enabled services 
855	#
856	for s in $allsvcs; do
857		service_is_enabled $s || continue
858		process_service $s || continue
859
860		ipfile=`fmri_to_file $s $IPF_SUFFIX`
861		if [ -n "$ipfile" -a -r "$ipfile" ]; then
862			check_ipf_syntax $ipfile
863			if [ $? -ne 0 ]; then
864				svc_mark_maintenance $s
865				continue
866			fi
867
868			svc_is_server $s
869			if [ $? -eq 0 ]; then
870				check_ipf_rules $ipfile
871				if [ $? -ne 0 ]; then
872					svc_mark_maintenance $s
873					continue
874				fi
875			fi
876			CONF_FILES="$CONF_FILES $ipfile"
877		fi
878
879		natfile=`fmri_to_file $s $NAT_SUFFIX`
880		if [ -n "$natfile" -a -r "$natfile" ]; then
881			check_nat_syntax $natfile
882			if [ $? -ne 0 ]; then
883				svc_mark_maintenance $s
884				continue
885			fi
886
887			NAT_FILES="$NAT_FILES $natfile"
888		fi
889	done
890
891	ipf_remove_lock
892	return 0
893}
894
895#
896# We update a services ipf ruleset in the following manners:
897# - service is disabled, tear down its rules.
898# - service is disable or refreshed(online), setup or update its rules.
899#
900service_update_rules()
901{
902	#
903	# If ipfilter isn't online or global policy is 'custom',
904	# nothing should be done.
905	#
906	service_check_state $SMF_FMRI $SMF_ONLINE || return 0
907	[ "`get_global_def_policy`" = "custom" ] && return 0
908
909	svc=$1
910
911	ipfile=`fmri_to_file $svc $IPF_SUFFIX`
912	[ -z "$ipfile" ] && return 0
913
914	remove_rules $ipfile
915
916	natfile=`fmri_to_file $svc $NAT_SUFFIX`
917	[ -n "$natfile" ] && remove_nat_rules $natfile
918
919	#
920	# Don't go further if service is disabled or in maintenance.
921	#
922	service_is_enabled $svc || return 0
923	service_check_state $1 $SMF_MAINT && return 0
924
925	process_service $svc || return 1
926	if [ -f "$ipfile" ]; then
927		check_ipf_syntax $ipfile
928		if [ $? -ne 0 ]; then
929			svc_mark_maintenance $svc
930			return 1
931		fi
932	fi
933
934	if [ -f "$natfile" ]; then
935		check_nat_syntax $natfile
936		if [ $? -ne 0 ]; then
937			svc_mark_maintenance $svc
938			return 1
939		fi
940	fi
941
942	if [ -f "$ipfile" ]; then
943		svc_is_server $svc
944		if [ $? -eq 0 ]; then
945			update_check_ipf_rules $ipfile
946			if [ $? -ne 0 ]; then
947				svc_mark_maintenance $svc
948				return 1
949			fi
950		fi
951
952		prepend_new_rules $ipfile
953
954		#
955		# reload Global Override rules to
956		# maintain correct ordering.
957		#
958		remove_rules $IPFILOVRCONF
959		prepend_new_rules $IPFILOVRCONF
960	fi
961
962	[ -f "$natfile" ] && append_new_nat_rules $natfile
963
964	return 0
965}
966
967#
968# Call the service_update_rules with appropriate svc fmri.
969#
970# This is called from '/lib/svc/method/ipfilter fw_update' whenever
971# a service is disabled/enabled/refreshed.
972#
973service_update()
974{
975	svc=$1
976	ret=0
977
978	ipf_get_lock
979	service_update_rules $svc || ret=1
980
981	ipf_remove_lock
982	return $ret
983}
984