Home | History | Annotate | Download | only in shell
      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 2009 Sun Microsystems, Inc.  All rights reserved.
     24 # Use is subject to license terms.
     25 #
     26 
     27 ETC_IPF_DIR=/etc/ipf
     28 IP6FILCONF=$ETC_IPF_DIR/ipf6.conf
     29 IPNATCONF=$ETC_IPF_DIR/ipnat.conf
     30 IPPOOLCONF=$ETC_IPF_DIR/ippool.conf
     31 VAR_IPF_DIR=/var/run/ipf
     32 IPFILCONF=$VAR_IPF_DIR/ipf.conf
     33 IPFILOVRCONF=$VAR_IPF_DIR/ipf_ovr.conf
     34 IPF_LOCK=/var/run/ipflock
     35 CONF_FILES=""
     36 NAT_FILES=""
     37 IPF_SUFFIX=".ipf"
     38 NAT_SUFFIX=".nat"
     39 
     40 # version for configuration upgrades
     41 CURRENT_VERSION=1
     42 
     43 IPF_FMRI="svc:/network/ipfilter:default"
     44 INETDFMRI="svc:/network/inetd:default"
     45 RPCBINDFMRI="svc:/network/rpc/bind:default"
     46 
     47 SMF_ONLINE="online"
     48 SMF_MAINT="maintenance"
     49 SMF_NONE="none"
     50 
     51 FW_CONTEXT_PG="firewall_context"
     52 METHOD_PROP="ipf_method"
     53 
     54 FW_CONFIG_PG="firewall_config"
     55 POLICY_PROP="policy"
     56 APPLY2_PROP="apply_to"
     57 EXCEPTIONS_PROP="exceptions"
     58 
     59 FW_CONFIG_DEF_PG="firewall_config_default"
     60 FW_CONFIG_OVR_PG="firewall_config_override"
     61 CUSTOM_FILE_PROP="custom_policy_file"
     62 OPEN_PORTS_PROP="open_ports"
     63 
     64 PREFIX_HOST="host:"
     65 PREFIX_NET="network:"
     66 PREFIX_POOL="pool:"
     67 PREFIX_IF="if:"
     68 
     69 SERVINFO=/usr/lib/servinfo
     70 
     71 #
     72 # Given a service, gets its config pg name 
     73 #
     74 get_config_pg()
     75 {
     76 	if [ "$1" = "$IPF_FMRI" ]; then
     77 		echo "$FW_CONFIG_DEF_PG"
     78 	else
     79 		echo "$FW_CONFIG_PG"
     80 	fi
     81 	return 0
     82 }
     83 
     84 #
     85 # Given a service, gets its firewall policy
     86 #
     87 get_policy()
     88 {
     89 	config_pg=`get_config_pg $1`
     90 	svcprop -p $config_pg/${POLICY_PROP} $1 2>/dev/null
     91 }
     92 
     93 get_global_def_policy()
     94 {
     95 	svcprop -p ${FW_CONFIG_DEF_PG}/${POLICY_PROP} $IPF_FMRI 2>/dev/null
     96 }
     97 
     98 #
     99 # Given a service, gets its firewall policy
    100 #
    101 get_exceptions()
    102 {
    103 	config_pg=`get_config_pg $1`
    104 	svcprop -p $config_pg/${EXCEPTIONS_PROP} $1 2>/dev/null
    105 }
    106 
    107 #
    108 # Given a service, gets its firewall policy
    109 #
    110 get_apply2_list()
    111 {
    112 	config_pg=`get_config_pg $1`
    113 	svcprop -p $config_pg/${APPLY2_PROP} $1 2>/dev/null
    114 }
    115 
    116 check_ipf_dir()
    117 {
    118 	[ -d $VAR_IPF_DIR ] && return 0
    119 	mkdir $VAR_IPF_DIR >/dev/null 2>&1 || return 1
    120 }
    121 
    122 #
    123 # fmri_to_file fmri suffix
    124 #
    125 fmri_to_file()
    126 {
    127 	check_ipf_dir || return 1
    128 	fprefix="${VAR_IPF_DIR}/`echo $1 | tr -s '/:' '__'`"
    129 	echo "${fprefix}${2}"
    130 }
    131 
    132 #
    133 # Return service's enabled property
    134 #
    135 service_is_enabled()
    136 {
    137 	#
    138 	# Temporary enabled state overrides the persistent state
    139 	# so check it first.
    140 	#
    141 	enabled_ovr=`svcprop -c -p general_ovr/enabled $1 2>/dev/null`
    142 	if [ -n "$enabled_ovr" ]; then
    143 		[ "$enabled_ovr" = "true" ] && return 0 || return 1
    144 	fi
    145 
    146 	enabled=`svcprop -c -p general/enabled $1 2>/dev/null`
    147 	[ -n "$enabled" -a "$enabled" = "true" ] && return 0 || return 1
    148 }
    149 
    150 #
    151 # Return whether service is desired state
    152 #
    153 # Args: fmri state
    154 # Return:
    155 #  0 - desired state is service's current state  
    156 #  1 - desired state is not service's current state  
    157 #
    158 service_check_state()
    159 {
    160 	#
    161 	# Make sure we're done with ongoing state transition
    162 	#
    163 	while [ "`svcprop -p restarter/next_state $1`" != "$SMF_NONE" ]; do
    164 		sleep 1
    165 	done
    166 	
    167 	[ "`svcprop -p restarter/state $1`" = "$2" ] && return 0 || return 1
    168 }
    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 #
    175 get_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 
    183 get_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 #
    195 value_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 #
    204 remove_rules()
    205 {
    206 	[ -f "$1" ] && ipf -r -f $1 >/dev/null 2>&1
    207 }
    208 
    209 remove_nat_rules()
    210 {
    211 	[ -f "$1" ] && ipnat -r -f $1 >/dev/null 2>&1
    212 }
    213 
    214 check_ipf_syntax()
    215 {
    216 	ipf -n -f $1 >/dev/null 2>&1
    217 }
    218 
    219 check_nat_syntax()
    220 {
    221 	ipnat -n -f $1 >/dev/null 2>&1
    222 }
    223 
    224 file_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 
    231 get_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 #
    241 sets_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 #
    262 update_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 
    272 server_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 #
    281 check_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 
    291 prepend_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 
    297 append_new_rules()
    298 {
    299 	check_ipf_syntax $1 && ipf -f $1 >/dev/null 2>&1
    300 }
    301 
    302 append_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 #
    310 tuple_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 #
    339 tuple_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 
    348 ipf_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 #
    375 ipf_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 #
    386 custom_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 #
    402 replace_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 #
    425 process_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 #
    512 generate_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 #
    593 process_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 #
    620 process_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 #
    663 create_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 #
    763 create_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 	if [ "$ovr_policy" = "none" ]; then 
    778 		echo "# global override policy is 'none'" >$IPFILOVRCONF
    779 		return 0
    780 	fi
    781 
    782 	TEMP=`mktemp /var/run/ipf_ovr.conf.pid$$.XXXXXX`
    783 	[ "$ovr_policy" = "deny" ] && acmd="block in log quick"
    784 	[ "$ovr_policy" = "allow" ] && acmd="pass in log"
    785 
    786 	apply2_list=`svcprop -p $FW_CONFIG_OVR_PG/$APPLY2_PROP $IPF_FMRI`
    787 	for name in $apply2_list; do
    788 		[ -z "$name" -o "$name" = '""' ] && continue
    789 
    790 		ifc=`get_interface $name`
    791 		if [ $? -eq 0 -a -n "$ifc" ]; then
    792 			echo "${acmd} on ${ifc} all" >>${TEMP}
    793 			continue
    794 		fi
    795 
    796 		addr=`get_IP ${name}`
    797 		if [ $? -eq 0 -a -n "$addr" ]; then
    798 			echo "${acmd} from ${addr} to any" >>${TEMP}
    799 		fi
    800 	done
    801 
    802 	replace_file ${IPFILOVRCONF} ${TEMP}
    803 	return $?
    804 }
    805 
    806 #
    807 # Service is put into maintenance state due to its invalid firewall
    808 # definition and/or policy.
    809 #
    810 svc_mark_maintenance()
    811 {
    812 	svcadm mark maintenance $1 >/dev/null 2>&1
    813 
    814 	date=`date`
    815 	echo "[ $date ${0}: $1 has invalid ipf configuration. ]"
    816 	echo "[ $date ${0}: placing $1 in maintenance. ]"
    817 
    818 	#
    819 	# Move service's rule files to another location since
    820 	# they're most likely invalid.
    821 	#
    822 	ipfile=`fmri_to_file $1 $IPF_SUFFIX`
    823 	[ -f "$ipfile" ] && mv $ipfile "$ipfile.bak"
    824 
    825 	natfile=`fmri_to_file $1 $NAT_SUFFIX`
    826 	[ -f "$natfile" ] && mv $natfile "$natfile.bak"
    827 
    828 	return 0
    829 }
    830 
    831 svc_is_server()
    832 {
    833 	svcprop -p $FW_CONFIG_PG $1 >/dev/null 2>&1
    834 }
    835 
    836 #
    837 # Create rules for enabled firewalling and client services.
    838 # - obtain the list of enabled services and process them
    839 # - save the list of rules file for later use
    840 #
    841 create_services_rules()
    842 {
    843 	#
    844 	# Do nothing if global policy is 'custom'
    845 	#
    846 	global_policy=`get_global_def_policy`
    847 	[ "$global_policy" = "custom" ] && return 0
    848 
    849 	ipf_get_lock
    850 
    851 	#
    852 	# Get all enabled services
    853 	#
    854 	allsvcs=`svcprop -cf -p general/enabled -p general_ovr/enabled '*' \
    855 	    2>/dev/null | sed -n 's,^\(svc:.*\)/:properties/.* true$,\1,p' | sort -u`
    856 
    857 	#
    858 	# Process enabled services 
    859 	#
    860 	for s in $allsvcs; do
    861 		service_is_enabled $s || continue
    862 		process_service $s || continue
    863 
    864 		ipfile=`fmri_to_file $s $IPF_SUFFIX`
    865 		if [ -n "$ipfile" -a -r "$ipfile" ]; then
    866 			check_ipf_syntax $ipfile
    867 			if [ $? -ne 0 ]; then
    868 				svc_mark_maintenance $s
    869 				continue
    870 			fi
    871 
    872 			svc_is_server $s
    873 			if [ $? -eq 0 ]; then
    874 				check_ipf_rules $ipfile
    875 				if [ $? -ne 0 ]; then
    876 					svc_mark_maintenance $s
    877 					continue
    878 				fi
    879 			fi
    880 			CONF_FILES="$CONF_FILES $ipfile"
    881 		fi
    882 
    883 		natfile=`fmri_to_file $s $NAT_SUFFIX`
    884 		if [ -n "$natfile" -a -r "$natfile" ]; then
    885 			check_nat_syntax $natfile
    886 			if [ $? -ne 0 ]; then
    887 				svc_mark_maintenance $s
    888 				continue
    889 			fi
    890 
    891 			NAT_FILES="$NAT_FILES $natfile"
    892 		fi
    893 	done
    894 
    895 	ipf_remove_lock
    896 	return 0
    897 }
    898 
    899 #
    900 # We update a services ipf ruleset in the following manners:
    901 # - service is disabled, tear down its rules.
    902 # - service is disable or refreshed(online), setup or update its rules.
    903 #
    904 service_update_rules()
    905 {
    906 	#
    907 	# If ipfilter isn't online or global policy is 'custom',
    908 	# nothing should be done.
    909 	#
    910 	service_check_state $SMF_FMRI $SMF_ONLINE || return 0
    911 	[ "`get_global_def_policy`" = "custom" ] && return 0
    912 
    913 	svc=$1
    914 
    915 	ipfile=`fmri_to_file $svc $IPF_SUFFIX`
    916 	[ -z "$ipfile" ] && return 0
    917 
    918 	remove_rules $ipfile
    919 
    920 	natfile=`fmri_to_file $svc $NAT_SUFFIX`
    921 	[ -n "$natfile" ] && remove_nat_rules $natfile
    922 
    923 	#
    924 	# Don't go further if service is disabled or in maintenance.
    925 	#
    926 	service_is_enabled $svc || return 0
    927 	service_check_state $1 $SMF_MAINT && return 0
    928 
    929 	process_service $svc || return 1
    930 	if [ -f "$ipfile" ]; then
    931 		check_ipf_syntax $ipfile
    932 		if [ $? -ne 0 ]; then
    933 			svc_mark_maintenance $svc
    934 			return 1
    935 		fi
    936 	fi
    937 
    938 	if [ -f "$natfile" ]; then
    939 		check_nat_syntax $natfile
    940 		if [ $? -ne 0 ]; then
    941 			svc_mark_maintenance $svc
    942 			return 1
    943 		fi
    944 	fi
    945 
    946 	if [ -f "$ipfile" ]; then
    947 		svc_is_server $svc
    948 		if [ $? -eq 0 ]; then
    949 			update_check_ipf_rules $ipfile
    950 			if [ $? -ne 0 ]; then
    951 				svc_mark_maintenance $svc
    952 				return 1
    953 			fi
    954 		fi
    955 
    956 		prepend_new_rules $ipfile
    957 
    958 		#
    959 		# reload Global Override rules to
    960 		# maintain correct ordering.
    961 		#
    962 		remove_rules $IPFILOVRCONF
    963 		prepend_new_rules $IPFILOVRCONF
    964 	fi
    965 
    966 	[ -f "$natfile" ] && append_new_nat_rules $natfile
    967 
    968 	return 0
    969 }
    970 
    971 #
    972 # Call the service_update_rules with appropriate svc fmri.
    973 #
    974 # This is called from '/lib/svc/method/ipfilter fw_update' whenever
    975 # a service is disabled/enabled/refreshed.
    976 #
    977 service_update()
    978 {
    979 	svc=$1
    980 	ret=0
    981 
    982 	ipf_get_lock
    983 	service_update_rules $svc || ret=1
    984 
    985 	ipf_remove_lock
    986 	return $ret
    987 }
    988